@@ -13,6 +13,7 @@ const isInteractivePromise = import('is-interactive')
13
13
const chalkPromise = import ( 'chalk' )
14
14
const chalkMarkdownPromise = import ( '../utils/chalk-markdown.js' )
15
15
const settingsPromise = import ( '../utils/settings.js' )
16
+ const sdkPromise = import ( '../utils/sdk.js' )
16
17
const ipc_version = require ( '../../package.json' ) . version
17
18
18
19
try {
33
34
* @typedef {import('stream').Writable } Writable
34
35
*/
35
36
36
- const pubTokenPromise = settingsPromise . then ( ( { getSetting } ) =>
37
- getSetting ( 'apiKey' ) || 'sktsec_t_--RAN5U4ivauy4w37-6aoKyYPDt5ZbaT5JBVMqiwKo_api'
38
- ) ;
37
+ const pubTokenPromise = sdkPromise . then ( ( { getDefaultKey, FREE_API_KEY } ) => getDefaultKey ( ) || FREE_API_KEY )
38
+ const apiKeySettingsPromise = sdkPromise . then ( async ( { setupSdk } ) => {
39
+ const sdk = await setupSdk ( ) ;
40
+ const result = await sdk . getSettings ( )
41
+ if ( ! result . success ) throw new Error ( 'failed to fetch API key settings' )
42
+ return result . data ;
43
+ } )
44
+
45
+ /** @type {Promise<{ id: string, issueRules: import('../utils/settings.js').IssueRules }[]> } */
46
+ const orgSettingsPromise = settingsPromise . then ( async ( { getSetting, updateSetting } ) => {
47
+ const orgs = getSetting ( 'orgs' ) || [ ] ;
48
+ if ( ! orgs . length ) return [ ] ;
49
+
50
+ const settings = await apiKeySettingsPromise
51
+ const newOrgs = orgs . filter ( org => settings . organizations [ org . id ] )
52
+ . map ( org => {
53
+ const curOrg = settings . organizations [ org . id ] ;
54
+ return {
55
+ id : org . id ,
56
+ issueRules : curOrg . plan . tier === 'enterprise'
57
+ ? curOrg . issueRules
58
+ : org . issueRules
59
+ }
60
+ } )
61
+
62
+ updateSetting ( 'orgs' , newOrgs ) ;
63
+
64
+ return newOrgs . map ( ( { id, issueRules } ) => {
65
+ const defaultedRules = { ...issueRules } ;
66
+ for ( const rule in settings . defaultIssueRules ) {
67
+ if ( ! ( rule in defaultedRules ) || (
68
+ typeof defaultedRules [ rule ] === 'object' &&
69
+ defaultedRules [ rule ] . action === 'defer'
70
+ ) ) {
71
+ defaultedRules [ rule ] = settings . defaultIssueRules [ rule ]
72
+ }
73
+ }
74
+ return {
75
+ id,
76
+ issueRules : defaultedRules
77
+ }
78
+ } )
79
+ } )
39
80
40
81
// shadow `npm` and `npx` to mitigate subshells
41
82
require ( './link.cjs' ) ( fs . realpathSync ( path . join ( __dirname , 'bin' ) ) , 'npm' )
@@ -76,6 +117,7 @@ async function * batchScan (
76
117
}
77
118
} )
78
119
}
120
+ // TODO: migrate to SDK
79
121
const pkgDataReq = https . request (
80
122
'https://api.socket.dev/v0/scan/batch' ,
81
123
{
@@ -245,7 +287,7 @@ class SafeArborist extends Arborist {
245
287
}
246
288
} else {
247
289
if ( await packagesHaveRiskyIssues ( this . registry , diff , null , null , output ) ) {
248
- throw new Error ( 'Socket npm Unable to prompt to accept risk, need TTY to do so' )
290
+ throw new Error ( 'Socket npm unable to prompt to accept risk, need TTY to do so' )
249
291
}
250
292
return true
251
293
}
@@ -357,7 +399,7 @@ function walk (diff, needInfoOn = []) {
357
399
* @param {InstallEffect[] } pkgs
358
400
* @param {import('ora')['default'] | null } ora
359
401
* @param {Readable | null } input
360
- * @param {Writable } ora
402
+ * @param {Writable } output
361
403
* @returns {Promise<boolean> }
362
404
*/
363
405
async function packagesHaveRiskyIssues ( registry , pkgs , ora = null , input , output ) {
@@ -374,64 +416,70 @@ async function packagesHaveRiskyIssues (registry, pkgs, ora = null, input, outpu
374
416
const spinner = ora ? ora ( ) . start ( getText ( ) ) : null
375
417
const pkgDatas = [ ]
376
418
try {
419
+ const orgSettings = await orgSettingsPromise
420
+ if ( orgSettings . length > 1 ) {
421
+ throw new Error ( 'multi-organization API keys not supported' )
422
+ }
423
+ // TODO: determine org based on cwd
424
+ const rules = orgSettings . length
425
+ ? orgSettings [ 0 ] . issueRules
426
+ : ( await apiKeySettingsPromise ) . defaultIssueRules
427
+
377
428
for await ( const pkgData of batchScan ( pkgs . map ( pkg => pkg . pkgid ) ) ) {
378
429
let failures = [ ]
430
+ let warns = [ ] ;
379
431
if ( pkgData . type === 'missing' ) {
380
432
failures . push ( {
381
433
type : 'missingDependency'
382
434
} )
383
435
continue
384
436
}
385
437
for ( const issue of ( pkgData . value ?. issues ?? [ ] ) ) {
386
- if ( [
387
- 'shellScriptOverride' ,
388
- 'gitDependency' ,
389
- 'httpDependency' ,
390
- 'installScripts' ,
391
- 'malware' ,
392
- 'didYouMean' ,
393
- 'hasNativeCode' ,
394
- 'troll' ,
395
- 'telemetry' ,
396
- 'invalidPackageJSON' ,
397
- 'unresolvedRequire' ,
398
- ] . includes ( issue . type ) ) {
399
- failures . push ( issue )
438
+ if ( rules [ issue . type ] ) {
439
+ if ( typeof rules [ issue . type ] == 'boolean' || rules [ issue . type ] . action === 'error' ) {
440
+ failures . push ( issue )
441
+ } else if ( rules [ issue . type ] . action == 'warn' ) {
442
+ warns . push ( issue ) ;
443
+ }
400
444
}
401
445
}
402
446
// before we ask about problematic issues, check to see if they already existed in the old version
403
447
// if they did, be quiet
404
- if ( failures . length ) {
448
+ if ( failures . length || warns . length ) {
405
449
const pkg = pkgs . find ( pkg => pkg . pkgid === `${ pkgData . pkg } @${ pkgData . ver } ` && pkg . existing ?. startsWith ( pkgData . pkg ) )
406
450
if ( pkg ?. existing ) {
407
451
for await ( const oldPkgData of batchScan ( [ pkg . existing ] ) ) {
408
452
if ( oldPkgData . type === 'success' ) {
409
- failures = failures . filter (
410
- issue => oldPkgData . value . issues . find ( oldIssue => oldIssue . type === issue . type ) == null
411
- )
453
+ const issueFilter = issue => oldPkgData . value . issues . find ( oldIssue => oldIssue . type === issue . type ) == null
454
+ failures = failures . filter ( issueFilter )
455
+ warns = warns . filter ( issueFilter )
412
456
}
413
457
}
414
458
}
415
459
}
416
- if ( failures . length ) {
417
- failed = true
460
+ if ( failures . length || warns . length ) {
461
+ failed = failures . length > 0
418
462
spinner ?. stop ( )
419
463
translations ??= JSON . parse ( fs . readFileSync ( path . join ( __dirname , '/translations.json' ) , 'utf-8' ) )
420
464
formatter ??= new ( ( await chalkMarkdownPromise ) . ChalkOrMarkdown ) ( false )
421
465
const name = pkgData . pkg
422
466
const version = pkgData . ver
423
- output . write ( `(socket) ${ formatter . hyperlink ( `${ name } @${ version } ` , `https://socket.dev/npm/package/${ name } /overview/${ version } ` ) } contains risks:\n` )
467
+ output . write ( `(socket) ${ formatter . hyperlink ( `${ name } @${ version } ` , `https://socket.dev/npm/package/${ name } /overview/${ version } ` ) } contains ${ failures . length ? 'serious ' : '' } risks:\n` )
424
468
if ( translations ) {
425
469
for ( const failure of failures ) {
426
- const type = failure . type
427
- if ( type ) {
428
- // @ts -ignore
429
- const issueTypeTranslation = translations . issues [ type ]
430
- // TODO: emoji seems to misalign terminals sometimes
431
- // @ts -ignore
432
- const msg = ` ${ issueTypeTranslation . title } - ${ issueTypeTranslation . description } \n`
433
- output . write ( msg )
434
- }
470
+ // @ts -ignore
471
+ const issueTypeTranslation = translations . issues [ failure . type ]
472
+ // TODO: emoji seems to misalign terminals sometimes
473
+ // @ts -ignore
474
+ const msg = ` ${ formatter . bold ( issueTypeTranslation . title ) } - ${ issueTypeTranslation . description } \n`
475
+ output . write ( msg )
476
+ }
477
+ for ( const warn of warns ) {
478
+ // @ts -ignore
479
+ const issueTypeTranslation = translations . issues [ warn . type ]
480
+ // @ts -ignore
481
+ const msg = ` ${ issueTypeTranslation . title } - ${ issueTypeTranslation . description } \n`
482
+ output . write ( msg )
435
483
}
436
484
}
437
485
spinner ?. start ( )
0 commit comments