AWS CodeArtifact: Private Package Mirrors Inside Coding-Agent Sandboxes

0 views

Your agent’s CodeBuild sandbox just ran npm install lodash from the public registry, pulled a typosquat, and “fixed” a bug by executing a postinstall script that exfiltrated the task role credentials. Separately, every cold sandbox burns 90 seconds resolving the same dependency graph from the internet. AWS CodeArtifact is the unfair advantage: private npm/PyPI/Maven/Go mirrors with controlled upstreams, domain policies, and IAM so agent sandboxes never talk to the raw public internet for packages. Pair with CodeBuild sandboxes and ECR Lambda containers — this post is the package supply-chain layer.

⚡ TL;DR: Create a CodeArtifact domain + repos with upstreams to public registries. Grant sandboxes codeartifact:GetAuthorizationToken + read-only package permissions. Point npm/pip/go at the mirror; block direct egress to registry.npmjs.org / pypi.org where possible. Related: CodeBuild sandboxes, Verified Permissions, IAM condition keys.

Why public fetches fail agent sandboxes

Agent sandboxes (CodeBuild, Fargate tasks, Lambda custom runtimes) repeatedly:

  1. Clone a repo
  2. Install language dependencies
  3. Run tests / formatters / linters

Public registries create three failures:

Failure Symptom CodeArtifact fix
Latency 30–120s installs per sandbox Cache hits inside your VPC/region
Supply chain Typosquat / compromised maintainer Upstream + optional package origin control / allowlists
Flaky egress NAT timeouts, rate limits Stable private endpoint + token
bash
# ❌ Agent buildspec talking to the public internet
phases:
  install:
    commands:
      - npm install   # hits registry.npmjs.org
      - pip install -r requirements.txt  # hits pypi.org
bash
# ✅ Same sandbox via CodeArtifact
phases:
  pre_build:
    commands:
      - export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \
          --domain cheatcoders --domain-owner $ACCOUNT --query authorizationToken --output text)
      - aws codeartifact login --tool npm --domain cheatcoders --repository agents-npm
      - aws codeartifact login --tool pip --domain cheatcoders --repository agents-pypi
  install:
    commands:
      - npm ci
      - pip install -r requirements.txt

Domain, repos, and upstreams

One domain per org (or per prod vs sandbox). Repos per ecosystem; attach external upstreams so first fetch populates the private cache.

typescript
// ✅ CDK-ish: domain + npm repo with public upstream
import * as codeartifact from "aws-cdk-lib/aws-codeartifact";

const domain = new codeartifact.CfnDomain(this, "Domain", {
  domainName: "cheatcoders",
});

const npmRepo = new codeartifact.CfnRepository(this, "NpmAgents", {
  domainName: domain.domainName,
  repositoryName: "agents-npm",
  externalConnections: ["public:npmjs"], // ✅ upstream
});

const pypiRepo = new codeartifact.CfnRepository(this, "PypiAgents", {
  domainName: domain.domainName,
  repositoryName: "agents-pypi",
  externalConnections: ["public:pypi"],
});

For stricter agents, publish an internal-only repo with no external connection and only packages your security team approved — slower onboarding, stronger guarantee.

python
# ✅ boto3: create repo without external connection (allowlist-only)
ca = boto3.client("codeartifact")
ca.create_repository(
    domain="cheatcoders",
    repository="agents-npm-locked",
    description="No public upstream — publish approved packages only",
)
# ❌ create_repository(..., externalConnections=["public:npmjs"]) when you need a lock

IAM for CodeBuild / Fargate / Lambda sandboxes

Sandboxes need tokens and read access — not domain admin.

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CodeArtifactAuth",
      "Effect": "Allow",
      "Action": ["codeartifact:GetAuthorizationToken", "sts:GetServiceBearerToken"],
      "Resource": "*"
    },
    {
      "Sid": "ReadPackages",
      "Effect": "Allow",
      "Action": [
        "codeartifact:ReadFromRepository",
        "codeartifact:GetRepositoryEndpoint",
        "codeartifact:ListPackages",
        "codeartifact:GetPackageVersion*"
      ],
      "Resource": [
        "arn:aws:codeartifact:REGION:ACCOUNT:repository/cheatcoders/agents-npm",
        "arn:aws:codeartifact:REGION:ACCOUNT:repository/cheatcoders/agents-pypi",
        "arn:aws:codeartifact:REGION:ACCOUNT:package/cheatcoders/agents-npm/*",
        "arn:aws:codeartifact:REGION:ACCOUNT:package/cheatcoders/agents-pypi/*"
      ]
    }
  ]
}

