Skip to content

Add configured input taint sources - #2

Merged
koriym merged 2 commits into
1.xfrom
codex/input-taint-sources
Jun 12, 2026
Merged

koriym merged 2 commits into
1.xfrom
codex/input-taint-sources

Conversation

@koriym

@koriym koriym commented Jun 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add a Psalm taint handler for configured root input classes.
  • Treat only configured root constructor #[Input] parameters as user-controlled input sources.
  • Keep downstream sanitized #[Input] objects and #[Inject] values untainted unless explicitly configured.
  • Add fixtures, integration tests, docs, and a runnable demo.

Tests

  • zsh -ic 'sphp85; composer tests'\n- zsh -ic 'sphp85; vendor/bin/psalm --config=demo/psalm.xml --taint-analysis --no-cache --no-progress' (expected taint errors)

Summary by CodeRabbit

  • New Features

    • Added taint-analysis support to mark configured constructor inputs as user-controlled.
  • Documentation

    • Expanded README with taint-analysis guide, config snippets, usage instructions, and a runnable demo.
  • Demos

    • Added a runnable demo demonstrating taint, sanitization, and injected-value behaviors.
  • Tests

    • Added integration and fixture tests covering promoted/assigned/inherited inputs, injected values, and sanitization re-entry.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7a6d8370-eca3-4453-8e07-f46e6ce1d102

📥 Commits

Reviewing files that changed from the base of the PR and between 2e0b7c3 and 142e8fa.

📒 Files selected for processing (5)
  • README.md
  • src/Handler/InputTaintHandler.php
  • tests/Fixture/Invalid/TaintedInheritedPromotedInput.php
  • tests/Fixture/psalm.xml
  • tests/PluginIntegrationTest.php
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/Fixture/psalm.xml
  • tests/PluginIntegrationTest.php
  • src/Handler/InputTaintHandler.php

📝 Walkthrough

Walkthrough

Introduces Psalm taint-analysis support: new InputTaintHandler that marks constructor parameters (and promoted properties) annotated with Ray\InputQuery\Attribute\Input as taint sources for configured root classes. Plugin wiring, fixtures, integration tests, demo code, and README documentation are included.

Changes

Input Taint Analysis Feature

Layer / File(s) Summary
InputTaintHandler implementation and plugin integration
src/Handler/InputTaintHandler.php, src/Plugin.php
Implements InputTaintHandler to return TaintKindGroup::ALL_INPUT for qualifying constructor variables and promoted properties annotated with #[Input]. Plugin parses <inputTaintSources> from psalm.xml, configures the handler, and registers the AddTaints hook.
Test fixture classes for taint scenarios
tests/Fixture/Invalid/TaintedAssignedInput.php, tests/Fixture/Invalid/TaintedPromotedInput.php, tests/Fixture/Invalid/TaintedPromotedInputObject.php, tests/Fixture/Invalid/TaintedInheritedPromotedInput.php, tests/Fixture/Valid/InjectedNotTainted.php, tests/Fixture/Valid/SanitizedReinput.php, tests/Fixture/psalm.xml
Adds fixtures demonstrating tainted promoted/assigned input properties, inherited promoted input, non-tainted injected values, and sanitized re-input that should not be re-tainted. Updates fixture psalm config to list taint source classes.
Integration test coverage for taint analysis
tests/PluginIntegrationTest.php
Adds taint-specific integration tests, separate cached taint results, assertion helpers (assertTaintIssue / assertNoTaintIssue), and updates the Psalm runner to accept extra CLI args and run --taint-analysis.
Demo code and user documentation
demo/InputTaintDemo.php, demo/README.md, demo/psalm.xml, README.md
Adds demo types (GreetingInput, SanitizedGreetingInput, TemplateRendererInterface), demo psalm config and README with expected results, and extends project README with taint-analysis documentation and demo run instructions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A taint-aware rabbit hops through input streams,
Marking constructor seeds with careful beams.
Fixtures and handlers line the testing trail,
Sanitized returns keep the warnings pale—
Demo and docs sing the security themes.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add configured input taint sources' directly matches the core feature introduced: a configurable Psalm taint handler for designated root input classes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/input-taint-sources

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
tests/PluginIntegrationTest.php (1)

230-249: ⚡ Quick win

Harden command argument handling in runPsalm.

$extraArgs is interpolated directly into a shell command. It’s currently controlled, but this is brittle and easy to misuse later. Prefer building escaped args explicitly.

