@coana-tech/cli 14.12.72 → 14.12.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.mjs +68 -23
- package/package.json +1 -1
- package/reachability-analyzers-cli.mjs +18 -1
- package/repos/coana-tech/alucard/alucard.jar +0 -0
- package/repos/coana-tech/goana/bin/goana-darwin-amd64.gz +0 -0
- package/repos/coana-tech/goana/bin/goana-darwin-arm64.gz +0 -0
- package/repos/coana-tech/goana/bin/goana-linux-amd64.gz +0 -0
- package/repos/coana-tech/goana/bin/goana-linux-arm64.gz +0 -0
package/cli.mjs
CHANGED
|
@@ -205801,9 +205801,13 @@ async function fetchManifestFilesFromManifestsTarHash(manifestsTarHash) {
|
|
|
205801
205801
|
throw new Error("we should never reach this point");
|
|
205802
205802
|
}
|
|
205803
205803
|
}
|
|
205804
|
-
async function fetchArtifactsFromManifestsTarHash(manifestsTarHash) {
|
|
205804
|
+
async function fetchArtifactsFromManifestsTarHash(manifestsTarHash, includePrecomputedReachabilityResults) {
|
|
205805
205805
|
try {
|
|
205806
|
-
const
|
|
205806
|
+
const params = new URLSearchParams({
|
|
205807
|
+
tarHash: manifestsTarHash,
|
|
205808
|
+
includePrecomputedReachabilityResults: String(includePrecomputedReachabilityResults ?? false)
|
|
205809
|
+
});
|
|
205810
|
+
const url2 = getSocketApiUrl(`orgs/${process.env.SOCKET_ORG_SLUG}/compute-artifacts?${params.toString()}`);
|
|
205807
205811
|
const responseData = (await axios2.post(url2, {}, { headers: getAuthHeaders() })).data;
|
|
205808
205812
|
return parseComputeArtifactsResponse(responseData);
|
|
205809
205813
|
} catch (e) {
|
|
@@ -228738,6 +228742,9 @@ function vulnerabilitiesDiff(oldVulnerabilities, newVulnerabilities, dismissedVu
|
|
|
228738
228742
|
// ../web-compat-utils/src/vulnerability-reachability.ts
|
|
228739
228743
|
function getVulnReachability(c3) {
|
|
228740
228744
|
if (c3.type === "noAnalysisCheck") return "REACHABLE";
|
|
228745
|
+
if (c3.type === "precomputed") {
|
|
228746
|
+
return c3.results.type === "unreachable" ? "UNREACHABLE" : "UNKNOWN";
|
|
228747
|
+
}
|
|
228741
228748
|
if (c3.type !== "success") {
|
|
228742
228749
|
return "UNKNOWN";
|
|
228743
228750
|
}
|
|
@@ -229845,11 +229852,11 @@ function getAllToplevelAncestors(artifactMap, artifactId) {
|
|
|
229845
229852
|
findAncestors(artifactId);
|
|
229846
229853
|
return Array.from(toplevelAncestors);
|
|
229847
229854
|
}
|
|
229848
|
-
async function fetchArtifactsFromSocket(rootWorkingDirectory, manifestsTarHash, mode) {
|
|
229855
|
+
async function fetchArtifactsFromSocket(rootWorkingDirectory, manifestsTarHash, mode, includePrecomputedReachabilityResults) {
|
|
229849
229856
|
logger.info("Fetching artifacts from Socket backend (this may take a while)");
|
|
229850
229857
|
logger.debug("manifests tar hash", manifestsTarHash);
|
|
229851
229858
|
try {
|
|
229852
|
-
const { artifacts } = await fetchArtifactsFromManifestsTarHash(manifestsTarHash);
|
|
229859
|
+
const { artifacts } = await fetchArtifactsFromManifestsTarHash(manifestsTarHash, includePrecomputedReachabilityResults);
|
|
229853
229860
|
const properPythonProjects = [];
|
|
229854
229861
|
const pipArtifactToRepresentativeManifest = {};
|
|
229855
229862
|
for (const artifact of artifacts) {
|
|
@@ -229955,7 +229962,8 @@ async function fetchArtifactsFromSocket(rootWorkingDirectory, manifestsTarHash,
|
|
|
229955
229962
|
vulnChainDetails: computeVulnChainDetails(artifacts, artifact.id),
|
|
229956
229963
|
vulnerabilityAccessPaths: vuln.reachabilityData?.undeterminableReachability ? vuln.reachabilityData.publicComment ?? "" : vuln.reachabilityData?.pattern ?? null,
|
|
229957
229964
|
ecosystem,
|
|
229958
|
-
artifactId: artifact.id
|
|
229965
|
+
artifactId: artifact.id,
|
|
229966
|
+
precomputedReachabilityResult: vuln.reachabilityData?.precomputedReachabilityResult ?? null
|
|
229959
229967
|
};
|
|
229960
229968
|
const vulnId = `${ecosystem}-${workspace}-${artifact.namespace}-${artifact.name}-${artifact.version}-${vulnerability.url}`;
|
|
229961
229969
|
if (!ecosystemWorkspaceVulnIds.has(vulnId)) {
|
|
@@ -230458,6 +230466,16 @@ function isShortestPath(root3, vulnPath) {
|
|
|
230458
230466
|
// ../web-compat-utils/src/analysis-error-keys.ts
|
|
230459
230467
|
var CLI_ANALYSIS_ERROR_MESSAGE = "Sharing log due to analysis error";
|
|
230460
230468
|
|
|
230469
|
+
// ../web-compat-utils/src/pluralize.ts
|
|
230470
|
+
function pluralize(count, word) {
|
|
230471
|
+
if (word === "vulnerability") {
|
|
230472
|
+
return count === 1 ? "vulnerability" : "vulnerabilities";
|
|
230473
|
+
} else {
|
|
230474
|
+
word;
|
|
230475
|
+
throw new Error(`Invalid word: ${word}`);
|
|
230476
|
+
}
|
|
230477
|
+
}
|
|
230478
|
+
|
|
230461
230479
|
// ../web-compat-utils/src/package-utils.ts
|
|
230462
230480
|
function extractNameAndVersion(packageNameAtVersion) {
|
|
230463
230481
|
const atNpmIdx = packageNameAtVersion.indexOf("@npm:");
|
|
@@ -230875,11 +230893,15 @@ function toSocketFacts(report, dependencyTrees, subPjToWsPathToDirectDependencie
|
|
|
230875
230893
|
};
|
|
230876
230894
|
component.reachability.push(reachabilityForGHSA);
|
|
230877
230895
|
}
|
|
230878
|
-
|
|
230879
|
-
|
|
230880
|
-
|
|
230881
|
-
|
|
230882
|
-
|
|
230896
|
+
if (vulnerability.codeAwareScanResult.type === "precomputed") {
|
|
230897
|
+
reachabilityForGHSA.reachability.push(vulnerability.codeAwareScanResult);
|
|
230898
|
+
} else {
|
|
230899
|
+
reachabilityForGHSA.reachability.push({
|
|
230900
|
+
...toSocketReachabilitySchema(vulnerability),
|
|
230901
|
+
workspacePath: vulnerability.workspacePath,
|
|
230902
|
+
subprojectPath: vulnerability.subprojectPath
|
|
230903
|
+
});
|
|
230904
|
+
}
|
|
230883
230905
|
}
|
|
230884
230906
|
return {
|
|
230885
230907
|
components
|
|
@@ -230958,11 +230980,15 @@ function toSocketFactsSocketDependencyTree(artifacts, vulnerabilities, tier1Reac
|
|
|
230958
230980
|
};
|
|
230959
230981
|
component.reachability.push(reachabilityForGHSA);
|
|
230960
230982
|
}
|
|
230961
|
-
|
|
230962
|
-
|
|
230963
|
-
|
|
230964
|
-
|
|
230965
|
-
|
|
230983
|
+
if (vulnerability.codeAwareScanResult.type === "precomputed") {
|
|
230984
|
+
reachabilityForGHSA.reachability.push(vulnerability.codeAwareScanResult);
|
|
230985
|
+
} else {
|
|
230986
|
+
reachabilityForGHSA.reachability.push({
|
|
230987
|
+
...toSocketReachabilitySchema(vulnerability),
|
|
230988
|
+
workspacePath: vulnerability.workspacePath,
|
|
230989
|
+
subprojectPath: vulnerability.subprojectPath
|
|
230990
|
+
});
|
|
230991
|
+
}
|
|
230966
230992
|
}
|
|
230967
230993
|
return {
|
|
230968
230994
|
components: artifacts,
|
|
@@ -245858,7 +245884,7 @@ async function onlineScan(dependencyTree, apiKey, timeout) {
|
|
|
245858
245884
|
}
|
|
245859
245885
|
|
|
245860
245886
|
// dist/version.js
|
|
245861
|
-
var version2 = "14.12.
|
|
245887
|
+
var version2 = "14.12.73";
|
|
245862
245888
|
|
|
245863
245889
|
// dist/cli-core.js
|
|
245864
245890
|
var { mapValues, omit, partition, pick } = import_lodash15.default;
|
|
@@ -246020,7 +246046,7 @@ var CliCore = class {
|
|
|
246020
246046
|
async computeAndOutputReportSocketMode(otherModulesCommunicator) {
|
|
246021
246047
|
logger.info("Fetching artifacts from Socket backend");
|
|
246022
246048
|
this.sendProgress("SCAN_FOR_VULNERABILITIES", true, ".", ".");
|
|
246023
|
-
const { artifacts, ecosystemToWorkspaceToAnalysisData, ecosystemToWorkspaceToVulnerabilities } = await fetchArtifactsFromSocket(this.rootWorkingDirectory, this.options.manifestsTarHash, "reachability");
|
|
246049
|
+
const { artifacts, ecosystemToWorkspaceToAnalysisData, ecosystemToWorkspaceToVulnerabilities } = await fetchArtifactsFromSocket(this.rootWorkingDirectory, this.options.manifestsTarHash, "reachability", this.options.useUnreachableFromPrecomputation);
|
|
246024
246050
|
this.sendProgress("SCAN_FOR_VULNERABILITIES", false, ".", ".");
|
|
246025
246051
|
const subProjects = Object.entries(ecosystemToWorkspaceToAnalysisData).flatMap(([ecosystem, workspaceToAnalysisData]) => {
|
|
246026
246052
|
return Object.entries(workspaceToAnalysisData).map(([workspace, analysisData]) => {
|
|
@@ -246245,7 +246271,9 @@ Subproject: ${subproject}`);
|
|
|
246245
246271
|
"timeout",
|
|
246246
246272
|
"analysisTimeout",
|
|
246247
246273
|
"concurrency",
|
|
246248
|
-
"excludeDirs"
|
|
246274
|
+
"excludeDirs",
|
|
246275
|
+
"useUnreachableFromPrecomputation",
|
|
246276
|
+
"minSeverity"
|
|
246249
246277
|
]), null, 2));
|
|
246250
246278
|
logger.info("available memory: %i MiB", os.freemem() / (1024 * 1024));
|
|
246251
246279
|
try {
|
|
@@ -246323,9 +246351,11 @@ Subproject: ${subproject}`);
|
|
|
246323
246351
|
const workspaceToAugmentedVulnerabilities = Object.fromEntries(await asyncMap(workspaces, async (workspacePath, index2) => {
|
|
246324
246352
|
analysisStarting?.(workspacePath, index2 + 1, totalWorkspaces);
|
|
246325
246353
|
const vulnerabilities = workspaceToVulnerabilities[workspacePath] ?? [];
|
|
246354
|
+
logger.info(`Process workspace ${workspacePath} with ${vulnerabilities.length} ${pluralize(vulnerabilities.length, "vulnerability")}`);
|
|
246326
246355
|
try {
|
|
246327
246356
|
const dataForAnalysis = workspacePathToDataForAnalysis[workspacePath];
|
|
246328
|
-
const [
|
|
246357
|
+
const [vulnsSatisfyingThreshold, vulnerabilitiesBelowThreshold] = this.options.minSeverity ? partition(vulnerabilities, (v) => !v.severity || shouldAnalyzeBasedOnSeverity(v.severity, this.options.minSeverity)) : [vulnerabilities, []];
|
|
246358
|
+
const [vulnsUnreachableFromPrecomputation, vulnerabilitiesToAnalyze] = this.options.useUnreachableFromPrecomputation ? partition(vulnsSatisfyingThreshold, (v) => "precomputedReachabilityResult" in v && v.precomputedReachabilityResult?.type === "unreachable") : [[], vulnsSatisfyingThreshold];
|
|
246329
246359
|
const vulnerabilitiesBelowThresholdWithResults = vulnerabilitiesBelowThreshold.map((v) => ({
|
|
246330
246360
|
...v,
|
|
246331
246361
|
results: {
|
|
@@ -246333,10 +246363,23 @@ Subproject: ${subproject}`);
|
|
|
246333
246363
|
message: `Reachability analysis not run since the severity of the vulnerability (${v.severity}) is lower than the min severity threshold: ${this.options.minSeverity}`
|
|
246334
246364
|
}
|
|
246335
246365
|
}));
|
|
246366
|
+
const vulnerabilitiesDeterminedUnreachableFromPrecomputation = vulnsUnreachableFromPrecomputation.map((v) => ({
|
|
246367
|
+
...v,
|
|
246368
|
+
results: {
|
|
246369
|
+
type: "precomputed",
|
|
246370
|
+
results: v.precomputedReachabilityResult
|
|
246371
|
+
}
|
|
246372
|
+
}));
|
|
246336
246373
|
if (vulnerabilitiesBelowThreshold.length > 0) {
|
|
246337
|
-
logger.info(`Reachability analysis
|
|
246374
|
+
logger.info(`Reachability analysis skipped for ${vulnerabilitiesBelowThreshold.length} ${pluralize(vulnerabilities.length, "vulnerability")} with severity below the min severity threshold: ${this.options.minSeverity}`);
|
|
246375
|
+
}
|
|
246376
|
+
if (vulnsUnreachableFromPrecomputation.length > 0) {
|
|
246377
|
+
logger.info(`Reachability analysis skipped for ${vulnsUnreachableFromPrecomputation.length} ${pluralize(vulnerabilities.length, "vulnerability")} that ${vulnerabilities.length !== 1 ? "are" : "is"} already known to be unreachable from precomputed (Tier 2) reachability analysis`);
|
|
246378
|
+
}
|
|
246379
|
+
if (vulnerabilitiesToAnalyze.length > 0) {
|
|
246380
|
+
logger.info(`Running reachability analysis for ${vulnerabilitiesToAnalyze.length} ${pluralize(vulnerabilities.length, "vulnerability")}`);
|
|
246338
246381
|
}
|
|
246339
|
-
const augmentedVulnerabilitiesToAnalyze = reachabilitySupported && !this.shouldExcludeAnalyzingWorkspace(subprojectPath, workspacePath) ? await this.runReachabilityAnalysis(otherModulesCommunicator, subprojectPath, workspacePath, dataForAnalysis, ecosystem, vulnerabilitiesToAnalyze) : vulnerabilitiesToAnalyze.map((v) => ({
|
|
246382
|
+
const augmentedVulnerabilitiesToAnalyze = vulnerabilitiesToAnalyze.length === 0 ? [] : reachabilitySupported && !this.shouldExcludeAnalyzingWorkspace(subprojectPath, workspacePath) ? await this.runReachabilityAnalysis(otherModulesCommunicator, subprojectPath, workspacePath, dataForAnalysis, ecosystem, vulnerabilitiesToAnalyze) : vulnerabilitiesToAnalyze.map((v) => ({
|
|
246340
246383
|
...v,
|
|
246341
246384
|
results: {
|
|
246342
246385
|
type: "otherError",
|
|
@@ -246344,6 +246387,7 @@ Subproject: ${subproject}`);
|
|
|
246344
246387
|
}
|
|
246345
246388
|
}));
|
|
246346
246389
|
const augmentedVulnerabilities = [
|
|
246390
|
+
...vulnerabilitiesDeterminedUnreachableFromPrecomputation,
|
|
246347
246391
|
...augmentedVulnerabilitiesToAnalyze,
|
|
246348
246392
|
...vulnerabilitiesBelowThresholdWithResults
|
|
246349
246393
|
];
|
|
@@ -246430,6 +246474,7 @@ Subproject: ${subproject}`);
|
|
|
246430
246474
|
const [vulnsWithActualAPIPatterns, result] = partition(vulnerabilities, (v) => Array.isArray(v.vulnerabilityAccessPaths));
|
|
246431
246475
|
for (const v of result)
|
|
246432
246476
|
v.results = typeof v.vulnerabilityAccessPaths === "string" ? { type: "noAnalysisCheck", message: v.vulnerabilityAccessPaths } : { type: "missingVulnerabilityPattern" };
|
|
246477
|
+
logger.info(`Reachability analysis not possible for ${result.length} of the ${pluralize(vulnerabilities.length, "vulnerability")}`);
|
|
246433
246478
|
if (!vulnsWithActualAPIPatterns.length)
|
|
246434
246479
|
return result;
|
|
246435
246480
|
this.sendProgress("REACHABILITY_ANALYSIS", true, subprojectPath, workspacePath);
|
|
@@ -246472,7 +246517,7 @@ Subproject: ${subproject}`);
|
|
|
246472
246517
|
}
|
|
246473
246518
|
return {
|
|
246474
246519
|
vulnerabilityUrl: v.url,
|
|
246475
|
-
vulnerabilityUnreachableByPrecomputation:
|
|
246520
|
+
vulnerabilityUnreachableByPrecomputation: "NOT_COMPUTED",
|
|
246476
246521
|
// vulnChainDetails is always present
|
|
246477
246522
|
// we only keep it as optional (potentially undefined) to
|
|
246478
246523
|
// handle requests to the backend from old version of the CLI.
|
|
@@ -246597,7 +246642,7 @@ async function getGitDataToMetadataIfAvailable(rootWorkingDirectory) {
|
|
|
246597
246642
|
// dist/index.js
|
|
246598
246643
|
var program2 = new Command();
|
|
246599
246644
|
var run2 = new Command();
|
|
246600
|
-
run2.name("run").argument("<path>", "File system path to folder containing the project").option("-o, --output-dir <path>", "Write json report to <path>/coana-report.json").option("-d, --debug", "Enable debug logging", false).option("-s, --silent", "Silence all debug/warning output", false).option("--silent-spinner", "Silence spinner", "CI" in process.env || !process.stdin.isTTY).option("-p, --print-report", "Print the report to the console", false).option("--offline-database <path>", "Path to a coana-offline-db.json file for running the CLI without internet connectivity", void 0).option("-t, --timeout <timeout>", "Set API <timeout> in milliseconds to Coana backend.", "300000").option("-a, --analysis-timeout <timeout>", "Set <timeout> in seconds for each reachability analysis run").option("--memory-limit <memoryInMB>", "Set memory limit for analysis to <memoryInMB> megabytes of memory.", "8192").option("-c, --concurrency <concurrency>", "Set the maximum number of concurrent reachability analysis runs. It's recommended to choose a concurrency level that ensures that each analysis run has at least the --memory-limit amount of memory available.", "1").option("--api-key <key>", "Set the Coana dashboard API key. By setting you also enable the dashboard integration.").addOption(new Option("--write-report-to-file", "Write the report dashboard-compatible report to dashboard-report.json. This report may help the Coana team debug issues with the report insertion mechanism.").default(false).hideHelp()).option("--project-name <repoName>", "Set the name of the repository. Used for dashboard integration.").option("--repo-url <repoUrl>", "Set the URL of the repository. Used for dashboard integration.").option("--include-dirs <relativeDirs...>", "globs for directories to include from the detection of subprojects (space-separated)(use relative paths from the project root). Notice, projects that are not included may still be scanned if they are referenced from included projects.").option("--exclude-dirs <relativeDirs...>", "globs for directories to exclude from the detection of subprojects (space-separated)(use relative paths from the project root). Notice, excluded projects may still be scanned if they are referenced from non-excluded projects.").option("--disable-analysis-splitting", "Limits Coana to at most 1 reachability analysis run per workspace").option("--print-analysis-log-file", "Store log output from the JavaScript/TypeScript reachability analysis in the file js-analysis.log file in the root of each workspace", false).option("--entry-points <entryPoints...>", "List of files to analyze for root workspace. The reachability analysis automatically analyzes all files used by the entry points. If not provided, all JavaScript and TypeScript files are considered entry points. For non-root workspaces, all JavaScript and TypeScript files are analyzed as well.").option("--include-projects-with-no-reachability-support", "Also runs Coana on projects where we support traditional SCA, but does not yet support reachability analysis.", false).option("--ecosystems <ecosystems...>", "List of ecosystems to analyze (space-separated). Currently NPM, PIP, MAVEN, NUGET and GO are supported. Default is all supported ecosystems.").addOption(new Option("--purl-types <purlTypes...>", "List of PURL types to analyze (space-separated). Currently npm, pypi, maven, nuget, golang and cargo are supported. Default is all supported purl types.").hideHelp()).option("--changed-files <files...>", "List of files that have changed. If provided, Coana only analyzes workspaces and modules that contain changed files.").option("--disable-report-submission", "Disable the submission of the report to the Coana dashboard. Used by the pipeline blocking feature.", false).option("--disable-analytics-sharing", "Disable analytics sharing.", false).option("--provider-project <path>", "File system path to folder containing the provider project (Only supported for Maven, Gradle, and SBT)").option("--provider-workspaces <dirs...>", "List of workspaces that build the provided runtime environment (Only supported for Maven, Gradle, and SBT)", (paths) => paths.split(" ")).option("--lightweight-reachability", "Runs Coana in lightweight mode. This increases analysis speed but also raises the risk of Coana misclassifying the reachability of certain complex vulnerabilities. Recommended only for use with Coana Guardrail mode.", false).addOption(new Option("--run-without-docker", "Run package managers and reachability analyzers without using docker").default(process.env.RUN_WITHOUT_DOCKER === "true").hideHelp()).addOption(new Option("--run-env <env>", "Specifies the environment in which the CLI is run. So far only MANAGED_SCAN and UNKNOWN are supported.").default("UNKNOWN").choices(["UNKNOWN", "MANAGED_SCAN"]).hideHelp()).addOption(new Option("--guardrail-mode", "Run Coana in guardrail mode. This mode is used to prevent new reachable vulnerabilities from being introduced into the codebase. Usually run as a CI check when pushing new commits to a pull request.")).option("--ignore-failing-workspaces", "Continue processing when a workspace fails instead of exiting. Failed workspaces will be logged at termination.", false).addOption(new Option("--socket-mode <output-file>", "Run Coana in socket mode and write report to <output-file>").hideHelp()).addOption(new Option("--manifests-tar-hash <hash>", "Hash of the tarball containing all manifest files already uploaded to Socket. If provided, Socket will be used for computing dependency trees.").hideHelp()).option("--skip-cache-usage", "Do not attempt to use cached analysis configuration from previous runs", false).addOption(new Option("--min-severity <severity>", "Set the minimum severity of vulnerabilities to analyze. Supported severities are info, low, moderate, high and critical.").choices(["info", "INFO", "low", "LOW", "moderate", "MODERATE", "high", "HIGH", "critical", "CRITICAL"])).version(version2).configureHelp({ sortOptions: true }).action(async (path2, options) => {
|
|
246645
|
+
run2.name("run").argument("<path>", "File system path to folder containing the project").option("-o, --output-dir <path>", "Write json report to <path>/coana-report.json").option("-d, --debug", "Enable debug logging", false).option("-s, --silent", "Silence all debug/warning output", false).option("--silent-spinner", "Silence spinner", "CI" in process.env || !process.stdin.isTTY).option("-p, --print-report", "Print the report to the console", false).option("--offline-database <path>", "Path to a coana-offline-db.json file for running the CLI without internet connectivity", void 0).option("-t, --timeout <timeout>", "Set API <timeout> in milliseconds to Coana backend.", "300000").option("-a, --analysis-timeout <timeout>", "Set <timeout> in seconds for each reachability analysis run").option("--memory-limit <memoryInMB>", "Set memory limit for analysis to <memoryInMB> megabytes of memory.", "8192").option("-c, --concurrency <concurrency>", "Set the maximum number of concurrent reachability analysis runs. It's recommended to choose a concurrency level that ensures that each analysis run has at least the --memory-limit amount of memory available.", "1").option("--api-key <key>", "Set the Coana dashboard API key. By setting you also enable the dashboard integration.").addOption(new Option("--write-report-to-file", "Write the report dashboard-compatible report to dashboard-report.json. This report may help the Coana team debug issues with the report insertion mechanism.").default(false).hideHelp()).option("--project-name <repoName>", "Set the name of the repository. Used for dashboard integration.").option("--repo-url <repoUrl>", "Set the URL of the repository. Used for dashboard integration.").option("--include-dirs <relativeDirs...>", "globs for directories to include from the detection of subprojects (space-separated)(use relative paths from the project root). Notice, projects that are not included may still be scanned if they are referenced from included projects.").option("--exclude-dirs <relativeDirs...>", "globs for directories to exclude from the detection of subprojects (space-separated)(use relative paths from the project root). Notice, excluded projects may still be scanned if they are referenced from non-excluded projects.").option("--disable-analysis-splitting", "Limits Coana to at most 1 reachability analysis run per workspace").option("--print-analysis-log-file", "Store log output from the JavaScript/TypeScript reachability analysis in the file js-analysis.log file in the root of each workspace", false).option("--entry-points <entryPoints...>", "List of files to analyze for root workspace. The reachability analysis automatically analyzes all files used by the entry points. If not provided, all JavaScript and TypeScript files are considered entry points. For non-root workspaces, all JavaScript and TypeScript files are analyzed as well.").option("--include-projects-with-no-reachability-support", "Also runs Coana on projects where we support traditional SCA, but does not yet support reachability analysis.", false).option("--ecosystems <ecosystems...>", "List of ecosystems to analyze (space-separated). Currently NPM, PIP, MAVEN, NUGET and GO are supported. Default is all supported ecosystems.").addOption(new Option("--purl-types <purlTypes...>", "List of PURL types to analyze (space-separated). Currently npm, pypi, maven, nuget, golang and cargo are supported. Default is all supported purl types.").hideHelp()).option("--changed-files <files...>", "List of files that have changed. If provided, Coana only analyzes workspaces and modules that contain changed files.").option("--disable-report-submission", "Disable the submission of the report to the Coana dashboard. Used by the pipeline blocking feature.", false).option("--disable-analytics-sharing", "Disable analytics sharing.", false).option("--provider-project <path>", "File system path to folder containing the provider project (Only supported for Maven, Gradle, and SBT)").option("--provider-workspaces <dirs...>", "List of workspaces that build the provided runtime environment (Only supported for Maven, Gradle, and SBT)", (paths) => paths.split(" ")).option("--lightweight-reachability", "Runs Coana in lightweight mode. This increases analysis speed but also raises the risk of Coana misclassifying the reachability of certain complex vulnerabilities. Recommended only for use with Coana Guardrail mode.", false).addOption(new Option("--run-without-docker", "Run package managers and reachability analyzers without using docker").default(process.env.RUN_WITHOUT_DOCKER === "true").hideHelp()).addOption(new Option("--run-env <env>", "Specifies the environment in which the CLI is run. So far only MANAGED_SCAN and UNKNOWN are supported.").default("UNKNOWN").choices(["UNKNOWN", "MANAGED_SCAN"]).hideHelp()).addOption(new Option("--guardrail-mode", "Run Coana in guardrail mode. This mode is used to prevent new reachable vulnerabilities from being introduced into the codebase. Usually run as a CI check when pushing new commits to a pull request.")).option("--ignore-failing-workspaces", "Continue processing when a workspace fails instead of exiting. Failed workspaces will be logged at termination.", false).addOption(new Option("--socket-mode <output-file>", "Run Coana in socket mode and write report to <output-file>").hideHelp()).addOption(new Option("--manifests-tar-hash <hash>", "Hash of the tarball containing all manifest files already uploaded to Socket. If provided, Socket will be used for computing dependency trees.").hideHelp()).option("--skip-cache-usage", "Do not attempt to use cached analysis configuration from previous runs", false).addOption(new Option("--min-severity <severity>", "Set the minimum severity of vulnerabilities to analyze. Supported severities are info, low, moderate, high and critical.").choices(["info", "INFO", "low", "LOW", "moderate", "MODERATE", "high", "HIGH", "critical", "CRITICAL"])).option("--use-unreachable-from-precomputation", "Skip the reachability analysis for vulnerabilities that are already known to be unreachable from the precomputed reachability analysis (Tier 2).", false).version(version2).configureHelp({ sortOptions: true }).action(async (path2, options) => {
|
|
246601
246646
|
process.env.DOCKER_IMAGE_TAG ??= version2;
|
|
246602
246647
|
options.ecosystems = options.ecosystems?.map((e) => e.toUpperCase());
|
|
246603
246648
|
options.minSeverity = options.minSeverity?.toUpperCase();
|
package/package.json
CHANGED
|
@@ -80792,6 +80792,9 @@ function hasReachableMatches(detectedOccurrences) {
|
|
|
80792
80792
|
// ../web-compat-utils/src/vulnerability-reachability.ts
|
|
80793
80793
|
function getVulnReachability(c) {
|
|
80794
80794
|
if (c.type === "noAnalysisCheck") return "REACHABLE";
|
|
80795
|
+
if (c.type === "precomputed") {
|
|
80796
|
+
return c.results.type === "unreachable" ? "UNREACHABLE" : "UNKNOWN";
|
|
80797
|
+
}
|
|
80795
80798
|
if (c.type !== "success") {
|
|
80796
80799
|
return "UNKNOWN";
|
|
80797
80800
|
}
|
|
@@ -112171,7 +112174,7 @@ async function analyzeWithHeuristics(state, vulns, heuristicsInOrder, doNotRecom
|
|
|
112171
112174
|
try {
|
|
112172
112175
|
newAnalysisRunListener();
|
|
112173
112176
|
const initialBucketContainingAllVulns = buckets.length === 1 && buckets[0] === bucket;
|
|
112174
|
-
logger.info(`Running analysis for ${vulnsForBucket.length}/${vulnerabilities.length} vulnerabilities.`);
|
|
112177
|
+
logger.info(`Running full reachability analysis for ${vulnsForBucket.length}/${vulnerabilities.length} vulnerabilities.`);
|
|
112175
112178
|
const result = await codeAwareScanner.runAnalysis(vulnsForBucket, bucket.heuristic, initialBucketContainingAllVulns, experiment);
|
|
112176
112179
|
const allowSplitInBuckets = !disableBucketing && bucket.heuristic.splitAnalysisInBuckets && !state.otherAnalysisOptions.disableBucketing && vulnDepIdentifiers.length > 1 && (result.type === "error" || result.reachedDependencies);
|
|
112177
112180
|
if (result.type === "success") {
|
|
@@ -112461,6 +112464,18 @@ var import_picomatch4 = __toESM(require_picomatch4(), 1);
|
|
|
112461
112464
|
import { existsSync as existsSync13 } from "fs";
|
|
112462
112465
|
import { rm as rm6 } from "fs/promises";
|
|
112463
112466
|
import { resolve as resolve20 } from "path";
|
|
112467
|
+
|
|
112468
|
+
// ../web-compat-utils/src/pluralize.ts
|
|
112469
|
+
function pluralize(count, word) {
|
|
112470
|
+
if (word === "vulnerability") {
|
|
112471
|
+
return count === 1 ? "vulnerability" : "vulnerabilities";
|
|
112472
|
+
} else {
|
|
112473
|
+
word;
|
|
112474
|
+
throw new Error(`Invalid word: ${word}`);
|
|
112475
|
+
}
|
|
112476
|
+
}
|
|
112477
|
+
|
|
112478
|
+
// dist/analyzers/npm-analyzer.js
|
|
112464
112479
|
var { partition: partition3, memoize: memoize2 } = import_lodash19.default;
|
|
112465
112480
|
var NpmAnalyzer = class {
|
|
112466
112481
|
state;
|
|
@@ -112486,6 +112501,7 @@ var NpmAnalyzer = class {
|
|
|
112486
112501
|
try {
|
|
112487
112502
|
const vulnerabilityScanner = new JSCodeAwareVulnerabilityScanner(this.state.rootWorkingDir, this.projectDir, this.state.reachabilityAnalysisOptions);
|
|
112488
112503
|
await vulnerabilityScanner.prepareDependencies(this.state, heuristicsInOrder[0]);
|
|
112504
|
+
logger.info(`Running import reachability analysis for ${vulns.length} ${pluralize(vulns.length, "vulnerability")}`);
|
|
112489
112505
|
let reachable;
|
|
112490
112506
|
try {
|
|
112491
112507
|
statusUpdater?.("Running import reachability analysis");
|
|
@@ -112519,6 +112535,7 @@ var NpmAnalyzer = class {
|
|
|
112519
112535
|
return /[*?{}]/.test(modGlob) ? checkGlob(modGlob) : !reachableModuleNames.has(modGlob);
|
|
112520
112536
|
});
|
|
112521
112537
|
}));
|
|
112538
|
+
logger.info(`Found ${unreachableVulns.length} ${pluralize(unreachableVulns.length, "vulnerability")} that ${unreachableVulns.length !== 1 ? "are" : "is"} unreachable from the import graph`);
|
|
112522
112539
|
}
|
|
112523
112540
|
const res = otherVulns.length ? await analyzeWithHeuristics(this.state, otherVulns, heuristicsInOrder, true, vulnerabilityScanner, analysisMetadataCollector, statusUpdater) : [];
|
|
112524
112541
|
if (unreachableVulns.length) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|