Tag agent roles and constrain with IAM condition keys. Deny codeartifact:PublishPackageVersion on sandbox roles — agents should consume, not publish, unless you have a deliberate “agent-built library” pipeline.

typescript
// ❌ Sandbox role with codeartifact:* — agent can delete repos / publish malware
Action: ["codeartifact:*"]

Wiring npm, pip, and go

bash
# npm — after aws codeartifact login OR manual
registry=https://cheatcoders-ACCOUNT.d.codeartifact.REGION.amazonaws.com/npm/agents-npm/
//cheatcoders-ACCOUNT.d.codeartifact.REGION.amazonaws.com/npm/agents-npm/:_authToken=${CODEARTIFACT_AUTH_TOKEN}
bash
# pip
pip config set global.index-url \
  "https://aws:${CODEARTIFACT_AUTH_TOKEN}@cheatcoders-ACCOUNT.d.codeartifact.REGION.amazonaws.com/pypi/agents-pypi/simple/"
bash
# Go modules via CodeArtifact (GOPROXY)
export GOPROXY="https://aws:${CODEARTIFACT_AUTH_TOKEN}@cheatcoders-ACCOUNT.d.codeartifact.REGION.amazonaws.com/go/agents-go/"

In CodeBuild sandboxes, put login in pre_build and refresh tokens (valid ~12 hours) at the start of each ephemeral project.

For Lambda container tools that vendor deps at image build time, run CodeArtifact login in the Dockerfile build stage on CodeBuild — not at Invoke — so SnapStart/cold paths never hit the registry.

Domain policies and debugging install failures

Attach a domain resource policy so only expected accounts/roles can read. When agents fail installs:

  1. Check token expiry (GetAuthorizationToken)
  2. Confirm repo endpoint matches tool config
  3. Check whether the package exists upstream vs blocked
  4. Inspect CodeArtifact CloudTrail for ReadFromRepository denies
python
# ✅ Debug: list package versions the sandbox should see
ca = boto3.client("codeartifact")
print(ca.list_packages(domain="cheatcoders", repository="agents-npm", maxResults=20))

# ❌ Debugging by curling registry.npmjs.org from the sandbox — defeats the mirror

Combine with network controls: VPC endpoints for CodeArtifact, deny egress to public registry hosts in the sandbox security group / NAT firewall. Authorize the install_deps tool via Cedar / Verified Permissions so only approved sandboxes can mutate lockfiles.

Caching, retention, and agent CI performance

CodeArtifact caches upstream packages after the first pull. Cold sandboxes still pay once per unique version; warm orgs amortize. Set lifecycle policies so unused versions do not grow forever — but do not expire versions still pinned in production lockfiles your agents re-install hourly.

typescript
// ✅ Measure install time with and without mirror (emit EMF)
console.log(
  JSON.stringify({
    _aws: { Timestamp: Date.now(), CloudWatchMetrics: [{
      Namespace: "CheatCoders/AgentSandbox",
      Dimensions: [["tenant"]],
      Metrics: [{ Name: "NpmInstallMs", Unit: "Milliseconds" }],
    }]},
    tenant: tenantId,
    NpmInstallMs: elapsedMs,
    mirror: "codeartifact",
  })
);

Practical targets for coding-agent CI:

  1. Vendor or layer common toolchains in the CodeBuild image (node_modules for formatters) so agents only resolve project deps.
  2. Use npm ci / lockfiles — agents that rewrite package.json from the model should still commit lockfile updates through a reviewed tool.
  3. Prefer internal packages for company SDKs; publish once from a non-agent pipeline, consume from sandboxes.
  4. On security incidents, delete or block a package version in CodeArtifact and force agents to fail closed rather than falling back to public npm.
bash
# ✅ Block a bad version after incident (ops, not agent)
aws codeartifact dispose-package-versions \
  --domain cheatcoders --repository agents-npm \
  --format npm --package evil-typosquat --versions 1.0.0

# ❌ Agent role allowed to dispose packages — ransomware-by-tool

When debugging “works on my laptop, fails in sandbox,” compare registry endpoints first. Ninety percent of agent install flakes are expired tokens or $LATEST build images without aws codeartifact login in pre_build.

Checklist

  • [ ] Domain + per-ecosystem repos with intentional upstreams (or locked allowlist repos)
  • [ ] Sandbox IAM: token + read only; no publish/delete
  • [ ] CodeBuild/Fargate pre_build login; Lambda images bake deps at build time
  • [ ] Block or monitor direct public registry egress from agent sandboxes
  • [ ] Tag-based IAM + Verified Permissions on install tools
  • [ ] Alert on publish attempts from sandbox roles (should be zero)

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.