Skip to content

Commit 2267b88

Browse files
authored
Feat/community rules expansion (#1)
* feat(rules): add destructive infrastructure gates * feat(rules): add agent persistence gates * feat(rules): add data exfiltration gates * feat(rules): add supply chain gates * feat(rules): add system integrity gates
1 parent c8d0a2c commit 2267b88

42 files changed

Lines changed: 2126 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# `exfil.browser-credential-read`
2+
3+
Block reads of browser credential / cookie stores and IDE session token storage.
4+
5+
## What it catches
6+
7+
**Chromium-family (Chrome, Chromium, Edge, Brave, Arc):**
8+
- `Login Data` — saved passwords (encrypted, but the key lives on disk)
9+
- `Cookies` — active session cookies (full impersonation)
10+
- `Web Data` — autofill, including credit cards
11+
- `Local State` — encryption key wrapper
12+
13+
**Firefox:**
14+
- `key4.db` — master encryption key
15+
- `logins.json` — saved logins
16+
- `cookies.sqlite` — session cookies
17+
- `signedInUser.json` — Sync account state
18+
19+
**Safari:**
20+
- `Cookies.binarycookies`
21+
22+
**Desktop apps holding live session tokens:**
23+
- Slack (`Cookies`, `storage/`) — workspace bearer tokens
24+
- Discord (`Local Storage/`, `Cookies`) — user tokens
25+
- Signal (`sql/db.sqlite`) — message DB
26+
- Cursor / VS Code (`globalStorage/`, `Local Storage/`) — Copilot/Anthropic tokens, MCP secrets, extension auth
27+
28+
**macOS keychain:**
29+
- `~/Library/Keychains/login.keychain-db`
30+
31+
## Why it matters
32+
33+
These are not config files — they are **live session databases**. Reading `Cookies` from Chrome gives you the user's GitHub, Slack, Gmail, AWS console, and SaaS SSO sessions, all at once. Reading Cursor's `globalStorage/` may surface the user's stored Anthropic API key and any MCP server tokens.
34+
35+
Real-world relevance:
36+
37+
- The [Claude Code source-map leak (March 2026)](https://www.zscaler.com/blogs/security-research/anthropic-claude-code-leak) noted that Claude Code "operates at the terminal level with access to local file systems, environment variables, and critically the `~/.anthropic/config` directory where API keys live" — IDE session storage is the same trust class.
38+
- The [Shai-Hulud npm worm](https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack) harvested "credentials from the developer's machine, including npm tokens, GitHub Personal Access Tokens, and cloud service keys" — the IDE session storage class is exactly that surface.
39+
- A prompt-injected agent reading these files is a one-step path to *full account takeover* on every web service the developer is logged into.
40+
41+
## False positives
42+
43+
- Reading `~/Library/Application Support/Google/Chrome/Default/Bookmarks` (bookmarks file) is **not** caught.
44+
- Reading `~/Library/Application Support/Code/User/settings.json` (VS Code settings) is **not** caught — only the storage paths that hold tokens.
45+
- Linux `gnome-keyring` and KWallet are not file-readable; this rule has no patterns for them.
46+
- A genuine debugging session that needs to inspect Chrome's `Login Data` will be denied. Approve one-shot if the operator is intentionally doing forensics.
47+
48+
## Test it
49+
50+
```bash
51+
agentlock fake-hook --session <id> --tool Read \
52+
--path '/Users/me/Library/Application Support/Google/Chrome/Default/Cookies'
53+
# expect: deny
54+
55+
agentlock fake-hook --session <id> --tool Read \
56+
--path '/Users/me/Library/Application Support/Code/User/settings.json'
57+
# expect: allow
58+
```
59+
60+
## Sources
61+
62+
- [Claude Code is leaking API keys into public package registries — TechTalks](https://bdtechtalks.com/2026/04/27/claude-code-api-token-leak/)
63+
- [Shai-Hulud npm Supply Chain Attack — Wiz](https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack)
64+
- [From .env to Leakage — Knostic](https://www.knostic.ai/blog/claude-cursor-env-file-secret-leakage)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
schema_version: 1
2+
id: exfil.browser-credential-read
3+
name: Block reads of browser credential / cookie stores and IDE session tokens
4+
description: |
5+
Denies Read tool calls against browser credential stores (Chrome /
6+
Chromium / Brave / Edge `Login Data` and `Cookies`, Firefox
7+
`key4.db` / `logins.json` / `cookies.sqlite`, Safari
8+
`Cookies.binarycookies`) and against IDE-extension session storage
9+
that frequently holds OAuth tokens (Slack, Cursor, VS Code, Discord,
10+
Signal). These are not "config files" — they are live session
11+
databases. An agent that reads them gives the LLM (and any
12+
downstream tool call) full impersonation power over the user's
13+
active web sessions.
14+
severity: critical
15+
tags:
16+
- secrets
17+
- browser
18+
- session
19+
- cookies
20+
- read
21+
authors:
22+
- github: RonCodes88
23+
license: Apache-2.0
24+
compatible_agentlock: ">=0.1.0"
25+
gate:
26+
match:
27+
tool: Read
28+
any_path_regex:
29+
- '(?:^|/)Library/Application Support/Google/Chrome/[^/]+/(?:Login Data|Cookies|Web Data|History|Local State)$'
30+
- '(?:^|/)Library/Application Support/Chromium/[^/]+/(?:Login Data|Cookies)$'
31+
- '(?:^|/)Library/Application Support/BraveSoftware/Brave-Browser/[^/]+/(?:Login Data|Cookies)$'
32+
- '(?:^|/)Library/Application Support/Microsoft Edge/[^/]+/(?:Login Data|Cookies)$'
33+
- '(?:^|/)Library/Application Support/Arc/User Data/[^/]+/(?:Login Data|Cookies)$'
34+
- '(?:^|/)\.config/google-chrome/[^/]+/(?:Login Data|Cookies)$'
35+
- '(?:^|/)\.config/chromium/[^/]+/(?:Login Data|Cookies)$'
36+
- '(?:^|/)\.config/microsoft-edge/[^/]+/(?:Login Data|Cookies)$'
37+
- '(?:^|/)\.config/BraveSoftware/Brave-Browser/[^/]+/(?:Login Data|Cookies)$'
38+
- '(?:^|/)Library/Application Support/Firefox/Profiles/[^/]+/(?:key4\.db|key3\.db|logins\.json|cookies\.sqlite|signedInUser\.json)$'
39+
- '(?:^|/)\.mozilla/firefox/[^/]+/(?:key4\.db|key3\.db|logins\.json|cookies\.sqlite|signedInUser\.json)$'
40+
- '(?:^|/)Library/Cookies/Cookies\.binarycookies$'
41+
- '(?:^|/)Library/Application Support/Slack/Cookies$'
42+
- '(?:^|/)Library/Application Support/Slack/storage/'
43+
- '(?:^|/)Library/Application Support/discord/Local Storage/'
44+
- '(?:^|/)Library/Application Support/discord/Cookies$'
45+
- '(?:^|/)Library/Application Support/Signal/sql/db\.sqlite$'
46+
- '(?:^|/)Library/Application Support/Cursor/User/globalStorage/'
47+
- '(?:^|/)Library/Application Support/Cursor/Local Storage/'
48+
- '(?:^|/)Library/Application Support/Code/User/globalStorage/'
49+
- '(?:^|/)Library/Application Support/Code/Local Storage/'
50+
- '(?:^|/)\.config/Code/User/globalStorage/'
51+
- '(?:^|/)\.config/Cursor/User/globalStorage/'
52+
- '(?:^|/)Library/Keychains/login\.keychain-db$'
53+
- '(?:^|/)Library/Keychains/login\.keychain$'
54+
evaluate:
55+
- kind: always
56+
action: deny

rules/cloud-cred-read/README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# `exfil.cloud-cred-read`
2+
3+
Block reads of cloud-SDK credential stores not already covered by `rogue.secret-read`.
4+
5+
## What it catches
6+
7+
| Provider | Path | Holds |
8+
|---|---|---|
9+
| gcloud | `~/.config/gcloud/application_default_credentials.json` | ADC OAuth2 refresh token |
10+
| gcloud | `~/.config/gcloud/access_tokens.db`, `credentials.db` | Account credentials |
11+
| gcloud | `~/.config/gcloud/legacy_credentials/` | Per-account creds |
12+
| Azure CLI | `~/.azure/accessTokens.json`, `azureProfile.json` | Bearer tokens, sub IDs |
13+
| Azure CLI | `~/.azure/msal_token_cache.{json,bin}` | MSAL token cache |
14+
| Docker | `~/.docker/config.json` | Container registry auth |
15+
| GCP | `*service*account*.json` | Service-account private keys |
16+
| Terraform | `~/.terraform.d/credentials.tfrc.json` | TFC/TFE tokens |
17+
| Terraform | `terraform.tfstate` (and `.backup`) | **Rendered secrets baked into state** |
18+
| Helm | `~/.helm/repository/repositories.yaml`, `~/.helm/registry/config.json` | Chart registry creds |
19+
| Databricks | `~/.databrickscfg`, `~/.databricks/token-cache.json` | Workspace tokens |
20+
| Snowflake | `~/.snowflake/connections.toml` | Per-connection passwords |
21+
| Heroku | `~/.config/heroku/config.json` | API key |
22+
| GitHub CLI | `~/.config/gh/hosts.yml` | OAuth tokens for `gh` |
23+
| 1Password CLI | `~/.config/op/config` | Account configuration |
24+
25+
## Why it matters
26+
27+
This rule complements `rogue.secret-read`, which already covers `.aws/credentials`, `.aws/config`, kubeconfig, SSH keys, .npmrc, .pypirc, .netrc, and .gnupg. Together they form a near-complete moat around the file-system credential surface.
28+
29+
The threat model is the same as the [Supabase MCP service-role incident](https://generalanalysis.com/blog/supabase-mcp-blog) — once a credential enters the agent's context, every downstream tool call is a potential exfil channel:
30+
31+
> *The cursor assistant operates the Supabase database with elevated access via the service_role, which bypasses all row-level security (RLS) protections.*
32+
33+
…the same logic applies to a GCP service-account JSON or an Azure access token. A prompt-injected agent that has just read `~/.config/gcloud/application_default_credentials.json` can do anything that account can do across GCP — and the credential is now also sitting in the LLM provider's context window.
34+
35+
`terraform.tfstate` is the under-appreciated entry: Terraform writes provisioned-resource secrets (DB passwords, generated API keys) into state in plaintext. Reading state is reading every secret Terraform has ever provisioned for that workspace.
36+
37+
## False positives
38+
39+
- A repo's own `terraform.tfstate` checked into version control (rare, anti-pattern, but happens) is caught — that's intentional, the secrets are real.
40+
- An agent debugging a `gh auth` issue that wants to inspect `~/.config/gh/hosts.yml` is denied. Use `gh auth status` instead — it doesn't leak the token.
41+
- Azure CLI's `~/.azure/clouds.config` (cloud profile, no secrets) is **not** caught.
42+
43+
## Test it
44+
45+
```bash
46+
agentlock fake-hook --session <id> --tool Read \
47+
--path ~/.config/gcloud/application_default_credentials.json
48+
# expect: deny
49+
50+
agentlock fake-hook --session <id> --tool Read \
51+
--path ~/.config/gcloud/active_config
52+
# expect: deny (just account name, but read is the install step for follow-on attacks)
53+
```
54+
55+
## Sources
56+
57+
- [Supabase MCP can leak your entire SQL database — General Analysis](https://generalanalysis.com/blog/supabase-mcp-blog)
58+
- [When AI Has Root: Lessons from the Supabase MCP Data Leak — Pomerium](https://www.pomerium.com/blog/when-ai-has-root-lessons-from-the-supabase-mcp-data-leak)
59+
- [From .env to Leakage: Mishandling of Secrets by Coding Agents — Knostic](https://www.knostic.ai/blog/claude-cursor-env-file-secret-leakage)

rules/cloud-cred-read/rule.yaml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
schema_version: 1
2+
id: exfil.cloud-cred-read
3+
name: Block reads of cloud-SDK credential stores (gcloud, Azure, Docker, Terraform)
4+
description: |
5+
Denies Read tool calls against the credential stores of cloud SDKs
6+
not already covered by `rogue.secret-read`: gcloud
7+
(`application_default_credentials.json`, `legacy_credentials/`),
8+
Azure CLI (`accessTokens.json`, `azureProfile.json`,
9+
`msal_token_cache.*`), Docker (`config.json`, which holds registry
10+
auth), GCP service-account JSON keys, Terraform credentials and
11+
state files (state can hold rendered secrets), Helm registry
12+
credentials, and Databricks tokens. Once the agent reads any of
13+
these, the tokens are in its context and can flow to any downstream
14+
tool call — the exact failure mode the Supabase MCP service-role
15+
incident demonstrated at scale.
16+
severity: critical
17+
tags:
18+
- secrets
19+
- cloud
20+
- gcloud
21+
- azure
22+
- docker
23+
- terraform
24+
- read
25+
authors:
26+
- github: RonCodes88
27+
license: Apache-2.0
28+
compatible_agentlock: ">=0.1.0"
29+
gate:
30+
match:
31+
tool: Read
32+
any_path_regex:
33+
- '(?:^|/)\.config/gcloud/application_default_credentials\.json$'
34+
- '(?:^|/)\.config/gcloud/access_tokens\.db$'
35+
- '(?:^|/)\.config/gcloud/credentials\.db$'
36+
- '(?:^|/)\.config/gcloud/legacy_credentials/'
37+
- '(?:^|/)\.config/gcloud/active_config$'
38+
- '(?:^|/)\.azure/accessTokens\.json$'
39+
- '(?:^|/)\.azure/azureProfile\.json$'
40+
- '(?:^|/)\.azure/msal_token_cache\.(?:json|bin)$'
41+
- '(?:^|/)\.azure/service_principal_entries\.json$'
42+
- '(?:^|/)\.docker/config\.json$'
43+
- '(?:^|/)gcp[._-]?service[._-]?account[^/]*\.json$'
44+
- '(?:^|/)service[._-]?account[._-]?key[^/]*\.json$'
45+
- '(?:^|/)service[._-]?account\.json$'
46+
- '(?:^|/)\.terraform\.d/credentials\.tfrc\.json$'
47+
- '(?:^|/)terraform\.tfstate(?:\.backup)?$'
48+
- '(?:^|/)\.terraform/terraform\.tfstate$'
49+
- '(?:^|/)\.helm/repository/repositories\.yaml$'
50+
- '(?:^|/)\.helm/registry/config\.json$'
51+
- '(?:^|/)\.databrickscfg$'
52+
- '(?:^|/)\.databricks/token-cache\.json$'
53+
- '(?:^|/)\.snowflake/connections\.toml$'
54+
- '(?:^|/)\.config/cloudflared/cert\.pem$'
55+
- '(?:^|/)\.config/heroku/config\.json$'
56+
- '(?:^|/)\.config/digitalocean/config\.yaml$'
57+
- '(?:^|/)\.fly/config\.yml$'
58+
- '(?:^|/)\.config/op/config$'
59+
- '(?:^|/)\.gh/hosts\.yml$'
60+
- '(?:^|/)\.config/gh/hosts\.yml$'
61+
evaluate:
62+
- kind: always
63+
action: deny
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# `rogue.cloud-resource-destroy`
2+
3+
Block destructive cloud-CLI commands that bypass confirmation prompts.
4+
5+
## What it catches
6+
7+
Across AWS, GCP, and Azure: any delete/terminate operation that uses a force/quiet/yes flag to skip the SDK's normal "are you sure?" prompt.
8+
9+
| Pattern | Why it's load-bearing |
10+
|---|---|
11+
| `aws s3 rb --force` | Recursively deletes a bucket and all its objects |
12+
| `aws s3 rm --recursive s3://...` | Wipes objects from a prefix without confirmation |
13+
| `aws ec2 terminate-instances` | Terminates EC2 (data on instance store is gone) |
14+
| `aws rds delete-db-instance` | Catches both `--skip-final-snapshot` and the bare form |
15+
| `aws rds delete-db-snapshot` | Removes the only recovery path |
16+
| `aws dynamodb delete-table` | Drops a table and its data |
17+
| `aws iam delete-*` | Wipes users/roles/policies/keys (lockout, audit gaps) |
18+
| `aws kms schedule-key-deletion` | Schedules a CMK for deletion → encrypted data unrecoverable |
19+
| `aws secretsmanager delete-secret` | Drops secrets (recovery window varies) |
20+
| `aws ecr batch-delete-image` | Removes container images mid-rollout |
21+
| `aws cloudformation delete-stack` | Tears down a whole stack |
22+
| `gcloud ... delete --quiet` | Skips GCP's interactive confirmation |
23+
| `gcloud projects delete` | Deletes an entire GCP project |
24+
| `az group delete --yes` | Deletes an Azure resource group with everything in it |
25+
26+
## Why it matters
27+
28+
Cloud providers ship interactive confirmation prompts on destructive operations specifically because mistakes here are unrecoverable. The flags this rule blocks (`--force`, `--quiet`, `-q`, `--yes`, `-y`, `--skip-final-snapshot`) exist for scripted pipelines that have *already* gone through human review. An autonomous agent invoking them is the worst-case combination: no human in the loop, no rollback.
29+
30+
The Replit AI incident wiped data for [1,200+ executives and 1,190+ companies](https://www.tomshardware.com/tech-industry/artificial-intelligence/ai-coding-platform-goes-rogue-during-code-freeze-and-deletes-entire-company-database-replit-ceo-apologizes-after-ai-engine-says-it-made-a-catastrophic-error-in-judgment-and-destroyed-all-production-data) during an active code freeze — the agent ran destructive commands "despite explicit instructions not to proceed without human approval". The same shape applies to any cloud CLI invocation that strips the confirmation prompt.
31+
32+
## False positives
33+
34+
- `aws s3 rm s3://bucket/key` (a single object, no `--recursive`) is **not** caught.
35+
- `aws ec2 stop-instances` is **not** caught — stopping is reversible.
36+
- `aws rds create-db-snapshot` followed by `aws rds delete-db-instance` is still caught on the delete. That's intentional — snapshot first, then have a human approve the delete.
37+
- `gcloud ... delete` *without* `--quiet` will trigger the SDK's own prompt and is **not** caught here.
38+
39+
## Test it
40+
41+
```bash
42+
agentlock fake-hook --session <id> --tool Bash --command 'aws s3 rb s3://prod-data --force'
43+
# expect: deny
44+
45+
agentlock fake-hook --session <id> --tool Bash --command 'aws s3 ls s3://prod-data'
46+
# expect: allow
47+
```
48+
49+
## Sources
50+
51+
- [Replit AI Wiped Production Database — Fortune](https://fortune.com/2025/07/23/ai-coding-tool-replit-wiped-database-called-it-a-catastrophic-failure/)
52+
- [Incident 1152 — AI Incident Database](https://incidentdatabase.ai/cite/1152/)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
schema_version: 1
2+
id: rogue.cloud-resource-destroy
3+
name: Block destructive cloud-CLI deletes (AWS, GCP, Azure)
4+
description: |
5+
Denies bash invocations of cloud-provider CLIs that destroy
6+
production-bearing resources while bypassing confirmation prompts:
7+
`aws s3 rb --force`, `aws ec2 terminate-instances`, `aws rds
8+
delete-db-instance --skip-final-snapshot`, `aws s3api delete-bucket`,
9+
`aws iam delete-*`, `gcloud … delete --quiet`, and `az group delete
10+
--yes`. Each shape removes the human-confirmation gate the cloud SDK
11+
normally enforces — exactly the shortcut an AI agent reaches for when
12+
it interprets cleanup as the right next action.
13+
severity: critical
14+
tags:
15+
- aws
16+
- gcp
17+
- azure
18+
- cloud
19+
- destructive
20+
- bash
21+
authors:
22+
- github: RonCodes88
23+
license: Apache-2.0
24+
compatible_agentlock: ">=0.1.0"
25+
gate:
26+
match:
27+
tool: Bash
28+
any_command_regex:
29+
- '\baws\s+s3\s+rb\s+(?:[^|;&]*\s+)?--force\b'
30+
- '\baws\s+s3\s+rm\s+(?:[^|;&]*\s+)?--recursive\s+s3://'
31+
- '\baws\s+s3api\s+delete-bucket\b'
32+
- '\baws\s+ec2\s+terminate-instances\b'
33+
- '\baws\s+rds\s+delete-db-instance\b'
34+
- '\baws\s+rds\s+delete-db-cluster\b'
35+
- '\baws\s+rds\s+delete-db-snapshot\b'
36+
- '\baws\s+dynamodb\s+delete-table\b'
37+
- '\baws\s+iam\s+delete-(?:user|role|policy|access-key|group)\b'
38+
- '\baws\s+kms\s+(?:schedule-key-deletion|disable-key)\b'
39+
- '\baws\s+secretsmanager\s+delete-secret\b'
40+
- '\baws\s+ssm\s+delete-parameter(?:s)?\b'
41+
- '\baws\s+ecr\s+(?:delete-repository|batch-delete-image)\b'
42+
- '\baws\s+lambda\s+delete-function\b'
43+
- '\baws\s+cloudformation\s+delete-stack\b'
44+
- '\bgcloud\s+(?:[^|;&]*\s+)?delete\s+(?:[^|;&]*\s+)?--quiet\b'
45+
- '\bgcloud\s+(?:[^|;&]*\s+)?delete\s+(?:[^|;&]*\s+)?-q\b'
46+
- '\bgcloud\s+projects\s+delete\b'
47+
- '\baz\s+group\s+delete\s+(?:[^|;&]*\s+)?--yes\b'
48+
- '\baz\s+group\s+delete\s+(?:[^|;&]*\s+)?-y\b'
49+
- '\baz\s+(?:vm|disk|sql|storage)\s+delete\s+(?:[^|;&]*\s+)?--yes\b'
50+
evaluate:
51+
- kind: always
52+
action: deny

0 commit comments

Comments
 (0)