--- name: security-advisory-lessons description: Apply RustFS security lessons distilled from repository GitHub Security Advisories. Use when making or reviewing RustFS code changes, doing security checks, handling PR review for auth/authz, IAM, storage, RPC, logging, CORS, console/browser, encryption, policy, or endpoint changes, and when deciding which security regression tests are required. --- # RustFS Security Advisory Lessons Use this skill as a RustFS-specific security lens before changing or approving code. For the distilled advisory lessons and review patterns, read [advisory-patterns.md](references/advisory-patterns.md). When currentness matters, fetch the live advisory inventory instead of relying on this skill as a status mirror: ```bash gh api repos/rustfs/rustfs/security-advisories --paginate \ --jq '.[] | {ghsa_id,state,severity,summary,updated_at}' ``` Fetch full advisory details only when the live summary suggests a new or changed lesson: ```bash gh api repos/rustfs/rustfs/security-advisories/ ``` For the full pattern map, read [advisory-patterns.md](references/advisory-patterns.md). ## Workflow ### 1. Scope the change - Identify touched routes, protocol frontends, handlers, storage paths, credentials, logs, browser surfaces, CI/release code, and policy checks. - Treat these paths as security-sensitive by default: `rustfs/src/admin/`, `rustfs/src/storage/`, `rustfs/src/auth.rs`, `rustfs/src/server/layer.rs`, `crates/iam/`, `crates/policy/`, `crates/credentials/`, `crates/ecstore/src/rpc/`, `crates/protocols/`, `crates/rio/`, and console preview/auth code. ### 2. Map to advisory classes - Read [advisory-patterns.md](references/advisory-patterns.md) for matching GHSA lessons. - Do not rely on advisory titles alone. Confirm whether the issue is authentication, authorization, input validation, storage invariant, browser isolation, logging, or operational hardening. ### 3. Verify fail-closed behavior - Check that unauthenticated, wrong-permission, cross-user, cross-bucket, malformed-input, and default-config cases fail explicitly. - Prefer exact action/permission checks over broad helper calls or inferred ownership. - Confirm lower storage/RPC layers do not bypass checks done in upper layers. ### 4. Require regression evidence - For behavior changes, add focused negative tests that reproduce the advisory class. - For sensitive fixes, include tests for the bypass form, not only the happy path. - If a test is impractical, explain the residual risk and provide a manual verification command. ### 5. Report clearly - Lead with concrete findings and file/line evidence. - Separate proven vulnerabilities from hardening risks. - Avoid exaggerating unauthenticated impact when the code actually rejects unauthenticated requests but allows a low-privileged authenticated bypass. ## Advisory-Derived Guardrails ### Auth and admin authorization - Every admin or diagnostic route needs an explicit authn and authz story. Route registration, router whitelist, and handler-level authorization must agree. - Match the admin action to the operation exactly. Copy-paste action constants are a known RustFS vulnerability class. - Avoid authentication-only helpers for state-changing admin APIs; use `validate_admin_request` or the established equivalent with the right `AdminAction`. - Read-only admin APIs such as metrics, server info, and diagnostics still require admin authorization; checking only that credentials exist is not enough. - Replication admin reads can expose remote target credentials; list/get target endpoints require replication/admin authorization and must not return secrets to low-privilege callers. - Do not assume admin-action `Resource` scoping constrains blast radius unless the policy engine actually enforces resources for that action. ### IAM and service accounts - Treat imported IAM payload fields as attacker-controlled: `parent`, `claims`, `accessKey`, `secretKey`, status, policy names, and groups. - For service account create/update/import, prove parent ownership or root/admin authority before writing credentials or claims. - Do not let `deny_only` or "no explicit deny" become an allow decision that skips required allow checks. - Test cross-user list/update/import flows with wrong, correct, self, parent, and root identities. ### S3 copy, multipart, and presigned POST - Multipart copy must enforce source `GetObject` and destination `PutObject` semantics equivalent to `CopyObject`, including copy-source and policy conditions. - Do not let `CreateMultipartUpload`, `UploadPartCopy`, `CompleteMultipartUpload`, or `AbortMultipartUpload` return success without authorization. - Presigned POST policies are server-side contracts. Enforce `content-length-range`, key prefix, exact metadata/content-type, and all signed policy conditions. ### Protocol frontends and IAM parity - FTP/FTPS, SFTP, gateway, and other protocol drivers must enforce IAM per operation before calling storage backends; authentication to a protocol listener is not authorization. - Match protocol commands to the same S3 actions as HTTP, such as `RETR` to `GetObject`, `SIZE`/`MDTM` to `HeadObject`, `MKD` to `CreateBucket`, and bucket probes to `ListBucket` or `HeadBucket`. - Review every handler in a protocol driver, not only the changed handler, because RustFS advisories show mixed guarded and unguarded siblings in the same driver. - Regression tests for protocol frontends should deny the shared authorization hook and prove the backend is not reached for the denied command. ### Paths, object keys, and filesystem access - Never join untrusted bucket/object/RPC path strings onto filesystem roots without normalization and boundary checks. - Reject or safely handle `..`, absolute paths, URL-encoded traversal, platform separators, empty components, and paths that canonicalize outside the intended root. - Validate both S3 object-key paths and internode/RPC disk paths; storage helpers can bypass S3 authorization if they trust already-parsed paths. - Archive auto-extract paths are object keys too. Validate tar/zip entry names before IAM checks and before storage writes, and prove cleaned paths cannot cross bucket or prefix boundaries. ### Secrets, default credentials, and crypto - Do not ship hard-coded shared tokens, HMAC secrets, private keys, or production test keys. - Defaults for internode/RPC auth must fail closed for network-reachable deployments or require explicit opt-in with loud warnings. - License or token validation must use signatures with embedded public/verifying keys only; do not use private-key decryption as authenticity. - Plan key rotation and key IDs when removing exposed keys. ### Logging and debug output - Logs must never include access keys beyond safe identifiers, secret keys, session tokens, JWT claims, HMAC secrets, expected signatures, license secrets, or raw response bodies containing credentials. - Treat `Debug` implementations, `?value` tracing, merged config dumps, and dependency-level HTTP body logging as leak surfaces. - Add log-capture tests or targeted unit tests for redaction wrappers when changing credential structs or response bodies. ### RPC, parsing, and panic safety - Treat all RPC payload bytes as attacker-controlled. Replace `unwrap`, `expect`, and panic-prone deserialization with typed errors. - Malformed request tests should cover empty bytes, truncated MessagePack/protobuf, invalid enum values, stale timestamps, and invalid signatures. - RPC authentication must be independently strong; do not depend on S3 admin credentials unless the fallback is explicit and safe. - RPC signatures must bind the exact generated gRPC method path, timestamp, and request method. Service-prefix signatures must not authorize a different concrete NodeService call. ### Browser, CORS, and console surfaces - Do not reflect arbitrary `Origin` while also allowing credentials. Default CORS should be no CORS unless explicitly configured. - Do not render user-controlled object content in a same-origin iframe with console credentials available to JavaScript. - Prefer origin separation for object preview/download, `nosniff`, CSP, strict content-type handling, and avoiding durable credentials in `localStorage`. - Console license/version-like metadata endpoints should expose only coarse public data unless authenticated, especially subject names and expiration timestamps. ### Profiling, debug, and health endpoints - Profiling and debug endpoints are not health checks. They require admin auth, opt-in enablement, rate limiting, and safe responses. - Do not return absolute filesystem paths or other deployment layout in unauthenticated or low-privilege responses. - Ensure health endpoint allowlists cannot accidentally include expensive diagnostics. ### Trusted proxy and network identity - Only honor `X-Forwarded-For` or `X-Real-IP` when the request came from a configured trusted proxy. - Direct clients must use the socket peer address for `aws:SourceIp` and policy condition evaluation. - Add tests for direct spoofed headers and trusted-proxy headers. ### SSE and storage invariants - Encryption metadata is not proof that bytes were encrypted on disk. - When touching reader/writer wrappers such as hashing, encryption, compression, or warp readers, verify wrapper order and inspect stored bytes in regression tests. - Avoid helper shortcuts that unwrap nested readers and accidentally bypass encryption or integrity layers. ## Review Prompts Use these prompts while reviewing a diff: - Could a low-privileged authenticated user reach this path with the wrong action, parent, bucket, or source object? - Does a non-HTTP protocol path call the same authorization boundary as the S3 API before touching storage? - Does a public/default/empty config change security behavior from fail-closed to fail-open? - Is any attacker-controlled value later used as a path, policy condition, credential identity, log field, URL, Origin, or response body? - Does this response contain stored replication, remote target, or service credentials that need redaction or stricter authorization? - Is an archive entry, object key, or policy resource normalized differently between authorization and storage? - Is the same operation implemented in multiple paths, such as `CopyObject` vs `UploadPartCopy`, and do all paths enforce the same security contract? - Does the test prove the exploit form is denied, or only that the intended form still works?