◉ANTIGRAVITY LABJP
Articles/Antigravity Basics
◉ Antigravity Basics/2026-07-27Advanced

Who Approved the Right Side of &&? Splitting Shell Commands Before Matching Allow Rules

The approval dialog showed part of what actually ran. Here is a harness that splits compound shell commands without breaking quotes or command substitution, matches allow rules per segment, and the numbers from running it over 45 real commands.

Antigravity CLI41permissions4security22shell4automation94

✦ Premium Article

The approval dialog read python3 scripts/redirect_integrity.py . --fix. My own gate script. I approved it without hesitating.

Days later I noticed there were no .bak files left in the repository and went back through the history. What actually ran was python3 scripts/redirect_integrity.py . --fix && rm -f content/*.bak. I had approved the first half. The deletion went through without me ever reading it.

Nothing was lost that I had not intended to delete anyway. What stayed with me was the gap itself: what I approved and what executed were not the same string.

Working as an indie developer, the approval dialog has exactly one reader. Whatever I wave through runs unopposed.

Reading the Antigravity changelog, I found that v1.1.7 addresses the display side — when only part of a compound shell command requires approval, the prompt now shows the full command. The display gets fixed. The question I could not answer was whether my own allow rules were written against the full command in the first place.

The dialog showed a fragment of what would run

Two separate layers are at work here.

The first is presentation: what the approval prompt puts in front of you. That is the part v1.1.7 improves.

The second is adjudication: the unit at which permit-or-prompt is decided for a command joined with && or ;. That lives in my configuration, and no upstream release fixes it for me.

My allow rules looked like this.

Bash(git add:*)
Bash(git commit:*)
Bash(python3:*)
Bash(npm run:*)

Read plainly, that means "permit commands beginning with python3." And python3 ... --fix && rm -f content/*.bak does begin with python3.

Matching on the leading token lets anything on the right side of && ride along. The rules were not underpowered; the unit of matching was wrong.

Questioning the head-token assumption

The fix is to move adjudication from "one command" to "one segment," where a segment is an individual command the shell will execute in sequence.

a && b || c ; d | e is five segments. git commit -m "a && b" is one, because the operator sits inside quotes. Getting this wrong breaks in both directions: split inside quotes and legitimate commits stop; fail to split outside them and a delete slips past.

So the splitter came first, as its own module. Anything built on a splitter that is wrong is decoration.

One clarification before the code: this is not a reimplementation of how Antigravity CLI adjudicates internally. It is a bench harness for checking whether my own allow rules behave as intended against the full command text. Whatever the tool decides upstream, the shape of my own configuration is something I can measure myself.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A quote-aware, substitution-aware command splitter with a five-case test suite you can drop into your own tooling
✦Measured difference between head-token matching and segment matching over the same command set, and how four extra rules restore the approval rate while still blocking the dangerous cases
✦Why command substitution belongs in a fail-closed bucket, and where splitting genuinely fails to help
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◉ Antigravity2026-08-10
The Line I Thought Matched Nothing Was Approving Everything: Auditing Empty Allowlist Entries
Allowlist entries that decompose to zero command words matched every command and silently auto-approved it. Here is how I scanned my own config, separated the hole the fix closed from the one it did not, and rewrote matching to a prefix-token comparison.
◉ Antigravity2026-08-20
Count What Writes Outside Your Workspace Before You Upgrade to CLI 1.1.14
Antigravity CLI 1.1.14 makes paths outside the workspace read-only by default. If your source assets and delivery targets live outside the repository, it is worth knowing what breaks before you upgrade. Here is a script that inventories your write targets, and the two ways it quietly undercounts them.
◉ Antigravity2026-10-26
When the Agent Stops Answering From Your Attachment After /compact, One Canary File Tells You Why
After compacting a long Antigravity CLI session, replies stopped reflecting the file you attached. A one-line canary file separates a version issue from how you attached it.