feat: protected files check for agent-created PRs#1
Merged
Conversation
Add picomatch for glob pattern matching in protected files feature. Also adds @types/picomatch for TypeScript support. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add src/protected-files.ts with: - Default protected patterns for CI config, dependency manifests, agent instructions, and access control files - gitignore-style pattern matching via picomatch (last match wins, negation with ! prefix) - Config resolution merging built-in defaults with user patterns - Category classification for violation reporting - checkProtectedFiles scans all create_pull_request actions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add Phase 2 (protected files check) between validation and sanitization - Read new inputs: protected-files, protected-files-action, protected-files-override-defaults - Block or warn when protected files are modified in create_pull_request - Renumber subsequent phases (sanitization -> 3, threats -> 4, exec -> 5) - Add three new inputs to action.yml with descriptions and defaults Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 57 tests covering: - Default pattern verification (CI config, deps, agent instructions, access control) - Custom patterns extending defaults - Negation patterns creating exceptions - Override defaults mode (only user patterns apply) - Actions without files are skipped - Non-PR actions pass through - Mixed action types (only create_pull_request checked) - Category classification correctness - Warn mode (violations reported but passed) - Block mode (violations cause failure) - Glob pattern specifics (** vs *, nested paths) - Dotfile matching (.github/, .claude/) - Safe file pass-through (src/index.ts, README.md) - Multiple violations in single PR - Last-match-wins pattern ordering - Empty/whitespace pattern filtering Also fixes isFileProtected to skip empty patterns gracefully (picomatch throws on empty strings). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rebuilt with ncc after adding protected files feature. Includes picomatch dependency bundled into dist/index.js. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The violation logging gate checked !protectedResult.passed, but checkProtectedFiles sets passed=true in warn mode regardless of violations. This meant warnings were never emitted. Changed the gate to check protectedResult.violations.length > 0 so both block errors and warn annotations are surfaced. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Agent output paths like ./package.json, sub/../CODEOWNERS, or .github//workflows//ci.yml could bypass picomatch patterns while Git API would normalize them to protected locations. Now paths are normalized with path.posix.normalize before matching, and paths that escape the repo root (../ or /) are flagged as security violations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The input was cast with "as block | warn" which provides no runtime protection. A typo like "Block" or "warnings" silently disables blocking. Now the input is validated at startup and the action fails fast with a clear message for invalid values. The unsafe type cast is removed since TS narrows the type after the guard clause. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dependency manifest patterns like package.json only matched at the repo root. In monorepos, apps/web/package.json would bypass protection. Updated all manifest patterns to use **/ prefix so they match at any depth. Updated CATEGORY_MAP to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nd monorepo depth Added test suites: - path normalization: ./ prefix, ../ traversal, // double slashes - path traversal rejection: ../ escape, absolute paths, normalized escape - monorepo depth matching: nested package.json, go.mod, all manifests - warn mode emits violations: violations array populated in warn mode Updated existing tests to use **/ prefix for dependency manifest patterns to match the new defaults. 69 tests passing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rebuilt after all four security fixes (warn mode gate, path normalization, input validation, monorepo depth matching). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Adds a new protected files phase to the pipeline (Phase 2, between constraint validation and secret sanitization). When an agent creates a PR that modifies sensitive file paths, the action blocks or warns based on configuration.
New pipeline
Features
.gitignore-compatible globs viapicomatchwith{ dot: true }.github/workflows/**), dependency manifests (**/package.json,**/go.mod, etc.), agent instructions (AGENTS.md,.claude/**,.codex/**), access control (CODEOWNERS)**/prefix to match at any directory depth!patternnegation creates exceptions (last match wins).override-defaults: trueto opt out entirely.block(hard fail) andwarn(annotate but continue)path.posix.normalize()prevents bypass via./,../,//. Path traversal attempts are rejected as security violations.New action inputs
protected-files''protected-files-actionblockblockorwarnprotected-files-override-defaultsfalseDesign decisions
Full analysis in research/protected-files-pattern-syntax.md and research/gh-aw-agentic-workflows.md.
**/prefix for manifests: security control should match at any depthReview-fix loop
Test evidence
protected-files.ts, 97.4% branchesnpm run allpasses (format, lint, test, build)Relates to: gh-aw gap analysis Priority 1