Taint tracking
What counts as a sensitive source, which of the three levels it gets, and what the two data-flow defaults changed.
grith does not read file contents, so it cannot follow the bytes of a credential through a program. What it does instead is record that a session touched a sensitive source, and score later outbound calls in the light of that.
That is the honest description of taint in grith: provenance at the path level, not dataflow at the byte level.
The three levels
| Level | Sources | Arms sticky containment |
|---|---|---|
| High | .ssh, id_rsa, id_ed25519, private_key, shadow | Yes |
| Medium | .env, .aws, credentials, secrets, .gnupg, .kube/config | No |
| Low | Anything else matching a configured source | No |
Medium deliberately does not arm sticky containment. Agent tools read their own credential files at startup, and locking a session down for that would make containment mean nothing.
The configured sources default to .env, .env.local, credentials.json,
secrets.yaml, id_rsa and id_ed25519. Network and shell are high-risk sinks; file
writes are medium-risk.
Scope and lifetime
Taint is registered against a scope key: the conversation id when there is one, otherwise the session's own uuid. There is no cross-session bleed - a taint registered in one session cannot influence scoring in another.
Registry entries live for one hour and are evicted on the next access.
What actually fires
Two defaults, both on, decide when taint contributes rather than just existing.
taint_data_flow_only = true. The old behaviour - any taint in the session adds 3.0
to every subsequent spawn and connect - is off. Taint now needs to be connected to the
call, not merely present in the session.
taint_outbound_requires_data_flow = true. An outbound-capable binary running under
taint - git push, aws s3 ls, npm publish - fires only when the spawn actually
references the tainted data. Running git push in a session that read a .env file an
hour ago is not, on its own, evidence of anything.
Together these are why a v0.3.1 session is much quieter than the same workload used to be, and they are the two settings to look at first if you expected taint to fire and it did not.
A matched spawn scores +3.0. Direct taint matches score 3.0, 4.0 or 5.0 by level, and the data-flow spawn variants score 1.5.
Where taint reaches beyond its own filter
Taint is filter 15, but two other things read it.
The env-exfiltration-risk meta-rule adds +5.0 when a
taint sourced from an env file is followed by an HTTP request - the concrete shape of
"read .env, post it somewhere". exfil-shape-under-session-taint adds +2.0 when an
exfiltration-shaped outbound call happens while the session carries taint.
The windowed session-containment filter also arms on any call carrying taint, which is how a single sensitive read raises the price of network and shell for the following ten minutes.
What it catches, and what it does not
Catches: read a credential file, then try to reach a destination that is not already trusted. Read a key, then spawn something that can talk to the network with it. Read a secret, then open a shell.
Does not catch: a credential the model has memorised and retypes as a literal - there
is no read to taint. A credential summarised into prose and posted as text; that is what
dlp-gate and secret-scan are for. Data leaving through a channel grith does not trap
at the syscall boundary.
Taint is one signal in a composite, and it is at its strongest in combination - taint plus an unknown destination plus a POST body is what turns a 3.0 into a denial.