Skip to content

Commit e612014

Browse files
authored
Create socket scan report and related commands (#361)
* Create `socket scan report` and related commands * Drop the license stuff for now
1 parent 65dc30c commit e612014

35 files changed

+2827
-227
lines changed

eslint.config.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,17 @@ module.exports = [
161161
parser: tsParser,
162162
parserOptions: {
163163
projectService: {
164-
allowDefaultProject: ['test/*.ts'],
164+
allowDefaultProject: [
165+
'test/*.ts',
166+
// src/utils/*
167+
'src/*/*.test.ts',
168+
// src/commands/xyz/*
169+
'src/*/*/*.test.ts'
170+
],
165171
defaultProject: 'tsconfig.json',
166-
tsconfigRootDir: rootPath
172+
tsconfigRootDir: rootPath,
173+
// Need this to glob the test files in /src. Otherwise it won't work.
174+
maximumDefaultProjectFileMatchCount_THIS_WILL_SLOW_DOWN_LINTING: 1_000_000
167175
}
168176
}
169177
},
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import path from 'node:path'
2+
3+
import { describe, expect } from 'vitest'
4+
5+
import constants from '../../../dist/constants.js'
6+
import { cmdit, invokeNpm } from '../../../test/utils'
7+
8+
const { CLI } = constants
9+
10+
describe('socket organization list', async () => {
11+
// Lazily access constants.rootBinPath.
12+
const entryPath = path.join(constants.rootBinPath, `${CLI}.js`)
13+
14+
cmdit(
15+
['organization', 'list', '--help'],
16+
'should support --help',
17+
async cmd => {
18+
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
19+
expect(stdout).toMatchInlineSnapshot(
20+
`
21+
"List organizations associated with the API key used
22+
23+
Usage
24+
$ socket organization list
25+
26+
Options
27+
--dryRun Do input validation for a command and exit 0 when input is ok
28+
--help Print this help.
29+
--json Output result as json
30+
--markdown Output result as markdown"
31+
`
32+
)
33+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
34+
"
35+
_____ _ _ /---------------
36+
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
37+
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
38+
|_____|___|___|_,_|___|_|.dev | Command: \`socket organization list\`, cwd: <redacted>"
39+
`)
40+
41+
expect(code, 'help should exit with code 2').toBe(2)
42+
expect(
43+
stderr,
44+
'header should include command (without params)'
45+
).toContain('`socket organization list`')
46+
}
47+
)
48+
49+
cmdit(
50+
['organization', 'list', '--dry-run'],
51+
'should be ok with org name and id',
52+
async cmd => {
53+
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
54+
expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`)
55+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
56+
"
57+
_____ _ _ /---------------
58+
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
59+
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
60+
|_____|___|___|_,_|___|_|.dev | Command: \`socket organization list\`, cwd: <redacted>"
61+
`)
62+
63+
expect(code, 'dry-run should exit with code 0 if input ok').toBe(0)
64+
}
65+
)
66+
})
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { stripIndents } from 'common-tags'
2+
import colors from 'yoctocolors-cjs'
3+
4+
import { logger } from '@socketsecurity/registry/lib/logger'
5+
6+
import { getOrganization } from './get-organization'
7+
import constants from '../../constants'
8+
import { commonFlags, outputFlags } from '../../flags'
9+
import { meowOrExit } from '../../utils/meow-with-subcommands'
10+
import { getFlagListOutput } from '../../utils/output-formatting'
11+
12+
import type { CliCommandConfig } from '../../utils/meow-with-subcommands'
13+
14+
const { DRY_RUN_BAIL_TEXT } = constants
15+
16+
const config: CliCommandConfig = {
17+
commandName: 'list',
18+
description: 'List organizations associated with the API key used',
19+
hidden: false,
20+
flags: {
21+
...commonFlags,
22+
...outputFlags
23+
},
24+
help: (command, _config) => `
25+
Usage
26+
$ ${command}
27+
28+
Options
29+
${getFlagListOutput(config.flags, 6)}
30+
`
31+
}
32+
33+
export const cmdOrganizationList = {
34+
description: config.description,
35+
hidden: config.hidden,
36+
run
37+
}
38+
39+
async function run(
40+
argv: string[] | readonly string[],
41+
importMeta: ImportMeta,
42+
{ parentName }: { parentName: string }
43+
): Promise<void> {
44+
const cli = meowOrExit({
45+
argv,
46+
config,
47+
importMeta,
48+
parentName
49+
})
50+
51+
const json = Boolean(cli.flags['json'])
52+
const markdown = Boolean(cli.flags['markdown'])
53+
if (json && markdown) {
54+
// Use exit status of 2 to indicate incorrect usage, generally invalid
55+
// options or missing arguments.
56+
// https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
57+
process.exitCode = 2
58+
logger.fail(stripIndents`
59+
${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
60+
61+
- The json and markdown flags cannot be both set, pick one
62+
`)
63+
return
64+
}
65+
66+
if (cli.flags['dryRun']) {
67+
logger.log(DRY_RUN_BAIL_TEXT)
68+
return
69+
}
70+
71+
await getOrganization(json ? 'json' : markdown ? 'markdown' : 'text')
72+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import path from 'node:path'
2+
3+
import { describe, expect } from 'vitest'
4+
5+
import constants from '../../../dist/constants.js'
6+
import { cmdit, invokeNpm } from '../../../test/utils'
7+
8+
const { CLI } = constants
9+
10+
describe('socket organization list', async () => {
11+
// Lazily access constants.rootBinPath.
12+
const entryPath = path.join(constants.rootBinPath, `${CLI}.js`)
13+
14+
cmdit(
15+
['organization', 'policy', 'security', '--help'],
16+
'should support --help',
17+
async cmd => {
18+
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
19+
expect(stdout).toMatchInlineSnapshot(
20+
`
21+
"Retrieve the security policy of an organization.
22+
23+
Usage
24+
$ socket organization policy security <org slug>
25+
26+
Options
27+
--dryRun Do input validation for a command and exit 0 when input is ok
28+
--help Print this help.
29+
--json Output result as json
30+
--markdown Output result as markdown
31+
32+
Your API token will need the \`security-policy:read\` permission otherwise
33+
the request will fail with an authentication error.
34+
35+
Examples
36+
$ socket organization policy security mycorp
37+
$ socket organization policy security mycorp --json"
38+
`
39+
)
40+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
41+
"
42+
_____ _ _ /---------------
43+
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
44+
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
45+
|_____|___|___|_,_|___|_|.dev | Command: \`socket organization policy security\`, cwd: <redacted>"
46+
`)
47+
48+
expect(code, 'help should exit with code 2').toBe(2)
49+
expect(
50+
stderr,
51+
'header should include command (without params)'
52+
).toContain('`socket organization policy security`')
53+
}
54+
)
55+
56+
cmdit(
57+
['organization', 'policy', 'security', '--dry-run'],
58+
'should reject dry run without proper args',
59+
async cmd => {
60+
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
61+
expect(stdout).toMatchInlineSnapshot(`""`)
62+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
63+
"
64+
_____ _ _ /---------------
65+
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
66+
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
67+
|_____|___|___|_,_|___|_|.dev | Command: \`socket organization policy security\`, cwd: <redacted>
68+
69+
\\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[37mInput error\\x1b[39m\\x1b[49m: Please provide the required fields:
70+
71+
- Org name as the first argument \\x1b[31m(missing!)\\x1b[39m
72+
- The json and markdown flags cannot be both set \\x1b[32m(ok)\\x1b[39m"
73+
`)
74+
75+
expect(code, 'dry-run should exit with code 2 if input bad').toBe(2)
76+
}
77+
)
78+
79+
cmdit(
80+
['organization', 'policy', 'security', 'fakeorg', '--dry-run'],
81+
'should be ok with org name and id',
82+
async cmd => {
83+
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
84+
expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`)
85+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
86+
"
87+
_____ _ _ /---------------
88+
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
89+
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
90+
|_____|___|___|_,_|___|_|.dev | Command: \`socket organization policy security\`, cwd: <redacted>"
91+
`)
92+
93+
expect(code, 'dry-run should exit with code 0 if input ok').toBe(0)
94+
}
95+
)
96+
})
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { stripIndents } from 'common-tags'
2+
import colors from 'yoctocolors-cjs'
3+
4+
import { logger } from '@socketsecurity/registry/lib/logger'
5+
6+
import { getSecurityPolicy } from './get-security-policy'
7+
import constants from '../../constants'
8+
import { commonFlags, outputFlags } from '../../flags'
9+
import { meowOrExit } from '../../utils/meow-with-subcommands'
10+
import { getFlagListOutput } from '../../utils/output-formatting'
11+
12+
import type { CliCommandConfig } from '../../utils/meow-with-subcommands'
13+
14+
const { DRY_RUN_BAIL_TEXT } = constants
15+
16+
// TODO: secret toplevel alias `socket security policy`?
17+
const config: CliCommandConfig = {
18+
commandName: 'security',
19+
description: 'Retrieve the security policy of an organization.',
20+
hidden: true,
21+
flags: {
22+
...commonFlags,
23+
...outputFlags
24+
},
25+
help: (command, _config) => `
26+
Usage
27+
$ ${command} <org slug>
28+
29+
Options
30+
${getFlagListOutput(config.flags, 6)}
31+
32+
Your API token will need the \`security-policy:read\` permission otherwise
33+
the request will fail with an authentication error.
34+
35+
Examples
36+
$ ${command} mycorp
37+
$ ${command} mycorp --json
38+
`
39+
}
40+
41+
export const cmdOrganizationPolicyPolicy = {
42+
description: config.description,
43+
hidden: config.hidden,
44+
run
45+
}
46+
47+
async function run(
48+
argv: string[] | readonly string[],
49+
importMeta: ImportMeta,
50+
{ parentName }: { parentName: string }
51+
): Promise<void> {
52+
const cli = meowOrExit({
53+
argv,
54+
config,
55+
importMeta,
56+
parentName
57+
})
58+
59+
const json = Boolean(cli.flags['json'])
60+
const markdown = Boolean(cli.flags['markdown'])
61+
62+
const [orgSlug = ''] = cli.input
63+
64+
if (!orgSlug || (json && markdown)) {
65+
// Use exit status of 2 to indicate incorrect usage, generally invalid
66+
// options or missing arguments.
67+
// https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
68+
process.exitCode = 2
69+
logger.fail(stripIndents`
70+
${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:
71+
72+
- Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}
73+
- The json and markdown flags cannot be both set ${json && markdown ? colors.red('(pick one!)') : colors.green('(ok)')}
74+
`)
75+
return
76+
}
77+
78+
if (cli.flags['dryRun']) {
79+
logger.log(DRY_RUN_BAIL_TEXT)
80+
return
81+
}
82+
83+
await getSecurityPolicy(
84+
orgSlug,
85+
json ? 'json' : markdown ? 'markdown' : 'text'
86+
)
87+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import path from 'node:path'
2+
3+
import { describe, expect } from 'vitest'
4+
5+
import constants from '../../../dist/constants.js'
6+
import { cmdit, invokeNpm } from '../../../test/utils'
7+
8+
const { CLI } = constants
9+
10+
describe('socket organization list', async () => {
11+
// Lazily access constants.rootBinPath.
12+
const entryPath = path.join(constants.rootBinPath, `${CLI}.js`)
13+
14+
cmdit(
15+
['organization', 'policy', '--help'],
16+
'should support --help',
17+
async cmd => {
18+
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
19+
expect(stdout).toMatchInlineSnapshot(
20+
`
21+
"Organization policy details
22+
23+
Usage
24+
$ socket organization policy <command>
25+
26+
Commands
27+
28+
29+
Options
30+
--dryRun Do input validation for a command and exit 0 when input is ok
31+
--help Print this help.
32+
33+
Examples
34+
$ socket organization policy --help"
35+
`
36+
)
37+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
38+
"
39+
_____ _ _ /---------------
40+
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
41+
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
42+
|_____|___|___|_,_|___|_|.dev | Command: \`socket organization policy\`, cwd: <redacted>"
43+
`)
44+
45+
expect(code, 'help should exit with code 2').toBe(2)
46+
expect(
47+
stderr,
48+
'header should include command (without params)'
49+
).toContain('`socket organization policy`')
50+
}
51+
)
52+
})

0 commit comments

Comments
 (0)