Skip to content

ci(plugins): validate manifests against JSON Schema on every PR#71

Merged
ethanolivertroy merged 6 commits into
GRCEngClub:mainfrom
DevamShah:ci/validate-plugin-manifests
Apr 26, 2026
Merged

ci(plugins): validate manifests against JSON Schema on every PR#71
ethanolivertroy merged 6 commits into
GRCEngClub:mainfrom
DevamShah:ci/validate-plugin-manifests

Conversation

@DevamShah

@DevamShah DevamShah commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a CI workflow that schema-validates every plugin.json plus the top-level .claude-plugin/marketplace.json on each PR.
  • Catches the exact class of bug fixed in fix(plugins): use object form for plugin.json author field #70 (author field as bare string instead of object) before it lands on main and breaks claude plugin install for end users.
  • Ships two committed JSON Schemas (schemas/plugin.schema.json, schemas/marketplace.schema.json) so the manifest contract is documented in the repo, and contributors can validate locally with one command.

Stacked on #70. The validator passes against the patched tree on that branch; against current main, it would correctly fail on the six manifests #70 fixes. Best reviewed after #70 merges.

Why a custom schema, not claude plugin validate

claude plugin validate works locally but rejects unknown keys in strict mode — including framework_metadata, which framework plugins (soc2, nist-800-53, cmmc, …) use intentionally. Running it across the repo would false-flag ~20 plugins.

The schemas here mirror what claude plugin install actually enforces at install time: required fields and field types, with additionalProperties: true for extension keys. Any manifest that passes here installs cleanly; any that fails won't.

Files

  • schemas/plugin.schema.json — plugin.json contract. Requires author to be an object, requires name and version, allows extension keys.
  • schemas/marketplace.schema.json — marketplace.json contract.
  • tests/validate-plugin-manifests.sh — ajv-cli loop, emits GitHub Actions error annotations on failure.
  • .github/workflows/validate-plugin-manifests.yml — triggers on manifest or schema changes. Matches the existing contract-test.yml style: Node 20, ajv-cli@5, ajv-formats@3, concurrency cancellation.

Test plan

  • All 33 current manifests pass against the new schemas (verified locally with bash tests/validate-plugin-manifests.sh)
  • Reverting any one manifest to the broken "author": "<string>" form fails the job with an inline ::error file=... annotation pointing at the offending file
  • Exit code is 1 on failure, 0 on success
  • Workflow path filter only triggers on relevant changes (manifest, schema, workflow, validation script)

Local usage

npm install --no-save ajv-cli@5 ajv-formats@3
bash tests/validate-plugin-manifests.sh

Same command CI runs.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores

    • Added CI workflow to validate plugin and marketplace manifests against JSON schemas
    • Updated plugin author metadata from string to structured object across connectors and plugins
    • Added JSON schemas for plugin and marketplace manifest validation
    • Added a validation script to check manifest compliance and fail on invalid manifests
  • Documentation

    • Updated contributing guide to reflect the new plugin manifest author structure

@DevamShah DevamShah requested a review from a team as a code owner April 26, 2026 13:25
@qodo-code-review

Copy link
Copy Markdown
ⓘ You are approaching your monthly quota for Qodo. Upgrade your plan

Review Summary by Qodo

Fix plugin manifest author field and add schema validation CI workflow

🐞 Bug fix 🧪 Tests

Grey Divider

Walkthroughs

Description
• Fixes six plugin manifests with invalid author field (bare string instead of object)
• Adds JSON Schema validation for all plugin and marketplace manifests in CI
• Implements local validation script using ajv-cli for contributor use
• Updates documentation example to show correct author object format
Diagram
flowchart LR
  A["Six broken plugin.json files<br/>author as string"] -->|"Fix to object form"| B["Valid manifests"]
  C["New JSON Schemas<br/>plugin + marketplace"] -->|"Validate on PR"| D["CI workflow"]
  E["Validation script<br/>ajv-cli"] -->|"Local + CI use"| D
  F["CONTRIBUTING.md<br/>example"] -->|"Updated to object"| B
Loading

Grey Divider

File Changes

1. tests/validate-plugin-manifests.sh 🧪 Tests +65/-0

Add manifest validation script with ajv-cli

tests/validate-plugin-manifests.sh


2. .github/workflows/validate-plugin-manifests.yml ⚙️ Configuration changes +41/-0

Add CI workflow for manifest schema validation

.github/workflows/validate-plugin-manifests.yml


3. schemas/plugin.schema.json 📝 Documentation +42/-0

Define plugin.json JSON Schema contract

schemas/plugin.schema.json


View more (8)
4. schemas/marketplace.schema.json 📝 Documentation +46/-0

Define marketplace.json JSON Schema contract

schemas/marketplace.schema.json


5. docs/CONTRIBUTING.md 📝 Documentation +1/-1

Update author field example to object form

docs/CONTRIBUTING.md


6. plugins/connectors/aws-inspector/.claude-plugin/plugin.json 🐞 Bug fix +1/-1

Fix author field from string to object

plugins/connectors/aws-inspector/.claude-plugin/plugin.json


7. plugins/connectors/gcp-inspector/.claude-plugin/plugin.json 🐞 Bug fix +1/-1

Fix author field from string to object

plugins/connectors/gcp-inspector/.claude-plugin/plugin.json


8. plugins/connectors/github-inspector/.claude-plugin/plugin.json 🐞 Bug fix +1/-1

Fix author field from string to object

plugins/connectors/github-inspector/.claude-plugin/plugin.json


9. plugins/connectors/okta-inspector/.claude-plugin/plugin.json 🐞 Bug fix +1/-1

Fix author field from string to object

plugins/connectors/okta-inspector/.claude-plugin/plugin.json


10. plugins/fedramp-ssp/.claude-plugin/plugin.json 🐞 Bug fix +1/-1

Fix author field from string to object

plugins/fedramp-ssp/.claude-plugin/plugin.json


11. plugins/oscal/.claude-plugin/plugin.json 🐞 Bug fix +1/-1

Fix author field from string to object

plugins/oscal/.claude-plugin/plugin.json


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented Apr 26, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. GHA command injection risk🐞 Bug ⛨ Security
Description
tests/validate-plugin-manifests.sh interpolates PR-controlled file paths into GitHub Actions
workflow commands (::group:: and ::error) without escaping. A crafted manifest filename could
inject additional workflow commands into CI logs and mislead reviewers or alter CI behavior.
Code

tests/validate-plugin-manifests.sh[R37-46]

