wave4: psalm-0001 flagship patch + linter/CI/config scan survey
psalm-0001: FileFilter.allowsClass runs in_array() on every class the analyzer visits. For C classes and F filter entries, per-run cost is O(C*F). Fix: lazy array_fill_keys hash set; O(1) probe per class. Bench: 336x at C=F=5000. Patch + ticket + bench + intel brief ship. wave4-linter-ci-survey.md: consolidated report on 40 projects scanned across linters (eslint, biome, prettier, pylint, ruff, black, rubocop, shellcheck, stylelint, sqlfluff, phpstan, PHP_CodeSniffer, psalm, rustfmt, golangci-lint, scalafmt, hadolint, yamllint, markdownlint, ktlint, detekt), CI runners (act, buildkite-agent, tektoncd/pipeline, concourse, woodpecker), config management (aws-cdk, cdk8s, kustomize), and build tools (rollup, parcel, vite, turborepo, nx, lerna, swc, babel, gulp). Clean scans (zero HIGH+ findings): hadolint, shellcheck, gulp. Document includes per-target finding counts and 7 triage follow-ups for future waves (PHP_CodeSniffer ReDoS, ktlint spacing rule, pylint MSG_ORDER.index, black pgen2 dfa, golangci-lint migrate, tektoncd forbidden-env scan, aws-cdk region-info).
This commit is contained in:
parent
788514bcf7
commit
bb81a1a3a0
9 changed files with 384 additions and 1 deletions
|
|
@ -0,0 +1,53 @@
|
|||
# UNDF: UNDF-2026-000001296
|
||||
# UNDF: UNDF-2026-XXXXXXXXX
|
||||
# CWE-407: Algorithmic Complexity -- O(C*F) -> O(C+F) in FileFilter::allowsClass
|
||||
#
|
||||
# Defect: allowsClass runs in_array(strtolower($cls), $this->fq_classlike_names, true)
|
||||
# on every class the analyzer visits. For C classes and F filter entries,
|
||||
# per-analysis cost is O(C*F). Psalm is already CPU-bound; this compounds
|
||||
# the scan time on large monorepos with large filter lists.
|
||||
#
|
||||
# Fix: Lazy-init a lowercase hash set ($fq_classlike_names_set) and probe via
|
||||
# isset() for O(1) per class. Built once per FileFilter instance and reused.
|
||||
#
|
||||
# Complexity gate (tests/test-psalm-cwe407.py):
|
||||
# C=F=1000: fixed must complete in <5ms
|
||||
# k-scaling 5x: time ratio must be <17.5x
|
||||
--- a/src/Psalm/Config/FileFilter.php
|
||||
+++ b/src/Psalm/Config/FileFilter.php
|
||||
@@ -74,6 +74,12 @@ class FileFilter
|
||||
*/
|
||||
protected array $fq_classlike_names = [];
|
||||
|
||||
+ /**
|
||||
+ * Lazy O(1) lookup set keyed by lowercased class name; rebuilt when
|
||||
+ * fq_classlike_names is set / mutated.
|
||||
+ */
|
||||
+ private ?array $fq_classlike_names_set = null;
|
||||
+
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
@@ -570,6 +576,8 @@ class FileFilter
|
||||
return true;
|
||||
}
|
||||
|
||||
public function allowsClass(string $fq_classlike_name): bool
|
||||
{
|
||||
if ($this->fq_classlike_patterns) {
|
||||
@@ -580,7 +588,14 @@ class FileFilter
|
||||
}
|
||||
}
|
||||
|
||||
- return in_array(strtolower($fq_classlike_name), $this->fq_classlike_names, true);
|
||||
+ if ($this->fq_classlike_names_set === null) {
|
||||
+ $this->fq_classlike_names_set = array_fill_keys(
|
||||
+ array_map('strtolower', $this->fq_classlike_names),
|
||||
+ true,
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ return isset($this->fq_classlike_names_set[strtolower($fq_classlike_name)]);
|
||||
}
|
||||
|
||||
public function allowsMethod(string $method_id): bool
|
||||
Loading…
Add table
Add a link
Reference in a new issue