opsentry 1.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. opsentry/VERSION +1 -0
  2. opsentry/__init__.py +5 -0
  3. opsentry/cli.py +295 -0
  4. opsentry/config/__init__.py +0 -0
  5. opsentry/config/generate.py +414 -0
  6. opsentry/config/hook_templates/block-dangerous-commands.sh.j2 +102 -0
  7. opsentry/config/hook_templates/block-data-exfiltration.sh.j2 +94 -0
  8. opsentry/config/hook_templates/block-environment-escape.sh.j2 +99 -0
  9. opsentry/config/hook_templates/block-git-commands.sh.j2 +46 -0
  10. opsentry/config/hook_templates/block-package-install.sh.j2 +95 -0
  11. opsentry/config/hook_templates/block-pii-leakage.sh.j2 +100 -0
  12. opsentry/config/hook_templates/block-scope-escape.sh.j2 +115 -0
  13. opsentry/config/hook_templates/block-sensitive-files.sh.j2 +97 -0
  14. opsentry/config/init_wizard.py +201 -0
  15. opsentry/config/rule_texts/01_sensitive_files.md +15 -0
  16. opsentry/config/rule_texts/02_client_data.md +10 -0
  17. opsentry/config/rule_texts/03_credentials.md +17 -0
  18. opsentry/config/rule_texts/04_sql_safety.md +33 -0
  19. opsentry/config/rule_texts/05_dangerous_code.md +29 -0
  20. opsentry/config/rule_texts/06_frontend_xss.md +9 -0
  21. opsentry/config/rule_texts/07_git_operations_block_all.md +13 -0
  22. opsentry/config/rule_texts/07_git_operations_read_only.md +18 -0
  23. opsentry/config/rule_texts/08_destructive_commands.md +12 -0
  24. opsentry/config/rule_texts/09_database_safety.md +17 -0
  25. opsentry/config/rule_texts/10_network_requests.md +10 -0
  26. opsentry/config/rule_texts/11_code_quality.md +13 -0
  27. opsentry/config/rule_texts/12_data_exfiltration.md +10 -0
  28. opsentry/config/rule_texts/13_package_install.md +9 -0
  29. opsentry/config/rule_texts/14_scope_boundaries.md +8 -0
  30. opsentry/config/rule_texts/15_env_isolation.md +9 -0
  31. opsentry/config/rule_texts/16_resource_limits.md +8 -0
  32. opsentry/config/rule_texts/17_compliance_privacy.md +9 -0
  33. opsentry/config/rule_texts/18_when_in_doubt.md +10 -0
  34. opsentry/config/schema.py +142 -0
  35. opsentry/config/templates/generic.yaml +84 -0
  36. opsentry/deployment/.claude/commands/code-health.md +42 -0
  37. opsentry/deployment/.claude/commands/governance-check.md +29 -0
  38. opsentry/deployment/.claude/commands/security-audit.md +41 -0
  39. opsentry/deployment/baseline.py +440 -0
  40. opsentry/deployment/blocklog_audit.py +497 -0
  41. opsentry/deployment/claude/CLAUDE.md +300 -0
  42. opsentry/deployment/claude/hooks/block-dangerous-commands.sh +106 -0
  43. opsentry/deployment/claude/hooks/block-data-exfiltration.sh +99 -0
  44. opsentry/deployment/claude/hooks/block-environment-escape.sh +96 -0
  45. opsentry/deployment/claude/hooks/block-git-commands.sh +37 -0
  46. opsentry/deployment/claude/hooks/block-package-install.sh +103 -0
  47. opsentry/deployment/claude/hooks/block-pii-leakage.sh +86 -0
  48. opsentry/deployment/claude/hooks/block-scope-escape.sh +210 -0
  49. opsentry/deployment/claude/hooks/block-sensitive-files.sh +97 -0
  50. opsentry/deployment/claude/settings.json +72 -0
  51. opsentry/deployment/install.sh +343 -0
  52. opsentry/deployment/patrol.sh +266 -0
  53. opsentry/deployment/test.sh +728 -0
  54. opsentry/deployment/update.sh +19 -0
  55. opsentry/deployment/verify.sh +201 -0
  56. opsentry-1.8.0.dist-info/METADATA +158 -0
  57. opsentry-1.8.0.dist-info/RECORD +61 -0
  58. opsentry-1.8.0.dist-info/WHEEL +5 -0
  59. opsentry-1.8.0.dist-info/entry_points.txt +2 -0
  60. opsentry-1.8.0.dist-info/licenses/LICENSE +190 -0
  61. opsentry-1.8.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env python3
