Repository: Checkmarx/kics (language: python)

Issue:
Crash upon excluded dangling symlink
Found a bug? You're welcome to [![GitHub Discussions](https://img.shields.io/badge/chat-discussions-blue.svg?style=flat-square)](https://github.com/Checkmarx/kics/discussions)

- **Please make sure to:**
  - Describe in details what the problem is
  - Attach a log file with relevant data preferably in DEBUG level (`--log-level=DEBUG`)
  - Attach the scanned sample files, anonymize the data if the original file cannot be provided
  - When attaching files to the issue make sure they are properly formatted

kics 1.7.1 from MegaLinter 7.1.0

### Expected Behavior

(Which results are expected from KICS?)

No crash.

### Actual Behavior

(Formatted logs and samples helps us to better understand the issue)

```console
$ docker run --rm --volume "$(pwd):/tmp/lint:rw" 'docker.io/oxsecurity/megalinter:v7'
7:16AM INF Excluded file /tmp/lint/.venv/bin/pyreverse from analyzer
7:16AM INF Excluded file /tmp/lint/.venv/bin/pytest from analyzer
7:16AM ERR failed to get file info: stat /tmp/lint/.venv/bin/python: no such file or directory
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x2218207]

goroutine 1 [running]:
github.com/Checkmarx/kics/pkg/analyzer.isConfigFile({0xc000ce3700, 0x1a}, {0x42cbed0, 0x1, 0x0?})
        /app/pkg/analyzer/analyzer.go:602 +0x107
github.com/Checkmarx/kics/pkg/analyzer.Analyze.func1({0xc000ce3700, 0x1a}, {0xc0007f81a0?, 0x0?}, {0x0?, 0x0?})
        /app/pkg/analyzer/analyzer.go:285 +0x257
path/filepath.walk({0xc000ce3700, 0x1a}, {0x2e32860, 0xc0007f81a0}, 0xc000e55568)
        /usr/local/go/src/path/filepath/path.go:480 +0x116
path/filepath.walk({0xc0000545e8, 0x13}, {0x2e32860, 0xc00084d2b0}, 0xc000e55568)
        /usr/local/go/src/path/filepath/path.go:504 +0x26f
path/filepath.walk({0xc0008aa140, 0xf}, {0x2e32860, 0xc00084cf70}, 0xc000e55568)
        /usr/local/go/src/path/filepath/path.go:504 +0x26f
path/filepath.walk({0xc000c72290, 0x9}, {0x2e32860, 0xc000b916c0}, 0xc000e55568)
        /usr/local/go/src/path/filepath/path.go:504 +0x26f
path/filepath.Walk({0xc000c72290, 0x9}, 0xc001011568)
        /usr/local/go/src/path/filepath/path.go:571 +0x6c
github.com/Checkmarx/kics/pkg/analyzer.Analyze(0xc000e558a8)
        /app/pkg/analyzer/analyzer.go:273 +0x2e5
github.com/Checkmarx/kics/pkg/scan.analyzePaths(0xc000e558a8)
        /app/pkg/scan/utils.go:185 +0x97
github.com/Checkmarx/kics/pkg/scan.(*Client).prepareAndAnalyzePaths(0xc0006801e0, {0x2e2b140, 0xc0001a8000})
        /app/pkg/scan/utils.go:62 +0x46f
github.com/Checkmarx/kics/pkg/scan.(*Client).initScan(0xc0006801e0, {0x2e2b140, 0xc0001a8000})
        /app/pkg/scan/scan.go:47 +0x10b
github.com/Checkmarx/kics/pkg/scan.(*Client).executeScan(0xc0006801e0, {0x2e2b140, 0xc0001a8000})
        /app/pkg/scan/scan.go:125 +0x45
github.com/Checkmarx/kics/pkg/scan.(*Client).PerformScan(0xc0006801e0, {0x2e2b140, 0xc0001a8000})
        /app/pkg/scan/client.go:86 +0x6c
github.com/Checkmarx/kics/internal/console.executeScan(0x27e0101?)
        /app/internal/console/scan.go:163 +0x111
github.com/Checkmarx/kics/internal/console.run(0x0?)
        /app/internal/console/scan.go:98 +0x1fe
github.com/Checkmarx/kics/internal/console.NewScanCmd.func2(0xc0001f4900?, {0x27cb6ef?, 0x5?, 0x5?})
        /app/internal/console/scan.go:43 +0x19
github.com/spf13/cobra.(*Command).execute(0xc0001f4900, {0xc0007885f0, 0x5, 0x5})
        /go/pkg/mod/github.com/spf13/cobra@v1.6.1/command.go:916 +0x862
github.com/spf13/cobra.(*Command).ExecuteC(0xc0001f4000)
        /go/pkg/mod/github.com/spf13/cobra@v1.6.1/command.go:1044 +0x3bd
github.com/spf13/cobra.(*Command).Execute(...)
        /go/pkg/mod/github.com/spf13/cobra@v1.6.1/command.go:968
github.com/spf13/cobra.(*Command).ExecuteContext(...)
        /go/pkg/mod/github.com/spf13/cobra@v1.6.1/command.go:961
github.com/Checkmarx/kics/internal/console.Execute()
        /app/internal/console/kics.go:90 +0xae
main.main()
        /app/cmd/console/main.go:12 +0x19
$ symlinks -rv .venv/
relative: /Users/REDACTED/.venv/bin/python3 -> python
absolute: /Users/REDACTED/.venv/bin/python -> /usr/local/opt/python@3.11/bin/python3.11
relative: /Users/REDACTED/.venv/bin/python3.11 -> python
```
However, the symlink is probably dangling since only the current directory is mounted into a container, so `/usr/local/opt/python@3.11/bin/python3.11` does not necessarily exist.

