Skip to content

feat: configurable cache TTL and .bun-security-ignore file#1

Merged
muneebs merged 18 commits intomainfrom
feat/cache-ttl-and-ignore-file
Apr 3, 2026
Merged

feat: configurable cache TTL and .bun-security-ignore file#1
muneebs merged 18 commits intomainfrom
feat/cache-ttl-and-ignore-file

Conversation

@muneebs
Copy link
Copy Markdown
Owner

@muneebs muneebs commented Apr 3, 2026

Summary

  • Configurable cache TTL — adds OSV_CACHE_TTL_MS and SNYK_CACHE_TTL_MS env vars so teams can increase the TTL in CI to avoid slow cold-start scans; default remains 24 hours
  • .bun-security-ignore file — TOML-based ignore file to suppress false positives and accepted risks; fatal advisories matching an ignore rule are downgraded to warn, warn advisories are dropped; supports per-entry expires dates and a * wildcard
  • Compiled outputbuild.ts added; dist/ is now published to npm for faster startup
  • README — updated with new sections, restored original emoji style, consolidated ignore file docs into a single section

Summary by CodeRabbit

  • New Features

    • Added .bun-security-ignore support to suppress or downgrade advisories with expiry
    • Configurable cache TTL per backend via env vars; backends honor TTLs
    • Added release build step and a bundled build output for publishing
    • Integrated Git hooks for pre-commit and pre-push checks
  • Documentation

    • README updated with badge, ignore-file format, cache TTL docs, CI examples, and wording tweaks
  • Tests

    • New unit and integration tests covering ignore-file behavior
  • Chores

    • Publishing adjusted to use built/dist artifacts

muneebs added 8 commits April 3, 2026 20:40
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
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 3, 2026

Warning

Rate limit exceeded

@muneebs has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 7 minutes and 45 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d22c3d67-f1e1-497a-8b19-13b8c2b26a92

📥 Commits

Reviewing files that changed from the base of the PR and between 35732d4 and f76f05d.

📒 Files selected for processing (4)
  • README.md
  • lefthook.yml
  • src/__tests__/scanner.test.ts
  • src/ignore.ts

Walkthrough

Introduces configurable cache TTLs, a new .bun-security-ignore TOML workflow to suppress/downgrade advisories, integrates ignore handling into scanner flows, adds a TypeScript build.ts bundling script and lefthook Git hooks, and shifts publishing to produce and export dist/index.js.

Changes

Cohort / File(s) Summary
CI / Build / Hooks
\.github/workflows/release.yml, build.ts, lefthook.yml
Added bun run build to release workflow, added build.ts bundling/minify script with logging and failure handling, and introduced lefthook config for pre-commit (format/lint/type-check/test) and pre-push (audit).
Package / Publish
package.json
Switched package entry to ./dist/index.js, publishable files to include dist, added build and type-check scripts, added bun run build to publish flow, and added lefthook devDependency.
Cache TTLs & Backend config
src/config.ts, src/snyk/config.ts, src/osv.ts, src/snyk/index.ts, src/cache.ts
Made OSV/Snyk cache TTLs configurable via OSV_CACHE_TTL_MS/SNYK_CACHE_TTL_MS (with 24h fallback), passed TTL into backend objects, and updated isFresh to accept an optional ttl parameter.
Ignore file feature
src/ignore.ts
Added ignore module: TOML subset loader for .bun-security-ignore (configurable via BUN_SECURITY_IGNORE_FILE), IgnoreEntry/IgnoreList types, expiry handling, NO_IGNORE flag, loadIgnoreList() and applyIgnoreList() that match package/advisory (wildcard, case-insensitive) and return keep/downgrade/drop decisions.
Scanner integration
src/scanner.ts
Loaded ignore list in parallel with cache, added Backend.ttl to interface, switched cache checks to use backend TTL, and applied ignore decisions to all advisory return paths (with interactive downgrades and suppression logging).
Tests
src/__tests__/ignore.test.ts, src/__tests__/scanner.test.ts
Added unit tests for applyIgnoreList/loadIgnoreList (matching, wildcards, expiry, reason handling), and integration tests for scanner behavior with ignore file in CI vs interactive, wildcard suppression, and package isolation.
Documentation
README.md
Added badge, described per-package configurable cache TTLs and new env vars (OSV_CACHE_TTL_MS, SNYK_CACHE_TTL_MS, OSV_NO_IGNORE, BUN_SECURITY_IGNORE_FILE), documented .bun-security-ignore format and behavior, moved local dev instructions, updated project structure and wording.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Poem

🐰 A tiny build hops into the flow,
Ignore rules whisper which advisories go,
TTLs bend to envvars, caches behave,
From source to dist the pipeline is brave,
Logs and tests cheer as the scanner prances.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main features introduced: configurable cache TTL and the new .bun-security-ignore file for suppressing advisories.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cache-ttl-and-ignore-file

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9d17554 and 39c0e97.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • .github/workflows/release.yml
  • README.md
  • build.ts
  • lefthook.yml
  • package.json
  • src/__tests__/ignore.test.ts
  • src/__tests__/scanner.test.ts
  • src/cache.ts
  • src/config.ts
  • src/ignore.ts
  • src/osv.ts
  • src/scanner.ts
  • src/snyk/config.ts
  • src/snyk/index.ts

Comment thread lefthook.yml Outdated
Comment thread README.md Outdated
Comment thread src/config.ts Outdated
Comment thread src/ignore.ts
Comment thread src/scanner.ts
Comment thread src/snyk/config.ts Outdated
muneebs added 6 commits April 3, 2026 21:54
…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.
@muneebs
Copy link
Copy Markdown
Owner Author

muneebs commented Apr 3, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 3, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@muneebs
Copy link
Copy Markdown
Owner Author

muneebs commented Apr 3, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 3, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/__tests__/scanner.test.ts (1)

243-266: Consider extracting shared mock setup to reduce duplication.

The beforeEach setup is largely identical to the first describe block (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

📥 Commits

Reviewing files that changed from the base of the PR and between 39c0e97 and 35732d4.

📒 Files selected for processing (7)
  • README.md
  • lefthook.yml
  • src/__tests__/scanner.test.ts
  • src/config.ts
  • src/ignore.ts
  • src/scanner.ts
  • src/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

Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread src/__tests__/scanner.test.ts
muneebs and others added 3 commits April 3, 2026 22:19
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
@muneebs muneebs merged commit 2b6e03f into main Apr 3, 2026
5 checks passed
@muneebs muneebs deleted the feat/cache-ttl-and-ignore-file branch April 3, 2026 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant