Amazon Macie: Scan Coding-Agent Artifact Buckets for Leaked Secrets

0 views

Your agent uploaded turn-8842-debug.tgz to the shared artifacts bucket — inside it: a .env from the customer repo, a GitHub PAT in a failing test log, and a screenshot of an IAM console. Nobody will open that tarball until a breach review. Amazon Macie is the unfair advantage for agent artifact stores: managed discovery jobs that classify secrets and sensitive data in S3 so you quarantine and rotate before the next sync job fans the object out. Pair with Secrets Manager rotation and S3 conditional writes — this post is the bucket scanning layer.

⚡ TL;DR: Run Macie discovery on every coding-agent artifact / eval / log bucket. Alert on AWS keys, private keys, and custom PAT detectors; auto-quarantine objects and rotate matching secrets. Prefer prevent (guardrails, .gitignore, pre-upload scanners) + detect (Macie). Related: GuardDuty credentials, CloudTrail Lake, CodeArtifact, Budgets.

Why agent buckets are secret landfills

Coding agents write a lot of semi-structured junk:

  1. Patch files and worktrees (may include .env.local)
  2. CI logs and pytest output (echoed tokens)
  3. Model traces and tool payloads (prompts with pasted secrets)
  4. Eval corpora copied from customer monorepos

Traditional DLP assumes curated document stores. Agent buckets look like /tenants/*/turns/*/. Macie is built for S3-scale discovery with managed + custom data identifiers.

Store Secret risk Macie fit
Artifact S3 ✅ High ✅ Primary
CloudWatch Logs ✅ High Use Logs + Insights; export hot paths to S3 for Macie
EFS workspaces ✅ High Snapshot/sync samples to S3 for scan (EFS post)
DynamoDB sessions Medium App-level redaction; not Macie’s home turf
ECR images Medium Image scanning + avoid baking .env (ECR+Lambda)

Turn on Macie and scope discovery jobs

Enable Macie per account (or Org), then define jobs that target only agent buckets — do not burn budget classifying unrelated data lakes on day one.

bash
# ✅ enable Macie
aws macie2 enable-macie

# ✅ discovery job: daily on agent artifacts prefix
aws macie2 create-classification-job \
  --name coding-agent-artifacts-daily \
  --job-type SCHEDULED \
  --schedule-frequency '{"dailySchedule":{}}' \
  --s3-job-definition '{
    "bucketDefinitions":[{
      "accountId":"111122223333",
      "buckets":["coding-agent-artifacts-prod"]
    }],
    "scoping":{
      "includes":{"and":[{
        "simpleScopeTerm":{
          "comparator":"STARTS_WITH",
          "key":"OBJECT_KEY",
          "values":["tenants/"]
        }
      }]}
    }
  }' \
  --managed-data-identifier-selector ALL
typescript
// ✅ custom data identifier for GitHub PATs / npm tokens (illustrative regex)
new macie.CfnCustomDataIdentifier(this, "GithubPat", {
  name: "github-pat-agent",
  regex: "ghp_[A-Za-z0-9]{20,}",
  description: "GitHub personal access tokens in agent artifacts",
  maximumMatchDistance: 50,
  keywords: ["github", "ghp_", "token"],
});

Findings → quarantine → rotate

Macie findings alone do not rotate secrets. Wire EventBridge:

python
# ✅ on sensitive finding: tag object quarantine + rotate likely secret
import boto3

s3 = boto3.client("s3")
sm = boto3.client("secretsmanager")

QUARANTINE = {"Key": "quarantine", "Value": "macie-sensitive"}

def handle_macie_finding(detail: dict):
    resources = detail.get("resourcesAffected", {})
    s3obj = resources.get("s3Object", {})
    bucket = s3obj.get("bucketArn", "").split(":")[-1]
    key = s3obj.get("key")
    if not bucket or not key:
        return
    s3.put_object_tagging(
        Bucket=bucket,
        Key=key,
        Tagging={"TagSet": [QUARANTINE, {"Key": "macieFindingId", "Value": detail.get("id", "")[:64]}]},
    )
    # deny GETs via bucket policy on quarantine=macie-sensitive (separate)
    severity = detail.get("severity", {}).get("description")
    # ✅ heuristic: if AWS secret key identifier matched, rotate agent AWS-adjacent secrets
    for detection in detail.get("classificationDetails", {}).get("result", {}).get("sensitiveData", []) or []:
        for d in detection.get("detections", []):
            if d.get("type") in ("AWS_CREDENTIALS", "PRIVATE_KEY"):
                sm.rotate_secret(SecretId="agent/shared/emergency-rotate-hook")
    return {"quarantined": f"s3://{bucket}/{key}", "severity": severity}
