Relationships
↔ sibling #3030#18 @swamp/digitalocean/space-key stores secret in plaintext - should mark as sensitive
Opened by stack72 · 4/7/2026· GitHub #29
Description
The @swamp/digitalocean/space-key extension model does not mark the secret field as sensitive in its ResourceSchema. When a Spaces key is created via the DigitalOcean API, the response includes both access_key and secret. The secret field is not declared in the schema and passes through via .passthrough(), resulting in it being stored in plaintext in the .swamp/ data directory.
Steps to reproduce
- Create a space-key model:
swamp model create @swamp/digitalocean/space-key my-key --global-arg name=test-key - Run the create method:
swamp model method run my-key create - Inspect the persisted data in
.swamp/data/— thesecretfield is stored in plaintext
Expected behavior
The secret field should be:
- Explicitly declared in the
ResourceSchema(not relying on.passthrough()) - Marked with
z.meta({ sensitive: true })so it is auto-vaulted - Replaced with a
${{ vault.get(...) }}reference in the persisted data
Suggested fix
In space_key.ts, update the ResourceSchema to explicitly include the secret field with sensitive metadata:
const ResourceSchema = z.object({
name: z.string().optional(),
grants: z.array(z.object({
bucket: z.string().optional(),
permission: z.string().optional(),
})).optional(),
access_key: z.string().optional(),
secret: z.string().meta({ sensitive: true }).optional(),
created_at: z.string().optional(),
}).passthrough();Environment
- swamp version: 20260401.170720.0-sha.ac267ac9
- Extension: @swamp/digitalocean v2026.03.31.1
Automoved by swampadmin from GitHub issue #29
Open
No activity in this phase yet.
sntxrr commented 9/30/2026, 7:38:01 PM
Still reproduces on @swamp/digitalocean 2026.09.29.1 (space-key model 2026.06.08.1). Two corrections to the suggested fix, plus the same gap in other DO models.
1. The field is secret_key, not secret. DigitalOcean's create response is {"key": {name, access_key, secret_key, grants, created_at}} (key_create_response.yml). create unwraps it and writes the whole object as state (space_key.ts#L170-L180). ResourceSchema is .passthrough() and does not declare the field (#L55-L63). So a fix that declares secret would leave secret_key in plaintext. It should be:
secret_key: z.string().meta({ sensitive: true }).optional(),2. The fix belongs in codegen. space_key.ts is generated ("Do not edit manually"). DO codegen only marks the injected token input as sensitive (extensionModelGenerator.ts#L135, #L184). No output field is marked in any DO model. secret_key exists only on the create-response schema, so the generator never sees it when building ResourceSchema. Suggested: merge create-response-only properties into ResourceSchema, and mark known secret fields sensitive (a name list, or a per-resource override map).
The same gap in other DO models (declared in ResourceSchema, returned by the DO API, not marked sensitive):
database_user.ts:password(L71),access_key(L73)database_cluster.ts:passwordand credential-bearinguriinconnection,private_connection,standby_connection,standby_private_connection,ui_connection,schema_registry_connection(L223-L278)database_replica.ts:connection/private_connectionpasswordanduri(L113-L128)function_namespace.ts:key(the namespace auth key) (L77)database_logsink.ts:config.key,config.datadog_api_key(L83, L90)
Behaviour change to call out: once these fields are marked, create/get/sync need a configured vault at write time (#566), so users without one will start failing. Values already persisted by earlier versions stay in older data versions. Keys minted before the fix should be rotated, and the fix alone doesn't do that.
Suggested tests:
- Codegen snapshot: the space-key
ResourceSchemacontainssecret_key: z.string().meta({ sensitive: true }). - Model level: a fake
fetchreturns DO's documented example (secret_key: "DOSECRETKEYEXAMPLE") fromPOST /v2/spaces/keys. Runcreate, then assert that the persistedstatebytes do not containDOSECRETKEYEXAMPLEand do contain avault.get(reference. There are no DO model tests today; the GCP/AWS_test.tsmodels are the nearest pattern.
sntxrr commented 9/30/2026, 7:38:22 PM
Correction: the CLI redactor treated the pinned commit SHAs in the links above as secrets, so they show as [REDACTED-SECRET-n]. Replace them with:
[REDACTED-SECRET-1](DO openapi):569464c1b5844f9f28ee982e5620e49f6a7887d8[REDACTED-SECRET-2](swamp-extensions):0a0f205485061a15d458dbc378f7aaa064f40145
Sign in to post a ripple.