Description
Adds a Composio-specific SECURITY.md as the source of truth for an automated PR security reviewer (Codex / Claude Code), plus the GitHub Actions workflow that drives it. Pilot for hermes — once it's behaving sensibly here, we'll fan out to the rest of the private repos.
Sections cover the bug classes called out by the security team:
- Account Takeover & Secret Exposure — anchored to the five inbound auth verifiers (
getAPIKeyInfo, JWT.verifyToken, getAuthInfoFromProjectJwt/verifyIntegrationJwt, resolveBearer, verifyAdminJwt) and the dataTypedEncrypted / AES-256-GCM at-rest pattern. Includes a question that flags PRs which "wake up" the defined-but-unused composio-apollo-org audience without a route allowlist.
- Access Control Bypass — tenant scoping via
TAPIAuthInfo, branded ID prefixes, the connected-accounts dbUtil pattern, the MCP resolver's jwt.project_id === server.projectId re-check, role/plan/feature-gate misuse, invite-code email-pinning.
- SSRF — the canonical
apps/apollo/src/common/utils/url/ssrf.ts guard (CIDR blocklist, IP pinning, redirect re-validation), plus questions for blind SSRF / DNS rebinding / TOCTOU resolution / unguarded Mercury HTTP.
- LFI / Path Traversal — file reads with user-controlled paths, static-file endpoints, Mercury's S3-only convention,
mercury/tools/_base/helpers/files.py:96 as the negative reference.
- XSS / CSRF / CORS —
dangerouslySetInnerHTML, Markdown render paths, blind XSS (Retool / Datadog / Slack), the layered CSRF defense (SameSite-strict cookie + production CORS allowlist + API-key-as-header), open redirect, Echo-Origin reuse.
- RCE — anchored to SEC-441: the
mercury/bundling/loaders/base.py:71 exec sink, the four invariants of _verify_bundle_signature, the MODULE_BUNDLE_VERIFICATION_PUBLIC_KEY_BASE64 config, the post-SEC-441 traceback-echo ban in handler.py:88-103, the lambdad.py:74-76 Function-URL time-bomb, the recipe-parser AST allowlist (F2/F10), plus general eval/SSTI/deserialization/file-upload→RCE coverage.
Every rule is framed as an open question for the reviewer so it can be applied to a diff without rote pattern matching.
The workflow uses anthropics/claude-code-action@v1 on pull_request: [opened, synchronize, reopened], reads SECURITY.md as the system prompt, posts exactly one consolidated "Security Audit" comment, and is advisory-only for the pilot. To make it merge-gating later, add the job's name to branch protection.
How did I test this PR
- Drafted SECURITY.md locally; confirmed every cited file/line exists in the current
master:
apps/apollo/src/lib/authMiddleware.ts, apps/apollo/src/lib/jwt_auth.ts, apps/apollo/src/common/lib/internal/auth/jwt.ts, apps/apollo/src/lib/connected_accounts/dbUtils/findConnectedAccounts.ts, apps/apollo/src/lib/connected_accounts/handleOAuthCallback.ts, apps/apollo/src/common/utils/url/ssrf.ts, apps/apollo/src/common/utils/cookie.ts, apps/apollo/src/server/middleware/cors.ts, apps/apollo/src/lib/triggers/webhook_ingress/signature.ts, apps/apollo/src/server/nextjs/resolver/resolvers.ts, wrangler/external-proxy/src/lib/encryption.ts, wrangler/external-proxy/src/lib/logger.ts, packages/types/src/id-constants.ts.
- Mercury references (
mercury/bundling/loaders/base.py:71, mercury/bundling/loader_service.py:26-53, mercury/serverless/handler.py:88-103, lambdad.py:74-76, mercury/parsers/recipe_parser.py, mercury/tools/_base/helpers/files.py:96) are intentional even though they live in a different repo — the reviewer reads SECURITY.md verbatim, but flag-anchored remediation pointers help anyone investigating.
- The workflow itself will run on this PR — the very first observable signal of "does the security reviewer behave correctly" is whatever it posts on this PR's diff. Expected outcome: zero Critical/High findings (this PR adds documentation + a workflow, no production code touched), advisory footer.
- Branch protection is unchanged. The new check is
Claude Security Review / security-review and is not on the required-checks list.