Skip to content

Keep input out of data class constructor stack frames - #16

Open
yuriizee wants to merge 1 commit into
mainfrom
claude/desktop-session-connection-smngnp
Open

yuriizee wants to merge 1 commit into
mainfrom
claude/desktop-session-connection-smngnp

Conversation

@yuriizee

@yuriizee yuriizee commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Every library entry point, compiled hydrator, cast and pipe marks its input #[\SensitiveParameter]. The one frame it cannot mark is the data class constructor: that is application code. When the constructor rejects its arguments, the exception keeps the raw arguments (e.g. a card number) in that frame of its trace. This happens with a TypeError for a wrong input type, and with an exception from the constructor's own validation. Error trackers that collect frame arguments (Sentry, Flare, Bugsnag) pick them up when zend.exception_ignore_args=Off. This holds even for #[Sensitive] fields.

PCI DSS v4.0.1 3.3.1 / 3.5.1 / 10.3.

Change

  • Redactor::scrubConstructorArguments() wraps the arguments of the data class's __construct frame in SensitiveParameterValue (as PHP does for a sensitive parameter). It edits the trace in place, through the whole getPrevious() chain. Exception type, message, file, line and previous chain are unchanged.
  • The compiled hydrator (from() and everything built on it), fromLazy() and with() route constructor exceptions through it. The collecting path (fromResult()) already swallows the exception and keeps only its message.
  • An inherited constructor is matched by its declaring class.

Tests

  • StackTraceRedactionTest gets 5 new paths:
    • constructor TypeError
    • constructor's own exception with a previous
    • inherited constructor with extra properties
    • fromLazy
    • with
  • Two new tests check that the type, message, file, previous chain and the wrapped values stay the same, and that a TypeError stays a TypeError.
  • 6 of the new tests fail without the fix.
  • Full suite: 916 tests green. PHPStan level 6: no errors. Pint: clean.
  • 100% coverage was not checked locally (pcov unavailable); CI runs it.

Upgrade note

File-cached hydrators are not regenerated automatically: run sdo:clear or sdo:warm after upgrading (noted in the CHANGELOG).

The library hands raw input to a constructor it does not own, so that
frame cannot carry #[\SensitiveParameter]. A TypeError for a wrong input
type, or an exception thrown by the constructor's own validation, kept
the arguments (e.g. a card number) in its trace, where error trackers
that collect frame arguments would pick them up.

from(), fromLazy() and with() now wrap that frame's arguments in
SensitiveParameterValue, in place, the way PHP does for a sensitive
parameter. The exception type, message, line and previous chain are
unchanged.

This branch has not been deployed

No deployments
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