Securing Snowflake CoCo CLI
Snowflake’s CoCo CLI is a powerful agentic coding tool, but like any tool that can read files, execute shell commands, and access your Snowflake environment, it comes with a meaningful attack surface. This post walks through the risks of running CoCo unsecured, why Podman’s sandbox falls short for many teams, and how to containerize CoCo with Docker in a way that’s genuinely hardened.
What Is CoCo CLI?
CoCo CLI is Snowflake’s command-line AI coding assistant, backed by Cortex-hosted models. It can read and write files, execute shell commands, and interact with your Snowflake environment. This makes it significantly more capable (and more dangerous) than a simple chat interface.
Out of the box, CoCo supports a range of tool capabilities:
| Tool | Description |
| EXECUTE_COMMAND | Run bash/shell commands |
| FILE_READ | Read file contents |
| FILE_WRITE | Create and modify files |
| FILE_EDIT | Edit existing files |
| WEB_ACCESS | Web search and fetch operations |
These tools operate under a tiered risk model that governs whether the CLI prompts for confirmation:
| Risk Level | Example Commands | Default Behavior |
| SAFE | ls, cat, echo, grep | Auto-approved |
| LOW | touch file.txt (new file creation) | Usually auto-approved |
| MEDIUM | nano file.txt, moderate bash | Prompts in Confirm mode |
| HIGH | rm, curl, wget, sudo | Always prompts |
| CRITICAL | rm -rf, destructive operations | Extra confirmation required |
This classification system is helpful, but it’s only as trustworthy as the environment it runs in. If the agent is compromised or if you’ve configured it to run non-interactively in a pipeline (which most people will want to) those guardrails disappear.
Risks of Running CoCo Unsecured
Before diving into hardening steps, it’s worth being concrete about what can go wrong:
Credential exfiltration. CoCo runs with the permissions of whatever process launched it. If your shell environment contains SNOWFLAKE_PASSWORD, AWS credentials, or SSH keys in ~/.ssh, the agent can read and exfiltrate them — either through a prompt injection in a file it reads or through a supply-chain compromise in a Cortex skill.
Unrestricted filesystem access. Running as your local user, the agent can read any file you can read. That includes ~/.aws/credentials, .env files with database passwords, private keys, and browser session data.
Network access without limits. The WEB_ACCESS tool and curl/wget via EXECUTE_COMMAND can exfiltrate data or pull down malicious payloads. Without network egress controls, there’s no way to contain the blast radius.
Privilege escalation. If the agent discovers sudo without a password (common in developer environments), a MEDIUM-risk operation can quickly become a CRITICAL one.
Overprivileged Snowflake role. If the service account used by CoCo has broad Snowflake privileges, a compromised session can read sensitive tables, clone data, or alter schemas
CoCo's Built-In Podman Sandbox
Snowflake ships CoCo with optional Podman-based sandboxing. Podman is a daemonless container runtime that supports rootless containers. Containers run as your user, not as root, which reduces the risk of a container’s escape resulting in host compromise.
The Podman sandbox is a meaningful improvement over running CoCo naked on your machine. It isolates the filesystem and provides a boundary around command execution.
However, Podman has real limitations in practice:
- Adoption is still limited. Most teams are running Docker in their CI pipelines, developer environments, and security scanning toolchains. Introducing Podman as a one-off dependency for CoCo creates operational friction and may not integrate with your existing image scanning, SIEM logging, or container policy tooling.
- Corporate environments often block it. Rootless Podman has specific kernel requirements (user.max_user_namespaces, cgroup v2) that are not always available on managed developer workstations.
- It doesn’t solve the upstream problems. Podman sandboxes the execution, but it doesn’t address credential hygiene, Snowflake RBAC, network egress controls, or image provenance.
For teams that are already Docker focused, building a hardened Docker image for CoCo is often the more practical and more complete solution.
Dockerizing CoCo Securely
The following approach layers multiple independent defenses. No single control is sufficient on its own; defense in depth is the goal. The Dockerfile snippets throughout this section are drawn from a working reference implementation.
1. Install the CLI Non-Interactively and Skip Podman
The CoCo install script is interactive by default. In a Docker build context it will hang or fail unless you suppress all prompts. Three environment variables control this:
- NON_INTERACTIVE=1 — skips all yes/no prompts
- SKIP_PODMAN=1 — skips the Podman install prompt (we’re using Docker instead)
- SKIP_PATH_PROMPT=1 — skips the shell PATH update prompt
The script also auto-detects /.dockerenv and sets these automatically but setting them explicitly is safer. It guarantees non-interactive behavior regardless of your build environment:
dockerfile
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl python3 python3-pip tar \
&& rm -rf /var/lib/apt/lists/*
# Install Snowflake CLI (required prerequisite)
RUN SNOW_CLI_VERSION="3.15.0" \
&& SNOW_CLI_DEB="snowflake-cli-${SNOW_CLI_VERSION}.x86_64.deb" \
&& curl -fsSL \
"https://sfc-repo.snowflakecomputing.com/snowflake-cli/linux_x86_64/${SNOW_CLI_VERSION}/${SNOW_CLI_DEB}" \
-o "/tmp/${SNOW_CLI_DEB}" \
&& dpkg -i "/tmp/${SNOW_CLI_DEB}" \
&& rm "/tmp/${SNOW_CLI_DEB}"
# Install CoCo CLI — suppress all interactive prompts
ENV HOME=/tmp
RUN NON_INTERACTIVE=1 SKIP_PODMAN=1 SKIP_PATH_PROMPT=1 \
sh -c "$(curl -LsS https://ai.snowflake.com/static/cc-scripts/install.sh)"
ENV PATH="/tmp/.local/bin:$PATH"
2. Authenticate via Short-Lived PAT Tokens
bash
docker run --rm \
-e SNOWFLAKE_ACCOUNT="myorg-myaccount" \
-e SNOWFLAKE_USER="cortex_svc_user" \
-e SNOWFLAKE_PAT="${SNOWFLAKE_PAT}" \
cortex-code:hardened
3. Redirect SNOWFLAKE_HOME to a Writable, Ephemeral Directory
dockerfile
ENV SNOWFLAKE_HOME=/tmp/.snowflake
4. Pre-Accept First-Run Dialogs in the Image
CoCo presents several interactive prompts on first launch — a trust/onboarding dialog, a telemetry notice, and user preference questions. In a container context these prompts will block execution. The fix is to pre-seed the config files that the CLI checks before showing each prompt.
settings.json controls for telemetry acceptance and user preferences. Copy it into the image at its expected runtime paths:
dockerfile
# Pre-accept first-run dialogs
COPY settings.json /tmp/.snowflake/cortex/settings.json
RUN chmod 644 /tmp/.snowflake/cortex/settings.json
5. Enforce a Managed Security Policy
dockerfile
RUN mkdir -p /etc/cortex
COPY managed-settings.json /etc/cortex/managed-settings.json
RUN chmod 644 /etc/cortex/managed-settings.json
6. Pre-Scan Volumes for Secrets Before Mounting
Mounting a local directory into a container is convenient — and dangerous. Developers routinely store .env files, private keys, and cloud credentials in project directories, often without realizing it. When you mount ~/projects/my-app into the container, you’re also mounting every secret that lives there.
Install detect-secrets in the image so it’s available as a pre-flight check in your entrypoint script:
dockerfile
RUN pip3 install --break-system-packages detect-secrets==1.5.0
bash
#!/bin/bash
set -e
if [ -d "/work" ]; then
echo "Scanning /work for secrets..."
detect-secrets scan /work > /tmp/.secrets.baseline
if detect-secrets audit /tmp/.secrets.baseline --only-allowlisted 2>&1 | grep -q "potential secret"; then
echo "ERROR: Secrets detected in mounted volume. Aborting." >&2
exit 1
fi
fi
exec "$@"
7. Use .dockerignore for Sensitive Data
A .dockerignore file prevents files from being sent to the Docker build context entirely, which means they can’t accidentally end up in your image layers — even in intermediate layers that tools like docker history can expose. Treat it as a security boundary, not just a build performance optimization:
.env
.env.*
*.pem
*.key
*.p12
.aws
.ssh
.secrets.baseline
.git
8. Drop Linux Capabilities and Block Privilege Escalation
bash
docker run --rm \
--cap-drop ALL \
--security-opt no-new-privileges \
-e SNOWFLAKE_ACCOUNT="myorg-myaccount" \
-e SNOWFLAKE_USER="cortex_svc_user" \
-e SNOWFLAKE_PAT="${SNOWFLAKE_PAT}" \
cortex-code:hardened
9. Create a Snowflake Service Account Following Least Privilege
The Snowflake role associated with your CoCo sessions should have exactly the permissions needed — nothing more. In practice, this means a dedicated service account with a custom role:
sql
CREATE ROLE cortex_code_role;
GRANT USAGE ON WAREHOUSE cortex_wh TO ROLE cortex_code_role;
GRANT USAGE ON DATABASE my_db TO ROLE cortex_code_role;
GRANT USAGE ON SCHEMA my_db.my_schema TO ROLE cortex_code_role;
GRANT SELECT ON ALL TABLES IN SCHEMA my_db.my_schema TO ROLE cortex_code_role;
CREATE USER cortex_svc_user
DEFAULT_ROLE = cortex_code_role
DEFAULT_WAREHOUSE = cortex_wh;
GRANT ROLE cortex_code_role TO USER cortex_svc_user;
10. Scan Built Images with a Security Scanner
bash
# Scan and fail on HIGH/CRITICAL findings
trivy image --exit-code 1 --severity HIGH,CRITICAL cortex-code:hardened
Putting It All Together: The Reference Dockerfile
Here’s the complete Dockerfile that implements all of the above:
dockerfile
FROM ubuntu:24.04
# ── System dependencies ────────────────────────────────────────────────────────
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl python3 python3-pip tar \
&& rm -rf /var/lib/apt/lists/*
# detect-secrets available in the entrypoint for pre-flight scanning
RUN pip3 install --break-system-packages detect-secrets==1.5.0
# ── Snowflake CLI (required prerequisite for CoCo) ─────────────────────
RUN SNOW_CLI_VERSION="3.15.0" \
&& SNOW_CLI_DEB="snowflake-cli-${SNOW_CLI_VERSION}.x86_64.deb" \
&& curl -fsSL \
"https://sfc-repo.snowflakecomputing.com/snowflake-cli/linux_x86_64/${SNOW_CLI_VERSION}/${SNOW_CLI_DEB}" \
-o "/tmp/${SNOW_CLI_DEB}" \
&& dpkg -i "/tmp/${SNOW_CLI_DEB}" \
&& rm "/tmp/${SNOW_CLI_DEB}"
# ── CoCo CLI ────────────────────────────────────────────────────────────
# HOME=/tmp so the binary lands in /tmp/.local/bin — same path used at runtime.
# NON_INTERACTIVE/SKIP_PODMAN/SKIP_PATH_PROMPT suppress all prompts during build.
ENV HOME=/tmp
RUN NON_INTERACTIVE=1 SKIP_PODMAN=1 SKIP_PATH_PROMPT=1 \
sh -c "$(curl -LsS https://ai.snowflake.com/static/cc-scripts/install.sh)"
# ── Pre-accept first-run dialogs ───────────────────────────────────────────────
COPY claude.json /tmp/.claude.json
COPY settings.json /tmp/.snowflake/cortex/settings.json
RUN chmod 644 /tmp/.claude.json /tmp/.snowflake/cortex/settings.json
# ── Managed security policy (root-owned, users cannot override) ───────────────
RUN mkdir -p /etc/cortex
COPY managed-settings.json /etc/cortex/managed-settings.json
RUN chmod 644 /etc/cortex/managed-settings.json
# ── Entrypoint ─────────────────────────────────────────────────────────────────
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod 755 /usr/local/bin/docker-entrypoint.sh
# ── Runtime environment ────────────────────────────────────────────────────────
ENV PATH="/tmp/.local/bin:$PATH"
ENV SNOWFLAKE_HOME=/tmp/.snowflake
WORKDIR /work
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["cortex"]
Run it with:
bash
docker run --rm \
--cap-drop ALL \
--security-opt no-new-privileges \
-e SNOWFLAKE_ACCOUNT="myorg-myaccount" \
-e SNOWFLAKE_USER="cortex_svc_user" \
-e SNOWFLAKE_PAT="${SNOWFLAKE_PAT}" \
-v "$(pwd)":/work \
cortex-code:hardened
Sandboxing vs. Running Locally: The Real Tradeoff
Running CoCo in a hardened Docker container is not zero-cost. It adds build and startup time, introduces image maintenance overhead, and requires your team to understand what’s in the container. For quick local experimentation, that overhead may feel excessive.
The moment CoCo is used on anything beyond a throwaway environment – a machine that touches production credentials, a developer laptop with SSH keys to shared infrastructure, a CI pipeline with cloud provider access security must be prioritized. The container boundary, the non-root user, the capability drops, and the credential isolation together create a meaningful gap between “the agent did something bad” and “the agent compromised your entire environment.”
The sandboxed Podman approach Snowflake ships is a good first step. The Docker approach described here goes further by addressing the upstream risks, credential hygiene, Snowflake RBAC, image provenance, network policy, and tool surface that sandboxing alone doesn’t solve.
Treat CoCo like you’d treat any other process that has shell access and network egress in your environment: with appropriate skepticism, explicit permissions, and layers of defense.