+  echo "::group::$file"
+  if ajv validate \
+      --spec=draft2020 \
+      -s "$schema" \
+      -d "$file" \
+      --errors=line; then
+    echo "  ✓ $file"
+  else
+    echo "::error file=$file::Manifest failed schema validation against $schema"
+    fail=1
Evidence
$file is sourced from find over the repository tree (PR-controlled on pull_request runs) and is
directly embedded into GitHub Actions workflow command syntax, which is delimiter/newline sensitive.

tests/validate-plugin-manifests.sh[33-49]
tests/validate-plugin-manifests.sh[55-58]
.github/workflows/validate-plugin-manifests.yml[3-21]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`tests/validate-plugin-manifests.sh` prints GitHub Actions workflow commands (`::group::...` and `::error file=...::...`) containing a manifest path taken from `find`. Because filenames are PR-controlled in a `pull_request` workflow, an attacker can craft a filename containing workflow-command delimiters/newlines to inject additional workflow commands.
## Issue Context
This only needs to be safe in CI, but the script currently emits workflow commands unconditionally.
## Fix Focus Areas
- tests/validate-plugin-manifests.sh[33-49]
- tests/validate-plugin-manifests.sh[55-58]
## Suggested fix
- Add a small escape helper for GitHub Actions workflow commands (escape `%`, `\r`, `\n` at minimum), e.g. replace `%`→`%25`, `\n`→`%0A`, `\r`→`%0D`.
- Use the escaped value when emitting `::group::` and `::error file=...::...`.
- (Optional) Only emit `::group::/::error` when `GITHUB_ACTIONS=true`; otherwise print normal messages for local runs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Zero-manifest pass possible 🐞 Bug ☼ Reliability
Description
tests/validate-plugin-manifests.sh will exit successfully even if no plugin manifests are discovered
(or if discovery fails), printing "All manifests valid." despite validating none. This can cause CI
to silently stop validating manifests after a repo layout change or discovery bug.
Code

tests/validate-plugin-manifests.sh[R55-65]

+while IFS= read -r manifest; do
+  validate_file "$plugin_schema" "$manifest"
+done < <(find plugins -type f -name plugin.json -path '*/.claude-plugin/*' | sort)
+
+echo
+echo "Validated $checked manifest(s)."
+if [[ $fail -ne 0 ]]; then
+  echo "::error::One or more manifests failed schema validation"
+  exit 1
+fi
+echo "All manifests valid."
Evidence
The script reads manifests from a find command; if it yields no lines, checked remains 0, fail
remains 0, and the script prints success. There is no guard requiring that at least one plugin
manifest was validated or that find succeeded.

tests/validate-plugin-manifests.sh[15-16]
tests/validate-plugin-manifests.sh[55-65]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The validation script can report success even when it validated zero plugin manifests (e.g., `find` returns no results, or the repo structure changes). This undermines the CI guardrail.
## Issue Context
The script already tracks `checked` and has a success message at the end; it just needs an explicit check that at least one plugin manifest was validated.
## Fix Focus Areas
- tests/validate-plugin-manifests.sh[55-65]
## Suggested fix
- After the `find` loop, add a guard such as:
- if `checked == 0` (or if only marketplace was validated and you expect plugins), emit `::error::No plugin manifests found` and exit non-zero.
- Alternatively, precompute the manifest list with `mapfile -t` and fail if the array is empty.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

3. Unused ajv-formats install 🐞 Bug ⚙ Maintainability
Description
The workflow installs ajv-formats@3, but the manifest validation schemas do not use any format
keywords, and the script does not load the formats plugin. This adds avoidable install time and
creates confusion about whether formats are being validated.
Code

.github/workflows/validate-plugin-manifests.yml[R37-38]

+      - name: Install ajv-cli + formats
+        run: npm install --no-save ajv-cli@5 ajv-formats@3
Evidence
The workflow installs ajv-formats, but tests/validate-plugin-manifests.sh invokes ajv validate
without -c ajv-formats. Separately, the new schemas define email/url fields as plain strings
without format, so ajv-formats provides no benefit here.

