mirror of
https://github.com/onyx-dot-app/onyx.git
synced 2026-10-21 19:02:45 -05:00
ci(cve-alerts): let the fix bot edit compiled requirements files (#16362)
<!-- claude-slack-attribution -->
_Requested by **Nikolas Garza** · [Slack
thread](https://onyx-company.slack.com/archives/C0C5SMAM4TT/p1792538621965719?thread_ts=1792538621.965719&cid=C0C5SMAM4TT)_
Before: The CVE Alerts fix bot could edit only `package.json` and
`pyproject.toml` (plus regenerated lockfiles). It could not fix a pin in
`initial-requirements.txt`, so the audit kept failing.
After: The bot can also edit compiled requirements files
(`*requirements*.txt`, today only `initial-requirements.txt`). It still
rejects source code, workflows, and all other files.
This lets the bot fix a pin in any manifest file that the audit can fail
on and that the workflow can safely validate.
How: PR #16337 missed a certifi pin in `initial-requirements.txt`, and
#16361 fixed it by hand. The change adds `**/*requirements*.txt` to the
editable file list and to the patch allowlist in `cve-alerts.yml`.
`cve-check-manifests.ts` now checks `.txt` edits: only the package's
`==` line may change, and only to the fixed version. `cve-fix.md` tells
the bot to edit that line and keep the header and `# via` comments.
`backend/requirements/*.txt` stay workflow-regenerated and off-limits to
the bot. Go and Rust alerts stay out of scope, because the bot has no
regeneration step for them.
Linear: none
---
_Generated by [Claude
Code](https://claude.ai/code/session_01E2K27DFf4J3iAGXYpe4xtR)_
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Lets the CVE Alerts fix bot edit compiled requirements files so it can
fix a pin in `initial-requirements.txt` instead of leaving the audit
failing.
- Requirements files are validated line by line: only the package's
plain `==` pin may change to the fixed version, byte-identical
elsewhere.
- `backend/requirements/*.txt` stay workflow-regenerated and off-limits;
source code, workflows, and other files are still rejected.
<sup>Written for commit 17a29722b7.
Summary will update on new commits.</sup>
<a
href="https://cubic.dev/pr/onyx-dot-app/onyx/pull/16362?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="View guided diff"
src="https://www.cubic.dev/buttons/review-in-cubic-light.svg"></picture></a>
<a
href="https://www.cubic.dev/action/auto-fix/pr/onyx-dot-app/onyx/16362?returnTo=https%3A%2F%2Fgithub.com%2Fonyx-dot-app%2Fonyx%2Fpull%2F16362&source=description"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"><img
alt="Turn on auto-fix"
src="https://www.cubic.dev/buttons/turn-on-auto-fix-light.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
- **Bug Fixes**
- CVE checks now include supported Python `requirements*.txt` files
alongside existing dependency manifests.
- Dependency updates are validated line by line to require exact fixed
versions while preserving file structure and metadata.
- Unsupported requirement syntax and unsafe version changes are rejected
during validation.
- **Documentation**
- Dependency-fix guidance now covers compiled requirements files and
preserves generated metadata during updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
ae9b863612
commit
4f69237d85
3 files changed
+69
-9
No files matched your search
@@ -1,6 +1,9 @@
|
||||
// Usage: bun cve-check-manifests.ts <package> <fixed version> <file>...
|
||||
// Fails unless a manifest's only changes from HEAD pin the package's dependency
|
||||
// entries to the fixed version, since CVE Alerts runs it on Claude's edits.
|
||||
// Manifests are package.json, pyproject.toml, and compiled requirements .txt files.
|
||||
// A requirements file may change only the version after "==" on the package's
|
||||
// plain pin line.
|
||||
import { $ } from "bun";
|
||||
|
||||
type Table = Record<string, unknown>;
|
||||
@@ -21,6 +24,13 @@ const normalizePython = (name: string) =>
|
||||
const requirementName = (requirement: string) =>
|
||||
/^\s*([A-Za-z0-9][A-Za-z0-9._-]*)/.exec(requirement)?.[1] ?? "";
|
||||
|
||||
const isRequirementsFile = (file: string) => file.endsWith(".txt");
|
||||
|
||||
// A requirements line that starts the package's entry. Indented lines (the
|
||||
// "# via" comments, hashes) and options never match.
|
||||
const isRequirementLine = (line: string, pkg: string) =>
|
||||
/^[A-Za-z0-9]/.test(line) && normalizePython(requirementName(line)) === normalizePython(pkg);
|
||||
|
||||
const isTable = (v: unknown): v is Table =>
|
||||
v !== null && typeof v === "object" && !Array.isArray(v);
|
||||
|
||||
@@ -134,7 +144,41 @@ function badSpecs(file: string, beforeText: string, afterText: string, pkg: stri
|
||||
return now.filter((r) => r.includes("@") || !pinned.test(r));
|
||||
}
|
||||
|
||||
function stripped(file: string, text: string, pkg: string): Table {
|
||||
// A compiled requirements file gets a line-by-line check, since a marker, a
|
||||
// continuation, a comment, or an option on a changed line can carry content that
|
||||
// a strip-and-compare would hide. Only a plain "name==version" line of the
|
||||
// package may change, and only its version.
|
||||
const PLAIN_PIN = /^([A-Za-z0-9][A-Za-z0-9._-]*==)([A-Za-z0-9._+!*-]+)(.*)$/s;
|
||||
const UNSAFE_PIN = /[\\#;@]|--/;
|
||||
|
||||
function requirementsProblems(beforeText: string, afterText: string, pkg: string, version: string): string[] {
|
||||
const was = beforeText.split("\n");
|
||||
const now = afterText.split("\n");
|
||||
if (was.length !== now.length) {
|
||||
return [`line count changed from ${was.length} to ${now.length}`];
|
||||
}
|
||||
const problems: string[] = [];
|
||||
now.forEach((line, i) => {
|
||||
const old = was[i]!;
|
||||
if (line === old) {
|
||||
return;
|
||||
}
|
||||
const pin = PLAIN_PIN.exec(old);
|
||||
const allowed =
|
||||
version !== "" &&
|
||||
isRequirementLine(old, pkg) &&
|
||||
pin !== null &&
|
||||
!UNSAFE_PIN.test(old) &&
|
||||
!UNSAFE_PIN.test(line) &&
|
||||
line === `${pin[1]}${version}${pin[3]}`;
|
||||
if (!allowed) {
|
||||
problems.push(`line ${i + 1} is not a plain version change of ${pkg}`);
|
||||
}
|
||||
});
|
||||
return problems;
|
||||
}
|
||||
|
||||
function stripped(file: string, text: string, pkg: string): unknown {
|
||||
return file.endsWith(".toml")
|
||||
? stripPython(Bun.TOML.parse(text) as Table, pkg)
|
||||
: stripNpm(JSON.parse(text) as Table, pkg);
|
||||
@@ -150,6 +194,13 @@ let failed = false;
|
||||
for (const file of files) {
|
||||
const beforeText = await $`git show HEAD:${file}`.text();
|
||||
const text = await Bun.file(file).text();
|
||||
if (isRequirementsFile(file)) {
|
||||
for (const problem of requirementsProblems(beforeText, text, pkg, version)) {
|
||||
console.log(`::error::${file} changes more than the ${pkg} version: ${problem}`);
|
||||
failed = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!Bun.deepEquals(stripped(file, beforeText, pkg), stripped(file, text, pkg))) {
|
||||
console.log(`::error::${file} changes more than ${pkg}`);
|
||||
failed = true;
|
||||
|
||||
Reference in new issue
Block a user