json
// ✅ bucket policy fragment: block reads of quarantined objects except IR role
{
  "Effect": "Deny",
  "Principal": "*",
  "Action": ["s3:GetObject"],
  "Resource": "arn:aws:s3:::coding-agent-artifacts-prod/*",
  "Condition": {
    "StringEquals": {"s3:ExistingObjectTag/quarantine": "macie-sensitive"},
    "ArnNotLike": {"aws:PrincipalArn": "arn:aws:iam::111122223333:role/ir-*"}
  }
}

Prevent as much as you detect

Macie is your backstop. Up-stack controls:

  • Pre-upload secret scan in the sandbox (gitleaks/trufflehog) before aws s3 cp
  • Strip tool I/O with Bedrock ApplyGuardrail
  • Never persist raw prompts with secrets — hash or redact in Step Functions result writers
  • Tenant-prefix IAM so one role cannot list all artifacts
Control When it fires Gap
Pre-upload scanner Before S3 put Bypass if agent skips hook
Guardrails Model/tool boundary Binary artifacts
Macie After object lands Hours of exposure window
GuardDuty Credential use Silent unused leak

Cost and sampling strategy

Macie pricing follows bytes classified. For huge eval dumps:

  • Exclude known-safe extensions (*.pyc, node_modules mirrors — better: do not upload them)
  • Sample historical prefixes; always-scan tenants/*/turns/*/logs/
  • Watch Budgets for Macie spend anomalies after a tenant floods artifacts
bash
# ❌ uploading entire node_modules to artifacts "for debug"
tar -czf /tmp/debug.tgz /work/repo   # includes secrets + megabytes Macie will bill
aws s3 cp /tmp/debug.tgz s3://artifacts/tenants/$T/turns/$TURN/

# ✅ allowlisted paths only
tar -czf /tmp/debug.tgz /tmp/pytest.log /tmp/patch.diff
aws s3 cp /tmp/debug.tgz s3://artifacts/tenants/$T/turns/$TURN/debug.tgz

Production checklist

  • [ ] Macie enabled in every account that holds agent artifacts
  • [ ] Scheduled discovery on artifact + eval buckets; custom identifiers for forge PATs
  • [ ] EventBridge → quarantine tag + IR notify + Secrets Manager rotate hook
  • [ ] Bucket policy denies GetObject on quarantined tags except IR roles
  • [ ] Pre-upload secret scanning in sandbox images
  • [ ] Guardrails on tool I/O; no raw secret prompts to durable storage
  • [ ] Macie findings reviewed weekly; false-positive identifiers tuned
  • [ ] Correlate with CloudTrail Lake who PutObject’d the offender

What “sensitive” should mean for coding agents

Tune managed identifiers so you care about:

  • AWS access keys and secret keys
  • Private keys (PEM)
  • Creds that match your forge/npm/pypi custom regexes
  • Customer PII if eval corpora may contain it

Noise like public README emails should not page IR. Start with HIGH severity credential types → auto quarantine, and route PII/medium findings to a weekly security review queue. That split keeps Macie actionable instead of another ignored dashboard.

FAQ

Q: Can Macie scan EFS directly?
A: No — copy or sync risk paths to S3 for classification, or run offline scanners on the mount in a locked-down job (EFS workspaces).

Q: Is Macie a replacement for Secrets Manager?
A: No. Secrets Manager stores and rotates; Macie finds copies that escaped into objects.

Q: How fast will we know?
A: Job cadence bound (daily/continuous). For zero-minute ambitions, rely on pre-upload scanners; Macie catches what slipped through.

Macie turns coding-agent artifact buckets from unaudited landfills into continuously classified stores. Quarantine on finding, rotate on credential types, and keep uploading boring — small allowlisted artifacts beat giant “debug” tarballs every time.

Multi-account rollout without boiling the ocean

Most CheatCoders-style fleets already have a security tooling account and many agent sandbox accounts. Recommended shape:

  1. Delegate Macie admin to the security account
  2. Auto-enable Macie on agent OUs via Organizations
  3. Central findings bucket / EventBridge bus in security account
  4. Per-sandbox jobs only on that account’s artifact buckets (avoid cross-account classify storms on day one)

When a finding references tenants/acme/..., page the tenant owner channel and disable the uploading tool via AppConfig until the path is clean. If the same tenant repeats, enforce stronger pre-upload gates in their Fargate Spot task definition (init container scanner required). Pair repeated AWS-key findings with the GuardDuty playbook — Macie found the copy; GuardDuty catches the use.

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.