feat: configurable cache TTL and .bun-security-ignore file#1
Conversation
Configurable cache TTL - Add OSV_CACHE_TTL_MS and SNYK_CACHE_TTL_MS env vars (values in ms, default 24h) - Expose ttl as a field on the Backend interface - createScanner passes backend.ttl into isFresh() instead of the hardcoded constant - isFresh() now accepts an optional ttl parameter (default 24h for backward compat) Ignore file (.bun-security-ignore) - New src/ignore.ts: TOML parser, loader, and matcher for the ignore file - Supported fields per [[ignore]] entry: package, advisories, reason, expires - advisories = ["*"] is a wildcard that suppresses all advisories for a package - Entries with an expires date (ISO "YYYY-MM-DD") automatically re-activate after that date - fatal advisories matched by an active ignore entry are downgraded to warn - warn advisories matched by an active ignore entry are dropped entirely - Both actions are logged to stderr so they remain visible in CI output - OSV_NO_IGNORE=true disables all ignore file processing (strict CI mode) - BUN_SECURITY_IGNORE_FILE overrides the default .bun-security-ignore path
- Rewrite build.ts for the scanner (was a copy from tamper-detection-service) - Bundle src/index.ts → dist/index.js (8 KB, minified, target: bun) - Update package.json exports to point to dist/index.js - Ship only dist/ in the npm package (drop raw src/) - Add build step to publish:npm so dist is always fresh before release
- New ignore.test.ts covers applyIgnoreList (keep/downgrade/drop, wildcard, expiry, case-insensitivity, NVD URLs) and loadIgnoreList (TOML parsing, multiple entries, missing reason) - Scanner integration tests verify downgrade, drop, wildcard, and package-scoping behaviour end-to-end through createScanner - README updated: configurable cache TTL, .bun-security-ignore section, env var tables, project structure, and feature list
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 7 minutes and 45 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
WalkthroughIntroduces configurable cache TTLs, a new Changes
Sequence DiagramsequenceDiagram
participant Scanner as Scanner.scan()
participant Cache as Cache
participant Backend as Backend (OSV/Snyk)
participant IgnoreLoader as loadIgnoreList()
participant Applier as applyIgnoreList()
participant Result as Advisories
par parallel loads
Scanner->>Cache: load cached advisories
Scanner->>IgnoreLoader: load .bun-security-ignore
and
Scanner->>Backend: fetch advisories (if needed)
end
IgnoreLoader-->>Scanner: IgnoreList
alt backend returned advisories
Backend-->>Scanner: fetched advisories
Scanner->>Scanner: merge cached + fetched
else cache hit / backend error
Cache-->>Scanner: cached advisories
end
Scanner->>Applier: apply ignore rules to advisories
loop for each advisory
Applier->>Applier: match package & advisory id
alt match & fatal
Applier->>Applier: downgrade to warn (interactive) / drop (CI)
else match & warn
Applier->>Applier: drop
else no match
Applier->>Applier: keep
end
end
Applier-->>Result: filtered advisories
Result-->>Scanner: return final list
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~55 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lefthook.yml`:
- Around line 32-38: The format and lint lefthook entries (hook names "format"
and "lint") are calling package.json scripts that hardcode paths (e.g.,
"format"/"lint" -> "biome ... ./src"), so passing {staged_files} has no effect
and stage_fixed never re-adds changes; either update the lefthook commands to
invoke biome/linters with the staged file args directly (e.g., run
biome/whatever CLI and pass {staged_files}), or modify the package.json "format"
and "lint" scripts to accept and forward CLI arguments to biome (so lefthook's
bun format {staged_files} / bun lint {staged_files} actually operate on the
provided file list); also keep stage_fixed: true on the hook entries so
formatted files are re-staged.
In `@README.md`:
- Around line 233-237: The example in bunfig.toml under the [install.security]
scanner key points to "../bun-osv-scanner/src/index.ts" from a different repo;
update the scanner value to the correct relative or absolute path to this
repository's entry file (replace "../bun-osv-scanner/src/index.ts" with the
current repo's path to its scanner entry, e.g.
"../<this-repo-name>/src/index.ts" or "./src/index.ts") so the example reflects
the actual local-dev layout.
In `@src/config.ts`:
- Around line 7-8: The current CACHE_TTL_MS uses || which treats 0 as falsy and
accepts negatives; change the assignment to explicitly parse and validate
Bun.env.OSV_CACHE_TTL_MS (e.g., const parsed = Number(Bun.env.OSV_CACHE_TTL_MS))
and then set CACHE_TTL_MS to parsed only when Number.isFinite(parsed) and parsed
>= 0, otherwise fall back to the default 24*60*60*1000; reference CACHE_TTL_MS
and Bun.env.OSV_CACHE_TTL_MS when applying this change.
In `@src/ignore.ts`:
- Around line 47-86: The parseIgnoreToml function currently keeps inline
comments and accepts incomplete entries; fix it by stripping inline comments
from rawVal before further parsing (remove text after an unquoted #), so quoted
strings and arrays don't retain trailing comments, then parse strings/arrays as
before; also ensure each pushed entry (when adding current to entries) has
required fields: normalize or default current.advisories to an array (e.g., []
if missing) and only push entries that have a non-empty package string (validate
current.package), so later calls like entry.advisories.includes(...) won't
throw; reference parseIgnoreToml, current, rawVal, entries, and IgnoreEntry when
making changes.
In `@src/scanner.ts`:
- Around line 95-99: The ignore-list logic currently downgrades ignored fatal
advisories to warn (making CI still fail); update the code in scanner.ts so that
when running in non-interactive/CI mode (e.g., process.env.CI === 'true' or
!process.stdin.isTTY) the ignored fatal advisories are removed from the returned
advisories rather than downgraded, while still writing a stderr log for
visibility; make this behavior configurable by adding an option (e.g.,
suppressIgnoredInCI or keepIgnoredAsWarn) to the function that applies the
ignore list so callers can override default CI behavior.
In `@src/snyk/config.ts`:
- Around line 15-16: The CACHE_TTL_MS export currently uses
"Number(Bun.env.SNYK_CACHE_TTL_MS) || 24*60*60*1000" which discards an explicit
0 and accepts negatives; change it to explicitly parse and validate the env
value: read Bun.env.SNYK_CACHE_TTL_MS into a variable, convert with Number or
parseInt, check Number.isFinite(value) and value >= 0, and only then use it;
otherwise fall back to the default 24*60*60*1000. Ensure the symbol name
CACHE_TTL_MS remains the export and clamp or reject negative values so 0 is
honored.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 01198958-c809-4dc6-858b-80754d8ca4b2
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (14)
.github/workflows/release.ymlREADME.mdbuild.tslefthook.ymlpackage.jsonsrc/__tests__/ignore.test.tssrc/__tests__/scanner.test.tssrc/cache.tssrc/config.tssrc/ignore.tssrc/osv.tssrc/scanner.tssrc/snyk/config.tssrc/snyk/index.ts
…ntries - Add stripInlineComment() so trailing # comments on string and array values are removed before parsing; fixes expires/advisories fields being corrupted when users follow the README examples with comments - Extract pushCurrent() helper used at both [[ignore]] boundaries and end-of-input; validates current.package before pushing and defaults current.advisories to [] so entry.advisories.includes() never throws on an entry that was missing its advisories field
Downgrading to warn still auto-cancels bun install in CI, making the ignore file ineffective for fatal advisories. In non-interactive environments (CI=true or no stdin TTY), ignored fatal advisories are now dropped entirely while still logging to stderr for visibility. Interactive sessions retain the downgrade-to-warn behaviour so the user can make a final call at the prompt.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Bun's built-in parser handles the full TOML spec natively — inline comments, proper string escaping, multiline strings, date literals — removing the need for stripInlineComment, pushCurrent, and all the line-by-line parsing logic. Bare TOML local dates in the expires field are normalised to ISO strings for consistency with the quoted form.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
src/__tests__/scanner.test.ts (1)
243-266: Consider extracting shared mock setup to reduce duplication.The
beforeEachsetup is largely identical to the firstdescribeblock (lines 50-72). A shared helper could reduce duplication while keeping the ignore-file-specific behavior parameterized.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/__tests__/scanner.test.ts` around lines 243 - 266, The beforeEach block duplicating setup (spyOn globalThis.fetch => fetchSpy, spyOn(Bun, 'file') => fileSpy with special-case for '.bun-security-ignore' returning IGNORE_TOML, spyOn(Bun, 'write') => writeSpy, and spyOn(fsPromises, 'rename') => renameSpy) should be extracted into a shared helper (e.g., setupDefaultMocks) used by both describe blocks; implement the helper to accept a parameter or callback to customize the Bun.file behavior for the ignore-file case and then replace the duplicated beforeEach with calls to setupDefaultMocks in both locations, ensuring the same symbols (fetchSpy, fileSpy, writeSpy, renameSpy, Bun.file, fsPromises.rename, IGNORE_TOML) are initialized by the helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@README.md`:
- Line 185: Update the example `expires` value to a future date so copied
configs don't immediately re-activate; change the `expires = "2025-12-31"`
example to a date beyond today (e.g., `expires = "2027-12-31"`) in the README
example where the `expires` key is shown.
- Around line 193-200: Update the README "Behaviour" table to show that ignored
advisories are handled differently depending on mode: in interactive sessions
matched `fatal` advisories are downgraded to `warn`, while in CI/non-interactive
sessions matched `fatal` advisories are suppressed (logged but not returned);
likewise show that `warn` advisories are dropped. Replace the example expiry
date `2025-12-31` with a future date such as `2026-12-31`. Also edit the module
comment in src/ignore.ts (the comment block around the top of the file) to
document the same CI/non-interactive behavior (i.e., that matched `fatal`
advisories are suppressed in CI or when stdin is not a TTY) so the docs match
the implementation in the scanner logic.
In `@src/__tests__/scanner.test.ts`:
- Around line 301-327: The test overrides process.stdin.isTTY but doesn't
preserve the original property descriptor, so save the original descriptor
before modifying (e.g., const origIsTTYDescriptor =
Object.getOwnPropertyDescriptor(process.stdin, 'isTTY')) and in the finally
block restore it using Object.defineProperty(process.stdin, 'isTTY',
origIsTTYDescriptor) or delete the property if origIsTTYDescriptor is undefined;
keep the existing origCI restore logic intact and apply these changes around the
Object.defineProperty calls in the scanner.test.ts block that sets isTTY.
---
Nitpick comments:
In `@src/__tests__/scanner.test.ts`:
- Around line 243-266: The beforeEach block duplicating setup (spyOn
globalThis.fetch => fetchSpy, spyOn(Bun, 'file') => fileSpy with special-case
for '.bun-security-ignore' returning IGNORE_TOML, spyOn(Bun, 'write') =>
writeSpy, and spyOn(fsPromises, 'rename') => renameSpy) should be extracted into
a shared helper (e.g., setupDefaultMocks) used by both describe blocks;
implement the helper to accept a parameter or callback to customize the Bun.file
behavior for the ignore-file case and then replace the duplicated beforeEach
with calls to setupDefaultMocks in both locations, ensuring the same symbols
(fetchSpy, fileSpy, writeSpy, renameSpy, Bun.file, fsPromises.rename,
IGNORE_TOML) are initialized by the helper.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6cfaecac-ba45-4366-987d-b46a2a445f57
📒 Files selected for processing (7)
README.mdlefthook.ymlsrc/__tests__/scanner.test.tssrc/config.tssrc/ignore.tssrc/scanner.tssrc/snyk/config.ts
✅ Files skipped from review due to trivial changes (1)
- lefthook.yml
🚧 Files skipped from review as they are similar to previous changes (4)
- src/config.ts
- src/snyk/config.ts
- src/ignore.ts
- src/scanner.ts
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Muneeb Samuels <muneebs@users.noreply.github.com>
- Expand README Behaviour table to show fatal advisories are downgraded to warn in interactive sessions but suppressed entirely in CI/non-TTY - Update ignore.ts module comment with the same CI/non-interactive detail - Update example expires date from 2025-12-31 to 2026-12-31 in both files - Fix scanner.test.ts to save and restore the original isTTY property descriptor instead of hardcoding value: undefined on teardown - Apply biome useLiteralKeys fixes (bracket → dot notation) in ignore.ts
- loadIgnoreList: split file-read and TOML-parse into separate try/catch blocks so ENOENT is silently ignored but a malformed ignore file logs a warning to stderr instead of silently producing zero entries - lefthook.yml: remove leading space from pre-push and pre-commit root keys; normalise child indentation to 2 spaces throughout - README: add `bunx lefthook install` to the setup steps so contributors get the pre-commit hooks after cloning
Summary
OSV_CACHE_TTL_MSandSNYK_CACHE_TTL_MSenv vars so teams can increase the TTL in CI to avoid slow cold-start scans; default remains 24 hours.bun-security-ignorefile — TOML-based ignore file to suppress false positives and accepted risks;fataladvisories matching an ignore rule are downgraded towarn,warnadvisories are dropped; supports per-entryexpiresdates and a*wildcardbuild.tsadded;dist/is now published to npm for faster startupSummary by CodeRabbit
New Features
.bun-security-ignoresupport to suppress or downgrade advisories with expiryDocumentation
Tests
Chores