Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
"lint-staged": "lint-staged",
"precommit": "lint-staged",
"prepare": "husky",
"bs": "npm run build:dist; npm exec socket --",
"s": "npm exec socket --",
"test": "run-s check test:*",
"test:prepare": "cross-env VITEST=1 npm run build",
"test:unit": "vitest --run",
Expand Down
9 changes: 9 additions & 0 deletions src/commands/dependencies/find-dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ export async function findDependencies({
return
}

logger.log(
'Request details: Offset:',
offset,
', limit:',
limit,
', is there more data after this?',
result.data.end ? 'no' : 'yes'
)

const options = {
columns: [
{ field: 'namespace', name: colors.cyan('Namespace') },
Expand Down
67 changes: 33 additions & 34 deletions src/commands/diff-scan/cmd-diff-scan-get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ import { logger } from '@socketsecurity/registry/lib/logger'

import { getDiffScan } from './get-diff-scan'
import constants from '../../constants'
import { commonFlags, outputFlags } from '../../flags'
import { AuthError } from '../../utils/errors'
import { commonFlags } from '../../flags'
import { meowOrExit } from '../../utils/meow-with-subcommands'
import { getFlagListOutput } from '../../utils/output-formatting'
import { getDefaultToken } from '../../utils/sdk'

import type { CliCommandConfig } from '../../utils/meow-with-subcommands'

Expand All @@ -20,36 +18,47 @@ const config: CliCommandConfig = {
hidden: false,
flags: {
...commonFlags,
after: {
type: 'string',
shortFlag: 'a',
default: '',
description: 'The full scan ID of the head scan'
},
before: {
type: 'string',
shortFlag: 'b',
default: '',
description: 'The full scan ID of the base scan'
},
after: {
type: 'string',
shortFlag: 'a',
default: '',
description: 'The full scan ID of the head scan'
depth: {
type: 'number',
default: 2,
description:
'Max depth of JSON to display before truncating, use zero for no limit (without --json/--file)'
},
preview: {
json: {
type: 'boolean',
shortFlag: 'p',
default: true,
description: 'A boolean flag to persist or not the diff scan result'
shortFlag: 'j',
default: false,
description:
'Output result as json. This can be big. Use --file to store it to disk without truncation.'
},
file: {
type: 'string',
shortFlag: 'f',
default: '',
description: 'Path to a local file where the output should be saved'
},
...outputFlags
description:
'Path to a local file where the output should be saved. Use `-` to force stdout.'
}
},
help: (command, config) => `
Usage
$ ${command} <org slug> --before=<before> --after=<after>

This command displays the package changes between two scans. The full output
can be pretty large depending on the size of your repo and time range. It is
best stored to disk to be further analyzed by other tools.

Options
${getFlagListOutput(config.flags, 6)}

Expand Down Expand Up @@ -88,6 +97,7 @@ async function run(
logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
- Specify a before and after full scan ID ${!before && !after ? colors.red('(missing before and after!)') : !before ? colors.red('(missing before!)') : !after ? colors.red('(missing after!)') : colors.green('(ok)')}\n
- To get full scans IDs, you can run the command "socket scan list <your org slug>".
The args are expecting a full \`aaa0aa0a-aaaa-0000-0a0a-0000000a00a0\` ID.\n
- Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n`)
return
}
Expand All @@ -97,23 +107,12 @@ async function run(
return
}

const apiToken = getDefaultToken()
if (!apiToken) {
throw new AuthError(
'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'
)
}

await getDiffScan(
{
outputJson: Boolean(cli.flags['json']),
outputMarkdown: Boolean(cli.flags['markdown']),
before,
after,
preview: Boolean(cli.flags['preview']),
orgSlug,
file: String(cli.flags['file'] || '')
},
apiToken
)
await getDiffScan({
outputJson: Boolean(cli.flags['json']),
before,
after,
depth: Number(cli.flags['depth']),
orgSlug,
file: String(cli.flags['file'] || '')
})
}
5 changes: 5 additions & 0 deletions src/commands/diff-scan/cmd-diff-scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ const description = 'Diff scans related commands'

export const cmdDiffScan: CliSubcommand = {
description,
// Hidden because it was broken all this time (nobody could be using it)
// and we're not sure if it's useful to anyone in its current state.
// Until we do, we'll hide this to keep the help tidier.
// And later, we may simply move this under `scan`, anyways.
hidden: true,
async run(argv, importMeta, { parentName }) {
await meowWithSubcommands(
{
Expand Down
138 changes: 103 additions & 35 deletions src/commands/diff-scan/get-diff-scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,71 @@ import util from 'node:util'
import colors from 'yoctocolors-cjs'

import { logger } from '@socketsecurity/registry/lib/logger'
import { SocketSdkReturnType } from '@socketsecurity/sdk'

import constants from '../../constants'
import { handleAPIError, queryAPI } from '../../utils/api'
import { handleAPIError, handleApiCall, queryAPI } from '../../utils/api'
import { AuthError } from '../../utils/errors'
import { getDefaultToken } from '../../utils/sdk'

export async function getDiffScan(
{
export async function getDiffScan({
after,
before,
depth,
file,
orgSlug,
outputJson
}: {
after: string
before: string
depth: number
file: string
orgSlug: string
outputJson: boolean
}): Promise<void> {
const apiToken = getDefaultToken()
if (!apiToken) {
throw new AuthError(
'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'
)
}

await getDiffScanWithToken({
after,
before,
depth,
file,
orgSlug,
outputJson
}: {
outputJson: boolean
outputMarkdown: boolean
before: string
after: string
preview: boolean
orgSlug: string
file: string
},
outputJson,
apiToken
})
}
export async function getDiffScanWithToken({
after,
apiToken,
before,
depth,
file,
orgSlug,
outputJson
}: {
after: string
apiToken: string
): Promise<void> {
depth: number
before: string
file: string
orgSlug: string
outputJson: boolean
}): Promise<void> {
// Lazily access constants.spinner.
const { spinner } = constants

spinner.start('Getting diff scan...')

const response = await queryAPI(
`${orgSlug}/full-scans/diff?before=${before}&after=${after}&preview`,
`orgs/${orgSlug}/full-scans/diff?before=${encodeURIComponent(before)}&after=${encodeURIComponent(after)}`,
apiToken
)
const data = await response.json()

if (!response.ok) {
const err = await handleAPIError(response.status)
Expand All @@ -45,34 +78,69 @@ export async function getDiffScan(
return
}

const result = await handleApiCall(
(await response.json()) as Promise<
SocketSdkReturnType<'GetOrgDiffScan'>['data']
>,
'Deserializing json'
)

spinner.stop()

if (file && !outputJson) {
fs.writeFile(file, JSON.stringify(data), err => {
err
? logger.error(err)
: logger.log(`Data successfully written to ${file}`)
})
return
}
const dashboardUrl = (result as any)?.['diff_report_url']
const dashboardMessage = dashboardUrl
? `\n View this diff scan in the Socket dashboard: ${colors.cyan(dashboardUrl)}`
: ''

// When forcing json, or dumping to file, serialize to string such that it
// won't get truncated. The only way to dump the full raw JSON to stdout is
// to use `--json --file -` (the dash is a standard notation for stdout)
if (outputJson || file) {
let json
try {
json = JSON.stringify(result, null, 2)
} catch (e) {
// Most likely caused by a circular reference (or OOM)
logger.error('There was a problem converting the data to JSON')
process.exitCode = 1
return
}

if (file && file !== '-') {
logger.log(`Writing json to \`${file}\``)
fs.writeFile(file, JSON.stringify(result, null, 2), err => {
if (err) {
logger.error(`Writing to \`${file}\` failed...`)
logger.error(err)
} else {
logger.log(`Data successfully written to \`${file}\``)
}
logger.error(dashboardMessage)
})
} else {
// TODO: expose different method for writing to stderr when simply dodging stdout
logger.error(`\n Diff scan result: \n`)
logger.log(json)
logger.error(dashboardMessage)
}

if (outputJson) {
logger.log(`\n Diff scan result: \n`)
logger.log(
util.inspect(data, { showHidden: false, depth: null, colors: true })
)
logger.log(
`\n View this diff scan in the Socket dashboard: ${colors.cyan((data as any)?.['diff_report_url'])}`
)
return
}

// In this case neither the --json nor the --file flag was passed
// Dump the JSON to CLI and let NodeJS deal with truncation

logger.log('Diff scan result:')
logger.log(data)
logger.log(
`\n 📝 To display the detailed report in the terminal, use the --json flag \n`
util.inspect(result, {
showHidden: false,
depth: depth > 0 ? depth : null,
colors: true,
maxArrayLength: null
})
)
logger.log(
`\n View this diff scan in the Socket dashboard: ${colors.cyan((data as any)?.['diff_report_url'])}`
`\n 📝 To display the detailed report in the terminal, use the --json flag \n`
)
logger.log(dashboardMessage)
}
2 changes: 2 additions & 0 deletions test/dry-run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ describe('dry-run on all commands', async () => {
- Specify a before and after full scan ID \\x1b[31m(missing before and after!)\\x1b[39m

- To get full scans IDs, you can run the command "socket scan list <your org slug>".
The args are expecting a full \`aaa0aa0a-aaaa-0000-0a0a-0000000a00a0\` ID.

- Org name as the first argument \\x1b[31m(missing!)\\x1b[39m"
`)

Expand Down
Loading