From a3cee2756c0ce60036f941b50eea5f54c9521687 Mon Sep 17 00:00:00 2001 From: Julian Lechner Date: Mon, 14 Sep 2026 01:07:46 +0200 Subject: [PATCH] feat(config): validate clients and scope security --- adapters/claude/rule-skills.tsv | 8 + adapters/codex/config/config.toml | 1 + docs/configuration.md | 12 +- scripts/build.ps1 | 45 ++- scripts/build.sh | 31 +- scripts/doctor.ps1 | 19 +- scripts/doctor.sh | 12 +- scripts/validate-config.py | 75 +++++ shared/rules/security-api.md | 7 + shared/rules/security-auth.md | 8 + shared/rules/security-crypto.md | 6 + shared/rules/security-data.md | 7 + shared/rules/security-files.md | 6 + shared/rules/security-network.md | 6 + shared/rules/security-supply-chain.md | 6 + shared/rules/security-web.md | 7 + shared/rules/security.md | 411 +------------------------- 17 files changed, 230 insertions(+), 437 deletions(-) create mode 100644 scripts/validate-config.py create mode 100644 shared/rules/security-api.md create mode 100644 shared/rules/security-auth.md create mode 100644 shared/rules/security-crypto.md create mode 100644 shared/rules/security-data.md create mode 100644 shared/rules/security-files.md create mode 100644 shared/rules/security-network.md create mode 100644 shared/rules/security-supply-chain.md create mode 100644 shared/rules/security-web.md diff --git a/adapters/claude/rule-skills.tsv b/adapters/claude/rule-skills.tsv index 3b2b215..8ba408e 100644 --- a/adapters/claude/rule-skills.tsv +++ b/adapters/claude/rule-skills.tsv @@ -7,6 +7,14 @@ git.md rules-git Git operations such as commits, branches, or merges microsoft.md rules-microsoft Microsoft 365, Azure DevOps, or Teams operations refactoring.md rules-refactoring refactoring existing code without changing behavior security.md rules-security any software development task: implementing, modifying, debugging, reviewing, testing, or configuring code, scripts, hooks, or infrastructure +security-auth.md rules-security-auth authentication, authorization, sessions, tokens, permissions, or tenant-boundary work +security-web.md rules-security-web web, browser, frontend, cookie, redirect, or XSS/CSRF work +security-api.md rules-security-api HTTP API, request handling, serialization, or endpoint work +security-data.md rules-security-data database, query, persistence, sensitive-data, or multi-tenant work +security-files.md rules-security-files file, upload, archive, path, process, IPC, or deserialization work +security-network.md rules-security-network network, URL fetch, proxy, outbound request, TLS, or SSRF work +security-crypto.md rules-security-crypto cryptography, secrets, credentials, key, or token work +security-supply-chain.md rules-security-supply-chain dependency, package, plugin, build, deployment, or CI supply-chain work typescript.md rules-typescript TypeScript or modern ECMAScript implementation work ui-ux.md rules-ui-ux UI or UX design and interaction decisions wpf.md rules-wpf WPF, XAML, or MVVM desktop UI work diff --git a/adapters/codex/config/config.toml b/adapters/codex/config/config.toml index 2621ec1..fc268fc 100644 --- a/adapters/codex/config/config.toml +++ b/adapters/codex/config/config.toml @@ -8,6 +8,7 @@ status_line = ["model", "reasoning", "project-name", "git-branch", "context-wind [agents] enabled = true max_concurrent_threads_per_session = 5 +max_depth = 1 [features] hooks = true diff --git a/docs/configuration.md b/docs/configuration.md index 985f4b6..f43168e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,13 +1,11 @@ # Configuration -Codex receives a generated global `AGENTS.md`, agent TOML files, skills, rule files, and a `config.toml` adapter with workspace sandboxing, interactive approval boundaries, automatic approval review, enabled sub-agents, and a five-thread concurrency limit (`max_concurrent_threads_per_session`, the current canonical key; the legacy `max_threads` alias is not emitted). Claude Code receives a generated global `CLAUDE.md`, agent Markdown files, skills, and `settings.json` with the `auto` permission mode, explicit `Agent` permission, and a five-operation tool/sub-agent concurrency limit. The exact installed locations are resolved from the user profile at install time; no machine-specific absolute path is stored in the repository. +Codex receives a generated global `AGENTS.md`, agent TOML files, skills, rule files, and a `config.toml` adapter. It keeps workspace sandboxing, automatic approval review, `agents.enabled = true`, `agents.max_concurrent_threads_per_session = 5`, and `agents.max_depth = 1`. Claude Code receives a generated global `CLAUDE.md`, agent Markdown files, skills, and `settings.json` with `auto` permissions, explicit `Agent` permission, and five-operation concurrency. Installation resolves user paths at runtime; the repository contains no machine-specific paths. -Claude's rule loading differs from Codex's by design: `general.md` is the only rule shipped as a plain, always-applied file (its text is also embedded directly in `CLAUDE.md`). Every technology- or situation-specific rule in `shared/rules/` is instead generated as a skill under `skills/rules/`, driven by `adapters/claude/rule-skills.tsv` (rule file, skill name, trigger description). Only a skill's name and description are ever permanently in context; Claude loads the full rule text only when it invokes the skill. Codex is unaffected — it keeps receiving every rule as a plain file and is told to load the matching one by path, exactly as before. +Codex keeps rules as files and loads only applicable focused rules. Claude embeds `general.md` and generates the remaining rules as skills from `adapters/claude/rule-skills.tsv`. `security.md` is a short universal baseline; focused security rules load only for authentication, web, API, data, files, network, cryptography, and supply-chain work. -Codex's current configuration schema has no `agents.max_depth` or other agent-recursion-depth key. The only `max_depth`-shaped setting in the schema is `network_proxy.glob_scan_max_depth`, which bounds glob-pattern expansion for the network proxy feature and is unrelated to agents; Claude Code's settings likewise expose no comparable key. The repository does not emit either as an invented key. +`agents.max_depth = 1` limits V1 Codex subagent recursion. `features.multi_agent_v2` is not enabled. If enabled later, V2 takes precedence over `agents.enabled` and ignores `agents.max_depth`. -`[agents].enabled = true` (set explicitly, matching its own documented default) is the current, documented way to enable Codex's multi-agent tools. A separate `[features].multi_agent` toggle exists in the schema but is not set here, since `[agents].enabled` already covers it and setting both would be redundant. `multi_agent_v2` does not appear in the current documented schema at all — it surfaced only in upstream issue titles describing an internal/experimental flag — so it is deliberately not configured. +The build parses TOML with Python's standard-library `tomllib`, validates the emitted Claude settings shape, and uses `codex --strict-config` against an isolated generated configuration when Codex is available. This rejects malformed and unknown Codex configuration keys for the installed CLI version. -Client settings remain thin adapters. Shared semantics remain in `shared/`. - -User plugins are managed separately through `adapters/plugins.tsv` and the client-native plugin commands. They are not copied into `generated/` because each client owns its plugin cache and authentication state. +Client settings remain thin adapters. Shared semantics remain in `shared/`. User plugins are managed separately through `adapters/plugins.tsv` and client-native installation commands. diff --git a/scripts/build.ps1 b/scripts/build.ps1 index aebf19a..8e05ba3 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -12,7 +12,7 @@ $pluginEntries = @(Import-Csv -LiteralPath $pluginManifest -Delimiter ([char]9)) if ($pluginEntries.Count -eq 0) { throw 'Plugin manifest must define at least one plugin.' } $pluginNames = @() foreach ($pluginEntry in $pluginEntries) { - foreach ($field in @('name','claude_plugin','codex_plugin')) { + foreach ($field in @('name','claude_plugin','codex_method')) { if ([string]::IsNullOrWhiteSpace($pluginEntry.$field)) { throw "Missing $field for plugin $($pluginEntry.name)." } } if ($pluginNames -contains $pluginEntry.name) { throw "Duplicate plugin name in manifest: $($pluginEntry.name)" } @@ -52,6 +52,32 @@ function Read-Field($path, $name) { function Quote-Toml($value) { return ('"' + $value.Replace('\','\\').Replace('"','\"').Replace("`r",'').Replace("`n",'\n') + '"') } +function Test-CodexSchema($configPath) { + if (-not (Get-Command codex -ErrorAction SilentlyContinue)) { + if ($env:REQUIRE_CODEX_SCHEMA -eq 'true') { throw 'Codex CLI is required for schema validation.' } + Write-Warning 'Codex CLI unavailable; skipped Codex schema validation.' + return + } + $tempHome = Join-Path ([System.IO.Path]::GetTempPath()) ("codex-schema-" + [Guid]::NewGuid()) + $previousCodexHome = $env:CODEX_HOME + try { + New-Item $tempHome -ItemType Directory -Force | Out-Null + Copy-Item $configPath (Join-Path $tempHome 'config.toml') -Force + $env:CODEX_HOME = $tempHome + $previousErrorActionPreference = $ErrorActionPreference + try { + $ErrorActionPreference = 'Continue' + $schemaOutput = @(& codex --strict-config --help 2>&1) + $schemaExitCode = $LASTEXITCODE + } finally { + $ErrorActionPreference = $previousErrorActionPreference + } + if ($schemaExitCode -ne 0) { throw "Codex schema validation failed: $configPath`n$($schemaOutput -join [Environment]::NewLine)" } + } finally { + $env:CODEX_HOME = $previousCodexHome + Remove-Item $tempHome -Recurse -Force -ErrorAction SilentlyContinue + } +} & (Join-Path $root 'shared/hooks/scripts/Test-SessionConfig.ps1') -RepositoryRoot $root @@ -75,6 +101,14 @@ Detect the languages, frameworks, tools, and change areas from the repository an Load rule files when their subject applies: +- rules/security-auth.md for authentication, authorization, sessions, tokens, permissions, or tenant boundaries. +- rules/security-web.md for browser, frontend, cookie, redirect, XSS, or CSRF work. +- rules/security-api.md for HTTP APIs, request handling, serialization, or endpoints. +- rules/security-data.md for databases, persistence, sensitive data, or multi-tenancy. +- rules/security-files.md for files, uploads, archives, paths, processes, IPC, or deserialization. +- rules/security-network.md for network access, URLs, TLS, proxies, or SSRF. +- rules/security-crypto.md for cryptography, secrets, credentials, keys, or tokens. +- rules/security-supply-chain.md for dependencies, packages, plugins, builds, deployments, or CI. - rules/angular.md for Angular work. - rules/typescript.md for TypeScript work. - rules/csharp.md for C# or .NET work. @@ -154,13 +188,10 @@ foreach ($platform in @('windows','linux')) { } $tomlPath = Join-Path $output "codex-$platform/config.toml" - $tomlContent = Get-Content -LiteralPath $tomlPath -Raw - if (([regex]::Matches($tomlContent, '(?&2; exit 1; } -open_brackets=$(grep -o '\[' "$toml_path" | wc -l) -close_brackets=$(grep -o '\]' "$toml_path" | wc -l) -[ "$open_brackets" -eq "$close_brackets" ] || { printf 'Generated Codex config.toml has unbalanced brackets: %s\n' "$toml_path" >&2; exit 1; } - settings_path="$output/claude-$platform/settings.json" -if command -v jq >/dev/null 2>&1; then - jq empty "$settings_path" >/dev/null 2>&1 || { printf 'Generated Claude settings.json is not valid JSON: %s\n' "$settings_path" >&2; exit 1; } -elif command -v python3 >/dev/null 2>&1 && python3 --version >/dev/null 2>&1; then - # On Windows, `python3` can resolve to a non-functional Microsoft Store app-execution - # alias that still passes `command -v`; probe it with a real invocation before trusting it. - python3 -c 'import json,sys; json.load(open(sys.argv[1], encoding="utf-8"))' "$settings_path" || { printf 'Generated Claude settings.json is not valid JSON: %s\n' "$settings_path" >&2; exit 1; } +python3 "$root/scripts/validate-config.py" "$toml_path" "$settings_path" +if command -v codex >/dev/null; then + schema_home=$(mktemp -d) + cp "$toml_path" "$schema_home/config.toml" + CODEX_HOME="$schema_home" codex --strict-config --help >/dev/null + rm -rf -- "$schema_home" +elif [ "${REQUIRE_CODEX_SCHEMA:-false}" = true ]; then + printf 'Codex CLI is required for schema validation.\n' >&2 + exit 1 +else + printf 'WARN Codex CLI unavailable; skipped Codex schema validation.\n' >&2 fi done diff --git a/scripts/doctor.ps1 b/scripts/doctor.ps1 index 4c9a09c..88427a7 100644 --- a/scripts/doctor.ps1 +++ b/scripts/doctor.ps1 @@ -42,9 +42,16 @@ if (Test-Path -LiteralPath $settingsPath) { } $configTomlPath = Join-Path $homePath '.codex/config.toml' if (Test-Path -LiteralPath $configTomlPath) { - $tomlContent = Get-Content -LiteralPath $configTomlPath -Raw - $balanced = (([regex]::Matches($tomlContent, '(?&1 + if ($LASTEXITCODE -eq 0) { Result 'PASS' 'installed .codex/config.toml parses as TOML' } else { Result 'FAIL' "installed .codex/config.toml is invalid TOML: $parseResult" } + if (Get-Command codex -ErrorAction SilentlyContinue) { + $previousCodexHome = $env:CODEX_HOME + try { + $env:CODEX_HOME = Split-Path $configTomlPath -Parent + $schemaResult = & codex --strict-config --help 2>&1 + if ($LASTEXITCODE -eq 0) { Result 'PASS' 'installed .codex/config.toml matches the installed Codex schema' } else { Result 'FAIL' "installed .codex/config.toml has unsupported Codex settings: $schemaResult" } + } finally { $env:CODEX_HOME = $previousCodexHome } + } else { Result 'WARN' 'Codex CLI unavailable; skipped installed Codex schema validation' } } foreach ($destination in @((Join-Path $homePath '.codex'), (Join-Path $homePath '.agents/skills'), (Join-Path $homePath '.claude'))) { @@ -64,6 +71,12 @@ foreach ($destination in @((Join-Path $homePath '.codex'), (Join-Path $homePath foreach ($path in @('shared/rules','shared/skills','shared/agents','shared/hooks/scripts')) { if (Test-Path (Join-Path $root $path)) { Result 'PASS' "source $path" } else { Result 'FAIL' "missing $path" } } +foreach ($client in @('codex','claude')) { + $config = if ($client -eq 'codex') { Join-Path $root 'adapters/codex/config/config.toml' } else { Join-Path $root 'adapters/claude/config/settings.json' } + $registered = if ($client -eq 'codex') { (Get-Content $config -Raw) -match '\[\[hooks\.Stop\]\]' } else { ((Get-Content $config -Raw | ConvertFrom-Json).hooks.PSObject.Properties.Name -contains 'Stop') } + if ($registered) { Result 'PASS' "$client Stop hook is registered" } else { Result 'WARN' "$client ships hook utilities, but no Stop hook is registered" } +} + if ($fail) { exit 1 } Write-Output 'PASS doctor' exit 0 diff --git a/scripts/doctor.sh b/scripts/doctor.sh index a7480f6..326fea9 100755 --- a/scripts/doctor.sh +++ b/scripts/doctor.sh @@ -55,14 +55,14 @@ if [ -f "$home_path/.claude/settings.json" ]; then fi fi if [ -f "$home_path/.codex/config.toml" ]; then - quote_count=$(grep -o '"' "$home_path/.codex/config.toml" | wc -l) - open_brackets=$(grep -o '\[' "$home_path/.codex/config.toml" | wc -l) - close_brackets=$(grep -o '\]' "$home_path/.codex/config.toml" | wc -l) - if [ $((quote_count % 2)) -eq 0 ] && [ "$open_brackets" -eq "$close_brackets" ]; then - result PASS 'installed .codex/config.toml looks structurally valid' + if python3 -c 'import sys,tomllib,pathlib; tomllib.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8-sig"))' "$home_path/.codex/config.toml"; then + result PASS 'installed .codex/config.toml parses as TOML' else - result FAIL 'installed .codex/config.toml has unbalanced quotes or brackets' + result FAIL 'installed .codex/config.toml is invalid TOML' fi + if command -v codex >/dev/null; then + if CODEX_HOME="$home_path/.codex" codex --strict-config --help >/dev/null 2>&1; then result PASS 'installed .codex/config.toml matches the installed Codex schema'; else result FAIL 'installed .codex/config.toml has unsupported Codex settings'; fi + else result WARN 'Codex CLI unavailable; skipped installed Codex schema validation'; fi fi for destination in "$home_path/.codex" "$home_path/.agents/skills" "$home_path/.claude"; do diff --git a/scripts/validate-config.py b/scripts/validate-config.py new file mode 100644 index 0000000..c39dbf4 --- /dev/null +++ b/scripts/validate-config.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +"""Validate the generated configuration formats without third-party packages.""" + +from __future__ import annotations + +import json +import sys +import tomllib +from pathlib import Path + + +CLAUDE_TOP_LEVEL = {"permissions", "env", "statusLine", "hooks"} +CLAUDE_PERMISSION_KEYS = {"defaultMode", "allow", "deny"} +CLAUDE_STATUS_LINE_KEYS = {"type", "command"} +CLAUDE_HOOK_KEYS = {"type", "command", "timeout"} +CLAUDE_PERMISSION_MODES = {"acceptEdits", "auto", "bypassPermissions", "default", "dontAsk", "plan"} + + +def fail(message: str) -> None: + raise ValueError(message) + + +def require_keys(value: object, allowed: set[str], label: str) -> dict[str, object]: + if not isinstance(value, dict): + fail(f"{label} must be an object") + unknown = set(value) - allowed + if unknown: + fail(f"{label} has unknown keys: {', '.join(sorted(unknown))}") + return value + + +def validate_claude(path: Path) -> None: + settings = require_keys(json.loads(path.read_text(encoding="utf-8-sig")), CLAUDE_TOP_LEVEL, str(path)) + permissions = require_keys(settings.get("permissions"), CLAUDE_PERMISSION_KEYS, "permissions") + if permissions.get("defaultMode") not in CLAUDE_PERMISSION_MODES: + fail("permissions.defaultMode is unsupported") + for key in ("allow", "deny"): + if not isinstance(permissions.get(key), list) or not all(isinstance(item, str) for item in permissions[key]): + fail(f"permissions.{key} must be an array of strings") + env = settings.get("env") + if not isinstance(env, dict) or not all(isinstance(key, str) and isinstance(value, str) for key, value in env.items()): + fail("env must be an object of string values") + status_line = require_keys(settings.get("statusLine"), CLAUDE_STATUS_LINE_KEYS, "statusLine") + if status_line.get("type") != "command" or not isinstance(status_line.get("command"), str): + fail("statusLine must be a command") + hooks = settings.get("hooks") + if not isinstance(hooks, dict): + fail("hooks must be an object") + for event, matchers in hooks.items(): + if not isinstance(event, str) or not isinstance(matchers, list): + fail("hooks must map event names to arrays") + for matcher in matchers: + if not isinstance(matcher, dict) or not isinstance(matcher.get("hooks"), list): + fail(f"hooks.{event} must contain hook arrays") + for hook in matcher["hooks"]: + hook = require_keys(hook, CLAUDE_HOOK_KEYS, f"hooks.{event}") + if hook.get("type") != "command" or not isinstance(hook.get("command"), str) or not isinstance(hook.get("timeout"), int): + fail(f"hooks.{event} contains an unsupported command hook") + + +def main() -> int: + if len(sys.argv) != 3: + print("usage: validate-config.py ", file=sys.stderr) + return 2 + tomllib.loads(Path(sys.argv[1]).read_text(encoding="utf-8-sig")) + validate_claude(Path(sys.argv[2])) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (OSError, ValueError, json.JSONDecodeError, tomllib.TOMLDecodeError) as error: + print(f"configuration validation failed: {error}", file=sys.stderr) + raise SystemExit(1) diff --git a/shared/rules/security-api.md b/shared/rules/security-api.md new file mode 100644 index 0000000..9e775f1 --- /dev/null +++ b/shared/rules/security-api.md @@ -0,0 +1,7 @@ +# API security + +* Authenticate and authorize protected endpoints; validate every request payload and identifier. +* Bound request and response sizes, pagination, query results, and expensive operations. +* Apply rate limits where abuse is plausible and keep administrative endpoints separately protected. +* Return only authorized data and never expose stack traces, internal models, or implementation details in API errors. +* Prefer explicit DTOs and schemas over deserializing arbitrary types or executable objects. diff --git a/shared/rules/security-auth.md b/shared/rules/security-auth.md new file mode 100644 index 0000000..697a8d2 --- /dev/null +++ b/shared/rules/security-auth.md @@ -0,0 +1,8 @@ +# Authentication and authorization security + +* Use established authentication standards and password hashing; never store plaintext or reversibly encrypted passwords. +* Enforce authorization server-side for every sensitive operation and verify resource ownership and tenant boundaries. +* Use least-privilege roles, short-lived sessions or tokens where supported, and invalidate credentials after sensitive account changes. +* Validate token issuer, audience, signature, and expiry before trusting claims. +* Protect authentication and privileged actions against brute force and privilege escalation. +* Test unauthenticated, unauthorized, cross-user, and cross-tenant access paths. diff --git a/shared/rules/security-crypto.md b/shared/rules/security-crypto.md new file mode 100644 index 0000000..9d0b81d --- /dev/null +++ b/shared/rules/security-crypto.md @@ -0,0 +1,6 @@ +# Cryptography and secrets security + +* Never implement custom cryptographic primitives or use obsolete algorithms. +* Use established libraries, authenticated encryption, secure randomness, and verified signatures and certificates. +* Treat tokens, private keys, connection strings, and credentials as secrets; keep them out of source, URLs, command lines, logs, fixtures, and frontend code. +* Use environment-appropriate secret storage, separate environments, and rotate compromised credentials. diff --git a/shared/rules/security-data.md b/shared/rules/security-data.md new file mode 100644 index 0000000..f58f89b --- /dev/null +++ b/shared/rules/security-data.md @@ -0,0 +1,7 @@ +# Database and data security + +* Use parameterized queries or ORM parameter binding; never concatenate untrusted input into SQL. +* Use least-privilege database accounts and protect connection strings. +* Scope tenant-owned queries explicitly and prevent cross-tenant cache, logging, and diagnostic leakage. +* Collect and retain only necessary sensitive data; encrypt it in transit and at rest where appropriate. +* Avoid raw database errors and use transactions where integrity requires atomicity. diff --git a/shared/rules/security-files.md b/shared/rules/security-files.md new file mode 100644 index 0000000..e8a8a59 --- /dev/null +++ b/shared/rules/security-files.md @@ -0,0 +1,6 @@ +# File and process security + +* Validate filenames, content, sizes, and paths; prevent traversal, zip-slip, and unsafe symbolic-link use. +* Restrict access to intended directories and do not execute uploaded or untrusted files. +* Do not concatenate untrusted input into shell commands; prefer direct APIs and validate process arguments when execution is required. +* Treat local files, IPC, and deserialized input as untrusted at relevant boundaries. diff --git a/shared/rules/security-network.md b/shared/rules/security-network.md new file mode 100644 index 0000000..d478c34 --- /dev/null +++ b/shared/rules/security-network.md @@ -0,0 +1,6 @@ +# Network and SSRF security + +* Use HTTPS and never disable certificate or TLS validation. +* Restrict inbound and outbound access to required ports, interfaces, hosts, and protocols. +* Treat user-controlled URLs as dangerous: allowlist schemes and hosts, block localhost, metadata, and internal ranges, and revalidate redirects. +* Set timeouts, response-size limits, bounded retries, and cancellation for remote operations. diff --git a/shared/rules/security-supply-chain.md b/shared/rules/security-supply-chain.md new file mode 100644 index 0000000..ee6b397 --- /dev/null +++ b/shared/rules/security-supply-chain.md @@ -0,0 +1,6 @@ +# Supply-chain security + +* Keep dependencies minimal, maintained, and from verified sources; respect lockfiles and review identity and compatibility before adding or upgrading packages. +* Pin versions where appropriate and remove unused dependencies. +* Treat plugins, build scripts, CI actions, and generated artifacts as part of the attack surface. +* Use least privilege for build and deployment identities and never expose credentials in pipelines, logs, or artifacts. diff --git a/shared/rules/security-web.md b/shared/rules/security-web.md new file mode 100644 index 0000000..48225e0 --- /dev/null +++ b/shared/rules/security-web.md @@ -0,0 +1,7 @@ +# Web and frontend security + +* Escape or sanitize untrusted content; never inject untrusted HTML or bypass framework sanitization. +* Protect state-changing browser requests from CSRF where applicable. +* Use `HttpOnly`, `Secure`, and appropriate `SameSite` cookie settings for sensitive sessions. +* Validate redirect targets, avoid sensitive data in URLs, and use CSP and clickjacking protections where applicable. +* Treat frontend code and browser storage as visible and never rely on client-side checks for authorization. diff --git a/shared/rules/security.md b/shared/rules/security.md index f05f96d..480be9f 100644 --- a/shared/rules/security.md +++ b/shared/rules/security.md @@ -1,404 +1,11 @@ -# Security Guidelines +# Security baseline -Security is a default requirement for all code, architecture, configuration and infrastructure changes. +Apply this baseline to every code, configuration, infrastructure, or review task. -## Core Principles - -* Prefer secure defaults. -* Apply least privilege everywhere. -* Minimize exposed attack surface. -* Treat all external input as untrusted. -* Never trust client-side validation alone. -* Fail securely. -* Prefer deny-by-default over allow-by-default for sensitive operations. -* Keep security controls explicit and auditable. -* Do not weaken security for convenience. -* Do not bypass security mechanisms to make code easier to implement. -* Do not introduce insecure temporary solutions. -* Prefer simple, well-understood security mechanisms over custom cryptography or clever security logic. - -## Secrets and Credentials - -* Never hardcode passwords, API keys, tokens, connection strings, private keys or secrets. -* Never commit secrets to source control. -* Never log secrets or credentials. -* Never expose secrets in exceptions, diagnostics or UI messages. -* Use secure secret storage appropriate to the environment. -* Prefer short-lived credentials where supported. -* Rotate compromised credentials immediately. -* Do not reuse credentials across environments. -* Keep development, staging and production credentials separate. -* Avoid secrets in URLs, query strings or command-line arguments. -* Do not store secrets in frontend code. -* Do not include real credentials in examples, tests or fixtures. - -## Authentication - -* Use established authentication standards and framework capabilities. -* Do not implement custom authentication protocols without a strong reason. -* Store passwords only using modern password hashing algorithms designed for password storage. -* Never store plaintext passwords. -* Never use reversible encryption for password storage. -* Support secure session expiration. -* Invalidate sessions or tokens when security-sensitive account state changes. -* Protect authentication endpoints against brute-force abuse where applicable. -* Avoid exposing whether an account exists unless required. -* Require stronger authentication for security-sensitive actions where appropriate. - -## Authorization - -* Enforce authorization on the server side. -* Never rely on hidden UI elements, disabled buttons or frontend guards as authorization. -* Check authorization for every sensitive operation. -* Prefer explicit permission checks. -* Use least-privilege roles and permissions. -* Avoid broad administrative permissions unless required. -* Prevent horizontal privilege escalation between users or tenants. -* Prevent vertical privilege escalation between permission levels. -* Verify ownership of resources before access or modification. -* Do not trust identifiers supplied by the client as proof of authorization. - -## Input Validation - -* Validate all input from users, APIs, files, databases, queues, environment variables and external systems. -* Validate type, length, range, format and allowed values. -* Prefer allowlists over denylists. -* Reject unexpected input. -* Normalize input before validation when appropriate. -* Do not rely solely on client-side validation. -* Validate identifiers before using them for resource access. -* Validate uploaded files by content, size and expected type where applicable. -* Do not trust file extensions alone. - -## Injection Prevention - -* Never concatenate untrusted input into SQL. -* Use parameterized queries or ORM parameter binding. -* Never concatenate untrusted input into shell commands. -* Avoid executing shell commands when a direct API is available. -* Validate and escape command arguments when process execution is required. -* Prevent LDAP, XPath, template and expression injection where applicable. -* Avoid dynamic code execution. -* Avoid `eval`, dynamic compilation and equivalent mechanisms unless strictly required. -* Treat template engines and interpreters as security boundaries. - -## Web Security - -* Prevent Cross-Site Scripting by using framework escaping and sanitization. -* Never inject untrusted HTML directly. -* Avoid bypassing framework sanitization. -* Protect state-changing browser requests against CSRF where applicable. -* Use secure cookies for sensitive session data. -* Use `HttpOnly` for authentication cookies where possible. -* Use `Secure` cookies in HTTPS environments. -* Configure `SameSite` appropriately. -* Use appropriate Content Security Policy where applicable. -* Avoid leaking sensitive data through referrers or URLs. -* Validate redirect targets to prevent open redirects. -* Protect against clickjacking where applicable. - -## API Security - -* Authenticate sensitive endpoints. -* Authorize every protected operation. -* Validate all request payloads. -* Apply reasonable request-size limits. -* Apply pagination and bounded queries. -* Apply rate limiting where abuse is possible. -* Avoid exposing internal models directly when this leaks implementation details. -* Return only data the caller is authorized to access. -* Do not expose stack traces or internal exception details. -* Avoid excessive information in error responses. -* Version public APIs intentionally. -* Protect administrative endpoints separately where appropriate. - -## Data Protection - -* Collect only data that is actually required. -* Minimize storage of sensitive data. -* Classify sensitive data explicitly. -* Encrypt sensitive data in transit. -* Encrypt sensitive data at rest where appropriate. -* Do not implement custom encryption algorithms. -* Use established cryptographic libraries. -* Do not use obsolete cryptographic algorithms. -* Do not use hardcoded encryption keys. -* Use cryptographically secure random number generation for security-sensitive values. -* Avoid exposing personal or sensitive data in logs. -* Delete sensitive data when it is no longer required. - -## Cryptography - -* Never design custom cryptographic primitives. -* Use current, established cryptographic standards. -* Use authenticated encryption when confidentiality and integrity are required. -* Verify signatures before trusting signed content. -* Validate certificates correctly. -* Never disable certificate validation. -* Never accept all TLS certificates. -* Do not downgrade TLS security. -* Use secure randomness for tokens, nonces, identifiers and keys. -* Never use predictable random generators for security-sensitive values. - -## Tokens and Sessions - -* Treat tokens as secrets. -* Keep token lifetime as short as practical. -* Validate issuer, audience, signature and expiration where applicable. -* Do not accept unsigned tokens unless explicitly designed and safe. -* Do not trust token contents before validation. -* Avoid storing long-lived access tokens in insecure browser storage. -* Rotate refresh tokens where appropriate. -* Revoke compromised tokens where possible. -* Prevent replay attacks where the protocol requires it. - -## File Security - -* Validate file names and paths. -* Prevent path traversal. -* Never concatenate untrusted input directly into filesystem paths. -* Restrict file access to intended directories. -* Use generated server-side file names where appropriate. -* Enforce file size limits. -* Validate uploaded content. -* Do not execute uploaded files. -* Store uploads outside executable web roots where applicable. -* Handle archive extraction safely. -* Prevent zip-slip and equivalent path traversal attacks. -* Avoid following untrusted symbolic links where security-sensitive. - -## Serialization - -* Treat deserialized input as untrusted. -* Avoid insecure polymorphic deserialization. -* Avoid deserializing arbitrary runtime types. -* Use explicit schemas or known DTOs. -* Restrict type resolution. -* Do not deserialize executable objects or behavior. -* Validate deserialized data before use. -* Avoid insecure legacy serializers. - -## Database Security - -* Use parameterized queries. -* Use least-privilege database accounts. -* Do not use administrative database accounts for normal application traffic. -* Restrict schema modification permissions in runtime accounts. -* Protect connection strings. -* Avoid exposing raw database errors. -* Limit query size and result sets. -* Prevent tenant data leakage. -* Verify tenant boundaries in every relevant query. -* Use transactions where integrity requires atomicity. - -## Logging and Monitoring - -* Never log secrets. -* Never log passwords. -* Never log authentication tokens. -* Avoid logging sensitive personal data. -* Use structured logging. -* Include security-relevant context without exposing sensitive values. -* Log authentication and authorization failures where appropriate. -* Log suspicious or high-risk actions where appropriate. -* Avoid log injection by treating user input as data. -* Do not let logging failures break critical application behavior. -* Ensure logs have appropriate access controls. - -## Error Handling - -* Fail securely. -* Do not expose internal stack traces to users. -* Do not reveal implementation details unnecessarily. -* Preserve diagnostic detail internally where safe. -* Avoid different error responses that reveal sensitive existence checks where not required. -* Do not swallow security-relevant exceptions. -* Do not continue execution after critical validation or authorization failures. - -## Dependencies - -* Keep dependencies minimal. -* Prefer maintained and widely used packages. -* Avoid abandoned packages. -* Avoid unnecessary dependencies for trivial functionality. -* Keep dependencies updated with security patches. -* Review dependency changes before adoption. -* Do not blindly update major versions without compatibility review. -* Remove unused dependencies. -* Treat transitive dependencies as part of the attack surface. -* Verify package identity before installation. -* Avoid untrusted package sources. -* Respect lockfiles. - -## Supply Chain Security - -* Pin dependency versions where appropriate. -* Protect build and deployment pipelines. -* Do not expose CI/CD credentials. -* Use least privilege for pipeline identities. -* Review third-party actions, plugins and build scripts. -* Avoid executing untrusted build scripts. -* Verify artifacts and sources where practical. -* Keep generated artifacts traceable to source. -* Do not publish secrets in build logs or artifacts. - -## Configuration - -* Use secure production defaults. -* Do not enable debug mode in production. -* Do not expose development endpoints in production. -* Separate environment-specific configuration. -* Validate security-sensitive configuration at startup. -* Fail startup when mandatory security configuration is missing. -* Do not silently fall back to insecure settings. -* Protect configuration files containing sensitive values. - -## Network Security - -* Use HTTPS for sensitive or authenticated communication. -* Do not disable TLS validation. -* Restrict outbound network access where practical. -* Restrict inbound services to required ports and interfaces. -* Use timeouts for network operations. -* Limit retries to avoid amplification or denial-of-service behavior. -* Validate remote endpoints where SSRF is possible. -* Do not allow arbitrary user-controlled URLs for privileged server-side requests. -* Block access to internal network ranges when handling untrusted remote URLs where applicable. - -## SSRF Prevention - -* Treat user-controlled URLs as dangerous. -* Validate schemes. -* Prefer allowlisted hosts. -* Resolve and verify target addresses where necessary. -* Prevent access to localhost, metadata endpoints and internal networks where not explicitly required. -* Revalidate after redirects. -* Limit redirects. -* Apply request timeouts and response-size limits. - -## Concurrency and Resource Abuse - -* Bound concurrency. -* Avoid unbounded task creation. -* Avoid unbounded queues. -* Limit request sizes. -* Limit collection sizes when processing external input. -* Apply timeouts to external operations. -* Apply cancellation where practical. -* Prevent expensive operations from being triggered repeatedly without limits. -* Protect endpoints against denial-of-service through algorithmic complexity. -* Avoid user-controlled regular expressions that may cause catastrophic backtracking. - -## Memory Safety and Resource Management - -* Dispose files, streams, sockets and other resources correctly. -* Prevent resource leaks. -* Avoid retaining sensitive data in memory longer than necessary. -* Avoid unsafe code unless explicitly required. -* Review pointer and memory operations carefully. -* Avoid exposing raw memory or buffers across trust boundaries. -* Clear sensitive buffers where warranted. - -## Multi-Tenant Systems - -* Treat tenant boundaries as security boundaries. -* Scope every tenant-owned query explicitly. -* Never trust tenant identifiers from the client without authorization. -* Prevent cross-tenant cache leakage. -* Prevent cross-tenant logging or diagnostics leakage. -* Keep tenant-specific secrets isolated. -* Test horizontal privilege escalation explicitly. - -## Frontend Security - -* Assume frontend code and data are visible to the user. -* Never embed secrets in frontend applications. -* Never rely on frontend checks for authorization. -* Treat browser storage as potentially accessible to malicious scripts. -* Avoid storing sensitive long-lived tokens unnecessarily. -* Escape or sanitize untrusted content. -* Do not bypass framework security controls without explicit justification. - -## Desktop Application Security - -* Treat local files and IPC input as untrusted where applicable. -* Do not assume local users or processes are trusted. -* Avoid storing secrets in plaintext configuration. -* Protect locally cached sensitive data. -* Validate update packages and downloaded executables. -* Do not execute arbitrary files or commands from untrusted input. -* Use least privilege and avoid unnecessary elevation. - -## Security-Sensitive Changes - -Changes affecting any of the following require additional scrutiny: - -* Authentication -* Authorization -* Cryptography -* Secrets -* User permissions -* File access -* Process execution -* Network access -* Input validation -* Serialization -* Database access -* Payment or financial data -* Personal or confidential data -* Admin functionality -* Deployment or infrastructure security - -For security-sensitive changes: - -* Prefer established framework functionality. -* Review trust boundaries. -* Review failure behavior. -* Review privilege requirements. -* Review input validation. -* Review logging for data leakage. -* Review backward compatibility for security implications. -* Add or update relevant security tests. - -## Security Testing - -* Test authorization failures. -* Test invalid and malicious input. -* Test boundary values. -* Test unauthenticated access. -* Test unauthorized resource access. -* Test cross-user and cross-tenant access where applicable. -* Test expired and invalid credentials. -* Test malformed payloads. -* Test path traversal where files are involved. -* Test injection risks where interpreters or databases are involved. -* Test rate and resource limits where abuse is realistic. -* Preserve regression tests for discovered security issues. - -## Agent Rules - -* Never intentionally weaken security controls without explicit user instruction. -* Never disable certificate validation, authentication, authorization, validation or security middleware to solve a problem. -* Never add hardcoded secrets. -* Never expose sensitive data for debugging convenience. -* Never bypass a security check because it blocks implementation. -* Never assume trusted input without a clearly defined trust boundary. -* Never silently choose a less secure implementation because it is easier. -* If a requested change creates a meaningful security risk, clearly identify the risk before proceeding. -* If requirements are ambiguous in a security-sensitive area, ask the user instead of making an autonomous security decision. - -## Priority Order - -Prioritize security decisions in this order: - -* Prevent unauthorized access -* Protect sensitive data -* Preserve integrity -* Minimize privileges -* Minimize attack surface -* Validate trust boundaries -* Maintain availability -* Preserve auditability -* Optimize usability and performance only within acceptable security constraints - -Security must not be traded away for convenience without an explicit and informed decision. +* Treat external input as untrusted and validate at trust boundaries. +* Prefer secure defaults, least privilege, explicit authorization, and fail-closed behavior. +* Never hardcode, commit, log, expose, or weaken protections for secrets and credentials. +* Use established framework and platform security mechanisms; do not invent cryptography or bypass validation, authentication, authorization, or TLS checks. +* Keep dependencies minimal and from trusted sources. +* Do not disclose sensitive data or internal implementation details in user-facing errors or diagnostics. +* Load the applicable focused security rule before changing authentication, web/UI, APIs, databases, files, network access, cryptography, secrets, or supply-chain configuration.