@codacy/tools-trivy-0 0.17.0 → 0.19.0

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/README.md CHANGED
@@ -4,6 +4,12 @@
4
4
 
5
5
  - [Overview](#overview)
6
6
  - [Umbrella patterns](#umbrella-patterns)
7
+ - [Issue parity with `codacy-trivy`](#issue-parity-with-codacy-trivy)
8
+ - [`sourceId` — why every issue carries its CVE](#sourceid--why-every-issue-carries-its-cve)
9
+ - [Scan flags that affect what is found](#scan-flags-that-affect-what-is-found)
10
+ - [Line-number resolution](#line-number-resolution)
11
+ - [Fix version](#fix-version)
12
+ - [Known divergences](#known-divergences)
7
13
  - [DB update management](#db-update-management)
8
14
  - [Malicious package detection](#malicious-package-detection)
9
15
  - [Updating patterns](#updating-patterns)
@@ -40,6 +46,113 @@ Trivy uses umbrella patterns rather than individual CVE patterns:
40
46
  | `Trivy_secret` | Error | Hardcoded secrets |
41
47
  | `Trivy_malicious_packages` | Error | Malicious packages (via OSV.dev API) |
42
48
 
49
+ ## Issue parity with `codacy-trivy`
50
+
51
+ Codacy Cloud analyses repositories with the `codacy-trivy` Docker engine, so a
52
+ repository scanned locally and uploaded must produce the same results as the same
53
+ repository scanned on the cloud. Three pieces of this adapter exist only to hold
54
+ that line; the reference implementation is `codacy-trivy`
55
+ `internal/tool/tool.go`, and the ports live in `src/vuln-mapping.ts`
56
+ (`fallbackSearchForLineNumber`, `findLeastDisruptiveFixedVersion`,
57
+ `purlPrettyPrint`). **Change them only alongside that file.**
58
+
59
+ ### `sourceId` — why every issue carries its CVE
60
+
61
+ Codacy derives a result's deterministic UUID from an MD5 over four keys: filename,
62
+ pattern id, the line's text with whitespace stripped, and `sourceId`
63
+ (`codacy-worker` `shared/rules/…/rules/components/ResultUUIDRules.scala`).
64
+
65
+ Trivy reports many CVEs under one of four severity umbrella patterns, so two CVEs
66
+ on the same lockfile line share three of those four keys. **Without `sourceId`
67
+ they collapse into a single Codacy result.** Every issue this adapter produces
68
+ therefore sets it:
69
+
70
+ | Finding | `sourceId` |
71
+ | ----------------- | ----------------------------- |
72
+ | Vulnerability | `VulnerabilityID` (the CVE) |
73
+ | Secret | Trivy's `RuleID` |
74
+ | Malicious package | The OSV advisory id (`MAL-…`) |
75
+
76
+ `src/metadata.ts` sets `needsSourceIdUpload: true`, which is what makes
77
+ `upload` include the field in the v2 payload (mirroring
78
+ `needs_source_id_upload` in the Go CLI's Trivy plugin manifest). Adapters without
79
+ the flag omit the field entirely — sending it where the cloud engine does not
80
+ would give the same finding a different UUID.
81
+
82
+ ### Scan flags that affect what is found
83
+
84
+ The vulnerability scan passes **`--detection-priority comprehensive`**, matching
85
+ `codacy-trivy`'s `DetectionPriority: PriorityComprehensive`. It reports more
86
+ vulnerabilities — some of which may be false positives — and it is **required for Go
87
+ standard-library findings**: verified against trivy 0.70, a `go.mod` scan without it
88
+ reports **zero** `stdlib` vulnerabilities (6 issues instead of 26 on a small
89
+ `go 1.20` module). Removing the flag would silently drop every stdlib finding and
90
+ make the go.mod handling below dead code.
91
+
92
+ ### Line-number resolution
93
+
94
+ Trivy reports **no line** for a vulnerable dependency, so the line is reconstructed:
95
+
96
+ 1. Look up the package's `Locations[0].StartLine`, **keyed by PURL** — the key the
97
+ cloud engine uses. `PkgID` is a secondary key for the rare package Trivy reports
98
+ without a PURL.
99
+ 2. On a miss or a zero, scan the manifest top to bottom for the first line whose
100
+ trimmed text contains the package name.
101
+ 3. Still nothing ⇒ emit a **file-level error** (`Line numbers not supported`,
102
+ deduplicated to one per file), not an issue on line 1. A confidently wrong
103
+ location is worse than an honest "unknown"; `codacy-trivy` makes the same call
104
+ via `mapIssuesWithoutLineNumber`.
105
+
106
+ **go.mod standard library.** Trivy reports standard-library vulnerabilities against
107
+ a package named `stdlib`, which never literally appears in `go.mod`. Trivy
108
+ [documents](https://trivy.dev/v0.59/docs/coverage/language/golang/#gomod-stdlib)
109
+ using the minimum of the `toolchain` and `go` directives, but in practice always
110
+ uses `toolchain` when it exists. So the scan returns the `toolchain` line as soon
111
+ as it sees one, and only falls back to the `go` directive line after reading the
112
+ whole file. This is not derivable from Trivy's docs — do not "simplify" it.
113
+
114
+ ### Fix version
115
+
116
+ Trivy's `FixedVersion` is a comma-separated **list** of every fixing version
117
+ (`"1.9.0, 1.10.0, 2.0.1"`). The message reports only the least disruptive one: the
118
+ smallest candidate strictly greater than the installed version, by semantic
119
+ version. When no candidate is greater (or the scheme isn't semver — Ruby's `~>`,
120
+ Maven qualifiers), Trivy's raw value is passed through; when `FixedVersion` is
121
+ empty the message reads `(no fix available)`.
122
+
123
+ Message format, matching the cloud engine verbatim:
124
+
125
+ ```
126
+ Insecure dependency {pretty-printed PURL} ({CVE}: {title}) (update to {version})
127
+ Insecure dependency {pretty-printed PURL} ({CVE}: {title}) (no fix available)
128
+ ```
129
+
130
+ Comparison uses a port of Go's `golang.org/x/mod/semver` (`compareSemver`), not
131
+ npm semver: it accepts two-component versions like `v1.2`, and treats an
132
+ unparseable version as lower than any valid one, which is what makes non-semver
133
+ schemes fall through instead of being ranked.
134
+
135
+ ### Known divergences
136
+
137
+ Both are deliberate; see `docs/tech-debt.md`.
138
+
139
+ - **Fix-version ordering.** `codacy-trivy` sorts the candidate strings _before_
140
+ adding the `v` prefix, so `x/mod/semver` sees them all as invalid, compares them
141
+ equal, and falls back to a lexicographic tiebreak — picking `1.10.0` over
142
+ `1.9.0`. We sort on the prefixed values and return the genuinely smallest.
143
+ - **Packages without a PURL.** `codacy-trivy` skips those vulnerabilities entirely.
144
+ We keep them, resolving the line by name and rendering the package as
145
+ `name@version`, because silently dropping a real vulnerability is worse than a
146
+ message that differs from the cloud's.
147
+ - **Secret message text.** Ours appends the secret's category
148
+ (`Possible hardcoded secret: {title} ({category})`); the cloud engine stops at
149
+ the title.
150
+ - **Offline scan.** `codacy-trivy` sets `OfflineScan: true` (its container is
151
+ air-gapped); we don't, so Trivy may resolve some Java/Maven dependencies over the
152
+ network and report slightly more accurate results for those ecosystems. Not
153
+ changed here: making local scans less accurate to match an air-gap constraint we
154
+ don't have would be the wrong trade.
155
+
43
156
  ## DB update management
44
157
 
45
158
  The adapter reads Trivy's `metadata.json` (at `{cacheDir}/db/metadata.json`) to
@@ -89,11 +202,12 @@ pnpm test # Run tests (requires trivy in PATH or auto-install)
89
202
 
90
203
  ## Notes for maintainers
91
204
 
92
- - Trivy runs **two separate scan invocations**: one for vulnerabilities (`--scanners vuln`) and one for secrets (`--scanners secret`). Malicious package detection piggybacks on the vuln scan output (no extra invocation). Which scans run depends on which umbrella patterns are enabled.
205
+ - Trivy runs **two separate scan invocations**: one for vulnerabilities (`--scanners vuln --detection-priority comprehensive`) and one for secrets (`--scanners secret`). Malicious package detection piggybacks on the vuln scan output (no extra invocation). Which scans run depends on which umbrella patterns are enabled.
93
206
  - The vulnerability database is downloaded lazily on first run to `--cache-dir ~/.codacy/cache/trivy/`. Internet access is required on first use. DB staleness is managed via `metadata.json` → `NextUpdate`.
94
207
  - Trivy scans **whole directories**, not individual files. Results are filtered to `ctx.targetFiles` after scanning.
95
208
  - The binary is downloaded from GitHub releases as a `.tar.gz` archive.
96
209
  - Vulnerability severity (CRITICAL/HIGH/MEDIUM/LOW) maps to the corresponding umbrella pattern.
97
- - Message format: `Insecure dependency {pkg}@{version} ({CVE}: {title}). Update to {fixedVersion}.`
210
+ - Message format, line resolution and `sourceId` are all parity-critical — see [Issue parity with `codacy-trivy`](#issue-parity-with-codacy-trivy) before touching them.
211
+ - A vulnerability whose line cannot be resolved becomes a file-level `AnalysisError` (`kind: "LineNumbersNotSupported"`), one per file, rather than an issue on line 1.
98
212
  - Malicious package detection uses the OSV.dev batch API. On failure, it degrades gracefully (warning only).
99
213
  - Config file: checks for `trivy.yaml` in the repo root.
package/dist/index.js CHANGED
@@ -252,9 +252,120 @@ var descriptor = {
252
252
  filePatterns: ["*"],
253
253
  supportedOS: ["linux", "darwin", "win32"],
254
254
  supportedArch: ["x64", "arm64"],
255
- supportedRunners: ["local", "container"]
255
+ supportedRunners: ["local", "container"],
256
+ // Trivy reports every CVE under one of four severity umbrella patterns, so its
257
+ // issues must upload `sourceId` (the CVE id) or two CVEs on the same manifest
258
+ // line collapse into a single Codacy result. Matches `needs_source_id_upload`
259
+ // in the Go CLI's Trivy plugin manifest.
260
+ needsSourceIdUpload: true
256
261
  };
257
262
 
263
+ // src/vuln-mapping.ts
264
+ function purlPrettyPrint(purl) {
265
+ const stripped = purl.startsWith("pkg:") ? purl.slice("pkg:".length) : purl;
266
+ try {
267
+ return decodeURIComponent(stripped);
268
+ } catch {
269
+ return stripped;
270
+ }
271
+ }
272
+ function fallbackSearchForLineNumber(lines, fileName, pkgName) {
273
+ if (!pkgName) return 0;
274
+ const isGoModStdLib = fileName.endsWith("go.mod") && pkgName === "stdlib";
275
+ let goDirectiveLine = 0;
276
+ for (let i = 0; i < lines.length; i++) {
277
+ const lineText = lines[i].trim();
278
+ const lineNumber = i + 1;
279
+ if (isGoModStdLib) {
280
+ if (lineText.startsWith("toolchain ")) return lineNumber;
281
+ if (lineText.startsWith("go ")) goDirectiveLine = lineNumber;
282
+ } else if (lineText.includes(pkgName)) {
283
+ return lineNumber;
284
+ }
285
+ }
286
+ return goDirectiveLine;
287
+ }
288
+ function findLeastDisruptiveFixedVersion(installedVersion, fixedVersion) {
289
+ const sanitize = (version) => `v${version.replace(/^v/, "")}`;
290
+ const sanitizedInstalled = sanitize(installedVersion);
291
+ const candidates = fixedVersion.split(",").map((v) => v.trim()).filter((v) => compareSemver(sanitize(v), sanitizedInstalled) > 0);
292
+ if (candidates.length === 0) return fixedVersion;
293
+ candidates.sort(
294
+ (a, b) => compareSemver(sanitize(a), sanitize(b)) || (a < b ? -1 : a > b ? 1 : 0)
295
+ );
296
+ return candidates[0];
297
+ }
298
+ function compareSemver(v, w) {
299
+ const pv = parseSemver(v);
300
+ const pw = parseSemver(w);
301
+ if (!pv && !pw) return 0;
302
+ if (!pv) return -1;
303
+ if (!pw) return 1;
304
+ return compareNumeric(pv.major, pw.major) || compareNumeric(pv.minor, pw.minor) || compareNumeric(pv.patch, pw.patch) || comparePrerelease(pv.prerelease, pw.prerelease);
305
+ }
306
+ var NUMERIC_IDENT = /^(0|[1-9]\d*)$/;
307
+ var ALPHANUMERIC_IDENT = /^[0-9A-Za-z-]+$/;
308
+ function parseSemver(version) {
309
+ if (!version.startsWith("v")) return null;
310
+ let rest = version.slice(1);
311
+ const plusIndex = rest.indexOf("+");
312
+ if (plusIndex >= 0) {
313
+ const build = rest.slice(plusIndex + 1);
314
+ rest = rest.slice(0, plusIndex);
315
+ if (!isValidDotSeparated(build, false)) return null;
316
+ }
317
+ let prerelease = "";
318
+ const dashIndex = rest.indexOf("-");
319
+ if (dashIndex >= 0) {
320
+ prerelease = rest.slice(dashIndex);
321
+ rest = rest.slice(0, dashIndex);
322
+ if (!isValidDotSeparated(prerelease.slice(1), true)) return null;
323
+ }
324
+ const parts = rest.split(".");
325
+ if (parts.length > 3) return null;
326
+ if (!parts.every((p) => NUMERIC_IDENT.test(p))) return null;
327
+ return {
328
+ major: parts[0],
329
+ minor: parts[1] ?? "0",
330
+ patch: parts[2] ?? "0",
331
+ prerelease
332
+ };
333
+ }
334
+ function isValidDotSeparated(value, isPrerelease) {
335
+ if (value === "") return false;
336
+ return value.split(".").every((ident) => {
337
+ if (!ALPHANUMERIC_IDENT.test(ident)) return false;
338
+ if (isPrerelease && /^\d+$/.test(ident) && !NUMERIC_IDENT.test(ident)) return false;
339
+ return true;
340
+ });
341
+ }
342
+ function compareNumeric(x, y) {
343
+ if (x === y) return 0;
344
+ if (x.length !== y.length) return x.length < y.length ? -1 : 1;
345
+ return x < y ? -1 : 1;
346
+ }
347
+ function comparePrerelease(x, y) {
348
+ if (x === y) return 0;
349
+ if (x === "") return 1;
350
+ if (y === "") return -1;
351
+ const xs = x.slice(1).split(".");
352
+ const ys = y.slice(1).split(".");
353
+ for (let i = 0; i < Math.min(xs.length, ys.length); i++) {
354
+ const dx = xs[i];
355
+ const dy = ys[i];
356
+ if (dx === dy) continue;
357
+ const nx = /^\d+$/.test(dx);
358
+ const ny = /^\d+$/.test(dy);
359
+ if (nx !== ny) return nx ? -1 : 1;
360
+ if (nx) {
361
+ const byLength = compareNumeric(dx, dy);
362
+ if (byLength !== 0) return byLength;
363
+ }
364
+ return dx < dy ? -1 : 1;
365
+ }
366
+ return xs.length < ys.length ? -1 : 1;
367
+ }
368
+
258
369
  // src/adapter.ts
259
370
  var TOOL_ID = "Trivy";
260
371
  var PATTERN_META = new Map(
@@ -403,9 +514,23 @@ async function runTrivyScan(binary, args, cwd, errors) {
403
514
  return null;
404
515
  }
405
516
  }
406
- async function parseVulnerabilities(output, enabledPatterns, targetFilesSet, repositoryRoot) {
517
+ var LINE_NUMBERS_NOT_SUPPORTED = "Line numbers not supported";
518
+ function recordUnlocatableFinding(errors, seenFiles, filePath) {
519
+ if (seenFiles.has(filePath)) return;
520
+ seenFiles.add(filePath);
521
+ errors.push({
522
+ toolId: TOOL_ID,
523
+ phase: "outputParse",
524
+ kind: "LineNumbersNotSupported",
525
+ message: LINE_NUMBERS_NOT_SUPPORTED,
526
+ level: "warning",
527
+ filePath
528
+ });
529
+ }
530
+ async function parseVulnerabilities(output, enabledPatterns, targetFilesSet, repositoryRoot, unlocatableFiles) {
407
531
  const issues = [];
408
- if (!output.Results) return issues;
532
+ const errors = [];
533
+ if (!output.Results) return { issues, errors };
409
534
  const linesCache = /* @__PURE__ */ new Map();
410
535
  for (const result of output.Results) {
411
536
  if (!result.Vulnerabilities) continue;
@@ -415,14 +540,21 @@ async function parseVulnerabilities(output, enabledPatterns, targetFilesSet, rep
415
540
  linesCache.set(relPath, await (0, import_tooling.readFileLines)(import_path.default.join(repositoryRoot, relPath)));
416
541
  }
417
542
  const lines = linesCache.get(relPath);
418
- const pkgLineByID = /* @__PURE__ */ new Map();
419
- if (result.Packages) {
420
- for (const pkg of result.Packages) {
421
- if (pkg.Locations && pkg.Locations.length > 0 && pkg.Locations[0].StartLine > 0) {
422
- pkgLineByID.set(pkg.ID, pkg.Locations[0].StartLine);
423
- }
424
- }
543
+ const lineByPurl = /* @__PURE__ */ new Map();
544
+ const lineByPkgId = /* @__PURE__ */ new Map();
545
+ for (const pkg of result.Packages ?? []) {
546
+ const startLine = pkg.Locations?.[0]?.StartLine ?? 0;
547
+ if (pkg.Identifier?.PURL) lineByPurl.set(pkg.Identifier.PURL, startLine);
548
+ if (pkg.ID) lineByPkgId.set(pkg.ID, startLine);
425
549
  }
550
+ const fallbackLines = /* @__PURE__ */ new Map();
551
+ const resolveFallbackLine = (pkgName) => {
552
+ const cached = fallbackLines.get(pkgName);
553
+ if (cached !== void 0) return cached;
554
+ const found = fallbackSearchForLineNumber(lines, relPath, pkgName);
555
+ fallbackLines.set(pkgName, found);
556
+ return found;
557
+ };
426
558
  for (const vuln of result.Vulnerabilities) {
427
559
  const patternId = VULN_SEVERITY_TO_PATTERN[vuln.Severity];
428
560
  if (!patternId) continue;
@@ -431,23 +563,34 @@ async function parseVulnerabilities(output, enabledPatterns, targetFilesSet, rep
431
563
  category: "Security",
432
564
  severity: "Info"
433
565
  };
434
- const title = vuln.Title ? `: ${vuln.Title}` : "";
435
- const fix = vuln.FixedVersion ? `Update to ${vuln.FixedVersion}.` : "No fix available.";
436
- const line = vuln.PkgID && pkgLineByID.get(vuln.PkgID) || 1;
566
+ const purl = vuln.PkgIdentifier?.PURL;
567
+ const reportedLine = (purl !== void 0 ? lineByPurl.get(purl) : void 0) ?? (vuln.PkgID !== void 0 ? lineByPkgId.get(vuln.PkgID) : void 0) ?? 0;
568
+ const line = reportedLine > 0 ? reportedLine : resolveFallbackLine(vuln.PkgName);
569
+ if (line <= 0) {
570
+ recordUnlocatableFinding(errors, unlocatableFiles, relPath);
571
+ continue;
572
+ }
573
+ const fixedVersion = findLeastDisruptiveFixedVersion(
574
+ vuln.InstalledVersion,
575
+ vuln.FixedVersion ?? ""
576
+ );
577
+ const fix = fixedVersion ? `(update to ${fixedVersion})` : "(no fix available)";
578
+ const pkgLabel = purl ? purlPrettyPrint(purl) : `${vuln.PkgName}@${vuln.InstalledVersion}`;
437
579
  issues.push({
438
580
  filePath: relPath,
439
581
  patternId,
440
582
  toolId: TOOL_ID,
441
583
  line,
442
584
  column: 1,
443
- message: `Insecure dependency ${vuln.PkgName}@${vuln.InstalledVersion} (${vuln.VulnerabilityID}${title}). ${fix}`,
585
+ message: `Insecure dependency ${pkgLabel} (${vuln.VulnerabilityID}: ${vuln.Title ?? ""}) ${fix}`,
444
586
  lineContent: (0, import_tooling.getLineContent)(lines, line),
445
587
  category: meta.category,
446
- severity: meta.severity
588
+ severity: meta.severity,
589
+ ...vuln.VulnerabilityID ? { sourceId: vuln.VulnerabilityID } : {}
447
590
  });
448
591
  }
449
592
  }
450
- return issues;
593
+ return { issues, errors };
451
594
  }
452
595
  async function parseSecrets(output, enabledPatterns, targetFilesSet, repositoryRoot) {
453
596
  const issues = [];
@@ -479,13 +622,14 @@ async function parseSecrets(output, enabledPatterns, targetFilesSet, repositoryR
479
622
  lineContent: (0, import_tooling.getLineContent)(lines, secret.StartLine),
480
623
  multilineContent: (0, import_tooling.getMultilineContent)(lines, secret.StartLine, secret.EndLine),
481
624
  category: meta.category,
482
- severity: meta.severity
625
+ severity: meta.severity,
626
+ ...secret.RuleID ? { sourceId: secret.RuleID } : {}
483
627
  });
484
628
  }
485
629
  }
486
630
  return issues;
487
631
  }
488
- async function parseMaliciousPackages(output, enabledPatterns, targetFilesSet, repositoryRoot, logger) {
632
+ async function parseMaliciousPackages(output, enabledPatterns, targetFilesSet, repositoryRoot, logger, unlocatableFiles) {
489
633
  const issues = [];
490
634
  const errors = [];
491
635
  const patternId = "Trivy_malicious_packages";
@@ -509,8 +653,13 @@ async function parseMaliciousPackages(output, enabledPatterns, targetFilesSet, r
509
653
  const key = `${ecosystem}:${pkg.Name}:${pkg.Version}:${relPath}`;
510
654
  if (seen.has(key)) continue;
511
655
  seen.add(key);
512
- const line = pkg.Locations && pkg.Locations.length > 0 && pkg.Locations[0].StartLine > 0 ? pkg.Locations[0].StartLine : 1;
513
- packages.push({ ecosystem, name: pkg.Name, version: pkg.Version, filePath: relPath, line });
656
+ packages.push({
657
+ ecosystem,
658
+ name: pkg.Name,
659
+ version: pkg.Version,
660
+ filePath: relPath,
661
+ reportedLine: pkg.Locations?.[0]?.StartLine ?? 0
662
+ });
514
663
  }
515
664
  }
516
665
  if (packages.length === 0) return { issues, errors };
@@ -564,18 +713,24 @@ async function parseMaliciousPackages(output, enabledPatterns, targetFilesSet, r
564
713
  linesCache.set(pkg.filePath, await (0, import_tooling.readFileLines)(import_path.default.join(repositoryRoot, pkg.filePath)));
565
714
  }
566
715
  const lines = linesCache.get(pkg.filePath);
716
+ const line = pkg.reportedLine > 0 ? pkg.reportedLine : fallbackSearchForLineNumber(lines, pkg.filePath, pkg.name);
717
+ if (line <= 0) {
718
+ recordUnlocatableFinding(errors, unlocatableFiles, pkg.filePath);
719
+ continue;
720
+ }
567
721
  for (const vuln of malVulns) {
568
722
  const summary = vuln.summary ? ` ${vuln.summary}` : "";
569
723
  issues.push({
570
724
  filePath: pkg.filePath,
571
725
  patternId,
572
726
  toolId: TOOL_ID,
573
- line: pkg.line,
727
+ line,
574
728
  column: 1,
575
729
  message: `Malicious package detected: ${pkg.name}@${pkg.version} (${vuln.id}).${summary}`,
576
- lineContent: (0, import_tooling.getLineContent)(lines, pkg.line),
730
+ lineContent: (0, import_tooling.getLineContent)(lines, line),
577
731
  category: meta.category,
578
- severity: meta.severity
732
+ severity: meta.severity,
733
+ ...vuln.id ? { sourceId: vuln.id } : {}
579
734
  });
580
735
  }
581
736
  }
@@ -742,6 +897,7 @@ var adapter = {
742
897
  }
743
898
  const enabledPatterns = new Set(codacyConfig.patterns.map((p) => p.patternId));
744
899
  const targetFilesSet = new Set(ctx.targetFiles);
900
+ const unlocatableFiles = /* @__PURE__ */ new Set();
745
901
  const vulnPatternIds = Object.values(VULN_SEVERITY_TO_PATTERN);
746
902
  const runMaliciousScan = enabledPatterns.size === 0 || enabledPatterns.has("Trivy_malicious_packages");
747
903
  const runVulnScan = enabledPatterns.size === 0 || vulnPatternIds.some((id) => enabledPatterns.has(id)) || runMaliciousScan;
@@ -776,7 +932,14 @@ var adapter = {
776
932
  "vuln",
777
933
  "--pkg-types",
778
934
  "library",
779
- "--list-all-pkgs"
935
+ "--list-all-pkgs",
936
+ // Matches codacy-trivy's `DetectionPriority: PriorityComprehensive`.
937
+ // Detects more vulnerabilities, potentially including some false positives
938
+ // — and it is REQUIRED for Go standard-library findings: without it Trivy
939
+ // reports zero `stdlib` vulnerabilities, so a go.mod scan silently misses
940
+ // every one of them and reports a fraction of the cloud's issue count.
941
+ "--detection-priority",
942
+ "comprehensive"
780
943
  ];
781
944
  const severities = [];
782
945
  if (enabledPatterns.size === 0) {
@@ -794,21 +957,23 @@ var adapter = {
794
957
  vulnArgs.push(ctx.repositoryRoot);
795
958
  const vulnOutput = await runTrivyScan(binary, vulnArgs, ctx.repositoryRoot, errors);
796
959
  if (vulnOutput) {
797
- issues.push(
798
- ...await parseVulnerabilities(
799
- vulnOutput,
800
- enabledPatterns,
801
- targetFilesSet,
802
- ctx.repositoryRoot
803
- )
960
+ const vulnResult = await parseVulnerabilities(
961
+ vulnOutput,
962
+ enabledPatterns,
963
+ targetFilesSet,
964
+ ctx.repositoryRoot,
965
+ unlocatableFiles
804
966
  );
967
+ issues.push(...vulnResult.issues);
968
+ errors.push(...vulnResult.errors);
805
969
  if (runMaliciousScan && ctx.runner !== "container") {
806
970
  const malResult = await parseMaliciousPackages(
807
971
  vulnOutput,
808
972
  enabledPatterns,
809
973
  targetFilesSet,
810
974
  ctx.repositoryRoot,
811
- ctx.logger
975
+ ctx.logger,
976
+ unlocatableFiles
812
977
  );
813
978
  issues.push(...malResult.issues);
814
979
  errors.push(...malResult.errors);
package/dist/metadata.js CHANGED
@@ -44,7 +44,12 @@ var descriptor = {
44
44
  filePatterns: ["*"],
45
45
  supportedOS: ["linux", "darwin", "win32"],
46
46
  supportedArch: ["x64", "arm64"],
47
- supportedRunners: ["local", "container"]
47
+ supportedRunners: ["local", "container"],
48
+ // Trivy reports every CVE under one of four severity umbrella patterns, so its
49
+ // issues must upload `sourceId` (the CVE id) or two CVEs on the same manifest
50
+ // line collapse into a single Codacy result. Matches `needs_source_id_upload`
51
+ // in the Go CLI's Trivy plugin manifest.
52
+ needsSourceIdUpload: true
48
53
  };
49
54
  // Annotate the CommonJS export names for ESM import in node:
50
55
  0 && (module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/tools-trivy-0",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "description": "Trivy adapter — CLI-mode security scanner",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,7 +8,7 @@
8
8
  "dist"
9
9
  ],
10
10
  "dependencies": {
11
- "@codacy/tooling": "0.17.0"
11
+ "@codacy/tooling": "0.19.0"
12
12
  },
13
13
  "license": "MIT",
14
14
  "author": "Codacy <support@codacy.com> (https://www.codacy.com)",