Skip to content

test: setattr/delattr hardening - #7291

Open
fracasula wants to merge 8 commits into
masterfrom
setattr-delattr-hardening-contract
Open

test: setattr/delattr hardening#7291
fracasula wants to merge 8 commits into
masterfrom
setattr-delattr-hardening-contract

Conversation

@fracasula

@fracasula fracasula commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Description

Contract tests for this PR, which routes every sandbox write (obj.attr = v, del obj.attr, setattr(), delattr()) through a new write guard.

WARNING: The tests should pass once we have a new PYT release.

They live in integration_test/pytransformer_contract/security_hardening_contract_test.go and must merge together with the pytransformer PR.

What is pinned

The write guard defaults to shadow (record and allow), so both sides of the switch are covered across the container boundary:

  • TestWriteGuardShadowContract — at the shipping default the write lands, the transformation runs to completion, transformer_write_guard_unsafe_total{kind} records it, and the alerted-on security series stays at zero.
  • TestWriteGuardEnforceContract / TestWriteGuardDelattrContract — under SANDBOX_WRITE_GUARD_ENFORCE=true a write to a module, a foreign class, or a reserved name is refused and lands on transformer_security_violations_total.
  • TestWriteGuardLibraryOwnershipContract — a transformation writing to its own library records nothing, the direction that decides whether the counter is readable at all.
  • TestWriteGuardOwnershipRegistryContract — ownership is keyed by identity, so an unhashable class is not over-blocked and a forged __hash__/__eq__ cannot launder ownership.
  • TestWriteGuardCounterUnitContract — the counter counts refused writes, not failed transformations.
  • TestTestFlowSecurityMetricsContract / TestTestFlowEnforceContract/test and /testRun replay their security signals onto the transformer_test_flow_* twins, never the production series.
  • TestGetattrAndModuleGuardContract — the getattr value guard and the raw __getattribute__ / __getattr__ block.
  • TestFactoryReprRiskContract — the new shadow-only telemetry: a transformation that both builds a factory-made class (namedtuple, three-argument type()) and renders text is flagged on transformer_factory_class_repr_risk_total, while a factory class that is never rendered stays quiet. It sizes the follow-up __module__ work and changes no behaviour.

The write-guard, preview-twin and factory-telemetry tests are candidate-only: the guard and those metrics are new, so no released baseline image exports them to compare against.

Linear Ticket

< Fixes PIPE-3322 >

Security

  • The code changed/added as part of this pull request won't create any security issues with how the software is being used.

Test-only change. It locks the contract that the pytransformer sandbox blocks these escapes, that the write guard refuses non-user-owned writes under enforce while changing nothing at the shadow default, and that preview traffic never reaches the production security series.

…enforce blocks

getattr and import guards now enforce unconditionally in the candidate
rudder-pytransformer image, so SANDBOX_GETATTR_GUARD_ENFORCE and
SANDBOX_IMPORT_GUARD_ENFORCE no longer exist. Drop both env args from the
candidate startup in TestGetattrAndModuleGuardContract and update the two
inline comments that referenced them; the existing assertBlockedOnCandidate
subtests are unchanged since the blocks now happen by default.

Add TestWriteGuardEnforceContract, a candidate-only sibling test that starts
a second candidate with SANDBOX_WRITE_GUARD_ENFORCE=true and asserts a
module write (json.loads = None) and a foreign-class write
(setattr(requests.exceptions.HTTPError, ...)) are both blocked with a clean
400 mentioning "not allowed" and "non-user-owned". Both targets are reached
by plain top-level import + attribute read, deliberately avoiding
socket.socket, which the getattr guard already blocks at the read step
(requests.utils.socket is not on the value guard's reachability allow-list)
and would therefore exercise the wrong guard's error message.

assertBlockedOnCandidate is promoted from a closure to a package-level
helper (parameterized by *bcTestEnv) so both tests share one assertion
definition.
@fracasula
fracasula force-pushed the setattr-delattr-hardening-contract branch from dea89a5 to d75af2d Compare August 19, 2026 08:42
@fracasula
fracasula marked this pull request as ready for review August 26, 2026 11:25
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.

3 participants