Skip to content

Commit

Permalink
Add cyclonedx command
Browse files Browse the repository at this point in the history
  • Loading branch information
jdalton committed May 31, 2024
1 parent c4f6c8c commit 254dd7f
Show file tree
Hide file tree
Showing 21 changed files with 8,274 additions and 2,566 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ jobs:
uses: SocketDev/workflows/.github/workflows/type-check.yml@master
with:
no-lockfile: true
ts-versions: ${{ github.event.schedule && 'next' || '4.9,next' }}
ts-libs: 'es2020;esnext'
ts-versions: ${{ github.event.schedule && 'next' || '5.4,next' }}
ts-libs: 'esnext'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/coverage
/coverage-ts
/node_modules
/.DS_Store
/.env
/.nyc_output
/.vscode
Expand Down
171 changes: 171 additions & 0 deletions lib/commands/cyclonedx/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { existsSync, promises as fs } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

import chalk from 'chalk'
import { $ } from 'execa'
import yargsParse from 'yargs-parser'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

const toLower = (/** @type {string} */ arg) => arg.toLowerCase()
const arrayToLower = (/** @type {string[]} */ arg) => arg.map(toLower)

const execaConfig = {
env: { NODE_ENV: '' },
localDir: path.join(__dirname, 'node_modules'),
}

const nodejsPlatformTypes = [
'javascript',
'js',
'nodejs',
'npm',
'pnpm',
'ts',
'tsx',
'typescript'
]

const yargsConfig = {
configuration: {
'camel-case-expansion': false,
'strip-aliased': true,
'parse-numbers': false,
'populate--': true,
},
coerce: {
author: arrayToLower,
filter: arrayToLower,
only: arrayToLower,
profile: toLower,
standard: arrayToLower,
type: toLower
},
default: {
profile: 'generic',
type: 'js',
validate: true,
},
alias: {
help: ['h'],
output: ['o'],
print: ['p'],
recurse: ['r'],
'resolve-class': ['c'],
type: ['t'],
version: ['v'],
},
array: [
{ key: 'author', type: 'string' },
{ key: 'exclude', type: 'string' },
{ key: 'filter', type: 'string' },
{ key: 'only', type: 'string' },
{ key: 'standard', type: 'string' }
],
boolean: [
'auto-compositions',
'babel',
'deep',
'evidence',
'fail-on-error',
'generate-key-and-sign',
'help',
'include-formulation',
'include-crypto',
'install-deps',
'print',
'required-only',
'server',
'validate',
'version',
],
string: [
'api-key',
'output',
'parent-project-id',
'profile',
'project-group',
'project-name',
'project-version',
'project-id',
'server-host',
'server-port',
'server-url',
'spec-version',
]
}

/**
*
* @param {{ [key: string]: boolean | null | number | string | (string | number)[]}} argv
* @returns {string[]}
*/
function argvToArray (/** @type {any} */ argv) {
if (argv['help']) return ['--help']
const result = []
for (const { 0: key, 1: value } of Object.entries(argv)) {
if (key === '_' || key === '--') continue
if (key === 'babel' || key === 'install-deps' || key === 'validate') {
result.push(`--${value ? key : `no-${key}`}`)
} else if (value === true) {
result.push(`--${key}`)
} else if (typeof value === 'string') {
result.push(`--${key}=${value}`)
} else if (Array.isArray(value)) {
result.push(`--${key}`, ...value)
}
}
if (argv['--']) {
result.push('--', ...argv['--'])
}
return result
}

