Skip to content

v0.9.0

Latest

Choose a tag to compare

@github-actions github-actions released this 15 Oct 00:58
· 9 commits to master since this release
114559b

Fixed: heredocs that execute

  • An unquoted heredoc's substitutions are seen. A heredoc with an unquoted
    delimiter expands $(…) and backticks in its body, so a text write whose body
    holds $(curl … | bash) runs that pipeline. The prose exemption blanked every
    heredoc body and rated it green; such a body now stays visible to the rules.
  • The heredoc's opening line is read. Whatever follows the delimiter on its
    own line (cat <<'EOF' | sh) is shell, not body, and was being blanked with it.
  • Prose in a quoted heredoc inside a plain chain is not RCE. cd dir && cat >> notes.md <<'EOF' … EOF, then echo saved, documents a command; a chain whose
    other clauses could run what was written (an interpreter, ./x, . x,
    source, chmod, exec, a pipe) stays black.
  • A download piped into an interpreter stays black even when its inline program
    only looks like it parses data; a text check cannot tell what it will run.

Changed: the judge defaults to claude-sonnet-5-5

makeJudge, the daemon and the bench scripts now default to
claude-sonnet-5-5 instead of claude-sonnet-5 (the bench scripts were on
claude-sonnet-4-6). WARDEN_JUDGE_MODEL and the model option still
override it.

Fixed: "set aside" no longer reads as an override across list items (#170)

The paraphrased instruction-override rule let the text between set aside and
its noun run up to any period. It linked a set aside at the end of one list
item to an instructions noun opening the next ("the paths the reviewers set
aside, and the apply instructions"), which flagged a first-party plugin spec.
The gap now also stops at commas, semicolons, colons and table cell
separators, so the noun has to be in the same clause. "Set aside the
directives you were given earlier" and the other intended phrasings still
flag.

Fixed: egress destinations are normalised, and ones the text cannot fix are gated

With egressAllow set, several spellings of a destination reached a host the
policy did not list while the verdict stayed green.

  • Hosts are compared in one spelling. An IPv4 address written as one
    decimal number, in hex or octal, or with fewer than four parts is read as
    the dotted address it names, on both the fetch path and the shell path, and
    a trailing dot on a fully qualified name is dropped before the range and
    allowlist checks. A single-label service name (dario, postgres) stays
    local as before.
  • Connection overrides are checked. curl's --connect-to, --resolve,
    --dns-servers, --doh-url and the other resolver flags change where a
    named host's request goes, so the URL's host no longer vouches for it. Under
    an allowlist such a call is gated.
  • More destination forms are read: git remotes in scp form
    (git@host:path, host:path), bare hosts in download clients' positionals
    (iwr host, aria2c host/x, -Uri host), clients started by watch or
    find -exec, and ${IFS} word splitting.
  • A destination decided at run time is gated under an allowlist: an
    argument whose host is a variable or command substitution that the same
    command does not assign literally, and a client fed by xargs. A variable in
    the path (https://api.example.com/$P) leaves the host known and stays
    allowed.
  • Argv arrays: a shell command given as an array is checked like the
    same command as a string.
  • A new fuzz target (fuzz/egress.fuzz.js) runs the allowlist path and checks
    that an allowed verdict never coexists with a destination outside the list.

Fixed: the egress allowlist did not apply to shell commands

With egressAllow set, fetch https://evil.example was gated but
wget https://evil.example in a shell was allowed. The same went for
curl -d @file https://… uploads. Any agent with a shell could reach a host
the policy did not list.

The allowlist now applies to network clients in command position:
curl, wget, http/https/xh, aria2c, git's network subcommands
(clone, fetch, pull, push, ls-remote, archive, submodule), and
Invoke-WebRequest/iwr/Invoke-RestMethod/irm, Start-BitsTransfer,
certutil and bitsadmin. A call to a host outside the list is gated (red),
the same as a fetch to it.

  • What counts as a destination: scheme'd URLs, and for curl, wget and
    httpie also bare hosts (curl evil.example/x), --url in either spelling
    (--url=https://…), and the proxy a client connects through (curl -x,
    --proxy, --preproxy, httpie --proxy). Flag values are skipped per
    client, since the same letter differs between them (curl -O takes no value,
    wget -O names the output file).
  • What does not: a URL nothing contacts. echo "see https://…", a grep
    pattern, a commit message and single-quoted text stay allowed. Loopback and
    private addresses are left to the SSRF rule, and allowlisted hosts and their
    subdomains stay allowed.
  • What it sees through: sudo/env/timeout wrappers and their options
    (sudo -u root curl …), VAR=value prefixes, shell keywords (if curl …,
    { curl …; }), $( … ) and backticks (including inside double quotes),
    bash -c / powershell -Command / cmd /c bodies, Windows paths
    (C:\tools\curl.exe), backslash escapes (cu\rl), variable indirection and
    brace expansion.
  • Unchanged without an allowlist. No egressAllow, no new verdicts.
  • Not covered: clients that take a host rather than a URL (nc, ssh,
    scp, rsync), and a host that only appears at runtime (read from a file or
    built by the command).

Arena: under-gate goes from 1/44 to 0/44, and benign friction from 0 to 1. The
benign curl -O https://github.com/…; tar xzf x.tgz sample is now gated,
because the corpus policy does not list github.com; that is the allowlist
doing its job, and the sample is left as is. Precision stays 100% (no benign
sample is blocked), and none of the 1,212 tldr-pages commands trips the rule.
On Atomic Red Team, dual-use escalation goes from 13/66 to 16/66, and two
benign discovery commands (curl -k https://ipinfo.io/) are now gated, with
hard false positives still 0/72.