Skip to content

Commit 393029a

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/parse-project-tags-for-code-report
2 parents b1ac008 + e30f6bc commit 393029a

File tree

19 files changed

+412
-28
lines changed

19 files changed

+412
-28
lines changed

cliv2/go.mod

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
github.com/rs/zerolog v1.34.0
1313
github.com/snyk/cli-extension-ai-bom v0.0.0-20260115091503-3d0699c466ef
1414
github.com/snyk/cli-extension-dep-graph v0.15.1
15-
github.com/snyk/cli-extension-iac v0.0.0-20250829110702-b41ac109dab0
15+
github.com/snyk/cli-extension-iac v0.0.0-20260115084339-e0c36e4dffdf
1616
github.com/snyk/cli-extension-iac-rules v0.0.0-20260115114457-a8ac3358ec57
1717
github.com/snyk/cli-extension-mcp-scan v0.0.0-20260120142932-0eea0566625a
1818
github.com/snyk/cli-extension-os-flows v0.0.0-20260115160519-84f621016a34
@@ -23,7 +23,7 @@ require (
2323
github.com/snyk/go-application-framework v0.0.0-20260126103810-195f34e2e0a2
2424
github.com/snyk/go-httpauth v0.0.0-20240307114523-1f5ea3f55c65
2525
github.com/snyk/snyk-iac-capture v0.6.5
26-
github.com/snyk/snyk-ls v0.0.0-20260123134153-c85af1965c75
26+
github.com/snyk/snyk-ls v0.0.0-20260128094006-1a31e5aa396e
2727
github.com/snyk/studio-mcp v1.3.0
2828
github.com/spf13/cobra v1.9.1
2929
github.com/spf13/pflag v1.0.10
@@ -94,7 +94,7 @@ require (
9494
github.com/clipperhouse/displaywidth v0.6.1 // indirect
9595
github.com/clipperhouse/stringish v0.1.1 // indirect
9696
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
97-
github.com/cloudflare/circl v1.6.1 // indirect
97+
github.com/cloudflare/circl v1.6.2 // indirect
9898
github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f // indirect
9999
github.com/containerd/console v1.0.3 // indirect
100100
github.com/creachadair/jrpc2 v1.3.0 // indirect
@@ -207,10 +207,10 @@ require (
207207
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
208208
github.com/sagikazarmark/locafero v0.7.0 // indirect
209209
github.com/samber/lo v1.52.0 // indirect
210-
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
210+
github.com/sergi/go-diff v1.4.0 // indirect
211211
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
212212
github.com/sirupsen/logrus v1.9.3 // indirect
213-
github.com/skeema/knownhosts v1.3.1 // indirect
213+
github.com/skeema/knownhosts v1.3.2 // indirect
214214
github.com/snyk/code-client-go v1.25.0 // indirect
215215
github.com/snyk/dep-graph/go v0.0.0-20251219134535-fcb262dc6d25 // indirect
216216
github.com/snyk/policy-engine v1.1.2 // indirect
@@ -250,7 +250,7 @@ require (
250250
go.uber.org/multierr v1.11.0 // indirect
251251
golang.org/x/crypto v0.47.0 // indirect
252252
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
253-
golang.org/x/net v0.48.0 // indirect
253+
golang.org/x/net v0.49.0 // indirect
254254
golang.org/x/oauth2 v0.34.0 // indirect
255255
golang.org/x/sync v0.19.0 // indirect
256256
golang.org/x/sys v0.40.0 // indirect

cliv2/go.sum

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfa
162162
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
163163
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
164164
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
165-
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
166-
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
165+
github.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ=
166+
github.com/cloudflare/circl v1.6.2/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
167167
github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0=
168168
github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4=
169169
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
@@ -522,21 +522,21 @@ github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppK
522522
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
523523
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
524524
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
525-
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
526-
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
525+
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
526+
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
527527
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
528528
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
529529
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
530530
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
531531
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
532-
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
533-
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
532+
github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
533+
github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
534534
github.com/snyk/cli-extension-ai-bom v0.0.0-20260115091503-3d0699c466ef h1:w+PLQGOM3wHGZ89Hhh16AL0OVFfgbZiGs8XYEYxHgks=
535535
github.com/snyk/cli-extension-ai-bom v0.0.0-20260115091503-3d0699c466ef/go.mod h1:vcRqbTJ3oEM4I6q88opeq2erDtdXW9TLKhU63iJXPM4=
536536
github.com/snyk/cli-extension-dep-graph v0.15.1 h1:SK1cMIfIzpmQhcfVnn77FZHQgcXT/3d9ZzTog1uPT3c=
537537
github.com/snyk/cli-extension-dep-graph v0.15.1/go.mod h1:Do/xNThRKSbZcIC2JCCgkBJ2X/h/YbN5i12znPEEvjY=
538-
github.com/snyk/cli-extension-iac v0.0.0-20250829110702-b41ac109dab0 h1:ecGoMisVTnz5xRnt9yXW2hlRrIyYM123yMt1NeNEo6s=
539-
github.com/snyk/cli-extension-iac v0.0.0-20250829110702-b41ac109dab0/go.mod h1:tLxyhtrRiEvbSLQ6PbCsl29ZXK6s2aunRuL6cSe/8cE=
538+
github.com/snyk/cli-extension-iac v0.0.0-20260115084339-e0c36e4dffdf h1:43ITDrUkpPC0Hy/CvPsuYQVdZVFoaol5CNK253YdS6A=
539+
github.com/snyk/cli-extension-iac v0.0.0-20260115084339-e0c36e4dffdf/go.mod h1:M5nvpEfsPTCaD7hNSzMBoeTgHaYHWct5fVPqn05szpQ=
540540
github.com/snyk/cli-extension-iac-rules v0.0.0-20260115114457-a8ac3358ec57 h1:8A/m+2Kqq7YylEZxAO/Ap6C5Fr+21WRM9BecxzOg098=
541541
github.com/snyk/cli-extension-iac-rules v0.0.0-20260115114457-a8ac3358ec57/go.mod h1:AFto63ozNmCXtKb5oTTD3Qz1jEl/HCqCVwpZCfTpSIE=
542542
github.com/snyk/cli-extension-mcp-scan v0.0.0-20260120142932-0eea0566625a h1:ElZXU6njO7McDQ7u7nAsJ5PCcwvA2+dEsadRM4P41JQ=
@@ -545,10 +545,10 @@ github.com/snyk/cli-extension-os-flows v0.0.0-20260115160519-84f621016a34 h1:Vtb
545545
github.com/snyk/cli-extension-os-flows v0.0.0-20260115160519-84f621016a34/go.mod h1:s3HX7yjdyP5PYe+ZTMyrJA5wv6hCnmfGdh7Nk5la6tY=
546546
github.com/snyk/cli-extension-sbom v0.0.0-20260109124810-cfdd074f8eeb h1:5cAi3VwdoE4d6kc6D6qSge11e/ALBMmuBatySFd8rfE=
547547
github.com/snyk/cli-extension-sbom v0.0.0-20260109124810-cfdd074f8eeb/go.mod h1:jIACVV10j4pW7LFrlYYtjn9mZm2JnXeFBM6/aTNJgvM=
548-
github.com/snyk/code-client-go v1.25.0 h1:1lcg6asMpMWwpaZLKVDdci3OrAv6rRoCsCcT8Ci/fUI=
549-
github.com/snyk/code-client-go v1.25.0/go.mod h1:YYggK3UbOHl5rg7uBhLzsKZBFNavcwFUse/EWCKWdzw=
550548
github.com/snyk/cli-extension-secrets v0.0.0-20260119125200-a69877b835d2 h1:IajF3ZDyMByXr5vLzQ9+fzLAKI0VT3I1fQBX+mtpft4=
551549
github.com/snyk/cli-extension-secrets v0.0.0-20260119125200-a69877b835d2/go.mod h1:7UzFzUBGY6hlY+fYx/IjjIjEFryxoAPaGvn985m/fcE=
550+
github.com/snyk/code-client-go v1.25.0 h1:1lcg6asMpMWwpaZLKVDdci3OrAv6rRoCsCcT8Ci/fUI=
551+
github.com/snyk/code-client-go v1.25.0/go.mod h1:YYggK3UbOHl5rg7uBhLzsKZBFNavcwFUse/EWCKWdzw=
552552
github.com/snyk/container-cli v0.0.0-20250321132345-1e2e01681dd7 h1:/2+2piwQtB9fEJCkXEOjboZjY+77lQfnvqBZ/60xNHk=
553553
github.com/snyk/container-cli v0.0.0-20250321132345-1e2e01681dd7/go.mod h1:38w+dcAQp9eG3P5t2eNS9eG0reut10AeJjLv5lJ5lpM=
554554
github.com/snyk/dep-graph/go v0.0.0-20251219134535-fcb262dc6d25 h1:dwJ4Kdp4c5aaWI+waHomarhouWF6BUYzfen0B6aqaNA=
@@ -563,8 +563,8 @@ github.com/snyk/policy-engine v1.1.2 h1:BYWigTxPjiQer4m2jYhO623KmGdmmzA3S60k9AJP
563563
github.com/snyk/policy-engine v1.1.2/go.mod h1:WW0eEb6adCEZ8CPGYsq19J6iPsvoeeTet+OWdU5Nrlw=
564564
github.com/snyk/snyk-iac-capture v0.6.5 h1:992DXCAJSN97KtUh8T5ndaWwd/6ZCal2bDkRXqM1u/E=
565565
github.com/snyk/snyk-iac-capture v0.6.5/go.mod h1:e47i55EmM0F69ZxyFHC4sCi7vyaJW6DLoaamJJCzWGk=
566-
github.com/snyk/snyk-ls v0.0.0-20260123134153-c85af1965c75 h1:vESgXzdRk44Gz3Ro1ZAodTD6DyexC4GFGGPbjkO/6CQ=
567-
github.com/snyk/snyk-ls v0.0.0-20260123134153-c85af1965c75/go.mod h1:+DFpqPnUgqNDCa1bhszp+oPh/MP4HGLQCf4lMTBwVVQ=
566+
github.com/snyk/snyk-ls v0.0.0-20260128094006-1a31e5aa396e h1:8TPdi3tQTCMbqU/4Bu3SSDPCw56WmDK6zjUFG68abAc=
567+
github.com/snyk/snyk-ls v0.0.0-20260128094006-1a31e5aa396e/go.mod h1:+DFpqPnUgqNDCa1bhszp+oPh/MP4HGLQCf4lMTBwVVQ=
568568
github.com/snyk/studio-mcp v1.3.0 h1:6eqjiaab9hILdoY4awAF2z+xj03VOB93DbLhlzYyRB4=
569569
github.com/snyk/studio-mcp v1.3.0/go.mod h1:+OuCQy/pOysingGYHNUojmpSVse/q36KODBaJlOvsNQ=
570570
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
@@ -708,8 +708,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
708708
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
709709
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
710710
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
711-
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
712-
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
711+
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
712+
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
713713
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
714714
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
715715
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

src/cli/commands/test/iac/local-execution/assert-iac-options-flag.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const keys: (keyof IaCTestFlags)[] = [
4444
// Report options
4545
'remote-repo-url',
4646
'target-name',
47+
'exclude',
4748
];
4849
const integratedKeys: (keyof IaCTestFlags)[] = ['snyk-cloud-environment'];
4950

src/cli/commands/test/iac/local-execution/directory-loader.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,49 @@
11
import * as path from 'path';
2-
import { makeFileAndDirectoryGenerator } from './file-utils';
2+
import {
3+
makeFileAndDirectoryGenerator,
4+
type ExclusionMatcher,
5+
} from './file-utils';
36
import { VALID_FILE_TYPES } from './types';
47
import { isLocalFolder } from '../../../../../lib/detect';
58

69
/**
710
* Gets all nested directories for the path that we ran a scan.
811
* @param pathToScan - the path to scan provided by the user
912
* @param maxDepth? - An optional `maxDepth` argument can be provided to limit how deep in the file tree the search will go.
13+
* @param isExcluded - A pre-compiled matcher function to skip specific paths
1014
* @returns {string[]} An array with all the non-empty nested directories in this path
1115
*/
1216
export function getAllDirectoriesForPath(
1317
pathToScan: string,
1418
maxDepth?: number,
19+
isExcluded: ExclusionMatcher = () => false,
1520
): string[] {
1621
// if it is a single file (it has an extension), we return the current path
1722
if (!isLocalFolder(pathToScan)) {
1823
return [path.resolve(pathToScan)];
1924
}
20-
return [...getAllDirectoriesForPathGenerator(pathToScan, maxDepth)];
25+
return [
26+
...getAllDirectoriesForPathGenerator(pathToScan, maxDepth, isExcluded),
27+
];
2128
}
2229

2330
/**
2431
* Gets all the directories included in this path
2532
* @param pathToScan - the path to scan provided by the user
2633
* @param maxDepth? - An optional `maxDepth` argument can be provided to limit how deep in the file tree the search will go.
34+
* @param isExcluded - A pre-compiled matcher function to skip specific paths
2735
* @returns {Generator<string>} - a generator which yields the filepaths for the path to scan
2836
*/
2937
function* getAllDirectoriesForPathGenerator(
3038
pathToScan: string,
3139
maxDepth?: number,
40+
isExcluded: ExclusionMatcher = () => false,
3241
): Generator<string> {
33-
for (const filePath of makeFileAndDirectoryGenerator(pathToScan, maxDepth)) {
42+
for (const filePath of makeFileAndDirectoryGenerator(
43+
pathToScan,
44+
maxDepth,
45+
isExcluded,
46+
)) {
3447
if (filePath.directory) yield filePath.directory;
3548
}
3649
}
@@ -39,32 +52,37 @@ function* getAllDirectoriesForPathGenerator(
3952
* Gets all file paths for the specific directory
4053
* @param pathToScan - the path to scan provided by the user
4154
* @param currentDirectory - the directory which we want to return files for
55+
* @param isExcluded - A pre-compiled matcher function to skip specific paths
4256
* @returns {string[]} An array with all the Terraform filePaths for this directory
4357
*/
4458
export function getFilesForDirectory(
4559
pathToScan: string,
4660
currentDirectory: string,
61+
isExcluded: ExclusionMatcher = () => false,
4762
): string[] {
4863
if (!isLocalFolder(pathToScan)) {
4964
if (
5065
shouldBeParsed(pathToScan) &&
51-
!isIgnoredFile(pathToScan, currentDirectory)
66+
!isIgnoredFile(pathToScan, currentDirectory) &&
67+
!isExcluded(pathToScan)
5268
) {
5369
return [pathToScan];
5470
}
5571
return [];
5672
} else {
57-
return [...getFilesForDirectoryGenerator(currentDirectory)];
73+
return [...getFilesForDirectoryGenerator(currentDirectory, isExcluded)];
5874
}
5975
}
6076

6177
/**
6278
* Iterates through the makeFileAndDirectoryGenerator function and gets all the Terraform files in the specified directory
6379
* @param pathToScan - the pathToScan to scan provided by the user
80+
* @param isExcluded - A pre-compiled matcher function to skip specific paths
6481
* @returns {Generator<string>} - a generator which holds all the filepaths
6582
*/
6683
export function* getFilesForDirectoryGenerator(
6784
pathToScan: string,
85+
isExcluded: ExclusionMatcher = () => false,
6886
): Generator<string> {
6987
for (const filePath of makeFileAndDirectoryGenerator(pathToScan)) {
7088
if (filePath.file && filePath.file.dir !== pathToScan) {
@@ -74,7 +92,8 @@ export function* getFilesForDirectoryGenerator(
7492
if (
7593
filePath.file &&
7694
shouldBeParsed(filePath.file.fileName) &&
77-
!isIgnoredFile(filePath.file.fileName, pathToScan)
95+
!isIgnoredFile(filePath.file.fileName, pathToScan) &&
96+
!isExcluded(filePath.file.fileName)
7897
) {
7998
yield filePath.file.fileName;
8099
}

src/cli/commands/test/iac/local-execution/file-utils.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
import { CUSTOM_RULES_TARBALL } from './rules/oci-pull';
1010
import { readdirSync } from 'fs';
1111
import { join } from 'path';
12+
import * as mm from 'micromatch';
13+
import { ExcludeFlagInvalidInputError } from '../../../../../lib/errors/exclude-flag-invalid-input';
1214

1315
function hashData(s: string): string {
1416
const hashedData = crypto.createHash('sha1').update(s).digest('hex');
@@ -72,16 +74,31 @@ export function computeCustomRulesBundleChecksum(): string | undefined {
7274
* makeFileAndDirectoryGenerator is a generator function that helps walking the directory and file structure of this pathToScan
7375
* @param root
7476
* @param maxDepth? - An optional `maxDepth` argument can be provided to limit how deep in the file tree the search will go.
77+
* @param isExcluded - Function to skip specific paths
7578
* @returns {Generator<object>} - a generator which yields an object with directories or paths for the path to scan
7679
*/
7780
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
78-
export function* makeFileAndDirectoryGenerator(root = '.', maxDepth?: number) {
81+
export function* makeFileAndDirectoryGenerator(
82+
root = '.',
83+
maxDepth?: number,
84+
isExcluded: ExclusionMatcher = () => false,
85+
) {
7986
function* generatorHelper(pathToScan, currentDepth) {
87+
if (isExcluded(pathToScan)) {
88+
return;
89+
}
90+
8091
{
8192
yield { directory: pathToScan };
8293
}
8394
if (maxDepth !== currentDepth) {
8495
for (const dirent of readdirSync(pathToScan, { withFileTypes: true })) {
96+
const fullPath = join(pathToScan, dirent.name);
97+
// Skip excluded directory paths
98+
if (isExcluded(fullPath)) {
99+
continue;
100+
}
101+
85102
if (
86103
dirent.isDirectory() &&
87104
fs.readdirSync(join(pathToScan, dirent.name)).length !== 0
@@ -103,3 +120,40 @@ export function* makeFileAndDirectoryGenerator(root = '.', maxDepth?: number) {
103120
}
104121
yield* generatorHelper(root, 0);
105122
}
123+
124+
export type ExclusionMatcher = (pathToCheck: string) => boolean;
125+
126+
/**
127+
* Creates a path matcher function from a comma-separated string of basenames.
128+
* @param rawExcludeFlag - Comma-separated basenames: "node_modules,temp"
129+
* @returns A function that takes a path and returns true if it should be excluded.
130+
*/
131+
export function createPathExclusionMatcher(
132+
rawExcludeFlag: string,
133+
): ExclusionMatcher {
134+
if (!rawExcludeFlag || rawExcludeFlag.trim() === '') {
135+
return () => false;
136+
}
137+
138+
const rawEntries = rawExcludeFlag.split(',');
139+
const patterns: string[] = [];
140+
141+
for (const entry of rawEntries) {
142+
const trimmed = entry.trim();
143+
144+
if (trimmed === '') {
145+
continue;
146+
}
147+
148+
// Strictly forbid paths (matches both / and \ for cross-platform safety)
149+
if (trimmed.includes('/') || trimmed.includes('\\')) {
150+
throw new ExcludeFlagInvalidInputError();
151+
}
152+
// Create global patterns to match the basename at any depth.
153+
// '**/name' matches a file or folder named 'name' anywhere.
154+
// '**/name/**' ensures if 'name' is a directory, its contents are also excluded.
155+
patterns.push(`**/${trimmed}`, `**/${trimmed}/**`);
156+
}
157+
// mm.matcher returns a pre-compiled regex function
158+
return mm.matcher(patterns);
159+
}

src/cli/commands/test/iac/local-execution/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { getErrorStringCode } from './error-utils';
3333
import { NoFilesToScanError } from './file-loader';
3434
import { Tag } from '../../../../../lib/types';
3535
import { CLI } from '@snyk/error-catalog-nodejs-public';
36+
import { createPathExclusionMatcher } from './file-utils';
3637

3738
// this method executes the local processing engine and then formats the results to adapt with the CLI output.
3839
// this flow is the default GA flow for IAC scanning.
@@ -49,12 +50,14 @@ export async function test(
4950
const attributes = parseAttributes(options);
5051

5152
const policy = await findAndLoadPolicy(pathToScan, 'iac', options);
53+
const isPathExcluded = createPathExclusionMatcher(options.exclude || '');
5254

5355
let allParsedFiles: IacFileParsed[] = [],
5456
allFailedFiles: IacFileParseFailure[] = [];
5557
const allDirectories = getAllDirectoriesForPath(
5658
pathToScan,
5759
options.detectionDepth,
60+
isPathExcluded,
5861
);
5962

6063
// we load and parse files directory by directory
@@ -63,6 +66,7 @@ export async function test(
6366
const filePathsInDirectory = getFilesForDirectory(
6467
pathToScan,
6568
currentDirectory,
69+
isPathExcluded,
6670
);
6771
if (
6872
currentDirectory === pathToScan &&

src/cli/commands/test/iac/local-execution/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ export type IaCTestFlags = Pick<
200200
// Report options
201201
| 'remote-repo-url'
202202
| 'target-name'
203+
| 'exclude'
203204
> & {
204205
// Supported flags not yet covered by Options or TestOptions
205206
'json-file-output'?: string;

src/cli/commands/test/iac/v2/assert-iac-options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const keys: (keyof IaCTestFlags)[] = [
3838
'remote-repo-url',
3939
'target-name',
4040
'target-reference',
41+
'exclude',
4142
// Hidden flag to use the output file from the IaC CLI extension
4243
'iac-test-output-file',
4344
];

src/cli/commands/test/iac/v2/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ async function prepareTestConfig(
107107
const insecure = options.insecure;
108108
const customRules = options['custom-rules'];
109109
const experimental = options.experimental;
110+
const exclude = getFlag(options, 'exclude') as string | undefined;
110111

111112
return {
112113
paths: relativePaths,
@@ -129,5 +130,6 @@ async function prepareTestConfig(
129130
customRules,
130131
experimental,
131132
iacNewEngine,
133+
exclude,
132134
};
133135
}

src/cli/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ function validateUnsupportedOptionCombinations(
433433
}
434434

435435
if (options.exclude) {
436-
if (!(options.allProjects || options.yarnWorkspaces)) {
436+
if (!options.iac && !(options.allProjects || options.yarnWorkspaces)) {
437437
throw new MissingOptionError('--exclude', [
438438
'--yarn-workspaces',
439439
'--all-projects',

0 commit comments

Comments
 (0)