Secret Serialization Skill Specification
Intent
secret-serialization catches credentials that leak through generated serialization. The leak needs a holder (a credential field on a type with generated repr, asdict, model_dump, or property enumeration) and a sink (code that serializes whole objects or every kwarg into logs, spans, errors, caches, or responses). The two sides are usually added separately, and each looks safe in its own diff.
The skill reports either side alone and searches the whole repository for the other side, so a leak is caught whichever half lands second.
Scope
In scope:
- Credential fields on auto-serializing types in Python and JavaScript/TypeScript.
- Partial exclusions, where one generated path is blocked and another is not.
- New or changed wholesale serialization sinks and weakened sink filters.
Out of scope:
- Direct interpolation of a secret into a log or response.
security-reviewcovers that. - Hardcoded secret literals and committed
.envfiles. Secret scanners cover those. - Secrets-manager architecture advice with no changed field or sink.
- Missing tests as a standalone finding.
Users And Trigger Context
- Primary users: Warden runs in repositories that keep credentials on client, config, or settings objects, added with
remote = "getsentry/skills", and engineers reviewing such changes locally. - Should trigger for: "secret serialization", "credential in repr", "repr=False", "SecretStr", "token in logs", "secret in spans", "dataclass secret field", "kwargs stringified into telemetry", "asdict leaks", "JSON.stringify config".
- Should not trigger for: general security review, hardcoded-secret scanning, or dataclass refactors with no credential fields.
This is opt-in and specialized. It reports an unexcluded credential field before a sink is proven, which is too noisy for a default review of every repository.
Runtime Contract
- Required first actions: find changed credential fields and changed sinks in the diff, then read the full class or sink and enumerate generated paths.
- Required evidence per finding: the field or sink, the generated path or serialization call, the exclusion looked for, and where instances travel or what the sink receives, including code outside the diff.
- Constraints:
- Keep
allowed-toolsspace-delimited (Read Grep Glob). Warden drops comma-suffixed tokens such asRead,, which leaves the agent with onlyfindandlsand caps every finding at medium because no sink can be traced. - Do not downgrade because the other side predates the diff.
- Judge holders by generated paths only (
__repr__,asdict,model_dump,toJSON, enumeration). A field is fully excluded when its mechanisms together block all of them. - Treat raw attribute access (
vars,__dict__,pickle,default=vars) as a sink category. No field flag or wrapper defeats it, so it never makes a fully excluded holder partial. - Do not treat underscore naming, TypeScript
private,__slots__, or a custom__init__as blocking any path. PydanticField(exclude=True)orField(repr=False)alone is partial. - Keep language-specific tables and examples in
references/.
- Keep
Source And Evidence Model
See SOURCES.md. Do not store secrets, customer data, span payloads, or internal repository identifiers in this skill.
Reference Architecture
SKILL.md: threat model, boundary withsecurity-review, credential heuristics, exclusion rules, investigation steps, report table, severity, exclusions, fix guidance, finding format.references/python.md: generated paths and exclusions per Python type, sink table, report and do-not-report examples, regression test shape.references/javascript-typescript.md: the same for JavaScript and TypeScript.
Validation
- Run
uv run scripts/quick_validate.py ../secret-serializationfromskills/skill-writer. - Manual check against the incident shape: a diff that adds an unexcluded credential field to a dataclass, with a pre-existing
str(value)kwarg sink elsewhere in the repository, should produce a high finding naming both. - Precision checks:
SecretStr,#private, a lazily readproperty, a filtered sink, andField(repr=False, exclude=True)with only repr ormodel_dumpsinks should produce no finding. - Recall checks:
Field(exclude=True)alone on a model passed tostr()should produce a partial-exclusion finding. Any credential-bearing instance, including a fully excluded orSecretStrone, passed topickleordefault=varsshould produce a raw attribute sink finding.
Known Limitations
- Credential detection depends on names, types, and sources. Oddly named credential fields are missed unless their source is an obvious secret read.
- Behavior of third-party serializers is limited to what the references document.
- Go, Java, and Rust are not covered by references. The core contract still applies.
Maintenance Notes
- Update
SKILL.mdwhen exclusion rules, severity, or the report table change. - Update
references/when a new serializer or sink causes a real leak or a repeated false positive. - Record each incident or review outcome that changes a decision in
SOURCES.md.