.github/workflows/validate-plugin-manifests.yml[37-41]
tests/validate-plugin-manifests.sh[38-43]
schemas/plugin.schema.json[23-33]
schemas/marketplace.schema.json[11-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
CI installs `ajv-formats@3`, but the manifest validation job doesn’t load it and the current schemas don’t use `format` constraints. This is extra work and slightly misleading.
## Issue Context
`tests/validate-contract-fixtures.sh` loads ajv-formats explicitly via `-c ajv-formats`, but `tests/validate-plugin-manifests.sh` does not.
## Fix Focus Areas
- .github/workflows/validate-plugin-manifests.yml[37-41]
- tests/validate-plugin-manifests.sh[38-43]
## Suggested fix (pick one)
- Simplest: remove `ajv-formats@3` from the workflow install command.
- Or, if you want parity/future-proofing: add `-c ajv-formats` to the `ajv validate` invocation and optionally add `format` constraints in the schemas (e.g., `email`, `uri`) when you actually want them enforced.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread tests/validate-plugin-manifests.sh Outdated
@DevamShah

Copy link
Copy Markdown
Contributor Author

Thanks for the catch — fixed in 987c9ca.

  • Added a gha_escape helper (%%25, CR→%0D, LF→%0A) per the workflow-commands docs and routed every ::group:: / ::error through it.
  • Also gated workflow-command output on $GITHUB_ACTIONS so local runs print plain ── filename / ERROR (filename): ... instead of ::group:: markers.

Verified:

  • A crafted path plugins/connectors/evil%inj/.claude-plugin/plugin.json now renders as evil%25inj in both the ::group:: line and the ::error file=…::… annotation — no injection.
  • All 33 current manifests still pass.
  • Regression detection intact: reverting any manifest to the broken "author": "<string>" form still fails the job (rc=1) with the inline error annotation.

@ethanolivertroy

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 26, 2026

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

@coderabbitai

coderabbitai Bot commented Apr 26, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 340285c5-7cf9-4056-a930-21953a04f0fa

📥 Commits

Reviewing files that changed from the base of the PR and between 7ef8be3 and 5330765.

📒 Files selected for processing (11)
  • .github/workflows/validate-plugin-manifests.yml
  • docs/CONTRIBUTING.md
  • plugins/connectors/aws-inspector/.claude-plugin/plugin.json
  • plugins/connectors/gcp-inspector/.claude-plugin/plugin.json
  • plugins/connectors/github-inspector/.claude-plugin/plugin.json
  • plugins/connectors/okta-inspector/.claude-plugin/plugin.json
  • plugins/fedramp-ssp/.claude-plugin/plugin.json
  • plugins/oscal/.claude-plugin/plugin.json
  • schemas/marketplace.schema.json
  • schemas/plugin.schema.json
  • tests/validate-plugin-manifests.sh
✅ Files skipped from review due to trivial changes (9)
  • docs/CONTRIBUTING.md
  • plugins/connectors/aws-inspector/.claude-plugin/plugin.json
  • plugins/connectors/github-inspector/.claude-plugin/plugin.json
  • plugins/connectors/gcp-inspector/.claude-plugin/plugin.json
  • plugins/connectors/okta-inspector/.claude-plugin/plugin.json
  • .github/workflows/validate-plugin-manifests.yml
  • schemas/plugin.schema.json
  • schemas/marketplace.schema.json
  • plugins/fedramp-ssp/.claude-plugin/plugin.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/validate-plugin-manifests.sh
  • plugins/oscal/.claude-plugin/plugin.json

📝 Walkthrough

Walkthrough

Adds JSON Schemas for plugin and marketplace manifests, updates several plugin manifests’ author fields to an object shape, adds a Bash validation script using ajv-cli, and adds a GitHub Actions workflow to run manifest validation on relevant PRs and pushes.

Changes

Cohort / File(s) Summary
Schema Definitions
schemas/plugin.schema.json, schemas/marketplace.schema.json
New JSON Schema (Draft 2020-12) files defining required and optional fields for plugin and marketplace manifests, including a structured author object shape.
Plugin Manifests
plugins/connectors/aws-inspector/.claude-plugin/plugin.json, plugins/connectors/gcp-inspector/.claude-plugin/plugin.json, plugins/connectors/github-inspector/.claude-plugin/plugin.json, plugins/connectors/okta-inspector/.claude-plugin/plugin.json, plugins/fedramp-ssp/.claude-plugin/plugin.json, plugins/oscal/.claude-plugin/plugin.json
Changed author from a plain string to an object with name (shape change only).
Validation Infrastructure
.github/workflows/validate-plugin-manifests.yml, tests/validate-plugin-manifests.sh
New GitHub Actions workflow that runs on manifest/schema changes and an executable Bash script that validates .claude-plugin/marketplace.json and plugins/**/plugin.json against the new schemas using ajv-cli, emitting CI annotations on failures.
Documentation
docs/CONTRIBUTING.md
Updated plugin scaffold example to show author as an object with name and email fields.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer
    participant GH as GitHub Actions
    participant Runner as Workflow Runner
    participant Repo as Repository
    participant Script as validate-plugin-manifests.sh
    participant AJV as ajv-cli

    Dev->>GH: push or open PR with manifest/schema changes
    GH->>Runner: trigger workflow (concurrency by ref)
    Runner->>Repo: checkout code
    Runner->>Runner: setup Node.js 20 and install ajv-cli / ajv-formats
    Runner->>Script: execute tests/validate-plugin-manifests.sh
    Script->>Repo: read `schemas/*`, `.claude-plugin/marketplace.json`, `plugins/**/plugin.json`
    Script->>AJV: validate each manifest
    AJV-->>Script: validation results
    Script-->>GH: emit logs and annotations for failures
    Script-->>Runner: exit status (non-zero on any failure)
    Runner-->>GH: report workflow result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰
I hopped through schemas, tidy and bright,
turned authors to objects, all snug and right,
AJV sniffed errors in the quiet night,
CI danced, green or red in sight,
I nibble a carrot — validation delight! 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'ci(plugins): validate manifests against JSON Schema on every PR' clearly and specifically describes the main change: adding CI validation for plugin manifests using JSON Schema on pull requests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
.github/workflows/validate-plugin-manifests.yml (1)

31-33: Pin GitHub Actions to immutable commit SHAs.

Lines 31 and 33 use floating major tags (@v4). Pin to commit SHAs instead to improve supply-chain safety and ensure deterministic runs. Current SHAs are:

  • actions/checkout@v434e114876b0b11c390a56381ad16ebd13914f8d5
  • actions/setup-node@v449933ea5288caeca8642d1e84afbd3f7d6820020
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/validate-plugin-manifests.yml around lines 31 - 33,
Replace the floating major tags for GitHub Actions with the provided immutable
commit SHAs: update the uses entries that currently reference
"actions/checkout@v4" and "actions/setup-node@v4" to use
"34e114876b0b11c390a56381ad16ebd13914f8d5" and
"49933ea5288caeca8642d1e84afbd3f7d6820020" respectively so the workflow (the
lines containing the action identifiers "actions/checkout@v4" and
"actions/setup-node@v4") is pinned to those exact commits for deterministic,
supply-chain-safe runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@schemas/plugin.schema.json`:
- Around line 15-19: The "version" property currently allows any non-empty
string but claims to be "Semver-compatible"; update the schema so the behavior
matches the description by adding a semver validation (e.g., a "pattern" with a
semver regex such as
^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-.]+)?(?:\\+[0-9A-Za-z-.]+)?$) to the "version"
property, or alternatively change the "description" text to remove the semver
claim; modify the "version" object in plugin.schema.json (the "version"
property) accordingly so the schema and description stay consistent.

In `@tests/validate-plugin-manifests.sh`:
- Around line 19-20: The script sets repo_root and then runs cd "$repo_root"
without checking success; modify the validation script so the cd to repo_root is
guarded and fails fast (e.g., check the exit status of the cd command and exit
with a non‑zero code or print an error and exit) so subsequent validation always
runs from the intended directory; update the lines that define repo_root and
call cd (the repo_root variable and the cd "$repo_root" invocation) to include
this failure guard.
- Around line 97-99: The current -f guard lets deletion of
.claude-plugin/marketplace.json skip validation; replace it with a hard-fail
when the marketplace manifest is missing: check for the presence of
.claude-plugin/marketplace.json and if absent print a clear error and exit 1,
otherwise call validate_file with marketplace_schema and
.claude-plugin/marketplace.json so CI fails when the top-level marketplace
manifest is deleted.

---

Nitpick comments:
In @.github/workflows/validate-plugin-manifests.yml:
- Around line 31-33: Replace the floating major tags for GitHub Actions with the
provided immutable commit SHAs: update the uses entries that currently reference
"actions/checkout@v4" and "actions/setup-node@v4" to use
"34e114876b0b11c390a56381ad16ebd13914f8d5" and
"49933ea5288caeca8642d1e84afbd3f7d6820020" respectively so the workflow (the
lines containing the action identifiers "actions/checkout@v4" and
"actions/setup-node@v4") is pinned to those exact commits for deterministic,
supply-chain-safe runs.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 03c51fba-f5c6-4892-bc3a-d1a8d6460e0a

📥 Commits

Reviewing files that changed from the base of the PR and between 5e81f27 and 987c9ca.

📒 Files selected for processing (11)
  • .github/workflows/validate-plugin-manifests.yml
  • docs/CONTRIBUTING.md
  • plugins/connectors/aws-inspector/.claude-plugin/plugin.json
  • plugins/connectors/gcp-inspector/.claude-plugin/plugin.json
  • plugins/connectors/github-inspector/.claude-plugin/plugin.json
  • plugins/connectors/okta-inspector/.claude-plugin/plugin.json
  • plugins/fedramp-ssp/.claude-plugin/plugin.json
  • plugins/oscal/.claude-plugin/plugin.json
  • schemas/marketplace.schema.json
  • schemas/plugin.schema.json
  • tests/validate-plugin-manifests.sh

Comment thread schemas/plugin.schema.json
Comment thread tests/validate-plugin-manifests.sh Outdated
Comment thread tests/validate-plugin-manifests.sh
DevamShah added a commit to DevamShah/custodia that referenced this pull request Apr 26, 2026
CodeRabbit posted 3 actionable comments on GRCEngClub#71. All addressed:

1. schemas/plugin.schema.json — `version` claimed semver-compatible
   but accepted any non-empty string. Add the semver 2.0.0 grammar as
   a `pattern` so the schema and description agree, and tighten the
   description to call out the semver enforcement explicitly. Also
   apply the same pattern to plugin entries inside
   schemas/marketplace.schema.json for consistency, so a marketplace
   that lists a plugin with a non-semver version is caught.

2. tests/validate-plugin-manifests.sh — `cd "$repo_root"` could
   silently fail and leave the script validating from the wrong
   working directory (Shellcheck SC2164). Guard it with an explicit
   `|| { echo "..."; exit 2; }`.

3. tests/validate-plugin-manifests.sh — a missing top-level
   `.claude-plugin/marketplace.json` previously passed silently
   (gated by `-f`). Since this repository is a Claude Code plugin
   marketplace, that file is required. Replace the silent skip with
   an explicit `emit_summary_error` and `exit 1`.

Verified locally:
- All 33 current manifests still pass.
- A plugin.json with `version: "badversion"` is now rejected by
  schemas/plugin.schema.json.
- A marketplace.json that lists a plugin with a non-semver version
  is now rejected by schemas/marketplace.schema.json.
- Removing .claude-plugin/marketplace.json fails the validator with
  rc=1 and a clear error message.
@DevamShah

DevamShah commented Apr 26, 2026

Copy link
Copy Markdown
Contributor Author

Pushed 7ef8be3 addressing the three actionable CodeRabbit comments:

# Comment Fix
1 version described as semver-compatible but only enforced as non-empty string (schemas/plugin.schema.json) Added the semver 2.0.0 grammar as a pattern regex on version, tightened the description to call out the enforcement explicitly. Also applied the same pattern to plugin entries inside schemas/marketplace.schema.json for consistency, so a marketplace listing a plugin with a non-semver version is also caught.
2 cd "$repo_root" could silently fail (Shellcheck SC2164) Guarded with || { echo "Failed to cd to repo root: $repo_root" >&2; exit 2; }
3 Missing .claude-plugin/marketplace.json previously passed silently Replaced the silent [[ -f ... ]] skip with an explicit emit_summary_error and exit 1. The marketplace manifest is mandatory for this repository — losing it should fail CI loudly.

Verified locally:

  • All 33 current manifests still pass.
  • A plugin.json with version: "badversion" is now rejected.
  • A marketplace.json that lists a plugin with a non-semver version is now rejected.
  • Removing .claude-plugin/marketplace.json fails the validator with rc=1 and a clear error message.

Ready for re-review whenever a maintainer can trigger another bot pass.

@coderabbitai

coderabbitai Bot commented Apr 26, 2026

Copy link
Copy Markdown

Only users with a collaborator, contributor, member, or owner role can interact with CodeRabbit.

The Claude Code plugin loader rejects manifests where `author` is a
string with `Validation errors: author: Invalid input: expected
object, received string`, which means the affected plugins cannot be
installed via `claude plugin install`.

Six plugins shipped with `author` as a bare string while the rest of
the marketplace already uses the object form. Normalise them to
`{ "name": "..." }` to match the working plugins (e.g.
`grc-engineer`, `soc2`, `nist-800-53`) and unblock installation:

- plugins/connectors/aws-inspector
- plugins/connectors/gcp-inspector
- plugins/connectors/github-inspector
- plugins/connectors/okta-inspector
- plugins/fedramp-ssp
- plugins/oscal

Also update the `plugin.json` example in `docs/CONTRIBUTING.md` so
new connector contributors land on the working schema by default.

Verified locally: all six plugins install successfully after the
patch (`claude plugin install <name>@grc-engineering-suite`).
Adds a CI workflow that schema-validates every plugin.json plus the
top-level marketplace.json on each PR. Catches the install-blocking
manifest errors that `claude plugin install` rejects — most notably
the `author` field type, which has already produced one round of
broken plugins in this repo.

Why two schemas instead of `claude plugin validate`:

`claude plugin validate` rejects unknown keys in strict mode, which
flags every framework plugin that uses `framework_metadata` (an
intentional extension). The schemas here mirror what the CLI's
*install-time* validator enforces — required fields and field types —
while leaving extension keys via `additionalProperties: true`. Any
manifest that passes here installs cleanly with `claude plugin
install`; any that fails will not.

Files:
- schemas/plugin.schema.json: plugin.json contract (author must be
  an object, name + version required, extension keys allowed)
- schemas/marketplace.schema.json: marketplace.json contract
- tests/validate-plugin-manifests.sh: ajv-cli loop with
  GitHub-Actions-friendly error annotations
- .github/workflows/validate-plugin-manifests.yml: triggers on
  manifest or schema changes; matches the existing contract-test
  workflow style (Node 20 + ajv-cli + ajv-formats)

Verified locally:
- All 33 current manifests pass
- Reverting any one to the broken `author: <string>` form fails the
  job with an inline error annotation pointing at the file
Filenames in `tests/validate-plugin-manifests.sh` came from `find`
over a PR-controlled tree and were interpolated unescaped into
`::group::` and `::error file=…::…` workflow commands. A crafted
filename containing `%`, CR, or LF could inject additional workflow
commands into the CI log, mislead reviewers, or alter CI behavior.

Add a `gha_escape` helper that applies the canonical escapes
documented at
https://docs.github.com/actions/reference/workflow-commands-for-github-actions
(`%` → `%25`, CR → `%0D`, LF → `%0A`) and route every workflow
command through it.

Also gate workflow-command output on `$GITHUB_ACTIONS` so local runs
print readable diagnostics instead of `::group::` markers.

Verified locally:
- All 33 current manifests still pass (CI and local mode)
- A crafted path `plugins/connectors/evil%inj/.claude-plugin/plugin.json`
  is rendered as `evil%25inj` in `::group::` and `::error` lines
- Reverting any manifest to the broken `author: <string>` form still
  fails the job (rc=1) with an inline error annotation
CodeRabbit posted 3 actionable comments on GRCEngClub#71. All addressed:

1. schemas/plugin.schema.json — `version` claimed semver-compatible
   but accepted any non-empty string. Add the semver 2.0.0 grammar as
   a `pattern` so the schema and description agree, and tighten the
   description to call out the semver enforcement explicitly. Also
   apply the same pattern to plugin entries inside
   schemas/marketplace.schema.json for consistency, so a marketplace
   that lists a plugin with a non-semver version is caught.

2. tests/validate-plugin-manifests.sh — `cd "$repo_root"` could
   silently fail and leave the script validating from the wrong
   working directory (Shellcheck SC2164). Guard it with an explicit
   `|| { echo "..."; exit 2; }`.

3. tests/validate-plugin-manifests.sh — a missing top-level
   `.claude-plugin/marketplace.json` previously passed silently
   (gated by `-f`). Since this repository is a Claude Code plugin
   marketplace, that file is required. Replace the silent skip with
   an explicit `emit_summary_error` and `exit 1`.

Verified locally:
- All 33 current manifests still pass.
- A plugin.json with `version: "badversion"` is now rejected by
  schemas/plugin.schema.json.
- A marketplace.json that lists a plugin with a non-semver version
  is now rejected by schemas/marketplace.schema.json.
- Removing .claude-plugin/marketplace.json fails the validator with
  rc=1 and a clear error message.
@DevamShah DevamShah force-pushed the ci/validate-plugin-manifests branch from 7ef8be3 to 62e4f8b Compare April 26, 2026 15:31
DevamShah added a commit to DevamShah/custodia that referenced this pull request Apr 26, 2026
Adds a Reference-depth plugin for India's Digital Personal Data
Protection Act, 2023 (DPDPA) and the DPDP Rules, 2025. Built for
CISOs, DPOs, GRC engineers, and platform teams operating in India or
offering goods or services to Data Principals located in India.

Plugin

- Slug: ind-dpdpa
- SCF framework ID: apac-ind-dpdpa-2023 (41 SCF → 96 framework controls)
- Depth: reference
- Regulator: Data Protection Board of India (DPB) under MeitY
- Region: APAC, country IN

Files (Reference-depth set per docs/FRAMEWORK-PLUGIN-GUIDE.md):

- plugins/frameworks/ind-dpdpa/.claude-plugin/plugin.json
- plugins/frameworks/ind-dpdpa/README.md
- plugins/frameworks/ind-dpdpa/commands/assess.md
- plugins/frameworks/ind-dpdpa/commands/scope.md
- plugins/frameworks/ind-dpdpa/commands/evidence-checklist.md
- plugins/frameworks/ind-dpdpa/commands/breach-process.md
- plugins/frameworks/ind-dpdpa/skills/ind-dpdpa-expert/SKILL.md
- .claude-plugin/marketplace.json (registration)

Coverage

- Act + Rules read together: substantive obligations from the Act,
  operational timelines and detail from the Rules notified November
  2025.
- Roles: Data Fiduciary, Processor, Principal, Consent Manager, SDF.
- Section 3 territorial and material applicability decision tree.
- Section 5–7 notice / consent / legitimate uses (no GDPR-style
  legitimate-interest framing — Section 7 is exhaustive).
- Section 8 Fiduciary obligations including 8(5) security safeguards
  and 8(6) breach notification (penalty bands ₹250 cr / ₹50 cr).
- Section 9 children's data (verifiable parental consent, no
  behavioural tracking, no targeted ads — penalty up to ₹200 cr).
- Section 10 SDF additional obligations (DPO, DPIA, audit; up to
  ₹150 cr).
- Sections 11–14 Data Principal rights (access, correction, erasure,
  grievance, nomination).
- Section 16 cross-border (default-permissive with restricted-
  territory blocklist; sectoral localisation overlays preserved).
- Section 17 exemptions.

Reference-depth differentiators vs scaffolded stub

- Substantive content in every SKILL section (no TODO markers).
- /ind-dpdpa:scope — full applicability + role + SDF-trigger walk.
- /ind-dpdpa:evidence-checklist — 14 obligation themes with
  collection guidance and "what good looks like" criteria.
- /ind-dpdpa:breach-process — DPDPA 72-hour timeline plus the
  parallel sectoral clocks (CERT-In 6h, RBI 6h, SEBI 6h, IRDAI per
  current direction, DoT/TRAI per Telecom Cyber Security Rules
  2024). Clock-collision is the most common DPDPA-era breach
  failure for regulated Fiduciaries; the playbook addresses it.

Sectoral overlay

Plugin flags overlap with RBI (Master Direction on IT Governance,
Cyber Security Framework, payment-system data localisation), SEBI
(CSCRF), IRDAI (Information & Cyber Security Guidelines), DoT/TRAI
(Telecom Cyber Security Rules 2024), NHA (ABDM/Health Data
Management Policy), NCIIPC (CII), and CERT-In (Direction
20(3)/2022). The plugin does not enforce sectoral rules — those
remain separate plugins or org-internal playbooks.

Constraints honoured

- No verbatim Act or Rules text (paraphrase only; section / rule
  references by number).
- No PII, no real org context, no credentials.
- No legal-advice phrasing; "engineering and assessment guidance
  only" disclaimer in every file.
- Cloud-agnostic implementation guidance.
- No hand-maintained crosswalks; defers to SCF for control mapping.
- No vendored copy of the Section 16 restricted-territories list
  (which is published by the Central Government and updates).

Coordination with PR GRCEngClub#66

PR GRCEngClub#66 (@AnandSundar) proposes a Stub-depth plugin for the same
framework. This PR is a Reference-depth contribution. Per
docs/FRAMEWORK-PLUGIN-GUIDE.md ("Level-ups are separate PRs"), the
intended sequence is: GRCEngClub#66 merges first, then this PR rebases as a
Stub→Reference upgrade. Happy to defer or coordinate per
maintainer preference.

Validation

- plugin.json passes the Reference manifest schema (PR GRCEngClub#71).
- marketplace.json passes the Reference marketplace schema (PR GRCEngClub#71).
- No matches in audit grep for: verbatim long quotes, real-org
  context, PII patterns, credentials, "legal advice" framing.
- Disclaimer present in every user-facing file.

Authored personally by Devam Shah.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/validate-plugin-manifests.sh`:
- Around line 42-48: The gha_escape function only URL-encodes %, CR and LF, but
property values (e.g., the file= in the ::error file=%s::%s annotation) must
also escape ':' and ',' to avoid annotation parsing errors; add a separate
property-escaping variant (e.g., gha_escape_property) or extend gha_escape to
accept a mode and, when used for properties, additionally replace ':' with %3A
and ',' with %2C (while preserving existing %->%25, CR->%0D, LF->%0A behavior)
and use that new escape for the file/other property substitutions where
annotations are constructed.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d58868a-dc4b-47d6-9528-2d79afade7c8

📥 Commits

Reviewing files that changed from the base of the PR and between 987c9ca and 7ef8be3.

📒 Files selected for processing (3)
  • schemas/marketplace.schema.json
  • schemas/plugin.schema.json
  • tests/validate-plugin-manifests.sh
✅ Files skipped from review due to trivial changes (2)
  • schemas/plugin.schema.json
  • schemas/marketplace.schema.json

Comment thread tests/validate-plugin-manifests.sh Outdated
…e data

CodeRabbit re-review on the previous fix flagged that `gha_escape`
applied data-only escaping (`%`, CR, LF) to *property* values inside
`::error file=...::...` annotations. Property values must additionally
escape `:` and `,` — those are the property-delimiter characters and a
crafted filename could otherwise inject fake `line=`/`title=` properties
or break the `::` separator.

This mirrors @actions/toolkit's split between escapeData and
escapeProperty, documented in
packages/core/src/command.ts of actions/toolkit.

Changes:

- Split `gha_escape` into `gha_escape_data` (message bodies) and
  `gha_escape_property` (key=value property values, additionally
  escaping `:` and `,`).
- `emit_error` now uses `gha_escape_property` for the `file=` property
  and `gha_escape_data` for the message body (matching the toolkit's
  Command.toString contract).
- `emit_group_start` and `emit_summary_error` continue to use
  `gha_escape_data` (their values aren't properties).

Verified locally with a crafted filename containing `:` `,` `%`:

- `::group::evil:inj,x%25`   (data: only `%` escaped)
- `::error file=evil%3Ainj%2Cx%25` (property: `:`,`,`,`%` all escaped)

All 33 current manifests still pass.
@DevamShah

Copy link
Copy Markdown
Contributor Author

Pushed f0cd40c addressing the post-rebase CodeRabbit finding on tests/validate-plugin-manifests.sh:48.

Issue: my gha_escape only applied data-style escaping (%, CR, LF), but the file= argument in ::error file=…::… is a workflow-command property, not a message. Property values must additionally escape : and , — those are the property delimiters, and a crafted filename could otherwise inject fake line=/title= properties or break the :: separator. This mirrors @actions/toolkit's split between escapeData and escapeProperty.

Fix:

  • Split gha_escape into gha_escape_data (message bodies, ::group::, ::error::) and gha_escape_property (key=value property values).
  • emit_error now uses gha_escape_property for file= and gha_escape_data for the message body — matching the toolkit's Command.toString contract.

Verified locally with a crafted filename containing : , %:

Context Output
::group:: (data) evil:inj,x%25 — only % escaped, : and , left as-is (correct for data)
::error file= (property) evil%3Ainj%2Cx%25: , % all escaped (correct for property)

All 33 current manifests still pass.

@greptile-apps

greptile-apps Bot commented Apr 26, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces a CI workflow that schema-validates all plugin.json manifests and the top-level marketplace.json on every relevant PR, shipping two JSON Schemas and a bash validation script. It also backfills the six plugin.json files that used a bare-string author field (the bug class fixed in #70) to the required object form.

Confidence Score: 5/5

Safe to merge; the one open finding is a P2 schema inconsistency that doesn't affect any current manifest.

All current manifests pass the new schemas, the workflow and script are well-constructed, and the only finding (author additionalProperties: false vs. the stated intent of allowing extension keys) has no impact today since no plugin uses extra author fields.

schemas/plugin.schema.json — the author.additionalProperties: false setting should be reviewed for consistency with the extension-key policy.

Important Files Changed

Filename Overview
.github/workflows/validate-plugin-manifests.yml New CI workflow triggering on manifest/schema/script changes; path filters look correct, concurrency cancellation mirrors existing style, no issues found.
schemas/plugin.schema.json New plugin manifest schema; author.additionalProperties: false is inconsistent with the top-level additionalProperties: true policy and the PR's stated intent to allow extension keys.
schemas/marketplace.schema.json New marketplace schema; owner correctly uses additionalProperties: true, semver pattern matches plugin schema, no issues found.
tests/validate-plugin-manifests.sh Solid validation script with careful GHA command injection escaping, correct --spec=draft2020, explicit per-file error tracking, and a safe find loop; no logic issues found.
docs/CONTRIBUTING.md Author example updated from bare string to object form, consistent with the schema and the plugin.json fixes.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[PR opened or push to main] --> B{Path filter matches?}
    B -- No --> Z[Workflow skipped]
    B -- Yes --> C[Checkout repo]
    C --> D[Setup Node 20]
    D --> E[Install ajv-cli and ajv-formats]
    E --> F[Run validate-plugin-manifests.sh]
    F --> G{marketplace.json exists?}
    G -- No --> X[Exit 1 - missing marketplace manifest]
    G -- Yes --> H[Validate marketplace.json against marketplace.schema.json]
    H --> I[Find all plugin.json files]
    I --> J[Validate each plugin.json against plugin.schema.json]
    J --> K{Any failures?}
    K -- Yes --> L[Emit error annotations and exit 1]
    K -- No --> M[Exit 0 - all manifests valid]
Loading

Reviews (2): Last reviewed commit: "Merge branch 'main' into ci/validate-plu..." | Re-trigger Greptile

Comment on lines +107 to +113
--errors=line; then
printf ' ✓ %s\n' "$file"
else
emit_error "$file" "Manifest failed schema validation against $schema"
fail=1
fi
emit_group_end

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 ajv-formats installed but never activated

ajv-formats is installed alongside ajv-cli but no -c ajv-formats flag is passed to ajv validate. In ajv@8 (which ajv-cli@5 bundles), format validation is disabled by default — ajv-formats must be explicitly loaded via -c ajv-formats for "format": "email" / "format": "uri" constraints to fire. The current schemas don't use format keywords so this has no immediate impact, but if "format" validators are added to the schemas later, they will silently pass without being checked.

Suggested change
--errors=line; then
printf ' ✓ %s\n' "$file"
else
emit_error "$file" "Manifest failed schema validation against $schema"
fail=1
fi
emit_group_end
if ajv validate \
--spec=draft2020 \
-c ajv-formats \
-s "$schema" \
-d "$file" \
--errors=line; then

@ethanolivertroy ethanolivertroy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving per pre-merge review pass: AI review feedback (qodo Bug+Security on GHA injection, CodeRabbit Major on deleted-marketplace.json, semver pattern, ajv-formats latent issue) addressed; schema validates current manifests; consistent with existing contract-test workflow style. Will close #70 as superseded after this lands.

@ethanolivertroy ethanolivertroy merged commit 9b568ca into GRCEngClub:main Apr 26, 2026
4 checks passed
ethanolivertroy pushed a commit to DevamShah/custodia that referenced this pull request Apr 26, 2026
Adds a Reference-depth plugin for India's Digital Personal Data
Protection Act, 2023 (DPDPA) and the DPDP Rules, 2025. Built for
CISOs, DPOs, GRC engineers, and platform teams operating in India or
offering goods or services to Data Principals located in India.

Plugin

- Slug: ind-dpdpa
- SCF framework ID: apac-ind-dpdpa-2023 (41 SCF → 96 framework controls)
- Depth: reference
- Regulator: Data Protection Board of India (DPB) under MeitY
- Region: APAC, country IN

Files (Reference-depth set per docs/FRAMEWORK-PLUGIN-GUIDE.md):

- plugins/frameworks/ind-dpdpa/.claude-plugin/plugin.json
- plugins/frameworks/ind-dpdpa/README.md
- plugins/frameworks/ind-dpdpa/commands/assess.md
- plugins/frameworks/ind-dpdpa/commands/scope.md
- plugins/frameworks/ind-dpdpa/commands/evidence-checklist.md
- plugins/frameworks/ind-dpdpa/commands/breach-process.md
- plugins/frameworks/ind-dpdpa/skills/ind-dpdpa-expert/SKILL.md
- .claude-plugin/marketplace.json (registration)

Coverage

- Act + Rules read together: substantive obligations from the Act,
  operational timelines and detail from the Rules notified November
  2025.
- Roles: Data Fiduciary, Processor, Principal, Consent Manager, SDF.
- Section 3 territorial and material applicability decision tree.
- Section 5–7 notice / consent / legitimate uses (no GDPR-style
  legitimate-interest framing — Section 7 is exhaustive).
- Section 8 Fiduciary obligations including 8(5) security safeguards
  and 8(6) breach notification (penalty bands ₹250 cr / ₹50 cr).
- Section 9 children's data (verifiable parental consent, no
  behavioural tracking, no targeted ads — penalty up to ₹200 cr).
- Section 10 SDF additional obligations (DPO, DPIA, audit; up to
  ₹150 cr).
- Sections 11–14 Data Principal rights (access, correction, erasure,
  grievance, nomination).
- Section 16 cross-border (default-permissive with restricted-
  territory blocklist; sectoral localisation overlays preserved).
- Section 17 exemptions.

Reference-depth differentiators vs scaffolded stub

- Substantive content in every SKILL section (no TODO markers).
- /ind-dpdpa:scope — full applicability + role + SDF-trigger walk.
- /ind-dpdpa:evidence-checklist — 14 obligation themes with
  collection guidance and "what good looks like" criteria.
- /ind-dpdpa:breach-process — DPDPA 72-hour timeline plus the
  parallel sectoral clocks (CERT-In 6h, RBI 6h, SEBI 6h, IRDAI per
  current direction, DoT/TRAI per Telecom Cyber Security Rules
  2024). Clock-collision is the most common DPDPA-era breach
  failure for regulated Fiduciaries; the playbook addresses it.

Sectoral overlay

Plugin flags overlap with RBI (Master Direction on IT Governance,
Cyber Security Framework, payment-system data localisation), SEBI
(CSCRF), IRDAI (Information & Cyber Security Guidelines), DoT/TRAI
(Telecom Cyber Security Rules 2024), NHA (ABDM/Health Data
Management Policy), NCIIPC (CII), and CERT-In (Direction
20(3)/2022). The plugin does not enforce sectoral rules — those
remain separate plugins or org-internal playbooks.

Constraints honoured

- No verbatim Act or Rules text (paraphrase only; section / rule
  references by number).
- No PII, no real org context, no credentials.
- No legal-advice phrasing; "engineering and assessment guidance
  only" disclaimer in every file.
- Cloud-agnostic implementation guidance.
- No hand-maintained crosswalks; defers to SCF for control mapping.
- No vendored copy of the Section 16 restricted-territories list
  (which is published by the Central Government and updates).

Coordination with PR GRCEngClub#66

PR GRCEngClub#66 (@AnandSundar) proposes a Stub-depth plugin for the same
framework. This PR is a Reference-depth contribution. Per
docs/FRAMEWORK-PLUGIN-GUIDE.md ("Level-ups are separate PRs"), the
intended sequence is: GRCEngClub#66 merges first, then this PR rebases as a
Stub→Reference upgrade. Happy to defer or coordinate per
maintainer preference.

Validation

- plugin.json passes the Reference manifest schema (PR GRCEngClub#71).
- marketplace.json passes the Reference marketplace schema (PR GRCEngClub#71).
- No matches in audit grep for: verbatim long quotes, real-org
  context, PII patterns, credentials, "legal advice" framing.
- Disclaimer present in every user-facing file.

Authored personally by Devam Shah.
ethanolivertroy added a commit that referenced this pull request Apr 26, 2026
* feat(frameworks): add Reference-depth India DPDPA plugin (ind-dpdpa)

Adds a Reference-depth plugin for India's Digital Personal Data
Protection Act, 2023 (DPDPA) and the DPDP Rules, 2025. Built for
CISOs, DPOs, GRC engineers, and platform teams operating in India or
offering goods or services to Data Principals located in India.

Plugin

- Slug: ind-dpdpa
- SCF framework ID: apac-ind-dpdpa-2023 (41 SCF → 96 framework controls)
- Depth: reference
- Regulator: Data Protection Board of India (DPB) under MeitY
- Region: APAC, country IN

Files (Reference-depth set per docs/FRAMEWORK-PLUGIN-GUIDE.md):

- plugins/frameworks/ind-dpdpa/.claude-plugin/plugin.json
- plugins/frameworks/ind-dpdpa/README.md
- plugins/frameworks/ind-dpdpa/commands/assess.md
- plugins/frameworks/ind-dpdpa/commands/scope.md
- plugins/frameworks/ind-dpdpa/commands/evidence-checklist.md
- plugins/frameworks/ind-dpdpa/commands/breach-process.md
- plugins/frameworks/ind-dpdpa/skills/ind-dpdpa-expert/SKILL.md
- .claude-plugin/marketplace.json (registration)

Coverage

- Act + Rules read together: substantive obligations from the Act,
  operational timelines and detail from the Rules notified November
  2025.
- Roles: Data Fiduciary, Processor, Principal, Consent Manager, SDF.
- Section 3 territorial and material applicability decision tree.
- Section 5–7 notice / consent / legitimate uses (no GDPR-style
  legitimate-interest framing — Section 7 is exhaustive).
- Section 8 Fiduciary obligations including 8(5) security safeguards
  and 8(6) breach notification (penalty bands ₹250 cr / ₹50 cr).
- Section 9 children's data (verifiable parental consent, no
  behavioural tracking, no targeted ads — penalty up to ₹200 cr).
- Section 10 SDF additional obligations (DPO, DPIA, audit; up to
  ₹150 cr).
- Sections 11–14 Data Principal rights (access, correction, erasure,
  grievance, nomination).
- Section 16 cross-border (default-permissive with restricted-
  territory blocklist; sectoral localisation overlays preserved).
- Section 17 exemptions.

Reference-depth differentiators vs scaffolded stub

- Substantive content in every SKILL section (no TODO markers).
- /ind-dpdpa:scope — full applicability + role + SDF-trigger walk.
- /ind-dpdpa:evidence-checklist — 14 obligation themes with
  collection guidance and "what good looks like" criteria.
- /ind-dpdpa:breach-process — DPDPA 72-hour timeline plus the
  parallel sectoral clocks (CERT-In 6h, RBI 6h, SEBI 6h, IRDAI per
  current direction, DoT/TRAI per Telecom Cyber Security Rules
  2024). Clock-collision is the most common DPDPA-era breach
  failure for regulated Fiduciaries; the playbook addresses it.

Sectoral overlay

Plugin flags overlap with RBI (Master Direction on IT Governance,
Cyber Security Framework, payment-system data localisation), SEBI
(CSCRF), IRDAI (Information & Cyber Security Guidelines), DoT/TRAI
(Telecom Cyber Security Rules 2024), NHA (ABDM/Health Data
Management Policy), NCIIPC (CII), and CERT-In (Direction
20(3)/2022). The plugin does not enforce sectoral rules — those
remain separate plugins or org-internal playbooks.

Constraints honoured

- No verbatim Act or Rules text (paraphrase only; section / rule
  references by number).
- No PII, no real org context, no credentials.
- No legal-advice phrasing; "engineering and assessment guidance
  only" disclaimer in every file.
- Cloud-agnostic implementation guidance.
- No hand-maintained crosswalks; defers to SCF for control mapping.
- No vendored copy of the Section 16 restricted-territories list
  (which is published by the Central Government and updates).

Coordination with PR #66

PR #66 (@AnandSundar) proposes a Stub-depth plugin for the same
framework. This PR is a Reference-depth contribution. Per
docs/FRAMEWORK-PLUGIN-GUIDE.md ("Level-ups are separate PRs"), the
intended sequence is: #66 merges first, then this PR rebases as a
Stub→Reference upgrade. Happy to defer or coordinate per
maintainer preference.

Validation

- plugin.json passes the Reference manifest schema (PR #71).
- marketplace.json passes the Reference marketplace schema (PR #71).
- No matches in audit grep for: verbatim long quotes, real-org
  context, PII patterns, credentials, "legal advice" framing.
- Disclaimer present in every user-facing file.

Authored personally by Devam Shah.

* docs(ind-dpdpa): address CodeRabbit review feedback

CodeRabbit posted 3 actionable comments on #72:

1. Add `bash` language tag to fenced code blocks (markdownlint MD040).
   Applied to commands/assess.md (Usage and Delegation blocks),
   commands/scope.md, commands/evidence-checklist.md,
   commands/breach-process.md.

2. Reclassify DDoS row in commands/breach-process.md.
   The row previously said "No" for DPDPA-breach status, which
   contradicted Section 2's definition (which includes loss-of-access
   to personal data as a breach of availability). Updated to
   "Context-dependent — likely Yes if the unavailability blocks Data
   Principals from accessing their personal data" and added the
   "document the determination basis" reminder consistent with the
   rest of the document.

3. (Already covered by item 1.)

* fix(ind-dpdpa): version baseline, Section 15 penalty, paraphrase Section 2(u)

- plugin.json: version 0.2.0 → 0.1.0 to match repo baseline (every other
  plugin manifest is at 0.1.0)
- SKILL.md (Penalty exposure): Section 15 Data Principal duty penalty is
  ₹10,000 per the Schedule, not ₹50 crore. Crore-scale penalties apply to
  Fiduciary failures; the Principal-side fine is the small one.
- SKILL.md + breach-process.md: paraphrase the Section 2(u) personal-data-
  breach definition rather than reproducing the Act text verbatim, per
  ground rule #2 in docs/CONTRIBUTING.md
- marketplace.json: register the ind-dpdpa plugin alongside the other
  framework plugins (resolves stale-base conflict against current main)

Validates against tests/validate-plugin-manifests.sh (37 manifests, all valid).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Ethan Troy <63926014+ethanolivertroy@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@DevamShah

Copy link
Copy Markdown
Contributor Author

Thanks for the merge @ethanolivertroy — happy this ships as a CI guardrail going forward. CodeRabbit's @actions/toolkit-aligned escapeData / escapeProperty split (the post-rebase finding on the ::error file=…:: annotation) was the catch I most appreciated learning from; the bug class would have stayed dormant until the next contributor shipped a filename containing : or ,. Greptile and Qodo's feedback rounded out the review nicely — the schema baseline they pushed me toward is the right one for the marketplace as it grows past 33 plugins.

Glad the six manifest fixes folded in cleanly with the validator landing in the same merge — one PR, one set of artefacts, no half-fixed state on main.

DevamShah added a commit to DevamShah/custodia that referenced this pull request Apr 27, 2026
Both substantive PRs (GRCEngClub#71 validator, GRCEngClub#72 India DPDPA plugin) are
merged into upstream main. PR GRCEngClub#70 closed-as-superseded (folded into
GRCEngClub#71). README updated to:

- Reflect the merged state of all three contributions, with explicit
  thanks for @ethanolivertroy's maintainer-edit pass that fixed the
  four blockers on GRCEngClub#72 before merge (version baseline, Section 15
  ₹10,000 correction, Section 2(u) paraphrase).
- Acknowledge @AnandSundar's original stub PR GRCEngClub#66 that locked in the
  SCF metadata baseline.
- Recommend the upstream Club marketplace as the primary install path
  now that the work has landed there.
- Preserve the upstream README verbatim as UPSTREAM-README.md.
@DevamShah DevamShah deleted the ci/validate-plugin-manifests branch April 27, 2026 02:41
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.

2 participants