GitHub Packages · MIT · Node ≥ 18

retention-warden

Lint your data-retention policy as code: every data category gets a period, a legal basis and an action at expiry.

Lint a declarative data-retention policy (YAML or JSON): missing or invalid periods, indefinite retention, conflicting rules, data categories held in systems with no rule, unknown categories/systems, missing legal basis, expiry action, trigger and owner, over-long periods, overdue reviews. SARIF, GitHub Action. Zero deps.

  1. 1 Point the @mrlouiswright scope at GitHub Packages in .npmrc

    @mrlouiswright:registry=https://npm.pkg.github.com
  2. 2 Install

    npm i -D @mrlouiswright/retention-warden
Installing from GitHub Packages needs a token

Published to GitHub Packages, not npmjs.com. GitHub Packages needs a GitHub token with read:packages to install, even for public packages: add //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} to .npmrc or run npm login --registry=https://npm.pkg.github.com.

~/project — zsh
$ npx retention-warden test/fixtures/bad/retention.ymlretention-warden — test/fixtures/bad/retention.ymlpolicy test/fixtures/bad/retention.yml HIGH     test/fixtures/bad/retention.yml:12 "chat" is held in postgres but no rule covers it [retention-category-unmapped]HIGH     test/fixtures/bad/retention.yml:21 Rules #1 and #2 disagree for account: P2Y/delete vs P10Y/delete [retention-conflict]HIGH     test/fixtures/bad/retention.yml:32 marketing is kept indefinitely [retention-indefinite]HIGH     test/fixtures/bad/retention.yml:36 Period "soon" for invoices in s3 is not understood [retention-period-invalid]MEDIUM   test/fixtures/bad/retention.yml:13 System "postgres" holds undeclared category "marketing" [retention-category-unknown]MEDIUM   test/fixtures/bad/retention.yml:22 account kept 10y, over maxPeriod 5y [retention-exceeds-max]
Sample output from the retention-warden README.

Why it exists

GDPR's storage-limitation principle, the UK GDPR and most security questionnaires ask the same thing: how long do you keep each kind of personal data, why, and what happens afterwards? The answer usually lives in a spreadsheet that drifts away from the systems it describes. retention-warden reads a small, declarative retention.yml (categories, systems and rules), checks it is complete and consistent, and fails CI when someone adds a new data category without a rule or sets an over-long or indefinite period.

CLI, library and GitHub Action. Node.js 18+, zero runtime dependencies, runs offline. By Louis Wright.

Features

Coverage

every category held in a system needs a rule (category-wide or system-specific); undeclared categories and systems are reported; unused categories are noted.

Periods

ISO 8601 (P30D, P6M, P2Y) or plain words (90 days, 6 years); invalid periods, missing periods and indefinite retention of personal data (without justification) are high severity.

Limits

defaults.maxPeriod and defaults.specialMaxPeriod (for special: true categories such as health data).

Consistency

conflicting rules for the same category and system, and exact duplicates.

Accountability

legal basis, action at expiry (delete, anonymise, archive, review, ...), trigger (when the clock starts), owner, and a reviewed date checked against reviewEveryMonths (default 12).

Precise locations

findings carry the line in your YAML file, so SARIF annotations land on the right rule.

Usage

# Example

 npx retention-warden

# Example

 npx retention-warden policies/retention.yml --fail-on medium

# Example

 npx retention-warden . --max-period P7Y --special-max-period P1Y

# Example

 npx retention-warden --format sarif > retention.sarif

CI / GitHub Action

permissions:
  contents: read
  security-events: write   # only needed for the SARIF upload
jobs:
  retention-warden:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
        with: { persist-credentials: false }
      - uses: MrLouisWright/retention-warden@v1
        with:
          path: .
          fail-on: high
          sarif: retention-warden.sarif
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with: { sarif_file: retention-warden.sarif }

Options & flags

FlagDescription
Policy
--max-period <p>Longest allowed period, e.g. P7Y (overrides defaults.maxPeriod)
--review-every-months <n>Review interval (default 12)
Common
-c, --config <file>JSON config (default: .retention-warden.json or "retentionWarden" in package.json)
-f, --format <fmt>text (default) | json | sarif | markdown
--json <file>Also write a JSON report
--sarif <file>Also write SARIF 2.1.0
--markdown <file>Also write Markdown (e.g. for a job summary)
--fail-on <sev>critical | high | medium | low | info | none (default: high)
--list-rulesPrint rules and default severities
--no-fixesHide fix suggestions in text output
--no-colorDisable colours

Full reference, config file format and API in the README.

Support this project

Support open-source security tools

I build free, open-source tools that help developers ship safer websites. Every coffee helps keep them maintained, documented and free for everyone.

Buy me a coffee (opens Stripe in a new tab)

Secure checkout by Stripe

Portrait of Louis Wright

About the author

Louis Wright

Cyber security researcher and software engineer based in the UK.

More tools by Louis Wright

58 tools

Sentinels security scanners & CI gates · 20