Skip to content

Semantic log records credential-shaped Input properties in plaintext (becoming_open.prop); honor #[\SensitiveParameter] #79

Description

@koriym

Summary

Be\Framework\SemanticLog\Logger::openChain() records every public property of the Input object into the becoming_open context's prop payload via ObjectPropertyExtractor (get_object_vars() + ReflectionClass). Nothing filters it, so a credential carried as a public promoted property — the normal shape of a Be Input — lands in the semantic log in plaintext.

#[\SensitiveParameter] on those constructor parameters does not help: it only redacts PHP stack traces, not a property dump.

Reproduction (BeMart, 1.x)

BeMart wires bear/event-sourcing's SemanticLogInvoker (which redacts request params by default) and Be Framework's becoming logger into the same observation log. Driving one admin login through the observe context:

php bin/observe.php post '/admin/login?loginId=test-admin&password=local-dev-admin-password&csrfToken=...'
php .claude/skills/bear-observe/harness/tree.php

renders:

└── resource_request uri=page://self/admin/login method=POST params={"loginId":"test-admin","password":"[FILTERED]",...} replayable=false → resource_response code=200
    └── becoming_open input=MyVendor\BeMart\Be\Input\AdminLoginInput prop={"loginId":"test-admin","password":"local-dev-admin-password"} → becoming_close exit=success ...

The resource layer redacted the password to [FILTERED]; one level down, becoming_open.prop carries the same value verbatim. grep -c local-dev-admin-password var/log/.../observe/latest.json → 1, and it is in the becoming_open context only.

The same applies to every credential-shaped public promoted property in a Be Input: ResetPasswordInput::$resetKey/$password, SetTwoFactorAuthInput::$authKey, ActivateCustomerInput::$secretKey, ChangeAdminPasswordInput::$currentPassword/$changePasswordFirst/$changePasswordSecond. BeMart already annotates all of these with #[\SensitiveParameter].

Where

  • src/SemanticLog/Logger.php — openChain() builds BecomingOpenContext(prop: $this->extractProperties($input)). BeingCloseContext / BeingFinalCloseContext carry a prop the same way.
  • src/SemanticLog/ObjectPropertyExtractor.php — extract() returns every storable public property; no redaction hook.

Proposal

Have ObjectPropertyExtractor honor #[\SensitiveParameter]: it already reflects the class, so for each promoted property check whether the matching constructor parameter carries the attribute and, if so, store a fixed sentinel (e.g. [FILTERED]) instead of the value. Same for the BeingClose / BeingFinalClose prop payloads, since a Final/Being can re-expose the same value as a public property (BeMart's TwoFactorAuthConfigured::$authKey does).

This reuses an attribute applications already apply for stack-trace safety, so an Input that is correctly annotated is protected in the semantic log with no extra configuration. Reusing the attribute is preferable to a name-based filter here because the Input is the authoritative declaration of what each field means — the application has already said which ones are secrets.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions