How do you stop a coding agent from using AWS credentials?

Block the AWS CLI, AWS API hosts, and AWS credential files in one policy, so a blocked route doesn't lead to another.

7 min read
In short: To stop a coding agent using AWS credentials, deny three things at the hook it fires before each tool call: the AWS CLI, requests to AWS API hosts, and reads of AWS credential files. Each layer covers a route the others miss, which matters for agents that look for workarounds in auto mode. Arcjet's aws-access starter policy does this across Claude Code, Copilot, Cursor, and Codex.

How do you stop a coding agent from using AWS credentials?

Deny AWS access at the hook the agent fires before each tool call, with three layers in one policy: refuse the AWS CLI, refuse requests to AWS API hosts, and refuse reads of AWS credential files. Each layer covers a path the others miss. An agent that can't run aws might call the API with curl, and an agent blocked from both might try to read ~/.aws/credentials and use the keys some other way.

The layering matters because coding agents are persistent problem-solvers. Claude Code in auto mode, or any agent working through a task without prompts, will look for another route when the first one fails. A single rule over the command name is a speed bump. Three rules over the command, the destination, and the credential files leave far fewer routes open.

Arcjet does this with the coding-agent.aws-access starter policy, which runs on every tool call across Claude Code, GitHub Copilot, Cursor, and OpenAI Codex. The hook comes from managed settings, so a developer can't remove it without administrator access, and the agent's own process enforces the denial.

Why is AWS access a coding agent risk?

A coding agent runs as the developer. Whatever the developer's shell can reach, the agent can reach: AWS CLI profiles, SSO sessions, access keys in ~/.aws/credentials, and AWS_ACCESS_KEY_ID variables in .env files. Those credentials often reach production accounts.

The risk isn't only a malicious prompt. A well-meaning agent asked to "clean up the test bucket" can run aws s3 rm --recursive against the wrong profile. An agent that reads a poisoned README or issue comment can be steered into listing buckets and sending the results to an external host. In both cases, the action looks like ordinary shell work to anything that only checks the command name.

What does an AWS access policy look like?

Arcjet policies are written in Rego, the policy language of Open Policy Agent. This one runs with Execute on set to Tool call:

cli_names := {"aws"}
aws_hosts := {".amazonaws.com", "amazonaws.com", ".aws.amazon.com", "aws.amazon.com"}
credential_markers := {".aws/credentials", ".aws/config", "AWS_ACCESS_KEY", "AWS_SECRET_ACCESS"}
deny contains "aws-cli" if {
input.values.tool_kind == "shell"
some token in input.values.command_tokens
lower(token) in cli_names
}
deny contains "aws-cli" if {
input.values.tool_kind == "shell"
some token in input.values.command_tokens
endswith(lower(token), "/aws")
}
deny contains "aws-destination" if {
some host in input.values.destinations
some marker in aws_hosts
endswith(lower(host), marker)
}
deny contains "aws-credential" if {
some path in input.values.paths
some marker in credential_markers
contains(path, marker)
}
deny contains "aws-credential" if {
input.values.tool_kind == "shell"
some marker in credential_markers
contains(input.values.command, marker)
}

Each rule reads a different input:

  • aws-cli reads command_tokens, the command split on whitespace and the shell's chaining operators. It catches aws s3 ls, AWS s3 ls, cd /tmp && aws s3 ls, and /usr/local/bin/aws s3 ls.
  • aws-destination reads destinations, the hosts a call would contact – URL arguments plus absolute URLs inside a shell command. It catches a WebFetch of an S3 URL and curl https://sts.amazonaws.com.
  • aws-credential reads paths and command. It catches a Read of ~/.aws/credentials and a shell command that names the file or an AWS key variable.

When a rule fires, Arcjet denies the call before it runs, and the agent receives only the rule ID. For the complete starter, including its stored tests, see the Block AWS access section of the coding agent policy docs.

What happens when the agent tries to get around it?

The following table shows how the layers respond to common workarounds:

WorkaroundExampleRule that denies it
Different casingAWS s3 lsaws-cli
Chained after another commandcd /tmp && aws s3 lsaws-cli
Absolute path to the binary/usr/local/bin/aws s3 lsaws-cli
Calling the API directlycurl https://sts.amazonaws.comaws-destination
A renamed binary that still names an AWS hostcloudctl fetch https://sts.amazonaws.comaws-destination
Reading the keyscat ~/.aws/credentialsaws-credential

One gap remains, and it's worth knowing about. A binary renamed to something that never mentions aws, and that never puts an AWS host in its command line, is outside the CLI and destination rules. The credential rule still denies reading the key files, and a session-level control covers what happens after a risky finding. For the broader set of secrets – SSH keys, .env files, and .npmrc – also publish the coding-agent.credential-access starter. For more information, see stopping coding agents reading credentials.

Which layers do you combine?

LayerStopsStarter policy
CLI name

aws s3 ls and its variants

coding-agent.aws-access
DestinationRequests to AWS API hosts from any toolcoding-agent.aws-access
Credential filesReads of AWS key files and other secrets

coding-agent.aws-access and coding-agent.credential-access

Session taint

Shell, web, MCP, and file writes after a prompt injection or credential finding earlier in the session

coding-agent.tainted-session

Arcjet runs every policy attached to a tool call in one round trip and applies the most restrictive decision, so publishing several single-purpose policies costs no extra latency. A denied AWS call doesn't taint the session by itself – taint flags come from screening tool results and from risk scoring of the session. For how that works, see what is session tainting?.

How do you block other cloud CLIs?

Edit the sets in the same policy. For Google Cloud, add gcloud and gsutil to cli_names and googleapis.com to the host set. For Azure, add az and hosts such as management.azure.com. Keep each cloud in the same policy, or publish one policy per cloud if different teams own them.

If some developers need AWS access from the agent – a platform team running infrastructure tasks, for example – scope the policy rather than deleting it. Keep the credential rule for everyone, and allow the CLI only for sessions that meet a condition you can check, such as a working directory under an infrastructure repository.

How do you roll out an AWS access policy?

  1. Publish the starter in dry run. Every Arcjet starter policy arrives in dry run, so it records what it would deny without blocking anything.
  2. Review a week of decisions. In the Arcjet Console, open the site's Activity and read the tool calls each rule would have denied. Expect some legitimate AWS use from platform teams.
  3. Adjust the sets. Remove hosts or tokens that caused false positives, or add conditions for teams that need access.
  4. Switch the rules to live. Change one rule at a time, starting with aws-credential, which has the fewest legitimate uses.

How does Arcjet keep coding agents away from AWS?

Arcjet enforces the coding-agent.aws-access policy on Claude Code, GitHub Copilot, Cursor, and OpenAI Codex through the hooks they already fire, with no agent to install on the laptop. The same destination and credential rules apply to agents you build with the Arcjet SDK. To require a person to approve risky commands instead of blocking them, see requiring human approval in Claude Code auto mode. For the full set of tools in this space, see coding agent security tools compared.

Frequently asked questions

How do you stop a coding agent from using AWS credentials?

Deny AWS access at the hook the agent fires before each tool call, with three layers in one policy: refuse the AWS CLI, refuse requests to AWS API hosts, and refuse reads of AWS credential files. Each layer covers a route the others miss.

Is blocking the AWS CLI enough?

No. An agent that can't run aws can call the API with curl or read ~/.aws/credentials. A policy over the command, the destinations, and the credential paths closes those routes. A renamed binary that never names an AWS host remains a known gap, which the credential rule and session tainting help contain.

Does this work in Claude Code auto mode?

Yes. The policy runs on every tool call whatever the permission mode, so a denied call is refused whether or not a person is approving actions. To also require human approval for risky tools, add a policy on the permission mode.

How do you block other cloud CLIs?

Edit the sets in the same policy: add gcloud and gsutil and googleapis.com for Google Cloud, or az and management.azure.com for Azure.

How do you roll out an AWS access policy?

Publish the starter in dry run, review a week of decisions in the Arcjet Console, adjust the sets for teams that need access, then switch the rules to live one at a time, starting with the credential rule.

AI runtime security in your code

Protect your AI agent workflows with Arcjet

Publish the AWS access starter policy in dry run and see which CLI calls, API requests, and key reads it would deny.