/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
export const cyclonedx = {
description: 'Create an SBOM with CycloneDX',
async run (argv_) {
const /** @type {any} */ yargv = {
__proto__: null,
// @ts-ignore
...yargsParse(argv_, yargsConfig)
}

let cleanupPackageLock = false
if (
yargv.type !== 'yarn' &&
nodejsPlatformTypes.includes(yargv.type) &&
existsSync('./yarn.lock')
) {
if (existsSync('./package-lock.json')) {
yargv.type = 'npm'
} else {
// Use synp to convert yarn.lock into package-lock.json for a more
// accurate sbom.
try {
await $(execaConfig)`synp --source-file ./yarn.lock`
yargv.type = 'npm'
cleanupPackageLock = true
} catch {}
}
}

if (yargv.output === undefined) {
yargv.output = 'socket-cyclonedx.json'
}

await $({
...execaConfig,
stdout: 'inherit'
})`cdxgen ${argvToArray(yargv)}`

if (cleanupPackageLock) {
try {
await fs.unlink('./package-lock.json')
} catch {}
}
// eslint-disable-next-line no-console
console.log(chalk.cyanBright(`${yargv.output} created!`))
}
}
9 changes: 5 additions & 4 deletions lib/commands/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export * from './cyclonedx/index.js'
export * from './info/index.js'
export * from './report/index.js'
export * from './npm/index.js'
export * from './npx/index.js'
export * from './login/index.js'
export * from './logout/index.js'
export * from './wrapper/index.js'
export * from './npm/index.js'
export * from './npx/index.js'
export * from './raw-npm/index.js'
export * from './raw-npx/index.js'
export * from './report/index.js'
export * from './wrapper/index.js'
4 changes: 2 additions & 2 deletions lib/commands/info/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { printFlagList } from '../../utils/formatting.js'
import { objectSome } from '../../utils/misc.js'
import { FREE_API_KEY, getDefaultKey, setupSdk } from '../../utils/sdk.js'

