PIPE_TO_SHELL decides that content crossed a pipe into an interpreter, and never asks whether that interpreter was given its own program to run.
https://github.com/blitzcrieg1/agentmetry/blob/master/apps/orchestrator/agentmetry/core/audit/detection/traits.py#L47
r"\b(curl|wget|iwr|invoke-webrequest|invoke-restmethod)\b[^|;&]*[|]\s*"
r"(iex|invoke-expression|python\d?|perl|ruby|node)\b",
python matches. python -c '<script>' matches identically. Those are opposite situations:
curl URL | python # stdin IS the program. A real cradle.
curl URL | python -c '<script>' # stdin is DATA for a fixed local program.
Same for perl -e, ruby -e, node -e, and python -m json.tool. In each case the inline flag supplies the program and the fetched bytes are input to it, not instructions.
Observed
Dispositioned false_positive today on correlation 716fbfb9-29ed-442a-bbaa-9f559faf582a:
curl -s "https://pypi.org/pypi/agentmetry/json" | python -c "
import json,sys
d=json.load(sys.stdin)
print('version:', ...)"
Reading this project's own PyPI release history. encoded-command-download fired critical, titled "Remote code fetched and executed", summarised as "A classic download cradle."
No downloaded code ran. json.load(sys.stdin) is the fetched bytes being parsed as data by a program that was already on the machine.
Why it is worth fixing rather than tolerating
Severity is the problem. Piping a URL into an interpreter is something developers do constantly to inspect JSON, and every one of those is currently a critical titled "Remote code fetched and executed". A critical that fires on curl | python -c is a critical an operator learns to close without reading, and the true cradle arrives wearing the same title.
The codebase already knows this distinction
core/audit/mitre.py reasons about exactly this, in the opposite direction. From interpreter_network_text:
Which view INTERPRETER_NETWORK reads depends on whether an inline-eval flag appears unmasked, because that is what makes a quoted body a program rather than a string.
So the concept is present and implemented. The two classifiers just do not share it. That is the same duplicate-classifier shape called out in #40 and in the encoded-command-download comments, one layer further out.
Direction
Do not match when the interpreter carries an inline-eval flag that supplies its own program (-c, -e, and -m for python).
One caveat that argues against suppressing outright: curl URL | python -c 'exec(sys.stdin.read())' really does execute fetched code, so an inline flag is not proof of safety. Downgrading rather than silencing keeps that case visible. A low here would match how piped_local is already handled: recorded for completeness, not drowning the criticals.
Blocked
traits.py and rules.py are ruleset fingerprint inputs. Editing either restarts the dogfood clock, currently 1 of 4. Hold with #44 and #49.
PIPE_TO_SHELLdecides that content crossed a pipe into an interpreter, and never asks whether that interpreter was given its own program to run.https://github.com/blitzcrieg1/agentmetry/blob/master/apps/orchestrator/agentmetry/core/audit/detection/traits.py#L47
pythonmatches.python -c '<script>'matches identically. Those are opposite situations:Same for
perl -e,ruby -e,node -e, andpython -m json.tool. In each case the inline flag supplies the program and the fetched bytes are input to it, not instructions.Observed
Dispositioned
false_positivetoday on correlation716fbfb9-29ed-442a-bbaa-9f559faf582a:Reading this project's own PyPI release history.
encoded-command-downloadfired critical, titled "Remote code fetched and executed", summarised as "A classic download cradle."No downloaded code ran.
json.load(sys.stdin)is the fetched bytes being parsed as data by a program that was already on the machine.Why it is worth fixing rather than tolerating
Severity is the problem. Piping a URL into an interpreter is something developers do constantly to inspect JSON, and every one of those is currently a critical titled "Remote code fetched and executed". A critical that fires on
curl | python -cis a critical an operator learns to close without reading, and the true cradle arrives wearing the same title.The codebase already knows this distinction
core/audit/mitre.pyreasons about exactly this, in the opposite direction. Frominterpreter_network_text:So the concept is present and implemented. The two classifiers just do not share it. That is the same duplicate-classifier shape called out in #40 and in the
encoded-command-downloadcomments, one layer further out.Direction
Do not match when the interpreter carries an inline-eval flag that supplies its own program (
-c,-e, and-mfor python).One caveat that argues against suppressing outright:
curl URL | python -c 'exec(sys.stdin.read())'really does execute fetched code, so an inline flag is not proof of safety. Downgrading rather than silencing keeps that case visible. Alowhere would match howpiped_localis already handled: recorded for completeness, not drowning the criticals.Blocked
traits.pyandrules.pyare ruleset fingerprint inputs. Editing either restarts the dogfood clock, currently 1 of 4. Hold with #44 and #49.