11import _ from 'lodash' ;
2+ < << << << HEAD
23import { VULNERABILITY_PATTERNS as patterns } from './patterns/index.js' ;
34import { getScannerForFile , PACKAGE_FILE_PATTERNS } from './scanners/index.js' ;
45import { repoCache } from './cache.js' ;
@@ -18,6 +19,11 @@ const recommendations = {
1819 dynamicRequire : 'Use static imports and proper dependency management' ,
1920 unsafeDeserialization : 'Validate and sanitize data before deserialization'
2021} ;
22+ = === ===
23+ import { corePatterns , enhancedPatterns , recommendations } from './patterns' ;
24+ import { getScannerForFile , PACKAGE_FILE_PATTERNS } from './scanners' ;
25+ import { repoCache } from './cache' ;
26+ > >>> >>> 81342 ac2800f95e58c70d0204d4e1fd9b4d976ed
2127
2228class VulnerabilityScanner {
2329 constructor ( config = { } ) {
@@ -28,8 +34,16 @@ class VulnerabilityScanner {
2834 ...config
2935 } ;
3036
37+ < << << << HEAD
3138 // Use all patterns
3239 this . vulnerabilityPatterns = patterns ;
40+ = === ===
41+ // Combine patterns based on configuration
42+ this . vulnerabilityPatterns = {
43+ ...corePatterns ,
44+ ...( this . config . enableNewPatterns ? enhancedPatterns : { } )
45+ } ;
46+ > >>> >>> 81342 ac2800f95e58c70d0204d4e1fd9b4d976ed
3347
3448 // Track rate limit information
3549 this . rateLimitInfo = null ;
@@ -42,6 +56,7 @@ class VulnerabilityScanner {
4256
4357 if ( token ) {
4458 headers . Authorization = `token ${ token } ` ;
59+ < << << << HEAD
4560 }
4661
4762 const response = await fetch ( url , { headers } ) ;
@@ -140,8 +155,31 @@ class VulnerabilityScanner {
140155 } catch ( error ) {
141156 console . error ( `Error fetching directory ${ item . path } :` , error . message ) ;
142157 }
158+ = === ===
159+ }
160+
161+ const response = await fetch ( url , { headers } ) ;
162+
163+ // Extract rate limit information from headers
164+ this . rateLimitInfo = {
165+ limit : parseInt ( response . headers . get ( 'x-ratelimit-limit' ) || '60' ) ,
166+ remaining : parseInt ( response . headers . get ( 'x-ratelimit-remaining' ) || '0' ) ,
167+ reset : parseInt ( response . headers . get ( 'x-ratelimit-reset' ) || '0' )
168+ } ;
169+
170+ if ( ! response . ok ) {
171+ if ( response . status === 403 && this . rateLimitInfo . remaining === 0 ) {
172+ const resetDate = new Date ( this . rateLimitInfo . reset * 1000 ) ;
173+ throw new Error ( `GitHub API rate limit exceeded. Resets at ${ resetDate . toLocaleString ( ) } ` ) ;
174+ }
175+ if ( response . status === 404 ) {
176+ throw new Error ( 'Repository or file not found. Check the URL and ensure you have access.' ) ;
177+ > >>> >>> 81342 ac2800f95e58c70d0204d4e1fd9b4d976ed
143178 }
179+ throw new Error ( `GitHub API error: ${ response . statusText } ` ) ;
144180 }
181+
182+ return response ;
145183 }
146184
147185 async scanFile ( fileContent , filePath ) {
@@ -202,6 +240,83 @@ class VulnerabilityScanner {
202240 } , [ ] ) ;
203241 }
204242
243+ async fetchRepositoryFiles ( url , token = null ) {
244+ const githubRegex = / g i t h u b \. c o m \/ ( [ ^ / ] + ) \/ ( [ ^ / ] + ) (?: \/ t r e e \/ [ ^ / ] + ) ? \/ ? ( .* ) / ;
245+ const match = url . match ( githubRegex ) ;
246+ if ( ! match ) {
247+ throw new Error ( 'Invalid GitHub URL format' ) ;
248+ }
249+
250+ // Check cache first
251+ const cachedData = repoCache . get ( url , token ) ;
252+ if ( cachedData ) {
253+ return {
254+ files : cachedData . files ,
255+ rateLimit : this . rateLimitInfo ,
256+ fromCache : true
257+ } ;
258+ }
259+
260+ const [ , owner , repo , path ] = match ;
261+ const apiUrl = `https://api.github.com/repos/${ owner } /${ repo } /contents/${ path } ` ;
262+
263+ try {
264+ const response = await this . fetchWithAuth ( apiUrl , token ) ;
265+ const data = await response . json ( ) ;
266+ const files = [ ] ;
267+
268+ await this . fetchFiles ( files , data , owner , repo , token ) ;
269+
270+ // Cache the results
271+ repoCache . set ( url , token , { files } ) ;
272+
273+ return {
274+ files,
275+ rateLimit : this . rateLimitInfo ,
276+ fromCache : false
277+ } ;
278+ } catch ( error ) {
279+ throw new Error ( error . message ) ;
280+ }
281+ }
282+
283+ async fetchFiles ( files , items , owner , repo , token ) {
284+ for ( const item of items ) {
285+ // Check if we're running low on rate limit
286+ if ( this . rateLimitInfo && this . rateLimitInfo . remaining < 5 ) {
287+ const resetDate = new Date ( this . rateLimitInfo . reset * 1000 ) ;
288+ console . warn ( `Warning: Rate limit running low. Resets at ${ resetDate . toLocaleString ( ) } ` ) ;
289+ }
290+
291+ const supportedExtensions = Object . keys ( PACKAGE_FILE_PATTERNS )
292+ . concat ( [ '.json' , '.py' , '.css' , '.html' , '.config' , '.conf' , '.sh' , '.patch' ,
293+ '.yaml' , '.yml' , 'Dockerfile' , '.ini' , '.js' , '.jsx' , '.ts' , '.tsx' ] ) ;
294+
295+ if ( item . type === 'file' &&
296+ ( supportedExtensions . some ( ext => item . name . toLowerCase ( ) . endsWith ( ext . toLowerCase ( ) ) ) ||
297+ supportedExtensions . some ( ext => item . name . toLowerCase ( ) === ext . toLowerCase ( ) ) ) ) {
298+ try {
299+ const response = await this . fetchWithAuth ( item . download_url , token ) ;
300+ const content = await response . text ( ) ;
301+ files . push ( {
302+ path : item . path ,
303+ content : content
304+ } ) ;
305+ } catch ( error ) {
306+ console . error ( `Error fetching ${ item . path } :` , error . message ) ;
307+ }
308+ } else if ( item . type === 'dir' ) {
309+ try {
310+ const response = await this . fetchWithAuth ( item . _links . self , token ) ;
311+ const data = await response . json ( ) ;
312+ await this . fetchFiles ( files , data , owner , repo , token ) ;
313+ } catch ( error ) {
314+ console . error ( `Error fetching directory ${ item . path } :` , error . message ) ;
315+ }
316+ }
317+ }
318+ }
319+
205320 generateReport ( findings ) {
206321 // Separate findings by scanner type
207322 const generalFindings = findings . filter ( f => f . scannerType === 'general' ) ;
0 commit comments