secure-code-agent 0.7.0__tar.gz → 0.9.0__tar.gz
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.
- {secure_code_agent-0.7.0/src/secure_code_agent.egg-info → secure_code_agent-0.9.0}/PKG-INFO +1 -1
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0/src/secure_code_agent.egg-info}/PKG-INFO +1 -1
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/__init__.py +1 -1
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/baseline.py +31 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/cli.py +29 -4
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/config.py +32 -2
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/git_tools.py +14 -3
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/renderers.py +6 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scoring.py +41 -1
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/LICENSE +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/README.md +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/pyproject.toml +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/setup.cfg +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_agent.egg-info/SOURCES.txt +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_agent.egg-info/dependency_links.txt +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_agent.egg-info/entry_points.txt +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_agent.egg-info/requires.txt +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_agent.egg-info/top_level.txt +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/data/semgrep-offline.yaml +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/findings.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/history.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/instructions.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/pillar.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/practice.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/remediation.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/ruleset.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/sarif.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanner_status.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/__init__.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/bandit_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/base.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/builtin_rules.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/checkov_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/floor.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/gitleaks_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/gosec_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/hadolint_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/njsscan_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/npm_audit_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/osv_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/pip_audit_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/rubocop_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/scorecard_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/semgrep_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/trivy_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/trufflehog_scanner.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/standards.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/suppressions.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/triage.py +0 -0
- {secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/verify.py +0 -0
|
@@ -10,6 +10,7 @@ operator has acknowledged at a point in time. On the next run:
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
13
|
+
import enum
|
|
13
14
|
import json
|
|
14
15
|
import shutil
|
|
15
16
|
import subprocess
|
|
@@ -32,6 +33,36 @@ class BaselineEntry:
|
|
|
32
33
|
notes: str = ""
|
|
33
34
|
|
|
34
35
|
|
|
36
|
+
class State(enum.Enum):
|
|
37
|
+
"""Why the baseline is the shape it is.
|
|
38
|
+
|
|
39
|
+
`load` returns an empty mapping for a baseline that is absent, one that
|
|
40
|
+
is unreadable, and one that is genuinely empty. Downstream those look
|
|
41
|
+
identical and every finding reads as new — which is correct for the
|
|
42
|
+
first case, a silent failure for the second, and worth saying out loud
|
|
43
|
+
in all three once `fail_on_new` gates by default.
|
|
44
|
+
|
|
45
|
+
On a first run the honest message is "there is nothing to compare
|
|
46
|
+
against yet", not "2 new findings since baseline", which claims a
|
|
47
|
+
baseline exists and that these appeared after it.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
ABSENT = "absent"
|
|
51
|
+
UNREADABLE = "unreadable"
|
|
52
|
+
PRESENT = "present"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def state(path: Path) -> State:
|
|
56
|
+
"""Distinguish a missing baseline from a broken one."""
|
|
57
|
+
if not path.exists():
|
|
58
|
+
return State.ABSENT
|
|
59
|
+
try:
|
|
60
|
+
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
61
|
+
except (OSError, json.JSONDecodeError):
|
|
62
|
+
return State.UNREADABLE
|
|
63
|
+
return State.PRESENT if isinstance(raw, dict) else State.UNREADABLE
|
|
64
|
+
|
|
65
|
+
|
|
35
66
|
def load(path: Path) -> dict[str, BaselineEntry]:
|
|
36
67
|
if not path.exists():
|
|
37
68
|
return {}
|
|
@@ -338,6 +338,7 @@ def _do_audit(args: argparse.Namespace) -> int:
|
|
|
338
338
|
# ----- baseline -----
|
|
339
339
|
baseline_path = _under_root(root, args.baseline or cfg.outputs["baseline_path"])
|
|
340
340
|
baseline = baseline_mod.load(baseline_path)
|
|
341
|
+
baseline_state = baseline_mod.state(baseline_path)
|
|
341
342
|
all_findings = baseline_mod.mark_new(all_findings, baseline)
|
|
342
343
|
|
|
343
344
|
# ----- scoring -----
|
|
@@ -377,8 +378,9 @@ def _do_audit(args: argparse.Namespace) -> int:
|
|
|
377
378
|
if cfg.loc_for_scoring:
|
|
378
379
|
loc = int(cfg.loc_for_scoring.get("value", 0))
|
|
379
380
|
test_loc = 0
|
|
381
|
+
docs_loc = 0
|
|
380
382
|
else:
|
|
381
|
-
loc, test_loc = loc_under(
|
|
383
|
+
loc, test_loc, docs_loc = loc_under(
|
|
382
384
|
target,
|
|
383
385
|
cfg.include_extensions,
|
|
384
386
|
cfg.exclude_patterns,
|
|
@@ -386,6 +388,7 @@ def _do_audit(args: argparse.Namespace) -> int:
|
|
|
386
388
|
# Same set the findings were filtered against. Numerator and
|
|
387
389
|
# denominator have to describe the same repository.
|
|
388
390
|
own_artifacts,
|
|
391
|
+
cfg.docs_patterns,
|
|
389
392
|
)
|
|
390
393
|
# Dependencies come off the code-condition score and onto their own axis.
|
|
391
394
|
# A CVE in a pinned dependency is fixed with a version bump; an injection
|
|
@@ -405,7 +408,7 @@ def _do_audit(args: argparse.Namespace) -> int:
|
|
|
405
408
|
score = score_findings(scored_findings, loc, measurable)
|
|
406
409
|
axes = (
|
|
407
410
|
summarize_axis("test tree", test_findings, test_loc),
|
|
408
|
-
summarize_axis("documentation", docs_findings),
|
|
411
|
+
summarize_axis("documentation", docs_findings, docs_loc or None),
|
|
409
412
|
summarize_axis("dependencies", dependency_findings),
|
|
410
413
|
)
|
|
411
414
|
# Naming an import on the command line asserts that it contributes coverage,
|
|
@@ -425,7 +428,12 @@ def _do_audit(args: argparse.Namespace) -> int:
|
|
|
425
428
|
]
|
|
426
429
|
coverage = evaluate_coverage(executions, required)
|
|
427
430
|
# Gates see the dependency advisories; the score does not.
|
|
428
|
-
gate
|
|
431
|
+
# The gate needs to know *why* the baseline is empty to describe a first
|
|
432
|
+
# run truthfully. Passed in the config dict rather than as a parameter so
|
|
433
|
+
# the gate signature stays the one every check shares.
|
|
434
|
+
gate = evaluate_gates(
|
|
435
|
+
gated, score, {**cfg.gates, "_baseline_state": baseline_state.value}, coverage
|
|
436
|
+
)
|
|
429
437
|
verdict = build_verdict(score, cfg.gates, coverage)
|
|
430
438
|
|
|
431
439
|
# ----- write outputs -----
|
|
@@ -897,7 +905,15 @@ def _under_root(root: Path, value: str) -> Path:
|
|
|
897
905
|
def _print_summary(
|
|
898
906
|
verdict, score, gate, ran, unavailable, coverage, paths, axes=(), trend=None
|
|
899
907
|
) -> None:
|
|
900
|
-
|
|
908
|
+
# "PASS" is a claim that something was checked. With no gate configured
|
|
909
|
+
# nothing was, and saying so is the difference between a report and a
|
|
910
|
+
# false assurance.
|
|
911
|
+
if not gate.passed:
|
|
912
|
+
status = "FAIL"
|
|
913
|
+
elif gate.enforced:
|
|
914
|
+
status = "PASS"
|
|
915
|
+
else:
|
|
916
|
+
status = "NOT CONFIGURED"
|
|
901
917
|
print(f"secure-code-agent · score {verdict.headline()} · gate {status}")
|
|
902
918
|
for reason in verdict.reasons:
|
|
903
919
|
print(f" ! grade withheld: {reason}")
|
|
@@ -921,6 +937,15 @@ def _print_summary(
|
|
|
921
937
|
if not gate.passed:
|
|
922
938
|
for reason in gate.reasons:
|
|
923
939
|
print(f" ✗ {reason}")
|
|
940
|
+
elif not gate.enforced:
|
|
941
|
+
print(
|
|
942
|
+
" ! no gate is configured, so nothing here could have failed. "
|
|
943
|
+
"This audit is a report, not a check."
|
|
944
|
+
)
|
|
945
|
+
print(
|
|
946
|
+
" Set gates.fail_on_severity in secure-code-agent.json to make "
|
|
947
|
+
"findings block a build; read the work order either way."
|
|
948
|
+
)
|
|
924
949
|
written = [
|
|
925
950
|
str(p)
|
|
926
951
|
for p in (
|
|
@@ -26,6 +26,17 @@ DEFAULT_EXCLUDES: tuple[str, ...] = (
|
|
|
26
26
|
".mypy_cache/",
|
|
27
27
|
"**/*.min.js",
|
|
28
28
|
"**/*.lock",
|
|
29
|
+
# Lockfiles that are not named `.lock`. `**/*.lock` catches
|
|
30
|
+
# `poetry.lock`, `Gemfile.lock`, `Cargo.lock` and `yarn.lock` and misses
|
|
31
|
+
# every lockfile the JavaScript ecosystem actually ships:
|
|
32
|
+
# `package-lock.json` alone was 9,699 of axios's 17,532 non-code lines
|
|
33
|
+
# and 5,845 of lodash's 6,222. A generated dependency manifest is not
|
|
34
|
+
# source, and counting it inflates the denominator that decides the
|
|
35
|
+
# grade.
|
|
36
|
+
"**/package-lock.json",
|
|
37
|
+
"**/npm-shrinkwrap.json",
|
|
38
|
+
"**/pnpm-lock.yaml",
|
|
39
|
+
"**/bun.lockb",
|
|
29
40
|
)
|
|
30
41
|
|
|
31
42
|
#: Conventional test-tree locations across the languages the floor reads.
|
|
@@ -169,7 +180,24 @@ class Config:
|
|
|
169
180
|
scanners: dict[str, ScannerConfig] = field(default_factory=dict)
|
|
170
181
|
severity_overrides: dict[str, str] = field(default_factory=dict)
|
|
171
182
|
category_overrides: dict[str, str] = field(default_factory=dict)
|
|
172
|
-
|
|
183
|
+
#: Default policy: **ratchet on regressions**, not on absolute state.
|
|
184
|
+
#:
|
|
185
|
+
#: `fail_on_new` is the one gate measured to work. Severity-based
|
|
186
|
+
#: defaults cannot: `fail_on_severity: ["critical"]` caught none of four
|
|
187
|
+
#: known-vulnerable control repositories, and `["critical","high"]`
|
|
188
|
+
#: failed five of ten well-maintained ones while still missing SQL
|
|
189
|
+
#: injection and `pickle.loads`, which Bandit rates *medium*. See D15.
|
|
190
|
+
#:
|
|
191
|
+
#: The ratchet reads no severity at all, so it inherits none of that. A
|
|
192
|
+
#: false positive is baselined once and never asked about again, which
|
|
193
|
+
#: is the property a threshold cannot have. Measured across an adoption
|
|
194
|
+
#: lifecycle: first run fails (everything is new), `--bump-baseline`
|
|
195
|
+
#: accepts existing debt, an introduced SQL injection fails, reverting
|
|
196
|
+
#: passes.
|
|
197
|
+
#:
|
|
198
|
+
#: An empty `{}` was the previous default and provided no floor at all —
|
|
199
|
+
#: an absent gate cannot trip, so every audit "passed".
|
|
200
|
+
gates: dict[str, Any] = field(default_factory=lambda: {"fail_on_new": True})
|
|
173
201
|
outputs: dict[str, str] = field(default_factory=lambda: dict(DEFAULT_OUTPUTS))
|
|
174
202
|
suppressions_file: str = ".scignore.yaml"
|
|
175
203
|
loc_for_scoring: dict[str, Any] | None = None
|
|
@@ -343,7 +371,9 @@ def _from_dict(raw: dict[str, Any]) -> Config:
|
|
|
343
371
|
raise ValueError(f"invalid severity override: {', '.join(invalid)}")
|
|
344
372
|
if invalid := sorted(set(cfg.category_overrides.values()) - _CATEGORIES):
|
|
345
373
|
raise ValueError(f"invalid category override: {', '.join(invalid)}")
|
|
346
|
-
|
|
374
|
+
# An operator who writes a `gates` block chooses their own policy
|
|
375
|
+
# entirely; the ratchet default applies only when they write none.
|
|
376
|
+
cfg.gates = _validate_gates(raw["gates"]) if "gates" in raw else dict(cfg.gates)
|
|
347
377
|
|
|
348
378
|
outputs = raw.get("outputs", {})
|
|
349
379
|
if not isinstance(outputs, dict):
|
|
@@ -97,8 +97,9 @@ def loc_under(
|
|
|
97
97
|
excludes: Iterable[str],
|
|
98
98
|
test_patterns: Iterable[str] = (),
|
|
99
99
|
skip: Iterable[Path] = (),
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
docs_patterns: Iterable[str] = (),
|
|
101
|
+
) -> tuple[int, int, int]:
|
|
102
|
+
"""Non-blank in-scope lines, split into (primary, test, docs).
|
|
102
103
|
|
|
103
104
|
The split exists because the score's denominator has to move with its
|
|
104
105
|
numerator. Scoring primary-tree findings over a LOC count that included the
|
|
@@ -106,6 +107,12 @@ def loc_under(
|
|
|
106
107
|
tested — the same numerator/denominator mismatch that `exclude_patterns`
|
|
107
108
|
already caused once, arriving by a different door.
|
|
108
109
|
|
|
110
|
+
Documentation is split for the same reason, and was not: its *findings*
|
|
111
|
+
move to their own axis and out of the score, while its *lines* stayed in
|
|
112
|
+
the primary denominator. FastAPI carries 7,160 lines of `docs/en/data/`
|
|
113
|
+
— translator and contributor lists — diluting the count its code is
|
|
114
|
+
graded against. Third occurrence of one mismatch.
|
|
115
|
+
|
|
109
116
|
`skip` names the run's own artifacts — the report, the baseline, the
|
|
110
117
|
suppressions file. Dropping their *findings* without dropping their
|
|
111
118
|
*lines* is that same mismatch a third time: an audit that wrote a
|
|
@@ -114,9 +121,11 @@ def loc_under(
|
|
|
114
121
|
unchanged repository returned 0.00 and 4.25.
|
|
115
122
|
"""
|
|
116
123
|
test_patterns = tuple(test_patterns)
|
|
124
|
+
docs_patterns = tuple(docs_patterns)
|
|
117
125
|
skip = {p.resolve() for p in skip}
|
|
118
126
|
primary = 0
|
|
119
127
|
test = 0
|
|
128
|
+
docs = 0
|
|
120
129
|
# `rglob` on a file yields nothing, so a single-file audit reported zero
|
|
121
130
|
# lines — and a zero denominator is not normalised at all, so the grade
|
|
122
131
|
# became the raw subtotal. Auditing one file is supported; it should be
|
|
@@ -138,6 +147,8 @@ def loc_under(
|
|
|
138
147
|
lines = sum(1 for line in text.splitlines() if line.strip())
|
|
139
148
|
if test_patterns and is_test_path(path, root, test_patterns):
|
|
140
149
|
test += lines
|
|
150
|
+
elif docs_patterns and is_test_path(path, root, docs_patterns):
|
|
151
|
+
docs += lines
|
|
141
152
|
else:
|
|
142
153
|
primary += lines
|
|
143
|
-
return primary, test
|
|
154
|
+
return primary, test, docs
|
|
@@ -51,6 +51,12 @@ def to_json(
|
|
|
51
51
|
},
|
|
52
52
|
"gate": {
|
|
53
53
|
"passed": gate.passed,
|
|
54
|
+
# Which gates could actually have failed. Empty means no policy
|
|
55
|
+
# was configured, and `passed: true` then says only that nothing
|
|
56
|
+
# tripped — not that anything was checked. A consumer treating
|
|
57
|
+
# `passed` as a security signal needs to see this.
|
|
58
|
+
"configured": list(gate.configured),
|
|
59
|
+
"enforced": gate.enforced,
|
|
54
60
|
"reasons": list(gate.reasons),
|
|
55
61
|
"tripped": list(gate.tripped),
|
|
56
62
|
},
|
|
@@ -551,6 +551,26 @@ class GateResult:
|
|
|
551
551
|
passed: bool
|
|
552
552
|
reasons: tuple[str, ...] # human-readable trip reasons
|
|
553
553
|
tripped: tuple[str, ...] = field(default_factory=tuple)
|
|
554
|
+
#: Gates that could actually have failed this run. Empty means no
|
|
555
|
+
#: policy was configured, which is **not** the same as passing one.
|
|
556
|
+
#:
|
|
557
|
+
#: `passed` stays True in that case — no gate tripped, which is
|
|
558
|
+
#: accurate — but reporting it as PASS is a claim nothing checked. A
|
|
559
|
+
#: 200,000-line repository carrying SQL injection, `shell=True`,
|
|
560
|
+
#: `pickle.loads` and `eval` printed "gate PASS" out of the box, with
|
|
561
|
+
#: no configuration, because an absent gate cannot trip. The work
|
|
562
|
+
#: order listed all seven findings correctly at the same time.
|
|
563
|
+
#:
|
|
564
|
+
#: `_require_configured_gates` already refuses `--fail-on-gate` in this
|
|
565
|
+
#: situation, calling it "a green build with no security floor". This
|
|
566
|
+
#: is the same fact, carried far enough to reach the summary line a
|
|
567
|
+
#: person actually reads.
|
|
568
|
+
configured: tuple[str, ...] = field(default_factory=tuple)
|
|
569
|
+
|
|
570
|
+
@property
|
|
571
|
+
def enforced(self) -> bool:
|
|
572
|
+
"""Did any gate actually stand between these findings and a pass?"""
|
|
573
|
+
return bool(self.configured)
|
|
554
574
|
|
|
555
575
|
|
|
556
576
|
# Every gate, with the predicate that decides whether it can actually trip.
|
|
@@ -611,6 +631,7 @@ def evaluate_gates(
|
|
|
611
631
|
passed=not tripped,
|
|
612
632
|
reasons=tuple(reasons),
|
|
613
633
|
tripped=tuple(tripped),
|
|
634
|
+
configured=active_gates(gate_config),
|
|
614
635
|
)
|
|
615
636
|
|
|
616
637
|
|
|
@@ -670,7 +691,26 @@ def _gate_fail_on_new(
|
|
|
670
691
|
]
|
|
671
692
|
if new_findings:
|
|
672
693
|
tripped.append("fail_on_new")
|
|
673
|
-
|
|
694
|
+
# What "new" means depends on whether there is anything to be new
|
|
695
|
+
# *against*. Saying "since baseline" when no baseline exists claims
|
|
696
|
+
# these findings appeared after one, which is the opposite of the
|
|
697
|
+
# truth on a first run.
|
|
698
|
+
baseline_state = gate_config.get("_baseline_state")
|
|
699
|
+
if baseline_state == "absent":
|
|
700
|
+
reasons.append(
|
|
701
|
+
f"{len(new_findings)} finding(s), and no baseline exists yet — on a first "
|
|
702
|
+
f"run everything is new because there is nothing to compare against. "
|
|
703
|
+
f"Work the order, then re-run with --bump-baseline to accept what is "
|
|
704
|
+
f"left and gate on regressions from there."
|
|
705
|
+
)
|
|
706
|
+
elif baseline_state == "unreadable":
|
|
707
|
+
reasons.append(
|
|
708
|
+
f"{len(new_findings)} finding(s) read as new because the baseline file "
|
|
709
|
+
f"could not be parsed. Fix or delete it — a broken baseline silently "
|
|
710
|
+
f"turns an established repository back into a first run."
|
|
711
|
+
)
|
|
712
|
+
else:
|
|
713
|
+
reasons.append(f"{len(new_findings)} new finding(s) since baseline")
|
|
674
714
|
|
|
675
715
|
|
|
676
716
|
def _gate_min_score(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_agent.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_agent.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_agent.egg-info/requires.txt
RENAMED
|
File without changes
|
{secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_agent.egg-info/top_level.txt
RENAMED
|
File without changes
|
{secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/data/semgrep-offline.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/__init__.py
RENAMED
|
File without changes
|
{secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/bandit_scanner.py
RENAMED
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/builtin_rules.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/gosec_scanner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/osv_scanner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.7.0 → secure_code_agent-0.9.0}/src/secure_code_audit/scanners/trivy_scanner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|