-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #118 from SocketDev/cg/addFullScansFeature
Add full scans feature
- Loading branch information
Showing
9 changed files
with
840 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
/* eslint-disable no-console */ | ||
|
||
import { stdin as inputText, stdout as output } from 'node:process' | ||
import * as readline from 'node:readline/promises' | ||
|
||
import chalk from 'chalk' | ||
import meow from 'meow' | ||
import open from 'open' | ||
import ora from 'ora' | ||
import { ErrorWithCause } from 'pony-cause' | ||
|
||
import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js' | ||
import { prepareFlags } from '../../utils/flags.js' | ||
import { printFlagList } from '../../utils/formatting.js' | ||
import { createDebugLogger } from '../../utils/misc.js' | ||
import { getPackageFilesFullScans } from '../../utils/path-resolve.js' | ||
import { getDefaultKey, setupSdk } from '../../utils/sdk.js' | ||
|
||
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */ | ||
export const create = { | ||
description: 'Create a scan', | ||
async run (argv, importMeta, { parentName }) { | ||
const name = parentName + ' create' | ||
|
||
const input = await setupCommand(name, create.description, argv, importMeta) | ||
if (input) { | ||
const spinnerText = 'Creating a scan... \n' | ||
const spinner = ora(spinnerText).start() | ||
|
||
await createFullScan(input, spinner) | ||
} | ||
} | ||
} | ||
|
||
const createFullScanFlags = prepareFlags({ | ||
repo: { | ||
type: 'string', | ||
shortFlag: 'r', | ||
default: '', | ||
description: 'Repository name', | ||
}, | ||
branch: { | ||
type: 'string', | ||
shortFlag: 'b', | ||
default: '', | ||
description: 'Branch name', | ||
}, | ||
commitMessage: { | ||
type: 'string', | ||
shortFlag: 'm', | ||
default: '', | ||
description: 'Commit message', | ||
}, | ||
commitHash: { | ||
type: 'string', | ||
shortFlag: 'ch', | ||
default: '', | ||
description: 'Commit hash', | ||
}, | ||
pullRequest: { | ||
type: 'number', | ||
shortFlag: 'pr', | ||
description: 'Commit hash', | ||
}, | ||
committers: { | ||
type: 'string', | ||
shortFlag: 'c', | ||
default: '', | ||
description: 'Committers', | ||
}, | ||
defaultBranch: { | ||
type: 'boolean', | ||
shortFlag: 'db', | ||
default: false, | ||
description: 'Make default branch', | ||
}, | ||
pendingHead: { | ||
type: 'boolean', | ||
shortFlag: 'ph', | ||
default: false, | ||
description: 'Set as pending head', | ||
}, | ||
tmp: { | ||
type: 'boolean', | ||
shortFlag: 't', | ||
default: false, | ||
description: 'Set the visibility (true/false) of the scan in your dashboard', | ||
} | ||
}) | ||
|
||
// Internal functions | ||
|
||
/** | ||
* @typedef CommandContext | ||
* @property {string} orgSlug | ||
* @property {string} repoName | ||
* @property {string} branchName | ||
* @property {string} committers | ||
* @property {string} commitMessage | ||
* @property {string} commitHash | ||
* @property {number | undefined} pullRequest | ||
* @property {boolean} defaultBranch | ||
* @property {boolean} pendingHead | ||
* @property {boolean} tmp | ||
* @property {string[]} packagePaths | ||
*/ | ||
|
||
/** | ||
* @param {string} name | ||
* @param {string} description | ||
* @param {readonly string[]} argv | ||
* @param {ImportMeta} importMeta | ||
* @returns {Promise<void|CommandContext>} | ||
*/ | ||
async function setupCommand (name, description, argv, importMeta) { | ||
const flags = { | ||
...createFullScanFlags | ||
} | ||
|
||
const cli = meow(` | ||
Usage | ||
$ ${name} [...options] | ||
Options | ||
${printFlagList(flags, 6)} | ||
Examples | ||
$ ${name} --org=FakeOrg --repo=test-repo --branch=main ./package.json | ||
`, { | ||
argv, | ||
description, | ||
importMeta, | ||
flags | ||
}) | ||
|
||
const { | ||
repo: repoName, | ||
branch: branchName, | ||
commitMessage, | ||
defaultBranch, | ||
pendingHead, | ||
tmp, | ||
committers, | ||
commitHash, | ||
pullRequest | ||
} = cli.flags | ||
|
||
if (!cli.input[0]) { | ||
cli.showHelp() | ||
return | ||
} | ||
|
||
const [orgSlug = ''] = cli.input | ||
|
||
const cwd = process.cwd() | ||
const socketSdk = await setupSdk() | ||
const supportedFiles = await socketSdk.getReportSupportedFiles() | ||
.then(res => { | ||
if (!res.success) handleUnsuccessfulApiResponse('getReportSupportedFiles', res, ora()) | ||
return res.data | ||
}).catch( | ||
/** @type {(cause: Error) => never} */ | ||
(cause) => { | ||
throw new ErrorWithCause('Failed getting supported files for report', { cause }) | ||
}) | ||
const debugLog = createDebugLogger(false) | ||
const packagePaths = await getPackageFilesFullScans(cwd, cli.input, supportedFiles, debugLog) | ||
|
||
if (!repoName || !branchName || !packagePaths.length) { | ||
console.error(`${chalk.bgRed('Input error')}: Please provide the required fields: \n | ||
- Repository name using --repo, \n | ||
- Branch name using --branch \n | ||
- At least one file path (e.g. ./package.json) .\n`) | ||
cli.showHelp() | ||
return | ||
} | ||
|
||
return { | ||
orgSlug, | ||
repoName, | ||
branchName, | ||
commitMessage, | ||
defaultBranch, | ||
pendingHead, | ||
tmp, | ||
packagePaths, | ||
commitHash, | ||
committers, | ||
pullRequest | ||
} | ||
} | ||
|
||
/** | ||
* @typedef FullScanData | ||
* @property {import('@socketsecurity/sdk').SocketSdkReturnType<'CreateOrgFullScan'>["data"]} data | ||
*/ | ||
|
||
/** | ||
* @param {CommandContext} input | ||
* @param {import('ora').Ora} spinner | ||
* @returns {Promise<void|FullScanData>} | ||
*/ | ||
async function createFullScan (input, spinner) { | ||
const socketSdk = await setupSdk(getDefaultKey()) | ||
const { | ||
orgSlug, | ||
repoName, | ||
branchName, | ||
commitMessage, | ||
defaultBranch, | ||
pendingHead, | ||
tmp, | ||
packagePaths | ||
} = input | ||
|
||
const result = await handleApiCall(socketSdk.createOrgFullScan(orgSlug, { | ||
repo: repoName, | ||
branch: branchName, | ||
commit_message: commitMessage, | ||
make_default_branch: defaultBranch, | ||
set_as_pending_head: pendingHead, | ||
tmp | ||
}, packagePaths), 'Creating scan') | ||
|
||
if (!result.success) { | ||
return handleUnsuccessfulApiResponse('CreateOrgFullScan', result, spinner) | ||
} | ||
spinner.stop() | ||
|
||
console.log('\n✅ Scan created successfully \n') | ||
const link = chalk.hex('#00FFFF').underline(`${result.data.html_report_url}`) | ||
console.log(`Available at: ${link} \n`) | ||
|
||
const rl = readline.createInterface({ input: inputText, output }) | ||
|
||
const answer = await rl.question('Would you like to open it in your browser? (y/n) ') | ||
|
||
answer.toLowerCase() === 'y' && open(`${result.data.html_report_url}`) | ||
|
||
rl.close() | ||
|
||
return { | ||
data: result.data | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
/* eslint-disable no-console */ | ||
|
||
import chalk from 'chalk' | ||
import meow from 'meow' | ||
import ora from 'ora' | ||
|
||
import { outputFlags } from '../../flags/index.js' | ||
import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js' | ||
import { printFlagList } from '../../utils/formatting.js' | ||
import { getDefaultKey, setupSdk } from '../../utils/sdk.js' | ||
|
||
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */ | ||
export const del = { | ||
description: 'Delete a scan', | ||
async run (argv, importMeta, { parentName }) { | ||
const name = parentName + ' del' | ||
|
||
const input = setupCommand(name, del.description, argv, importMeta) | ||
if (input) { | ||
const spinnerText = 'Deleting scan...' | ||
const spinner = ora(spinnerText).start() | ||
await deleteOrgFullScan(input.orgSlug, input.fullScanId, spinner) | ||
} | ||
} | ||
} | ||
|
||
// Internal functions | ||
|
||
/** | ||
* @typedef CommandContext | ||
* @property {boolean} outputJson | ||
* @property {boolean} outputMarkdown | ||
* @property {string} orgSlug | ||
* @property {string} fullScanId | ||
*/ | ||
|
||
/** | ||
* @param {string} name | ||
* @param {string} description | ||
* @param {readonly string[]} argv | ||
* @param {ImportMeta} importMeta | ||
* @returns {void|CommandContext} | ||
*/ | ||
function setupCommand (name, description, argv, importMeta) { | ||
const flags = { | ||
...outputFlags | ||
} | ||
|
||
const cli = meow(` | ||
Usage | ||
$ ${name} <org slug> <scan ID> | ||
Options | ||
${printFlagList(flags, 6)} | ||
Examples | ||
$ ${name} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0 | ||
`, { | ||
argv, | ||
description, | ||
importMeta, | ||
flags | ||
}) | ||
|
||
const { | ||
json: outputJson, | ||
markdown: outputMarkdown, | ||
} = cli.flags | ||
|
||
if (cli.input.length < 2) { | ||
console.error(`${chalk.bgRed('Input error')}: Please specify an organization slug and a scan ID.\n`) | ||
cli.showHelp() | ||
return | ||
} | ||
|
||
const [orgSlug = '', fullScanId = ''] = cli.input | ||
|
||
return { | ||
outputJson, | ||
outputMarkdown, | ||
orgSlug, | ||
fullScanId | ||
} | ||
} | ||
|
||
/** | ||
* @typedef FullScanData | ||
* @property {import('@socketsecurity/sdk').SocketSdkReturnType<'deleteOrgFullScan'>["data"]} data | ||
*/ | ||
|
||
/** | ||
* @param {string} orgSlug | ||
* @param {string} fullScanId | ||
* @param {import('ora').Ora} spinner | ||
* @returns {Promise<void|FullScanData>} | ||
*/ | ||
async function deleteOrgFullScan (orgSlug, fullScanId, spinner) { | ||
const socketSdk = await setupSdk(getDefaultKey()) | ||
const result = await handleApiCall(socketSdk.deleteOrgFullScan(orgSlug, fullScanId), 'Deleting scan') | ||
|
||
if (!result.success) { | ||
return handleUnsuccessfulApiResponse('deleteOrgFullScan', result, spinner) | ||
} | ||
|
||
console.log('\n ✅ Scan deleted successfully. \n') | ||
|
||
spinner.stop() | ||
|
||
return { | ||
data: result.data | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { create } from './create.js' | ||
import { del } from './delete.js' | ||
import { list } from './list.js' | ||
import { metadata } from './metadata.js' | ||
import { stream } from './stream.js' | ||
import { meowWithSubcommands } from '../../utils/meow-with-subcommands.js' | ||
|
||
const description = 'Scans related commands' | ||
|
||
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */ | ||
export const scan = { | ||
description, | ||
run: async (argv, importMeta, { parentName }) => { | ||
await meowWithSubcommands( | ||
{ | ||
create, | ||
stream, | ||
list, | ||
del, | ||
metadata | ||
}, | ||
{ | ||
argv, | ||
description, | ||
importMeta, | ||
name: parentName + ' scan', | ||
} | ||
) | ||
} | ||
} |
Oops, something went wrong.