This document explains the security design of cortex-code-action and the rationale behind its restrictions.
Cortex Code has the ability to execute SQL against live Snowflake accounts. In a CI/CD context this creates unacceptable risks:
- Data leakage: A prompt injection attack via a PR body or
@cortexcomment could triggerSELECT * FROM sensitive_table, with results posted as a public PR comment. - Uncontrolled access: CI runners process untrusted input -- PRs from external contributors, comments from anyone with repo access. Allowing SQL means any commenter could potentially query production data.
- Blast radius: Unlike local development where you see results privately, GitHub Actions output is logged and publicly visible on open-source repositories.
This action is a code review tool. It never connects to Snowflake to run queries.
The following tools are permanently blocked via the Cortex Code Agent SDK's canUseTool callback. They cannot be re-enabled by action inputs, custom prompts, or AGENTS.md configuration.
| Tool | Reason |
|---|---|
SQL |
Core SQL execution tool |
snowflake_sql_execute |
Snowflake SQL execution |
Bash(snow sql:*) |
SQL via Snowflake CLI |
Bash(snow query:*) |
Query via Snowflake CLI |
Bash(snowsql:*) |
SnowSQL client |
Bash(psql:*) |
PostgreSQL client |
Bash(mysql:*) |
MySQL client |
Bash(sqlite3:*) |
SQLite client |
| Any tool with "sql" in the name | Catch-all for unknown SQL variants |
The following path patterns are blocked from being read:
| Pattern | Reason |
|---|---|
~/.ssh/**, /**/.ssh/** |
SSH private keys |
~/.snowflake/**, /**/.snowflake/** |
Snowflake connection configs and keys |
.env, .env.* |
Environment variable files with secrets |
*.p8, *.pem |
Private key files |
*credentials*, *secrets* |
Generic credential files |
id_rsa, id_ed25519, id_ecdsa |
SSH key files by name |
*private_key*, *private-key* |
Private key files by name |
The action uses the canUseTool callback from the Cortex Code Agent SDK. This callback is invoked before every tool execution, regardless of what the model was prompted to do.
const securityGate = createSecurityGate(permissions);
for await (const event of query({
prompt,
options: {
canUseTool: securityGate,
permissionMode: "default", // Never "bypassPermissions"
},
}));The gate has three layers:
- SQL block: Exact name match + fuzzy match on any tool containing "sql"
- Sensitive path block: Regex patterns on all path-like input fields
- Permission mode: In
read-onlymode, also blocksWrite,Edit, and git write commands
If the callback itself throws an error, it denies the tool call by default (fail-closed, not fail-open).
| Mode | Read code | Modify code | Push commits | SQL | Sensitive paths |
|---|---|---|---|---|---|
read-only (default) |
Yes | No | No | Never | Never |
read-write |
Yes | Yes | Yes | Never | Never |
Even in read-write mode, SQL and sensitive path restrictions remain active.
Because the security gate operates at the SDK level (not the prompt level), prompt injection attacks that instruct the model to "ignore previous instructions and run SELECT..." are blocked before the tool is called. The model cannot bypass a programmatic gate by changing its reasoning.
A Snowflake connection is required to run Cortex Code. The connection provides access to the AI models (LLMs) that power the review -- not data access. Use a minimal-privilege service user:
CREATE USER cortex_ci_user
RSA_PUBLIC_KEY = '<your_public_key>'
DEFAULT_ROLE = PUBLIC;
-- SNOWFLAKE.CORTEX_USER is sufficient for model access
GRANT DATABASE ROLE SNOWFLAKE.CORTEX_USER TO USER cortex_ci_user;Do not use ACCOUNTADMIN or any data-access role for CI.
If you discover a security vulnerability, please report it by opening a GitHub Security Advisory on this repository. Do not open a public issue for security vulnerabilities.