/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
export const info = {
description: 'Look up info regarding a package',
async run (argv, importMeta, { parentName }) {
Expand Down Expand Up @@ -109,7 +109,7 @@ function setupCommand (name, description, argv, importMeta) {
/**
* @typedef PackageData
* @property {import('@socketsecurity/sdk').SocketSdkReturnType<'getIssuesByNPMPackage'>["data"]} data
* @property {Record<import('../../utils/format-issues').SocketIssue['severity'], number>} severityCount
* @property {Record<import('../../utils/format-issues.js').SocketIssue['severity'], number>} severityCount
* @property {import('@socketsecurity/sdk').SocketSdkReturnType<'getScoreByNPMPackage'>["data"]} score
*/

Expand Down
2 changes: 1 addition & 1 deletion lib/commands/login/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { getSetting, updateSetting } from '../../utils/settings.js'

const description = 'Socket API login'

/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
export const login = {
description,
run: async (argv, importMeta, { parentName }) => {
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/logout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { updateSetting } from '../../utils/settings.js'

const description = 'Socket API logout'

/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
export const logout = {
description,
run: async (argv, importMeta, { parentName }) => {
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/npm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { fileURLToPath } from 'url'

const description = 'npm wrapper functionality'

/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
export const npm = {
description,
run: async (argv, _importMeta, _ctx) => {
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/npx/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { fileURLToPath } from 'url'

const description = 'npx wrapper functionality'

/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
export const npx = {
description,
run: async (argv, _importMeta, _ctx) => {
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/report/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { createDebugLogger } from '../../utils/misc.js'
import { getPackageFiles } from '../../utils/path-resolve.js'
import { setupSdk } from '../../utils/sdk.js'

/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
export const create = {
description: 'Create a project report',
async run (argv, importMeta, { parentName }) {
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/report/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { meowWithSubcommands } from '../../utils/meow-with-subcommands.js'

const description = 'Project report related commands'

/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
export const report = {
description,
run: async (argv, importMeta, { parentName }) => {
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/report/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { getSeverityCount, formatSeverityCount } from '../../utils/format-issues
import { printFlagList } from '../../utils/formatting.js'
import { setupSdk } from '../../utils/sdk.js'

/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
export const view = {
description: 'View a project report',
async run (argv, importMeta, { parentName }) {
Expand Down
2 changes: 1 addition & 1 deletion lib/commands/wrapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { printFlagList } from '../../utils/formatting.js'
const BASH_FILE = `${homedir.homedir()}/.bashrc`
const ZSH_BASH_FILE = `${homedir.homedir()}/.zshrc`

/** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */
/** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
export const wrapper = {
description: 'Enable or disable the Socket npm/npx wrapper',
async run (argv, importMeta, { parentName }) {
Expand Down
3 changes: 1 addition & 2 deletions lib/utils/meow-with-subcommands.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ import { printFlagList, printHelpList } from './formatting.js'
*/

/**
* @template {import('meow').AnyFlags} Flags
* @param {Record<string, CliSubcommand>} subcommands
* @param {import('meow').Options<Flags> & { aliases?: CliAliases, argv: readonly string[], name: string }} options
* @param {import('meow').Options<any> & { aliases?: CliAliases, argv: readonly string[], name: string }} options
* @returns {Promise<void>}
*/
export async function meowWithSubcommands (subcommands, options) {
Expand Down
1 change: 0 additions & 1 deletion lib/utils/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export function stringJoinWithSeparateFinalSeparator (list, separator = ' and ')

/**
* Returns a new object with only the specified keys from the input object
*
* @template {Record<string,any>} T
* @template {keyof T} K
* @param {T} input
Expand Down
7 changes: 2 additions & 5 deletions lib/utils/path-resolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { isErrnoException } from './type-helpers.cjs'

/**
* There are a lot of possible folders that we should not be looking in and "ignore-by-default" helps us with defining those
*
* @type {readonly string[]}
*/
const ignoreByDefault = directories()
Expand All @@ -31,7 +30,6 @@ const BASE_GLOBBY_OPTS = {

/**
* Resolves package.json and lockfiles from (globbed) input paths, applying relevant ignores
*
* @param {string} cwd The working directory to use when resolving paths
* @param {string[]} inputPaths A list of paths to folders, package.json files and/or recognized lockfiles. Supports globs.
* @param {import('@socketsecurity/config').SocketYml|undefined} config
Expand All @@ -57,18 +55,18 @@ export async function getPackageFiles (cwd, inputPaths, config, supportedFiles,
debugLog(`Mapped ${entries.length} entries to ${packageFiles.length} files:`, packageFiles)

const includedPackageFiles = config?.projectIgnorePaths?.length
// @ts-ignore
? ignore()
.add(config.projectIgnorePaths)
.filter(packageFiles.map(item => path.relative(cwd, item)))
.map(item => path.resolve(cwd, item))
.map((/** @type {string} */ item) => path.resolve(cwd, item))
: packageFiles

return includedPackageFiles
}

/**
* Takes paths to folders, package.json and/or recognized lock files and resolves them to package.json + lockfile pairs (where possible)
*
* @param {string[]} entries
* @param {import('@socketsecurity/sdk').SocketSdkReturnType<"getReportSupportedFiles">['data']} supportedFiles
* @returns {Promise<string[]>}
Expand All @@ -86,7 +84,6 @@ export async function mapGlobResultToFiles (entries, supportedFiles) {

/**
* Takes a single path to a folder, package.json or a recognized lock file and resolves to a package.json + lockfile pair (where possible)
*
* @param {string} entry
* @param {import('@socketsecurity/sdk').SocketSdkReturnType<'getReportSupportedFiles'>['data']} supportedFiles
* @returns {Promise<string[]>}
Expand Down
3 changes: 0 additions & 3 deletions lib/utils/sdk.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export const FREE_API_KEY = 'sktsec_t_--RAN5U4ivauy4w37-6aoKyYPDt5ZbaT5JBVMqiwKo

/**
* This API key should be stored globally for the duration of the CLI execution
*
* @type {string | undefined}
*/
let defaultKey
Expand All @@ -26,7 +25,6 @@ export function getDefaultKey () {

/**
* The API server that should be used for operations
*
* @type {string | undefined}
*/
let defaultAPIBaseUrl
Expand All @@ -41,7 +39,6 @@ export function getDefaultAPIBaseUrl () {

/**
* The API server that should be used for operations
*
* @type {string | undefined}
*/
let defaultApiProxy
Expand Down
Loading

0 comments on commit 254dd7f

Please sign in to comment.