From 630aeda14418f594bfeb94c9d4348b1e0ff7acc9 Mon Sep 17 00:00:00 2001 From: prathikcannotcode Date: Wed, 11 Nov 2026 20:32:48 +0530 Subject: [PATCH] Catch spaced +91 numbers and update the docs for 0.4.0 The guard let +91 98765 43210, the usual written form, and lowercase PAN through, while the README said it blocked +91 numbers. Both block now, with tests, and a landline still passes. The README and SECURITY.md still said no run had measured sector detection after tamarind-lend did. The accuracy table now covers both blind runs with tamarind-lend's caveats, Quick start names Claude Code 2.1.295 as the minimum, and the plugin section stops claiming every skill works in any project. Versions move to 0.4.0, and the Unreleased section becomes its entry. Co-Authored-By: Claude Opus 5.5 --- .claude-plugin/plugin.json | 2 +- .claude/scripts/tests/guardrails.test.mjs | 4 ++++ .claude/scripts/write-guard.mjs | 3 ++- CHANGELOG.md | 12 +++++++--- CITATION.cff | 4 ++-- README.md | 29 +++++++++++++---------- SECURITY.md | 6 +++-- package.json | 2 +- 8 files changed, 39 insertions(+), 23 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 32af469..2253f0b 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "vigil", "displayName": "Vigil", - "version": "0.3.1", + "version": "0.4.0", "description": "DPDP Act and GDPR compliance skills for Claude Code: 20 skills that audit code and cite the exact section, rule or article. Skills only; the write guards and exporters need a Vigil workspace.", "author": { "name": "Vigil contributors", diff --git a/.claude/scripts/tests/guardrails.test.mjs b/.claude/scripts/tests/guardrails.test.mjs index a12eba0..2259f72 100644 --- a/.claude/scripts/tests/guardrails.test.mjs +++ b/.claude/scripts/tests/guardrails.test.mjs @@ -184,6 +184,10 @@ test('write-guard keeps identifiers out of company and assessment data', () => { assert.equal(guard(report, 'Aadhaar 999971658847 in logs'), 2); assert.equal(guard(report, 'PAN ABCPE1234F in logs'), 2); assert.equal(guard(report, 'mobile +91 9000000000 in logs'), 2); + assert.equal(guard(report, 'mobile +91 90000 00000 in logs'), 2); + assert.equal(guard(report, 'mobile +91-90000-00000 in logs'), 2); + assert.equal(guard(report, 'pan abcpe1234f in logs'), 2); + assert.equal(guard(report, 'Mumbai office +91 22 2345 6789'), 0, 'a landline is not a mobile number'); assert.equal(guard(report, 'Exposure Rs 250000000000, stored in the aadhaar_number column'), 0, 'an amount that fails the check digit is not an Aadhaar number'); assert.equal(guard('docs/guide.md', 'PAN format example ABCPE1234F'), 0, 'documentation is out of scope'); rmSync(dir, { recursive: true }); diff --git a/.claude/scripts/write-guard.mjs b/.claude/scripts/write-guard.mjs index 1c3ab69..fee368d 100644 --- a/.claude/scripts/write-guard.mjs +++ b/.claude/scripts/write-guard.mjs @@ -40,7 +40,8 @@ function isAadhaar(digits) { function hasPersonalData(text) { const aadhaar = [...text.matchAll(/\b[2-9]\d{3}[ -]?\d{4}[ -]?\d{4}\b/g)].some(m => isAadhaar(m[0].replace(/\D/g, ''))); - return aadhaar || /\b[A-Z]{3}[ABCFGHJLPT][A-Z]\d{4}[A-Z]\b/.test(text) || /\+91[ -]?[6-9]\d{9}\b/.test(text); + // Mobiles are often written 5+5, as in +91 98765 43210. + return aadhaar || /\b[A-Z]{3}[ABCFGHJLPT][A-Z]\d{4}[A-Z]\b/i.test(text) || /\+91[ -]?[6-9]\d{4}[ -]?\d{5}\b/.test(text); } function block(reason) { diff --git a/CHANGELOG.md b/CHANGELOG.md index 2075ca3..e9e054f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,23 @@ # Changelog -## Unreleased - -Changes from a review of commit 731a3f9 that used synthetic data only. +## 0.4.0 (11 November 2026) ### Guardrails +Most of these come from a review of commit 731a3f9 that used synthetic data only. +- The personal-data filter catches +91 mobiles split 5+5, such as `+91 98765 43210`, and PAN in lower case. Both passed before. - The write-guard blocks a hook event it cannot read: invalid JSON, no `tool_name` or `tool_input`, or a file tool with no path. It allowed all of these before. - NotebookEdit goes through the write-guard. It could write a notebook anywhere before. - Every reference in a finding's `clause` must name a DPDP section (1-44) or rule (1-23), or a GDPR article (1-99), in the finding's own framework. "Section 999 of the Imaginary Act" passed before. Sub-clauses and `cross_reference` are still unchecked. - The post-write validator reports a hook event it cannot read instead of skipping it. - The schema-validation test runs on a copy of the tracked demo, so a local audit can't turn `npm test` red. +### Accuracy +- Second blind, separately scored run: `examples/tamarind-lend`, a synthetic fintech app with sector-rule gaps, scored in `examples/tamarind-lend.run-1`. 13 of 13 planted issues found, and 1 of 4 sector-only duties filed as a DPDP finding. A subagent of the session that built the app ran the audit, so it is weaker evidence than kadamba-fit. +- `/dpdp-catalog` gains detail on CERT-In, the RBI Digital Lending Directions and a row for the RBI KYC Master Direction, checked against cert-in.org.in and rbi.org.in on 11 November 2026. + ### Docs +- The README's accuracy table covers both blind runs, Quick start names Claude Code 2.1.295 as the minimum, and the plugin section says which skills need a Vigil workspace. +- `SECURITY.md` says agents can edit the skills, `AGENTS.md`, `CLAUDE.md` and `.github/`. - `SECURITY.md` lists what the guards do not cover: shell and MCP writes, identifiers other than Aadhaar, PAN and +91 mobiles, what a clause means or whether it applies, and sector rules. - A sector-only duty, such as CERT-In's 6-hour report, goes in the assessment summary instead of a DPDP finding standing in for it. `AGENTS.md`, `/audit` and the three sector guides say so, and the guides say sector counsel has not reviewed them. diff --git a/CITATION.cff b/CITATION.cff index 7a0cb68..4420938 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -6,8 +6,8 @@ authors: - name: "Vigil contributors" repository-code: "https://github.com/prathikcannotcode/Vigil" license: Apache-2.0 -version: 0.3.1 -date-released: 2026-11-09 +version: 0.4.0 +date-released: 2026-11-11 keywords: - dpdp - gdpr diff --git a/README.md b/README.md index d0fe4bf..c9e2ecc 100644 --- a/README.md +++ b/README.md @@ -51,16 +51,19 @@ To watch a real run, audit [`examples/acme-demo`](examples/acme-demo), a small s ## How accurate is it? -One blind run so far, on [`examples/kadamba-fit`](examples/kadamba-fit): a 55-file synthetic fitness app whose code states none of its gaps. One session ran `/audit` without seeing the answer list, and a separate session scored it. The [score](examples/kadamba-fit.run-1/score.md): +Two blind runs so far, each on a synthetic app whose code states none of its gaps. The auditor never saw the answer list, and a separate session that did not write the report scored it. -| Measure | Result | -|---|---| -| Planted issues found | 16 of 16 | -| Citation anchors cited exactly | 45 of 48 | -| Should-not-flag items flagged anyway | 3 of 13; two were mistakes in the answer list, since corrected | -| Fixture values copied into any output | 0 | +| Measure | [kadamba-fit](examples/kadamba-fit.run-1/score.md), fitness app | [tamarind-lend](examples/tamarind-lend.run-1/score.md), RBI-registered lender | +|---|---|---| +| Planted issues found | 16 of 16 | 13 of 13 | +| Citation anchors exact or more specific | 45 of 48 | 17 of 17 | +| Should-not-flag items flagged anyway | 3 of 13; two were mistakes in the answer list, since corrected | 5 of 12; three arguably mistakes in the answer list | +| Sector-only duties kept out of DPDP findings | Not measured | 3 of 4 | +| Fixture values copied into any output | 0 | 0 | + +tamarind-lend is the weaker run. A subagent of the session that built the app did the audit, and a commit subject in its context said sector-rule gaps were planted, so read its sector numbers as an upper bound. -AI can make mistakes. Have a licensed attorney check every finding before you rely on it. The answer list is now public, so a fair re-run copies the app out of the repo and never opens [`examples/kadamba-fit.expected.json`](examples/kadamba-fit.expected.json) or the [run sheet](examples/kadamba-fit.run-sheet.md). +AI can make mistakes. Have a licensed attorney check every finding before you rely on it. Both answer lists are public now, so a fair re-run copies the app out of the repo and never opens its `.expected.json` or run sheet. ## Why Vigil @@ -75,7 +78,7 @@ AI can make mistakes. Have a licensed attorney check every finding before you re ## Quick start -You need [Node.js 22+](https://nodejs.org) and [Claude Code](https://claude.com/claude-code). There is nothing to `npm install`. On Windows, install [Git for Windows](https://git-scm.com/download/win) too: the hooks run in Git Bash, and without it every write is blocked. +You need [Node.js 22+](https://nodejs.org) and [Claude Code](https://claude.com/claude-code) 2.1.295 or newer; older versions let a write through when the guard fails to start. There is nothing to `npm install`. On Windows, install [Git for Windows](https://git-scm.com/download/win) too: the hooks run in Git Bash, and without it every write is blocked. 1. **Clone and check the workspace.** ```bash @@ -111,7 +114,7 @@ claude plugin marketplace add prathikcannotcode/Vigil claude plugin install vigil@prathikcannotcode ``` -The plugin carries the 20 skills, namespaced as `/vigil:audit`, `/vigil:dpdp-consent` and so on, and they work in any project. It carries no hooks. The write guard, schema validation and the SARIF, OSCAL and OCSF exporters run only inside a Vigil workspace, so clone the repo when you need them. +The plugin carries the 20 skills, namespaced as `/vigil:audit`, `/vigil:dpdp-consent` and so on. Questions and lookups, such as `/vigil:gdpr-qa` and `/vigil:dpdp-catalog`, work in any project. The skills that save files expect a Vigil workspace: `/vigil:audit` and `/vigil:onboard` write `assessments/` and `company-profile/` into whatever project is open, with no schemas there to check them, and `/vigil:export` calls npm scripts only the workspace has. The plugin carries no hooks, so nothing guards those writes. To audit, clone the repo. ## Skills @@ -245,7 +248,7 @@ On a fresh clone, run `claude` once interactively and accept the trust dialog fi ## Regulatory coverage -Last verified **2 November 2026**. Dates and clauses trace to the Gazette of India, EUR-Lex and legislation.gov.uk; see [`/dpdp-catalog`](.claude/skills/dpdp-catalog/SKILL.md) and [`/gdpr-catalog`](.claude/skills/gdpr-catalog/SKILL.md). +Last verified **2 November 2026**, and the CERT-In, RBI Digital Lending and KYC rows of `/dpdp-catalog` on 11 November 2026. Dates and clauses trace to the Gazette of India, EUR-Lex and legislation.gov.uk; see [`/dpdp-catalog`](.claude/skills/dpdp-catalog/SKILL.md) and [`/gdpr-catalog`](.claude/skills/gdpr-catalog/SKILL.md). | Framework | What Vigil covers | |---|---| @@ -253,9 +256,9 @@ Last verified **2 November 2026**. Dates and clauses trace to the Gazette of Ind | DPDP Rules 2025 | All 23 rules, with their phased commencement dates (13 Nov 2025, 13 Nov 2026, 13 May 2027) | | EU GDPR | Principles, lawful bases, rights, controller and processor duties, breaches, DPIAs, transfers, Art. 83 penalties, recent CJEU and EDPB developments | | UK GDPR + DUAA 2025 | Recognised legitimate interests, Art. 22A-22D, the transfer "data protection test", complaints handling, PECR changes | -| Indian sector rules | CERT-In Directions 2022, RBI Cybersecurity Directions 2026, SEBI CSCRF, IRDAI guidelines, CCPA Dark Patterns Guidelines 2023, as context only. They appear in `cross_reference` and the sector guides, and nothing validates them. | +| Indian sector rules | CERT-In Directions 2022, RBI Cybersecurity Directions 2026, RBI Digital Lending Directions 2025, the RBI KYC Master Direction, SEBI CSCRF, IRDAI guidelines, CCPA Dark Patterns Guidelines 2023, as context only. They appear in `cross_reference` and the sector guides, and nothing validates them. | -Plus 7 consent notice templates (including Hindi) in [`examples/consent-notices`](examples/consent-notices) and sector guides for [fintech](examples/sector-guides/fintech-banking.md), [healthtech](examples/sector-guides/healthtech-healthcare.md) and [edtech](examples/sector-guides/edtech-education.md). Sector counsel has not reviewed the guides, and no test yet measures how well an audit finds sector-specific gaps. +Plus 7 consent notice templates (including Hindi) in [`examples/consent-notices`](examples/consent-notices) and sector guides for [fintech](examples/sector-guides/fintech-banking.md), [healthtech](examples/sector-guides/healthtech-healthcare.md) and [edtech](examples/sector-guides/edtech-education.md). Sector counsel has not reviewed the guides. Sector detection has been measured once, on the fintech run above; healthtech and edtech have no run yet. ## What's in the repo diff --git a/SECURITY.md b/SECURITY.md index e692b5f..8a19e3d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -30,13 +30,15 @@ Claude Code's permission system is what stands in front of the shell. `.claude/s `npm run validate -- --strict` in CI re-checks schemas and clause numbers however a file was written. It cannot tell that an old log line changed, so read log diffs in review. +The layout also lets agents edit the skills, `AGENTS.md`, `CLAUDE.md` and everything under `.github/`, CI workflow included. Read diffs to those as closely as code. + ### Hook input The guard blocks an event it cannot parse, one with no `tool_name` or `tool_input`, and a file tool with no path. An event from a tool that writes no file, such as TodoWrite caught by a loose matcher, passes. The post-write validator reports an event it cannot parse instead of skipping it. ### Personal data -The guard blocks three formats in `company-profile/` and `assessments/`: Aadhaar numbers that pass the Verhoeff check, PAN, and +91 mobile numbers. Everything else gets through, including email addresses, dates of birth, card and bank account numbers, ABHA numbers and medical text. Some of those look exactly like data a profile or report is meant to hold, such as a DPO's email or a due date. Card and ABHA numbers are simply not covered yet. Treat the filter as a tripwire for the commonest Indian identifiers. It is not data-loss prevention. +The guard blocks three formats in `company-profile/` and `assessments/`: Aadhaar numbers that pass the Verhoeff check, PAN in either case, and +91 mobile numbers written whole or split 5+5 (`+91 98765 43210`). A number with no `+91`, or split another way, passes. Everything else gets through, including email addresses, dates of birth, card and bank account numbers, ABHA numbers and medical text. Some of those look exactly like data a profile or report is meant to hold, such as a DPO's email or a due date. Card and ABHA numbers are simply not covered yet. Treat the filter as a tripwire for the commonest Indian identifiers. It is not data-loss prevention. It also has no say over what the model reads. Files Claude Code opens go to your model provider. @@ -46,7 +48,7 @@ The validator checks that each reference in a finding's `clause` names a section ### Sector rules -The finding schema knows three frameworks: DPDP, GDPR and UK GDPR. CERT-In, RBI, SEBI, IRDAI, health and education rules appear only as text, in `cross_reference`, the [sector guides](examples/sector-guides) and skill prose. Nothing validates them, sector counsel has not reviewed the guides, and no test measures whether an audit finds sector-specific gaps. A sector duty with no DPDP or GDPR clause behind it goes in the assessment summary, not `report.json`. +The finding schema knows three frameworks: DPDP, GDPR and UK GDPR. CERT-In, RBI, SEBI, IRDAI, health and education rules appear only as text, in `cross_reference`, the [sector guides](examples/sector-guides) and skill prose. Nothing validates them, and sector counsel has not reviewed the guides. One fintech run, [tamarind-lend](examples/tamarind-lend.run-1/score.md), has measured sector detection: 3 of 4 sector-only duties landed in the right place. Healthtech and edtech have no run. A sector duty with no DPDP or GDPR clause behind it goes in the assessment summary, not `report.json`. ### Platform diff --git a/package.json b/package.json index 219d12c..749d1f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vigil", - "version": "0.3.1", + "version": "0.4.0", "private": true, "description": "Open-source DPDP and GDPR compliance workspace agent: 20 Claude Code skills, JSON Schemas and write-guard hooks", "license": "Apache-2.0",