You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: services/apps/git_integration/src/crowdgit/services/vulnerability_scanner/README.md
+23-3Lines changed: 23 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -20,9 +20,14 @@ The binary follows the same **subprocess + JSON stdout** pattern used by the `so
20
20
21
21
## Key design decisions
22
22
23
-
### OSV Scanner returns a non-nil error when vulnerabilities are found
23
+
### OSV Scanner uses sentinel errors for expected outcomes
24
24
25
-
This is counterintuitive but intentional — OSV Scanner behaves like `grep`: it returns a non-zero/error when it finds matches. We treat this as success and only propagate the error if no results came back alongside it.
25
+
OSV Scanner returns sentinel errors for two non-failure cases:
26
+
27
+
-`ErrVulnerabilitiesFound` — the scanner found vulnerabilities. Like `grep` returning exit code 1 on a match, this is an expected outcome. We treat it as success and surface the results normally.
28
+
-`ErrNoPackagesFound` — the repo contains no scannable package manifests (e.g. a pure C or shell project). This is not a failure — the scanner ran correctly and determined there is nothing to check. The scan record gets status `no_packages_found` and the Python side does not treat it as an error.
29
+
30
+
Any other non-nil error from the scanner is a real failure.
@@ -59,9 +64,19 @@ On each scan, rather than deleting all previous findings and inserting fresh one
59
64
60
65
This preserves history. You can tell when a vulnerability was first detected, when it was last seen, and when it went away — without needing a separate audit log. It also makes it cheap to compute `new_count` (rows that were truly inserted, not updated) and `resolved_count` (rows still carrying the newly-set `resolved_at` after the upsert pass).
61
66
67
+
### Transitive dependency scanning
68
+
69
+
By default the scanner resolves the full transitive dependency graph, not just direct dependencies declared in lockfiles. This catches vulnerabilities in indirect deps that the project never explicitly references.
70
+
71
+
Transitive scanning is expensive for repos with large or deep package ecosystems — projects like ML frameworks with hundreds of transitive Python dependencies can exhaust the 3-minute scan timeout. The scanner handles this automatically:
72
+
73
+
1.**First scan**: attempt with transitive=true. If it times out, retry immediately with transitive=false (direct deps only). The result of whichever mode succeeded is stored.
74
+
2.**Subsequent scans**: reuse the same mode as the previous completed scan (`transitive_deps_scanned` stored per scan record). This avoids flip-flopping between modes and keeps results comparable across scans.
75
+
76
+
62
77
### Scan tracking
63
78
64
-
Every invocation creates a row in `vulnerability_scans` before the scan starts (status: running) and updates it on completion with duration, counts, and any error. This makes it possible to detect stalled or crashed scans and gives a simple history of scan health per repo.
79
+
Every invocation creates a row in `vulnerability_scans` before the scan starts (status: `running`) and updates it on completion with duration, counts, and any error. Terminal statuses are `success`, `no_packages_found`, and `failure`. This makes it possible to detect stalled or crashed scans and gives a simple history of scan health per repo.
65
80
66
81
## Building
67
82
@@ -85,3 +100,8 @@ Output is a JSON object on stdout:
0 commit comments