Proposed refactor
-    /** `@return` list<array<string, mixed>> */
-    private static function runPsalm(string $extraArgs = ''): array
+    /** `@param` list<string> $extraArgs
+     *  `@return` list<array<string, mixed>>
+     */
+    private static function runPsalm(array $extraArgs = []): array
@@
-        $cmd = sprintf(
-            '%s --config=%s %s --output-format=json --no-cache --no-progress 2>/dev/null',
-            escapeshellarg($psalmBin),
-            escapeshellarg($config),
-            $extraArgs,
-        );
+        $escapedExtraArgs = array_map(static fn (string $arg): string => escapeshellarg($arg), $extraArgs);
+        $cmd = sprintf(
+            '%s --config=%s %s --output-format=json --no-cache --no-progress 2>/dev/null',
+            escapeshellarg($psalmBin),
+            escapeshellarg($config),
+            implode(' ', $escapedExtraArgs),
+        );

And at call site:

-        self::$cachedTaintIssues = self::runPsalm('--taint-analysis');
+        self::$cachedTaintIssues = self::runPsalm(['--taint-analysis']);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/PluginIntegrationTest.php` around lines 230 - 249, The runPsalm
function currently interpolates $extraArgs directly into $cmd which is brittle
and unsafe; change runPsalm to build a secure argument array instead of string
interpolation: split or accept $extraArgs as an array (update callers if
needed), call escapeshellarg on each element, merge with the base args (the
psalm binary and --config option built with escapeshellarg) and then join them
when composing $cmd (or better, use a proper Process/exec invocation that
accepts an array). Update references to runPsalm and the $extraArgs parameter so
callers pass an array of extra args or allow both string/array and normalize to
an escaped array internally; ensure $cmd uses the escaped tokens (references:
runPsalm, $extraArgs, $cmd, escapeshellarg).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 6-7: The current README sentence implies global behavior; update
the line mentioning `Ray\InputQuery\Attribute\Input` to say that Psalm taint
analysis treats constructor parameters annotated with
`Ray\InputQuery\Attribute\Input` as user-controlled input only for configured
root input classes (e.g., “...are treated as user-controlled input for
configured root input classes”), so the scope matches the detailed section later
and avoids implying global behavior.

In `@src/Handler/InputTaintHandler.php`:
- Around line 197-199: The code uses
$classStorage->declaring_property_ids[$propertyName] (a fully-qualified property
id like Fully\Qualified\ClassName::$prop) directly as $declaringClass and passes
it to $codebase->classlikes->getStorageFor(), which expects a class FQCN;
normalize the declaring property id first by extracting the class portion (strip
the trailing ::$propertyName part, and any leading backslash) before assigning
$declaringClass, then call getStorageFor($declaringClass) so inherited promoted
#[Input] properties resolve correctly; reference:
$classStorage->declaring_property_ids, $propertyName, $declaringClass, and
getStorageFor().

---

Nitpick comments:
In `@tests/PluginIntegrationTest.php`:
- Around line 230-249: The runPsalm function currently interpolates $extraArgs
directly into $cmd which is brittle and unsafe; change runPsalm to build a
secure argument array instead of string interpolation: split or accept
$extraArgs as an array (update callers if needed), call escapeshellarg on each
element, merge with the base args (the psalm binary and --config option built
with escapeshellarg) and then join them when composing $cmd (or better, use a
proper Process/exec invocation that accepts an array). Update references to
runPsalm and the $extraArgs parameter so callers pass an array of extra args or
allow both string/array and normalize to an escaped array internally; ensure
$cmd uses the escaped tokens (references: runPsalm, $extraArgs, $cmd,
escapeshellarg).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7647e42a-ff74-4219-a820-13e8a8cebe92

📥 Commits

Reviewing files that changed from the base of the PR and between 6a6b1eb and 2e0b7c3.

📒 Files selected for processing (13)
  • README.md
  • demo/InputTaintDemo.php
  • demo/README.md
  • demo/psalm.xml
  • src/Handler/InputTaintHandler.php
  • src/Plugin.php
  • tests/Fixture/Invalid/TaintedAssignedInput.php
  • tests/Fixture/Invalid/TaintedPromotedInput.php
  • tests/Fixture/Invalid/TaintedPromotedInputObject.php
  • tests/Fixture/Valid/InjectedNotTainted.php
  • tests/Fixture/Valid/SanitizedReinput.php
  • tests/Fixture/psalm.xml
  • tests/PluginIntegrationTest.php

Comment thread README.md Outdated
Comment thread src/Handler/InputTaintHandler.php Outdated
@koriym

koriym commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@koriym
koriym merged commit 38a2b96 into 1.x Jun 12, 2026
3 checks passed
@koriym
koriym deleted the codex/input-taint-sources branch June 12, 2026 17:31
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