@@ -3,7 +3,7 @@ import path from 'node:path'
33
44import micromatch from 'micromatch'
55
6- import { isAddressedByFile } from './scripts/build-tool.mts'
6+ import { BUILD_FILES } from './scripts/build-tool.mts'
77import { realpathOrResolved } from '../../utils/fs.mts'
88import { globWithGitIgnore } from '../../utils/glob.mts'
99import { excludePathToScanIgnores } from '../scan/exclude-paths.mts'
@@ -15,31 +15,15 @@ import type { SocketYml } from '@socketsecurity/config'
1515
1616const BUILD_TOOLS : BuildTool [ ] = [ 'gradle' , 'maven' , 'sbt' ]
1717
18- // One glob set per ecosystem's build-descriptor marker(s); `settings.gradle(.kts)`
19- // covers Kotlin-DSL roots that have no root `build.gradle`. Maven mirrors the
20- // `pomxml` pattern of the API's supported files, so every POM a scan uploads is
21- // also a build root here.
22- const MARKERS_BY_TOOL : Record < BuildTool , string [ ] > = {
23- __proto__ : null ,
24- gradle : [
25- 'build.gradle' ,
26- 'build.gradle.kts' ,
27- 'settings.gradle' ,
28- 'settings.gradle.kts' ,
29- ] ,
30- maven : [ '{*-*.,}pom{.xml,}' ] ,
31- sbt : [ 'build.sbt' ] ,
32- } as unknown as Record < BuildTool , string [ ] >
33-
34- // Case-insensitive for POMs, as a scan matches them; Gradle and sbt look for
35- // their build files by exact name.
36- const MARKER_MATCHERS_BY_TOOL = new Map (
18+ const BUILD_FILE_MATCHERS = new Map (
3719 BUILD_TOOLS . map ( tool => {
38- const options = { dot : true , nocase : tool === 'maven' }
39- const matchers = MARKERS_BY_TOOL [ tool ] . map ( marker =>
40- micromatch . matcher ( marker , options ) ,
41- )
42- return [ tool , ( basename : string ) => matchers . some ( m => m ( basename ) ) ]
20+ const files = BUILD_FILES [ tool ]
21+ return [
22+ tool ,
23+ files . byFile
24+ ? micromatch . matcher ( files . nocaseGlob , { dot : true , nocase : true } )
25+ : ( basename : string ) => files . names . includes ( basename ) ,
26+ ]
4327 } ) ,
4428)
4529
@@ -51,11 +35,11 @@ export function buildRootPath(root: BuildRoot): string {
5135 return path . join ( root . dir , root . entryFile ?? '' )
5236}
5337
54- function markerTool (
38+ function buildFileTool (
5539 tools : readonly BuildTool [ ] ,
5640 basename : string ,
5741) : BuildTool | undefined {
58- return tools . find ( tool => MARKER_MATCHERS_BY_TOOL . get ( tool ) ?.( basename ) )
42+ return tools . find ( tool => BUILD_FILE_MATCHERS . get ( tool ) ?.( basename ) )
5943}
6044
6145// A disabled ecosystem is skipped entirely by the glob scan below, so a
@@ -115,12 +99,11 @@ export function isBuildRootCovered(
11599 tool : BuildTool ,
116100 root : BuildRoot ,
117101) : boolean {
118- if ( root . entryFile ) {
119- return covered . has ( buildRootPath ( root ) )
102+ const files = BUILD_FILES [ tool ]
103+ if ( files . byFile ) {
104+ return ! ! root . entryFile && covered . has ( buildRootPath ( root ) )
120105 }
121- return MARKERS_BY_TOOL [ tool ] . some ( marker =>
122- covered . has ( path . join ( root . dir , marker ) ) ,
123- )
106+ return files . names . some ( name => covered . has ( path . join ( root . dir , name ) ) )
124107}
125108
126109function compareStrings ( a : string , b : string ) : number {
@@ -156,7 +139,7 @@ export async function findBuildToolCandidates({
156139 excludePaths ?: string [ ] | undefined
157140 // The socket.yml a scan applies to its upload, so a build file a scan would
158141 // not upload is not built either.
159- socketConfig ? : SocketYml | undefined
142+ socketConfig : SocketYml | undefined
160143 sockJson : SocketJson
161144} ) : Promise < Map < BuildTool , BuildRoot [ ] > > {
162145 const enabledTools = BUILD_TOOLS . filter (
@@ -170,35 +153,36 @@ export async function findBuildToolCandidates({
170153 const additionalIgnores = ( excludePaths ?? [ ] ) . flatMap (
171154 excludePathToScanIgnores ,
172155 )
173- // Markers are matched in the filter, after the ignores, so --exclude-paths
174- // and .gitignore stay case-sensitive as they are for a scan.
175- const hits = await globWithGitIgnore ( [ '**/*' ] , {
176- absolute : true ,
177- additionalIgnores,
178- cwd,
179- filter : hit => ! ! markerTool ( enabledTools , path . basename ( hit ) ) ,
180- socketConfig,
181- } )
156+ // Build files are matched after the walk, as a scan matches them after its
157+ // ignores, so --exclude-paths and .gitignore stay case-sensitive.
158+ const hits = await globWithGitIgnore (
159+ enabledTools . flatMap ( tool => {
160+ const files = BUILD_FILES [ tool ]
161+ return ( files . byFile ? [ files . walkGlob ] : files . names ) . map (
162+ glob => `**/${ glob } ` ,
163+ )
164+ } ) ,
165+ { absolute : true , additionalIgnores, cwd, socketConfig } ,
166+ )
182167
183168 const rootsByTool = new Map < BuildTool , Map < string , BuildRoot > > (
184169 enabledTools . map ( tool => [ tool , new Map < string , BuildRoot > ( ) ] ) ,
185170 )
186- const resolvedHits = await Promise . all (
187- hits . map ( async hit => {
188- const basename = path . basename ( hit )
189- return {
190- basename,
171+ const matched = hits . flatMap ( hit => {
172+ const basename = path . basename ( hit )
173+ const tool = buildFileTool ( enabledTools , basename )
174+ return tool ? [ { basename, hit, tool } ] : [ ]
175+ } )
176+ const roots = await Promise . all (
177+ matched . map ( async ( { basename, hit, tool } ) => ( {
178+ root : {
191179 dir : await realpathOrResolved ( path . dirname ( hit ) ) ,
192- // Set: the glob filter kept only marker hits.
193- tool : markerTool ( enabledTools , basename ) as BuildTool ,
194- }
195- } ) ,
180+ entryFile : BUILD_FILES [ tool ] . byFile ? basename : undefined ,
181+ } ,
182+ tool ,
183+ } ) ) ,
196184 )
197- for ( const { basename, dir, tool } of resolvedHits ) {
198- const root = {
199- dir,
200- entryFile : isAddressedByFile ( tool ) ? basename : undefined ,
201- }
185+ for ( const { root, tool } of roots ) {
202186 rootsByTool . get ( tool ) ?. set ( buildRootPath ( root ) , root )
203187 }
204188
0 commit comments