Skip to content

feat: mask what leaves the machine with mnfst/http-redact - #50

Open
guillaumegay13 wants to merge 3 commits into
mainfrom
feat/http-redact
Open

guillaumegay13 wants to merge 3 commits into
mainfrom
feat/http-redact

Conversation

@guillaumegay13

Copy link
Copy Markdown
Contributor

Closes the client-side part of #40 (secrets in outbound calls) and implements mnfst/app#191 §4 for PHP.

What changes

Credential values are now masked by mnfst/http-redact (threat model in its README), replaced in place by REDACTED, before anything leaves the process:

Before Now
Tracked call URL path sent as is (webhook secrets included) path secrets masked (Slack, Discord, Telegram, Teams, random tokens)
Heal: query, headers credential names only names + vendor key shapes, JWTs, cookies (each value), Bearer REDACTED keeps its scheme
Heal: body top-level credential keys left out; nested ones sent in clear masked at any depth, plus keys/tokens in free text
Heal: error response, retry error report sent as is masked (a provider echoing the key is covered)
OAuth code / code_verifier / state sent masked

src/Masked.php is the only caller of the library.

Retry restore (Replay)

  • A value the heal left as sent (REDACTED, or a string masked partway) takes the original back; a masked value it left out of an object is put back (same rule as query credentials); a value an operation changed stays.
  • Lists, path segments and repeated query names are restored only while their shape is unchanged; otherwise the retry is refused, so a secret can never move to another element.
  • A mask at any address where the original holds none refuses the retry: REDACTED never reaches the API.

Merge and the SDK's own secret lists are removed.

Latency

  • Successful calls: redactUrl on the already-stripped URL at record time, ~2.5 µs median (the record step itself costs ~21 µs; masking must happen before the spool file on disk). First call boots the engine once (~2.7 ms, ~0.7 MB).
  • Heal failures only: ~15 µs median, in front of the heal round trip.

Before merging

  • Blocked on publishing mnfst/http-redact to Packagist. composer.json points at a local path repository (../http-redact/packages/php); replace it with "mnfst/http-redact": "^1.0" from Packagist.
  • Update backend/src/v1/__fixtures__/mnfst-php.wire.jsonl in mnfst/app from php scripts/wire-fixtures.php (checked locally: sdk-wire.spec.ts passes on the new bytes).

Tests

331 pass: new MaskedTest and ReplayRestoreTest (restore, shape changes, mask never sent, userinfo with @), old masking tests ported. A security review (probes on every outbound channel and on restore placement) found no unmasked channel; its three Important findings are fixed in this branch.

The integration suite has a pre-existing intermittent failure (one random test, roughly one run in three, e.g. TrackingTest::testOutOfRangeRecordsAreNeverWritten); main shows the same rate. Not addressed here.

Credential values are replaced in place by REDACTED wherever they sit (query, path,
headers, cookies, nested body fields, vendor keys and tokens in free text, the error
response) instead of the SDK's own name lists, which withheld only top-level body keys
and never looked at paths or values.

- Masked is the one caller of the library: heal payload, retry error report, failure
  messages, onHeal URL. Tracked URLs mask their path at record time (~2.5 µs), before
  the spool file.
- Replay restores before a retry: a value the heal left as sent takes the original back,
  a masked value it left out of an object is put back, a changed value stays. Lists,
  paths and repeated query names are restored only while their shape is unchanged, and a
  mask at any address the original does not hold refuses the retry.
- Merge and the local secret lists are removed. Docs and wire fixtures updated.

Refs #40, mnfst/app#191
@guillaumegay13
guillaumegay13 marked this pull request as ready for review September 27, 2026 05:33
Fixing an API call does not need them, and they carry sessions. The retry keeps the app's
own headers, cookies included.
The masker now masks a response by shape only (mnfst/http-redact b18c4e0): "code":
"invalid_value" was turning into REDACTED, hiding what a fix is found by.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant