Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/guardrails/profile/agents/incident-responder.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ permission:
"rm -rf *": deny
"rm -r *": deny
"sudo *": deny
"curl * | sh*": deny
"wget * | sh*": deny
"git log*": allow
"git diff*": allow
"git show*": allow
Expand All @@ -33,6 +31,8 @@ permission:
"pwd": allow
"which *": allow
"curl *": ask
"curl * | sh*": deny
"wget * | sh*": deny
"node *": allow
"bun *": allow
---
Expand Down
8 changes: 7 additions & 1 deletion packages/guardrails/profile/agents/security-engineer.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ permission:
"*.pem": deny
"*.key": deny
"*secret*": deny
grep: allow
grep:
"*": allow
"*.env*": deny
"*credentials*": deny
"*.pem": deny
"*.key": deny
"*secret*": deny
Comment on lines +13 to +18
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

These grep deny patterns won’t actually prevent reading .env/key material via the grep tool, because GrepTool asks permission against params.pattern (the regex searched for), not the file paths being scanned. As a result, grep "DATABASE_URL" over a repo will still return matches from .env files. To fix #105 you likely need to (a) remove/disable grep for this agent, or (b) update the grep tool/permission model to authorize based on searched file paths (or at least exclude sensitive globs in the tool itself before returning matches).

Suggested change
"*": allow
"*.env*": deny
"*credentials*": deny
"*.pem": deny
"*.key": deny
"*secret*": deny
"*": deny

Copilot uses AI. Check for mistakes.
glob: allow
edit:
"*": deny
Expand Down
6 changes: 3 additions & 3 deletions packages/guardrails/profile/plugins/guardrail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ export default async function guardrail(input: {
const protectedBranch = /^(main|master|develop|dev)$/
if (/\bgit\s+push\b/i.test(cmd)) {
// Check explicit branch target
const explicitMatch = cmd.match(/\bgit\s+push\s+\S+\s+(?:HEAD:)?(\S+)/i)
const explicitMatch = cmd.match(/\bgit\s+push\s+(?:(?:-\w+|--[\w-]+)\s+)*\S+\s+(?:HEAD:)?(\S+)/i)
if (explicitMatch && protectedBranch.test(explicitMatch[1])) {
Comment on lines 514 to 518
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

The new git push regex still treats many valid flag forms as the remote name, so protected-branch pushes can still bypass the block (e.g., git push --push-option=ci.skip origin main where --push-option=... doesn’t match --[\w-]+, or flags that take a separate argument like -o ci.skip). Consider tokenizing the command (shellwords) and skipping all leading options (handling --opt=value and --opt value / -o value) before extracting <remote> <refspec> and checking the refspec/branch.

Copilot uses AI. Check for mistakes.
throw new Error(text("direct push to protected branch blocked — use a PR workflow"))
}
Expand All @@ -524,7 +524,7 @@ export default async function guardrail(input: {
throw new Error(text("direct push to protected branch blocked — use a PR workflow"))
}
// Plain `git push` with no branch — check current branch
if (!/\bgit\s+push\s+\S+\s+\S+/i.test(cmd)) {
if (!/\bgit\s+push\s+(?:(?:-\w+|--[\w-]+)\s+)*\S+\s+\S+/i.test(cmd)) {
try {
Comment on lines 526 to 528
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

Same issue for the “plain git push” detection regex: options with attached or separate values (e.g. -o ci.skip, --push-option=...) won’t be consumed by the flag group and can cause the pattern to mis-detect whether a branch was provided, which may skip the intended protected-branch checks. Using the same tokenization-based approach here would avoid regex edge cases.

Copilot uses AI. Check for mistakes.
const result = await git(input.worktree, ["branch", "--show-current"])
if (result.stdout && protectedBranch.test(result.stdout.trim())) {
Expand Down Expand Up @@ -609,7 +609,7 @@ export default async function guardrail(input: {
out.output += "\n\n📝 Source code edited (3+ operations). Check if related documentation (README, AGENTS.md, ADRs) needs updating."
}
// Auto-format reminder after 3+ source edits
if (nextEditCount >= 3 && nextEditCount % 3 === 0) {
if (code(file) && nextEditCount >= 3 && nextEditCount % 3 === 0) {
out.output = (out.output || "") + "\n🎨 " + nextEditCount + " source edits — consider running formatter (`prettier --write`, `biome format`, `go fmt`)."
}
}
Expand Down
Loading