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
1 change: 1 addition & 0 deletions src/commands/ci/handle-ci.mts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export async function handleCi(autoManifest: boolean): Promise<void> {
// When 'pendingHead' is true, it requires 'branchName' set and 'tmp' false.
pendingHead: true,
pullRequest: 0,
reach: false,
repoName,
readOnly: false,
report: true,
Expand Down
10 changes: 10 additions & 0 deletions src/commands/scan/cmd-scan-create.mts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ const config: CliCommandConfig = {
description:
'Similar to --dry-run except it can read from remote, stops before it would create an actual report',
},
reach: {
type: 'boolean',
default: false,
hidden: true,
description:
'Run tier 1 full application reachability analysis during the scanning process',
},
repo: {
type: 'string',
shortFlag: 'r',
Expand Down Expand Up @@ -197,6 +204,7 @@ async function run(
markdown,
org: orgFlag,
pullRequest,
reach,
readOnly,
setAsAlertsPage: pendingHeadFlag,
tmp,
Expand All @@ -213,6 +221,7 @@ async function run(
org: string
pullRequest: number
readOnly: boolean
reach: boolean
setAsAlertsPage: boolean
tmp: boolean
}
Expand Down Expand Up @@ -413,6 +422,7 @@ async function run(
outputKind,
pendingHead: Boolean(pendingHead),
pullRequest: Number(pullRequest),
reach: Boolean(reach),
readOnly: Boolean(readOnly),
repoName,
report,
Expand Down
1 change: 1 addition & 0 deletions src/commands/scan/create-scan-from-github.mts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ async function scanOneRepo(
outputKind,
pendingHead: true,
pullRequest: 0,
reach: false,
readOnly: false,
repoName: repoSlug,
report: false,
Expand Down
129 changes: 127 additions & 2 deletions src/commands/scan/handle-create-new-scan.mts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import { fetchSupportedScanFileNames } from './fetch-supported-scan-file-names.m
import { handleScanReport } from './handle-scan-report.mts'
import { outputCreateNewScan } from './output-create-new-scan.mts'
import constants from '../../constants.mts'
import { handleApiCall } from '../../utils/api.mts'
import { checkCommandInput } from '../../utils/check-input.mts'
import { spawnCoana } from '../../utils/coana.mts'
import { getPackageFilesForScan } from '../../utils/path-resolve.mts'
import { setupSdk } from '../../utils/sdk.mts'
import { readOrDefaultSocketJson } from '../../utils/socketjson.mts'
import { detectManifestActions } from '../manifest/detect-manifest-actions.mts'
import { generateAutoManifest } from '../manifest/generate_auto_manifest.mts'

import type { OutputKind } from '../../types.mts'
import type { CResult, OutputKind } from '../../types.mts'

export async function handleCreateNewScan({
autoManifest,
Expand All @@ -28,6 +31,7 @@ export async function handleCreateNewScan({
outputKind,
pendingHead,
pullRequest,
reach,
readOnly,
repoName,
report,
Expand All @@ -46,6 +50,7 @@ export async function handleCreateNewScan({
pendingHead: boolean
pullRequest: number
outputKind: OutputKind
reach: boolean
readOnly: boolean
repoName: string
report: boolean
Expand Down Expand Up @@ -106,8 +111,30 @@ export async function handleCreateNewScan({
return
}

let scanPaths: string[] = packagePaths

// If reachability is enabled, perform reachability analysis
if (reach) {
const reachResult = await performReachabilityAnalysis({
packagePaths,
orgSlug,
cwd,
repoName,
branchName,
outputKind,
interactive,
})

if (!reachResult.ok) {
await outputCreateNewScan(reachResult, outputKind, interactive)
return
}

scanPaths = reachResult.data?.scanPaths || []
}

const fullScanCResult = await fetchCreateOrgFullScan(
packagePaths,
scanPaths,
orgSlug,
{
commitHash,
Expand Down Expand Up @@ -153,3 +180,101 @@ export async function handleCreateNewScan({
await outputCreateNewScan(fullScanCResult, outputKind, interactive)
}
}

async function performReachabilityAnalysis({
branchName,
cwd,
orgSlug,
packagePaths,
repoName,
}: {
packagePaths: string[]
orgSlug: string
cwd: string
repoName: string
branchName: string
outputKind: OutputKind
interactive: boolean
}): Promise<CResult<{ scanPaths?: string[] }>> {
logger.info('Starting reachability analysis...')

packagePaths = packagePaths.filter(
p =>
/* Exclude DOT_SOCKET_DOT_FACTS_JSON from previous runs */ !p.includes(
constants.DOT_SOCKET_DOT_FACTS_JSON,
),
)
Comment on lines +201 to +206
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter packagePaths is being reassigned without proper declaration. According to the Google TypeScript Style Guide, variables requiring reassignment should use let instead of const. Either declare the parameter as let packagePaths in the function signature, or create a new variable with let for the filtered result to avoid reassigning the parameter directly.

Suggested change
packagePaths = packagePaths.filter(
p =>
/* Exclude DOT_SOCKET_DOT_FACTS_JSON from previous runs */ !p.includes(
constants.DOT_SOCKET_DOT_FACTS_JSON,
),
)
const filteredPackagePaths = packagePaths.filter(
p =>
/* Exclude DOT_SOCKET_DOT_FACTS_JSON from previous runs */ !p.includes(
constants.DOT_SOCKET_DOT_FACTS_JSON,
),
)

Spotted by Diamond (based on custom rule: Custom rules)

Is this helpful? React 👍 or 👎 to let us know.


// Lazily access constants.spinner.
const { spinner } = constants

// Setup SDK for uploading manifests
const sockSdkCResult = await setupSdk()
if (!sockSdkCResult.ok) {
return sockSdkCResult
}
const sockSdk = sockSdkCResult.data

// Upload manifests to get tar hash
spinner.start('Uploading manifests for reachability analysis...')
const uploadCResult = await handleApiCall(
sockSdk.uploadManifestFiles(orgSlug, packagePaths),
{ desc: 'upload manifests' },
)
spinner.stop()

if (!uploadCResult.ok) {
return uploadCResult
}

const tarHash = (uploadCResult.data as { tarHash?: string })?.tarHash
if (!tarHash) {
return {
ok: false,
message: 'Failed to get manifest tar hash',
cause: 'Server did not return a tar hash for the uploaded manifests',
}
}

logger.success(`Manifests uploaded successfully. Tar hash: ${tarHash}`)

// Run Coana with the manifests tar hash
logger.info('Running reachability analysis with Coana...')
const coanaResult = await spawnCoana(
[
'run',
cwd,
'--output-dir',
cwd,
'--socket-mode',
constants.DOT_SOCKET_DOT_FACTS_JSON,
'--disable-report-submission',
'--manifests-tar-hash',
tarHash,
],
{
cwd,
stdio: 'inherit',
env: {
...process.env,
SOCKET_REPO_NAME: repoName,
SOCKET_BRANCH_NAME: branchName,
SOCKET_CLI_VERSION: constants.ENV.INLINED_SOCKET_CLI_VERSION,
},
},
)

if (!coanaResult.ok) {
return coanaResult
}

logger.success('Reachability analysis completed successfully')

// Use the DOT_SOCKET_DOT_FACTS_JSON file for the scan
return {
ok: true,
data: {
scanPaths: [constants.DOT_SOCKET_DOT_FACTS_JSON],
},
}
}
14 changes: 12 additions & 2 deletions src/utils/coana.mts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { spawn } from '@socketsecurity/registry/lib/spawn'

import { getDefaultOrgSlug } from '../commands/ci/fetch-default-org-slug.mts'
import constants from '../constants.mts'
import { getDefaultToken } from './sdk.mts'
import { getDefaultOrgSlug } from '../commands/ci/fetch-default-org-slug.mts'

import type { CResult } from '../types.mts'
import type {
Expand All @@ -15,10 +15,20 @@ export async function spawnCoana(
options?: SpawnOptions | undefined,
extra?: SpawnExtra | undefined,
): Promise<CResult<unknown>> {
const { env: spawnEnv } = { __proto__: null, ...options } as SpawnOptions
const {
env: spawnEnv,
spinner,
stdio,
} = { __proto__: null, ...options } as SpawnOptions
const orgSlugCResult = await getDefaultOrgSlug()
const SOCKET_CLI_API_TOKEN = getDefaultToken()
const SOCKET_ORG_SLUG = orgSlugCResult.ok ? orgSlugCResult.data : undefined

// Stop spinner before streaming output if stdio is 'inherit'
if (stdio === 'inherit' && spinner) {
spinner.stop()
}

try {
const output = await spawn(
constants.execPath,
Expand Down
Loading