2
+ """Interactive setup wizard that generates guardrails.yaml from user choices."""
3
+
4
+ import argparse
5
+ import shutil
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ CONFIG_DIR = Path(__file__).parent
10
+ TEMPLATES_DIR = CONFIG_DIR / "templates"
11
+
12
+ TEMPLATES = {
13
+ "1": ("generic", "Generic (recommended starting point)"),
14
+ "2": ("fintech", "Fintech / Banking"),
15
+ "3": ("healthcare", "Healthcare"),
16
+ "4": ("manufacturing", "Manufacturing / IoT"),
17
+ }
18
+
19
+ PII_LOCALES = {"US": "US (SSN, credit cards)", "KR": "KR (Korean RRN)", "EU": "EU (IBAN, national IDs)"}
20
+ GIT_POLICIES = {"1": "block_all", "2": "read_only", "3": "allow_all"}
21
+ INFRA_POLICIES = {"1": "block", "2": "read_only", "3": "allow"}
22
+ COMPLIANCE_OPTS = {"1": "iso_27001", "2": "soc2", "3": "hipaa", "4": "pci_dss"}
23
+ LLM_PROVIDERS = {"1": "bedrock", "2": "anthropic", "3": "openai", "4": "local"}
24
+
25
+
26
+ def ask(prompt: str, choices: dict[str, str] | None = None, default: str = "") -> str:
27
+ """Ask user a question with optional numbered choices."""
28
+ if choices:
29
+ print()
30
+ for key, label in choices.items():
31
+ print(f" [{key}] {label}")
32
+ while True:
33
+ suffix = f" [{default}]" if default else ""
34
+ answer = input(f"\n{prompt}{suffix}: ").strip()
35
+ if not answer and default:
36
+ return default
37
+ if choices and answer in choices:
38
+ return answer
39
+ if not choices and answer:
40
+ return answer
41
+ if choices:
42
+ print(f" Please enter one of: {', '.join(choices.keys())}")
43
+
44
+
45
+ def ask_multi(prompt: str, choices: dict[str, str]) -> list[str]:
46
+ """Ask user to select multiple options (comma-separated)."""
47
+ print()
48
+ for key, label in choices.items():
49
+ print(f" [{key}] {label}")
50
+ print(f" [0] None")
51
+ while True:
52
+ answer = input(f"\n{prompt} (comma-separated, or 0 for none): ").strip()
53
+ if answer == "0":
54
+ return []
55
+ parts = [p.strip() for p in answer.split(",")]
56
+ if all(p in choices for p in parts):
57
+ return [choices[p] for p in parts]
58
+ print(f" Please enter valid choices from: {', '.join(choices.keys())}, or 0")
59
+
60
+
61
+ def run_interactive() -> str:
62
+ """Run the interactive wizard. Returns path to the generated guardrails.yaml."""
63
+ print("=" * 50)
64
+ print(" OpSentry Setup Wizard")
65
+ print("=" * 50)
66
+
67
+ # Step 1: Template
68
+ choice = ask("Select an industry template:", TEMPLATES, default="1")
69
+ template_name = TEMPLATES[choice][0]
70
+
71
+ # Copy template
72
+ src = TEMPLATES_DIR / f"{template_name}.yaml"
73
+ dest = Path("guardrails.yaml")
74
+ shutil.copy2(src, dest)
75
+
76
+ # Load as text for in-place edits
77
+ content = dest.read_text()
78
+
79
+ # Step 2: Organization
80
+ org_name = ask("Organization name (optional, press Enter to skip):", default="")
81
+ if org_name:
82
+ content = content.replace(' name: ""', f' name: "{org_name}"')
83
+
84
+ # Step 3: PII locales
85
+ print("\nWhich PII locales do you need?")
86
+ print(" US (SSN, credit cards) is always included.")
87
+ extra_locales = []
88
+ kr = input(" Include KR (Korean RRN)? [y/N]: ").strip().lower()
89
+ if kr in ("y", "yes"):
90
+ extra_locales.append("KR")
91
+ eu = input(" Include EU (IBAN)? [y/N]: ").strip().lower()
92
+ if eu in ("y", "yes"):
93
+ extra_locales.append("EU")
94
+
95
+ locales_str = ", ".join(f'"{l}"' for l in ["US"] + extra_locales)
96
+ # Replace the locales line
97
+ import re
98
+ content = re.sub(r'locales:\s*\[.*?\]', f'locales: [{locales_str}]', content)
99
+
100
+ # Step 4: Git policy
101
+ print("\nGit policy for AI agents:")
102
+ git_choice = ask("Select git policy:", {
103
+ "1": "Block all git commands (most secure, recommended)",
104
+ "2": "Allow read-only (status, log, diff)",
105
+ "3": "Allow all (not recommended)",
106
+ }, default="1")
107
+ git_policy = GIT_POLICIES[git_choice]
108
+ content = re.sub(r'git_policy:\s*".*?"', f'git_policy: "{git_policy}"', content)
109
+
110
+ # Step 5: Infra tools
111
+ print("\nInfrastructure tools policy:")
112
+ for tool in ("docker", "terraform", "kubectl"):
113
+ tool_choice = ask(f" {tool}:", {
114
+ "1": "Block all",
115
+ "2": "Read-only (recommended)",
116
+ "3": "Allow all",
117
+ }, default="2")
118
+ policy = INFRA_POLICIES[tool_choice]
119
+ content = re.sub(rf'{tool}:\s*".*?"', f'{tool}: "{policy}"', content)
120
+
121
+ # Step 6: Compliance
122
+ frameworks = ask_multi("Compliance frameworks:", COMPLIANCE_OPTS)
123
+ if frameworks:
124
+ fw_str = ", ".join(f'"{f}"' for f in frameworks)
125
+ content = re.sub(r'frameworks:\s*\[.*?\]', f'frameworks: [{fw_str}]', content)
126
+
127
+ # Step 7: LLM provider
128
+ llm_choice = ask("LLM provider for CI agents:", {
129
+ "1": "AWS Bedrock",
130
+ "2": "Anthropic API",
131
+ "3": "OpenAI-compatible",
132
+ "4": "Local (Ollama/LM Studio)",
133
+ }, default="1")
134
+ provider = LLM_PROVIDERS[llm_choice]
135
+ content = re.sub(r'llm_provider:\s*".*?"', f'llm_provider: "{provider}"', content)
136
+
137
+ # Write
138
+ dest.write_text(content)
139
+ print(f"\n{'=' * 50}")
140
+ print(f" Created {dest}")
141
+ print(f" Run: python3 config/generate.py guardrails.yaml")
142
+ print(f"{'=' * 50}")
143
+ return str(dest)
144
+
145
+
146
+ def run_non_interactive(args: argparse.Namespace) -> str:
147
+ """Generate guardrails.yaml from CLI flags."""
148
+ template_name = args.template or "generic"
149
+ src = TEMPLATES_DIR / f"{template_name}.yaml"
150
+ if not src.exists():
151
+ print(f"ERROR: Template not found: {src}", file=sys.stderr)
152
+ sys.exit(1)
153
+
154
+ dest = Path(args.output or "guardrails.yaml")
155
+ shutil.copy2(src, dest)
156
+
157
+ content = dest.read_text()
158
+ import re
159
+
160
+ if args.org:
161
+ content = content.replace(' name: ""', f' name: "{args.org}"')
162
+
163
+ if args.pii:
164
+ locales_str = ", ".join(f'"{l}"' for l in args.pii.split(","))
165
+ content = re.sub(r'locales:\s*\[.*?\]', f'locales: [{locales_str}]', content)
166
+
167
+ if args.git_policy:
168
+ content = re.sub(r'git_policy:\s*".*?"', f'git_policy: "{args.git_policy}"', content)
169
+
170
+ if args.compliance:
171
+ fw_str = ", ".join(f'"{f}"' for f in args.compliance.split(","))
172
+ content = re.sub(r'frameworks:\s*\[.*?\]', f'frameworks: [{fw_str}]', content)
173
+
174
+ if args.llm:
175
+ content = re.sub(r'llm_provider:\s*".*?"', f'llm_provider: "{args.llm}"', content)
176
+
177
+ dest.write_text(content)
178
+ print(f"Created {dest}")
179
+ return str(dest)
180
+
181
+
182
+ def main():
183
+ parser = argparse.ArgumentParser(description="OpSentry Setup Wizard")
184
+ parser.add_argument("--non-interactive", action="store_true", help="Run without prompts")
185
+ parser.add_argument("--template", help="Template: generic, fintech, healthcare, manufacturing")
186
+ parser.add_argument("--org", help="Organization name")
187
+ parser.add_argument("--pii", help="PII locales, comma-separated: US,KR,EU")
188
+ parser.add_argument("--git-policy", help="Git policy: block_all, read_only, allow_all")
189
+ parser.add_argument("--compliance", help="Compliance frameworks, comma-separated: iso_27001,soc2,hipaa,pci_dss")
190
+ parser.add_argument("--llm", help="LLM provider: bedrock, anthropic, openai, local")
191
+ parser.add_argument("--output", "-o", help="Output path (default: guardrails.yaml)")
192
+ args = parser.parse_args()
193
+
194
+ if args.non_interactive:
195
+ run_non_interactive(args)
196
+ else:
197
+ run_interactive()
198
+
199
+
200
+ if __name__ == "__main__":
201
+ main()
@@ -0,0 +1,15 @@
1
+ ## 1. Sensitive File Access — NEVER Read or Modify
2
+
3
+ You must never read, open, display, reference, or modify the contents of the following files or patterns. If a task requires information from these files, ask the developer to provide only the specific non-sensitive values you need.
4
+
5
+ - `.env`, `.env.*`, `.env.local`, `.env.production`, `.env.staging`
6
+ - `credentials.json`, `credentials.yaml`, `credentials.toml`
7
+ - Any file with `secret`, `secrets`, `token`, or `tokens` in its name
8
+ - `*.pem`, `*.key`, `*.crt`, `*.p12`, `*.pfx` (SSL/TLS certificates and private keys)
9
+ - `~/.ssh/*` (SSH keys and config)
10
+ - `~/.aws/*`, `~/.azure/*`, `~/.gcloud/*` (cloud provider credentials)
11
+ - `*.keystore`, `*.jks` (Java keystores)
12
+ - `snowflake.config`, `profiles.yml`, `connections.toml` (database connection configs)
13
+ - Any file inside directories named `secrets/`, `credentials/`, `private/`, or `keys/`
14
+
15
+ If you encounter a file matching these patterns during a task, skip it and inform the developer.
@@ -0,0 +1,10 @@
1
+ ## 2. Client Confidential Data — STRICTLY CONFIDENTIAL
2
+
3
+ You must never include any client-specific or business-sensitive identifiers in your outputs, code comments, commit messages, logs, or prompts to external services. This includes but is not limited to:
4
+
5
+ - Client or partner company names
6
+ - Internal project identifiers or account IDs
7
+ - Business-specific metrics, KPIs, or proprietary formulas
8
+ - Any data that could identify a specific client's setup or operations
9
+
10
+ When writing code that handles client data, use generic variable names and placeholder values in examples. Never hardcode real client data. If you need to create test data, generate clearly fake values (e.g., `client_name = "EXAMPLE_CLIENT_001"`).
@@ -0,0 +1,17 @@
1
+ ## 3. Credentials and Connection Strings — NEVER Hardcode
2
+
3
+ You must never write credentials, API keys, tokens, passwords, or connection strings directly into code. This applies to all contexts including tests, scripts, configuration files, and documentation.
4
+
5
+ **Always do this:**
6
+ - Reference environment variables: `os.environ.get("DB_PASSWORD")`
7
+ - Use secrets manager references
8
+ - Use placeholder values in examples: `"your-api-key-here"` or `"<DB_TOKEN>"`
9
+ - Use `.env.example` files with empty values to document required variables
10
+
11
+ **Never do this:**
12
+ - `password = "actual_password_123"`
13
+ - `api_key = "sk-abc123..."`
14
+ - `connection_string = "postgresql://user:pass@host/db"`
15
+ - Embed tokens in URLs, headers, or query parameters as literal strings
16
+
17
+ If you see hardcoded credentials in existing code, flag it to the developer immediately and suggest refactoring to use environment variables or the secrets manager.
@@ -0,0 +1,33 @@
1
+ ## 4. SQL Safety — Prevent Injection Vulnerabilities
2
+
3
+ All SQL queries must use parameterized queries or prepared statements. Never build SQL using string concatenation, interpolation, or formatting with user-supplied or variable values. This applies to every language and database.
4
+
5
+ **Dangerous patterns by language (never generate these):**
6
+
7
+ Python:
8
+ - `f"SELECT * FROM t WHERE id = '{val}'"` / `"..." + val` / `"..." % val`
9
+
10
+ Java/JDBC:
11
+ - `"SELECT * FROM t WHERE id = '" + val + "'"` via `Statement.execute()`
12
+
13
+ JavaScript/TypeScript:
14
+ - `` `SELECT * FROM t WHERE id = '${val}'` `` / `"..." + val`
15
+
16
+ C (embedded SQL):
17
+ - `sprintf(query, "SELECT * FROM t WHERE id = '%s'", val)`
18
+
19
+ **Safe patterns by language (always use these):**
20
+
21
+ Python:
22
+ - `cursor.execute("SELECT * FROM t WHERE id = %s", (val,))`
23
+
24
+ Java/JDBC:
25
+ - `PreparedStatement ps = conn.prepareStatement("SELECT * FROM t WHERE id = ?"); ps.setString(1, val);`
26
+
27
+ JavaScript/TypeScript:
28
+ - `db.query("SELECT * FROM t WHERE id = $1", [val])` (pg) / `db.query("SELECT * FROM t WHERE id = ?", [val])` (mysql2)
29
+
30
+ C:
31
+ - Use the database library's parameterized API (e.g., `mysql_stmt_prepare` + `mysql_stmt_bind_param`)
32
+
33
+ No exceptions regardless of database (MySQL, PostgreSQL, Snowflake, SQLite, etc.).
@@ -0,0 +1,29 @@
1
+ ## 5. Dangerous Code Patterns — Do Not Use
2
+
3
+ Never generate code using the following patterns. If existing code uses them, flag and suggest safe alternatives.
4
+
5
+ **Python:**
6
+ - `eval()` / `exec()` — use `ast.literal_eval()` or `json.loads()` for data parsing
7
+ - `pickle.loads()` on untrusted data — use `json` or other safe formats
8
+ - `subprocess.run(..., shell=True)` with variable input — use a list: `subprocess.run(["cmd", "arg1"])`
9
+ - `os.system()` — use `subprocess.run()` instead
10
+ - `__import__()` with variable input — can load arbitrary modules
11
+
12
+ **Java:**
13
+ - `Runtime.getRuntime().exec(userInput)` — use `ProcessBuilder` with explicit argument lists
14
+ - `ScriptEngine.eval()` with untrusted input — arbitrary code execution
15
+ - `ObjectInputStream.readObject()` on untrusted data — deserialization attacks. Use JSON or validated schemas
16
+ - `Class.forName()` with user-controlled input — can instantiate arbitrary classes
17
+ - `System.loadLibrary()` / `System.load()` with variable input — arbitrary native code execution
18
+
19
+ **JavaScript / TypeScript:**
20
+ - `eval()` / `new Function(userInput)` — use `JSON.parse()` for data
21
+ - `child_process.exec(userInput)` — use `child_process.execFile()` or `spawn()` with argument arrays
22
+ - `require(userInput)` / dynamic `import(userInput)` — can load arbitrary modules
23
+ - `vm.runInNewContext()` with untrusted input — sandbox escapes are well-documented
24
+
25
+ **C / C++:**
26
+ - `system()` with variable input — use `execvp()` or `posix_spawn()` with explicit argument arrays
27
+ - `gets()` — always use `fgets()` with buffer size
28
+ - `sprintf()` / `strcpy()` without bounds — use `snprintf()` / `strncpy()` or safer alternatives
29
+ - `dlopen()` / `dlsym()` with user-controlled paths — arbitrary code loading
@@ -0,0 +1,9 @@
1
+ ## 6. Frontend Security — Prevent XSS
2
+
3
+ When generating frontend code (JavaScript, TypeScript, React, HTML):
4
+
5
+ - Never assign unsanitized user input to `innerHTML`, `outerHTML`, or `document.write()`
6
+ - Use `textContent` instead of `innerHTML` when displaying user-provided text
7
+ - In React, never use `dangerouslySetInnerHTML` unless the content is sanitized with a library like DOMPurify
8
+ - Always escape user input before rendering it in HTML context
9
+ - Never construct HTML strings with template literals containing user input
@@ -0,0 +1,13 @@
1
+ ## 7. Git Operations — Do Not Execute
2
+
3
+ You must not execute any git commands. The developer manages version control themselves.
4
+
5
+ **Never run:**
6
+ - `git commit`, `git push`, `git pull`, `git merge`
7
+ - `git reset` (especially `--hard`)
8
+ - `git push --force` or `git push --force-with-lease`
9
+ - `git checkout` to switch branches
10
+ - `git stash`, `git rebase`, `git cherry-pick`
11
+ - `git tag`, `git branch -d`, `git branch -D`
12
+
13
+ If a developer asks you to help with git workflows, provide the commands as text for them to review and run manually. Do not execute them.
@@ -0,0 +1,18 @@
1
+ ## 7. Git Operations — Read-Only Permitted
2
+
3
+ You may use read-only git commands to understand the repository state. You must not execute any git commands that modify the repository.
4
+
5
+ **Allowed (read-only):**
6
+ - `git status`, `git log`, `git diff`, `git show`
7
+ - `git branch` (list only, no `-d` or `-D`)
8
+ - `git remote -v`, `git describe`
9
+
10
+ **Never run:**
11
+ - `git commit`, `git push`, `git pull`, `git merge`
12
+ - `git reset` (especially `--hard`)
13
+ - `git push --force` or `git push --force-with-lease`
14
+ - `git checkout` to switch branches
15
+ - `git stash`, `git rebase`, `git cherry-pick`
16
+ - `git tag`, `git branch -d`, `git branch -D`
17
+
18
+ If a developer asks you to help with git workflows beyond read-only commands, provide the commands as text for them to review and run manually.
@@ -0,0 +1,12 @@
1
+ ## 8. Destructive Commands — Never Execute
2
+
3
+ The following commands must never be run under any circumstances:
4
+
5
+ - `rm -rf` or any recursive forced deletion
6
+ - `rm -r` on directories outside the immediate working scope
7
+ - `sudo` anything — you should never need root access
8
+ - `chmod 777` — never set world-readable/writable/executable permissions
9
+ - `mkfs`, `dd`, `fdisk` — disk-level operations
10
+ - `kill -9` on system processes
11
+ - `systemctl stop`, `service stop` on production services
12
+ - Any command that downloads and pipes to shell: `curl ... | sh`, `wget ... | bash`
@@ -0,0 +1,17 @@
1
+ ## 9. Database Safety — No Direct Production Access
2
+
3
+ You must not autonomously connect to or execute queries against any database. Your role is to help write queries, not execute them.
4
+
5
+ **Never do:**
6
+ - Construct and run `snowsql`, `mysql`, `psql` connection commands
7
+ - Execute queries using credentials found in config files
8
+ - Run `DROP TABLE`, `DROP DATABASE`, `TRUNCATE TABLE`
9
+ - Run `DELETE FROM` without a WHERE clause
10
+ - Run `ALTER TABLE` or schema modifications
11
+ - Run `UPDATE` without a WHERE clause
12
+
13
+ **Instead:**
14
+ - Write the query and present it to the developer for review
15
+ - Use clearly commented placeholder values for any connection parameters
16
+ - Always include a WHERE clause in DELETE and UPDATE statements
17
+ - Add a comment like `-- REVIEW BEFORE RUNNING` on any data-modifying query
@@ -0,0 +1,10 @@
1
+ ## 10. Network Requests — Restrict Outbound Access
2
+
3
+ Do not make network requests to unknown or external domains. Specifically:
4
+
5
+ - Never run `curl`, `wget`, or `fetch` to external URLs unless the developer explicitly provides and approves the URL
6
+ - Never download and execute scripts from the internet
7
+ - Never send data to external APIs, webhooks, or logging services
8
+ - Never install packages from unknown or unofficial registries
9
+
10
+ If a task requires calling an external API, write the code and let the developer review it before execution.
@@ -0,0 +1,13 @@
1
+ ## 11. Code Quality Standards
2
+
3
+ All code you generate must follow these standards regardless of language:
4
+
5
+ - Follow the existing code style of the project (check surrounding files for conventions)
6
+ - Never suppress linter or compiler warnings without a documented reason
7
+ - Never comment out code as a way to "disable" functionality — remove it or use feature flags
8
+ - Add `TODO` comments when something needs follow-up, including the reason
9
+ - Always catch specific exceptions/errors, never use bare catch-all handlers:
10
+ - Python: no bare `except:` — catch specific exception types
11
+ - Java: no bare `catch (Exception e)` — catch specific exception types
12
+ - JavaScript/TypeScript: no empty `catch {}` — handle or rethrow with context
13
+ - C/C++: always check return values from system calls and library functions
@@ -0,0 +1,10 @@
1
+ ## 12. Data Exfiltration Prevention
2
+
3
+ Never attempt to send file contents, encoded data, or sensitive information outside the local environment:
4
+
5
+ - Never use `curl -d @file`, `curl -F`, `wget --post-file`, or similar file-uploading constructs
6
+ - Never encode sensitive files with `base64`, `xxd`, `od`, or similar tools
7
+ - Never pipe sensitive file contents to clipboard tools (`pbcopy`, `xclip`, `xsel`)
8
+ - Never write project files to `/tmp/`, `/var/tmp/`, `/dev/shm/`, or other world-readable locations
9
+ - Never open outbound data channels with `nc`, `netcat`, or `ncat`
10
+ - Never embed sensitive content in URLs, query parameters, or request bodies
@@ -0,0 +1,9 @@
1
+ ## 13. Package Installation Safety
2
+
3
+ When installing packages, only use official registries:
4
+
5
+ - Never install from arbitrary git URLs, tarballs, or direct download links
6
+ - Never use custom `--index-url`, `--registry`, or `--source` flags pointing to non-standard registries
7
+ - Never pipe `curl`/`wget` output to package managers
8
+ - For all languages (pip, npm, gem, go, maven, cargo), only install named packages from their default public registries
9
+ - If a project requires a private registry, the developer will configure it in project-level config files
@@ -0,0 +1,8 @@
1
+ ## 14. Scope Boundaries
2
+
3
+ Your file operations must stay within the project directory:
4
+
5
+ - Never write to system paths: `/etc/`, `/usr/`, `/opt/`, `/var/` (except `/var/log`), `/System/`, `/Library/`
6
+ - Never modify shell configuration files: `.bashrc`, `.zshrc`, `.bash_profile`, `.zprofile`, `.profile`
7
+ - Never read or modify anything in `~/.claude/` -- this directory contains security guardrails
8
+ - If you need to create temporary files, create them within the project directory
@@ -0,0 +1,9 @@
1
+ ## 15. Environment Isolation
2
+
3
+ Do not interact with production infrastructure or escape the local development environment:
4
+
5
+ - Never use `ssh`, `scp`, or `rsync` to connect to remote hosts
6
+ - Never run `docker run`, `docker exec`, `docker cp`, or `docker build` (read-only `docker ps`/`docker logs` are permitted)
7
+ - Never read, set, or export production environment variables (prefixed with `PROD_` or `PRODUCTION_`)
8
+ - Never run destructive infrastructure commands: `terraform apply/destroy/import`, `kubectl delete/exec/apply`
9
+ - Read-only infrastructure commands are permitted: `terraform plan/validate`, `kubectl get/describe/logs`
@@ -0,0 +1,8 @@
1
+ ## 16. Resource Limits
2
+
3
+ Operate efficiently and avoid unbounded operations:
4
+
5
+ - Never generate individual files exceeding {{ max_file_lines }} lines -- propose splitting into modules instead
6
+ - Avoid infinite loops or recursive operations without clear termination conditions
7
+ - If a task requires processing a large number of files, process them in bounded batches
8
+ - Do not make repeated failing tool calls -- after 2 failures, stop and explain the issue
@@ -0,0 +1,9 @@
1
+ ## 17. Compliance and Data Privacy
2
+
3
+ Protect personally identifiable information (PII) and respect licensing:
4
+
5
+ - Never include real names, email addresses, phone numbers, or physical addresses in generated code, comments, or test data
6
+ - Use clearly synthetic test data: `user@example.com`, `Jane Doe`, `555-0100`
7
+ - Never copy code from external sources without noting the license
8
+ - When referencing open-source code, verify license compatibility (avoid GPL in proprietary codebases unless explicitly approved)
9
+ - Never log, print, or display PII in debug output or error messages
@@ -0,0 +1,10 @@
1
+ ## 18. When in Doubt
2
+
3
+ If you are uncertain whether an action violates these rules:
4
+
5
+ - Do not proceed with the action
6
+ - Explain what you were about to do and why you stopped
7
+ - Ask the developer for explicit guidance
8
+ - Default to the safer option in all cases
9
+
10
+ These guardrails exist to protect client data, company infrastructure, and code quality. They are not suggestions — they are requirements.
@@ -0,0 +1,142 @@
1
+ """Schema validation for guardrails.yaml configuration files."""
2
+
3
+ import sys
4
+ from typing import Any
5
+
6
+ VALID_PII_LOCALES = {"US", "KR", "EU"}
7
+ VALID_GIT_POLICIES = {"block_all", "read_only", "allow_all"}
8
+ VALID_INFRA_POLICIES = {"block", "read_only", "allow"}
9
+ VALID_COMPLIANCE_FRAMEWORKS = {"iso_27001", "soc2", "hipaa", "pci_dss"}
10
+ VALID_LLM_PROVIDERS = {"bedrock", "anthropic", "openai", "local"}
11
+ VALID_SCAN_MODES = {"changed", "full"}
12
+
13
+ RULE_KEYS = [
14
+ "sensitive_file_access", "client_confidential_data", "credentials_no_hardcode",
15
+ "sql_safety", "dangerous_code_patterns", "frontend_security_xss",
16
+ "git_operations", "destructive_commands", "database_safety",
17
+ "network_requests", "code_quality_standards", "data_exfiltration",
18
+ "package_installation", "scope_boundaries", "environment_isolation",
19
+ "resource_limits", "compliance_data_privacy", "when_in_doubt",
20
+ ]
21
+
22
+
23
+ def validate_config(config: dict[str, Any]) -> list[str]:
24
+ """Validate a guardrails config dict. Returns list of error strings (empty = valid)."""
25
+ errors: list[str] = []
26
+
27
+ # Version
28
+ version = config.get("version")
29
+ if version is None:
30
+ errors.append("Missing required field: version")
31
+ elif str(version) != "2.0":
32
+ errors.append(f"Unsupported version: {version} (expected 2.0)")
33
+
34
+ # Organization (optional but must be dict if present)
35
+ org = config.get("organization", {})
36
+ if not isinstance(org, dict):
37
+ errors.append("organization must be a mapping")
38
+
39
+ # Rules
40
+ rules = config.get("rules", {})
41
+ if not isinstance(rules, dict):
42
+ errors.append("rules must be a mapping")
43
+ else:
44
+ for key in RULE_KEYS:
45
+ val = rules.get(key)
46
+ if val is None:
47
+ errors.append(f"Missing rule toggle: rules.{key}")
48
+ elif not isinstance(val, bool):
49
+ errors.append(f"rules.{key} must be true or false, got {type(val).__name__}")
50
+
51
+ # PII
52
+ pii = config.get("pii", {})
53
+ if not isinstance(pii, dict):
54
+ errors.append("pii must be a mapping")
55
+ else:
56
+ locales = pii.get("locales", [])
57
+ if not isinstance(locales, list):
58
+ errors.append("pii.locales must be a list")
59
+ else:
60
+ for loc in locales:
61
+ if loc not in VALID_PII_LOCALES:
62
+ errors.append(f"Invalid PII locale: {loc} (valid: {', '.join(sorted(VALID_PII_LOCALES))})")
63
+
64
+ # Git policy
65
+ git_policy = config.get("git_policy", "block_all")
66
+ if git_policy not in VALID_GIT_POLICIES:
67
+ errors.append(f"Invalid git_policy: {git_policy} (valid: {', '.join(sorted(VALID_GIT_POLICIES))})")
68
+
69
+ # Infra tools
70
+ infra = config.get("infra_tools", {})
71
+ if not isinstance(infra, dict):
72
+ errors.append("infra_tools must be a mapping")
73
+ else:
74
+ for tool in ("docker", "terraform", "kubectl"):
75
+ val = infra.get(tool, "read_only")
76
+ if val not in VALID_INFRA_POLICIES:
77
+ errors.append(f"Invalid infra_tools.{tool}: {val} (valid: {', '.join(sorted(VALID_INFRA_POLICIES))})")
78
+
79
+ # Blocked files
80
+ bf = config.get("blocked_files", {})
81
+ if isinstance(bf, dict):
82
+ extra = bf.get("extra_patterns", [])
83
+ if not isinstance(extra, list):
84
+ errors.append("blocked_files.extra_patterns must be a list")
85
+ elif bf is not None:
86
+ errors.append("blocked_files must be a mapping")
87
+
88
+ # Blocked commands
89
+ bc = config.get("blocked_commands", {})
90
+ if isinstance(bc, dict):
91
+ extra = bc.get("extra_patterns", [])
92
+ if not isinstance(extra, list):
93
+ errors.append("blocked_commands.extra_patterns must be a list")
94
+ elif bc is not None:
95
+ errors.append("blocked_commands must be a mapping")
96
+
97
+ # Client data
98
+ cd = config.get("client_data", {})
99
+ if isinstance(cd, dict):
100
+ patterns = cd.get("patterns", [])
101
+ if not isinstance(patterns, list):
102
+ errors.append("client_data.patterns must be a list")
103
+ else:
104
+ for i, p in enumerate(patterns):
105
+ if not isinstance(p, dict) or "pattern" not in p or "name" not in p:
106
+ errors.append(f"client_data.patterns[{i}] must have 'pattern' and 'name' fields")
107
+ elif cd is not None:
108
+ errors.append("client_data must be a mapping")
109
+
110
+ # Compliance
111
+ comp = config.get("compliance", {})
112
+ if isinstance(comp, dict):
113
+ frameworks = comp.get("frameworks", [])
114
+ if not isinstance(frameworks, list):
115
+ errors.append("compliance.frameworks must be a list")
116
+ else:
117
+ for fw in frameworks:
118
+ if fw not in VALID_COMPLIANCE_FRAMEWORKS:
119
+ errors.append(f"Invalid compliance framework: {fw} (valid: {', '.join(sorted(VALID_COMPLIANCE_FRAMEWORKS))})")
120
+ elif comp is not None:
121
+ errors.append("compliance must be a mapping")
122
+
123
+ # CI agents
124
+ ci = config.get("ci_agents", {})
125
+ if isinstance(ci, dict):
126
+ provider = ci.get("llm_provider", "bedrock")
127
+ if provider not in VALID_LLM_PROVIDERS:
128
+ errors.append(f"Invalid ci_agents.llm_provider: {provider}")
129
+ scan_mode = ci.get("scan_mode", "changed")
130
+ if scan_mode not in VALID_SCAN_MODES:
131
+ errors.append(f"Invalid ci_agents.scan_mode: {scan_mode}")
132
+ elif ci is not None:
133
+ errors.append("ci_agents must be a mapping")
134
+
135
+ # Resource limits
136
+ rl = config.get("resource_limits", {})
137
+ if isinstance(rl, dict):
138
+ mfl = rl.get("max_file_lines", 500)
139
+ if not isinstance(mfl, int) or mfl < 1:
140
+ errors.append("resource_limits.max_file_lines must be a positive integer")
141
+
142
+ return errors