### Steps to Reproduce the Problem

See actual behavior. The exact flags that are provided to kics cannot be determined, since MegaLinter is in between. I don't expect that to matter in this case, though. The config file content doesn't matter. Workaround is to add an emptydir volume to the docker invocation: `--volume /tmp/lint/.venv`.

### Specifications
(N/A if not applicable)

-   Version: 1.7.1
-   Platform: MacOS with Linux VM (Rancher Desktop 1.9.0)
-   Subsystem: 



Discussion / hints:

Fix patch:
diff --git a/pkg/analyzer/analyzer.go b/pkg/analyzer/analyzer.go
index 278bed844c2..e5fa23bd630 100644
--- a/pkg/analyzer/analyzer.go
+++ b/pkg/analyzer/analyzer.go
@@ -277,7 +277,7 @@ func Analyze(a *Analyzer) (model.AnalyzedPaths, error) {
 
 			ext := utils.GetExtension(path)
 
-			if hasGitIgnoreFile && gitIgnore.MatchesPath(path) {
+			if (hasGitIgnoreFile && gitIgnore.MatchesPath(path)) || isDeadSymlink(path) {
 				ignoreFiles = append(ignoreFiles, path)
 				a.Exc = append(a.Exc, path)
 			}
@@ -591,6 +591,11 @@ func isExcludedFile(path string, exc []string) bool {
 	return false
 }
 
+func isDeadSymlink(path string) bool {
+	fileInfo, _ := os.Stat(path)
+	return fileInfo == nil
+}
+
 func isConfigFile(path string, exc []string) bool {
 	for i := range exc {
 		exclude, err := provider.GetExcludePaths(exc[i])
@@ -599,7 +604,7 @@ func isConfigFile(path string, exc []string) bool {
 		}
 		for j := range exclude {
 			fileInfo, _ := os.Stat(path)
-			if fileInfo.IsDir() {
+			if fileInfo != nil && fileInfo.IsDir() {
 				continue
 			}
 


Test patch:
diff --git a/pkg/analyzer/analyzer_test.go b/pkg/analyzer/analyzer_test.go
index d1cd41b6a2e..dfdfb7e1060 100644
--- a/pkg/analyzer/analyzer_test.go
+++ b/pkg/analyzer/analyzer_test.go
@@ -28,7 +28,8 @@ func TestAnalyzer_Analyze(t *testing.T) {
 			wantTypes: []string{"dockerfile", "googledeploymentmanager", "cloudformation", "crossplane", "knative", "kubernetes", "openapi", "terraform", "ansible", "azureresourcemanager", "dockercompose", "pulumi", "serverlessfw"},
 			wantExclude: []string{
 				filepath.FromSlash("../../test/fixtures/analyzer_test/not_openapi.json"),
-				filepath.FromSlash("../../test/fixtures/analyzer_test/pnpm-lock.yaml")},
+				filepath.FromSlash("../../test/fixtures/analyzer_test/pnpm-lock.yaml"),
+				filepath.FromSlash("../../test/fixtures/analyzer_test/dead_symlink")},
 			typesFromFlag:        []string{""},
 			excludeTypesFromFlag: []string{""},
 			wantLOC:              563,
@@ -215,7 +216,7 @@ func TestAnalyzer_Analyze(t *testing.T) {
 				filepath.FromSlash("../../test/fixtures/analyzer_test/openAPI.json"),
 				filepath.FromSlash("../../test/fixtures/analyzer_test/openAPI_test/openAPI.json"),
 				filepath.FromSlash("../../test/fixtures/analyzer_test/pnpm-lock.yaml"),
-			},
+				filepath.FromSlash("../../test/fixtures/analyzer_test/dead_symlink")},
 			typesFromFlag:        []string{"ansible", "pulumi"},
 			excludeTypesFromFlag: []string{""},
 			wantLOC:              261,
@@ -232,7 +233,7 @@ func TestAnalyzer_Analyze(t *testing.T) {
 				filepath.FromSlash("../../test/fixtures/analyzer_test/not_openapi.json"),
 				filepath.FromSlash("../../test/fixtures/analyzer_test/undetected.yaml"),
 				filepath.FromSlash("../../test/fixtures/analyzer_test/pnpm-lock.yaml"),
-			},
+				filepath.FromSlash("../../test/fixtures/analyzer_test/dead_symlink")},
 			typesFromFlag:        []string{""},
 			excludeTypesFromFlag: []string{"ansible", "pulumi"},
 			wantLOC:              534,
@@ -247,6 +248,22 @@ func TestAnalyzer_Analyze(t *testing.T) {
 			wantExclude: []string{
 				filepath.FromSlash("../../test/fixtures/analyzer_test/pnpm-lock.yaml"),
 				filepath.FromSlash("../../test/fixtures/analyzer_test/not_openapi.json"),
+				filepath.FromSlash("../../test/fixtures/analyzer_test/dead_symlink")},
+			typesFromFlag:        []string{""},
+			excludeTypesFromFlag: []string{""},
+			wantLOC:              563,
+			wantErr:              false,
+			gitIgnoreFileName:    "",
+			excludeGitIgnore:     false,
+		},
+		{
+			name:      "analyze_test_ignore_dead_symlink",
+			paths:     []string{filepath.FromSlash("../../test/fixtures/analyzer_test")},
+			wantTypes: []string{"ansible", "azureresourcemanager", "cloudformation", "crossplane", "dockercompose", "dockerfile", "googledeploymentmanager", "knative", "kubernetes", "openapi", "pulumi", "serverlessfw", "terraform"},
+			wantExclude: []string{
+				filepath.FromSlash("../../test/fixtures/analyzer_test/pnpm-lock.yaml"),
+				filepath.FromSlash("../../test/fixtures/analyzer_test/not_openapi.json"),
+				filepath.FromSlash("../../test/fixtures/analyzer_test/dead_symlink"),
 			},
 			typesFromFlag:        []string{""},
 			excludeTypesFromFlag: []string{""},
diff --git a/test/fixtures/analyzer_test/dead_symlink b/test/fixtures/analyzer_test/dead_symlink
new file mode 120000
index 00000000000..8b137891791
--- /dev/null
+++ b/test/fixtures/analyzer_test/dead_symlink
@@ -0,0 +1,1 @@
+