wave17 survey: 6 clean-scan additions (flask, black, mypy, sanic, vite, prettier)
10 PHP fw / Python tooling / JS bundler targets scanned. Honor roll cumulative: 86 projects. Two borderline candidates documented for type-aware follow-up (symfony PropertyAccessor::writeCollection O(P*C), pyright callHierarchyProvider O(C^2)) — both real but need type-aware helpers, not single-line set hoists.
This commit is contained in:
parent
878876c7af
commit
e55ba1608e
1 changed files with 116 additions and 0 deletions
116
whitepaper/outreach/wave17-php-python-bundler-survey.md
Normal file
116
whitepaper/outreach/wave17-php-python-bundler-survey.md
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
# Wave 17 — PHP Frameworks, Python Tooling, JS Bundlers
|
||||
|
||||
**Survey date:** 2026-04-25
|
||||
**Tool:** unmoad (9 active MOAD detectors, HIGH+ severity filter)
|
||||
**Scope:** 10 projects across PHP frameworks (laravel, symfony), Python web frameworks (flask, sanic), Python tooling (ruff, black, mypy, pyright), and JS bundler/formatter (vite, prettier).
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Wave 17 totals 1,370 HIGH+ findings across 10 projects. **Six new clean-scan honor roll entries** (flask, black, mypy, sanic, vite, prettier). Honor roll cumulative: **86 projects** across waves 3-17.
|
||||
|
||||
**No flagship CWE-407 patches ship this pass.** Two borderline-real candidates documented (symfony PropertyAccessor::writeCollection O(P × C) collection diff, pyright CallHierarchyProvider O(C²) outgoing-call dedup) but both require careful type-handling work beyond a single set-hoist. Logged for follow-up rather than shipped half-baked.
|
||||
|
||||
## Clean-scan honor roll — 6 new entries
|
||||
|
||||
| Project | Lang | Role | Notes |
|
||||
|---------|------|------|-------|
|
||||
| **flask** | Python | Microframework | 6 findings: `_cv_app: ContextVar[AppContext]` is intentional Flask app-context plumbing (the framework's central design); `cache.set` and `secrets.token_hex()` references are docs samples. **clean** |
|
||||
| **black** | Python | Code formatter | 13 findings: `pgen.py:54/56` `dfa.index()` for pgen2 grammar build (one-time at startup); `comments.py:143` `remainder.count("\n")` is single-char count not list-element scan; `ipynb_magics.py` mask matching bounded. **clean** |
|
||||
| **mypy** | Python | Static type checker | 36 findings: `mypyc/test-data/` test fixtures, `mypyc/lib-rt` strncmp on fixed function names, `imaplib.pyi` / `smtplib.pyi` `cram_md5` API names (typeshed reference, not implementation). **clean** |
|
||||
| **sanic** | Python | Async web framework | 56 findings: most in `examples/`, `guide/` docs, `scripts/release.py`, vendored livereload.js. ContextVar.set patterns are intentional request-id propagation. **clean** |
|
||||
| **vite** | TS | Frontend build tool | 77 findings: ALL in `playground/__tests__/` test specs (CSS sourcemap tests, HMR SSR tests, asset tests). Core vite clean. |
|
||||
| **prettier** | JS | Opinionated code formatter | 52 findings: `String.includes(needle)` substring checks (literal print, template-literal print, srcset descriptor, CLI argv flag). Bounded by source-text length per print call. **clean** |
|
||||
|
||||
Honor roll now stands at **86 projects** validated zero-real-finding under MOAD scanning.
|
||||
|
||||
## Per-target findings
|
||||
|
||||
| Project | Lang | Total | M1 | M3 | M4 | M5 | M6 | M7 | M9 | M11 | Triage |
|
||||
|---------|------|------:|---:|---:|---:|---:|---:|---:|---:|----:|--------|
|
||||
| symfony | PHP/JS | 520 | 232 | - | 109 | - | 9 | 96 | - | 74 | 58 in vendored mermaid-flowchart. `PropertyAccessor::writeCollection` real but complex — see investigation. `LokaliseProvider`, `MandrillApiTransport` use bounded fixed enums. |
|
||||
| ruff | Rust | 388 | 166 | 30 | 6 | - | - | 172 | 2 | 12 | `completion.rs:1142` `existing_class_bases.contains` — `existing_class_bases: Option<FxHashSet<Name>>` (O(1)). `nodes.rs` `InterpolatedStringFlagsInner::TRIPLE_QUOTED.contains` is bitflags (Wave 9 pattern). `shell_injection.rs` `text.contains('*')` is single-char substring. |
|
||||
| framework (laravel) | PHP | 112 | 36 | - | 1 | - | 7 | 42 | 10 | 16 | `in_array` patterns on small bounded enums (rules, attributes, methods). PHP idiom for fixed validation rule lists. |
|
||||
| pyright | TS | 110 | 80 | - | 6 | 4 | 11 | 3 | 6 | - | `typeEvaluator.ts:1670/1671` `findIndex` per evaluation (bounded by string concat count). `docStringUtils.ts` paramOffset substring search bounded by docstring length. `callHierarchyProvider` real but borderline — see investigation. |
|
||||
| **vite** | TS | 77 | 72 | - | 1 | 1 | 3 | - | - | - | All in playground tests. **clean** |
|
||||
| **sanic** | Python | 56 | 5 | 44 | 2 | - | 2 | 1 | - | 2 | Examples + docs + ContextVar. **clean** |
|
||||
| **prettier** | JS | 52 | 47 | - | 5 | - | - | - | - | - | Substring checks bounded by source text. **clean** |
|
||||
| **mypy** | Python | 36 | 19 | 2 | 1 | - | 3 | 3 | - | 8 | Test fixtures + stub files. **clean** |
|
||||
| **black** | Python | 13 | 8 | 2 | 1 | - | - | - | - | 2 | pgen2 grammar build + char count. **clean** |
|
||||
| **flask** | Python | 6 | - | 2 | 3 | 1 | - | - | - | - | Intentional Flask context-var design. **clean** |
|
||||
|
||||
## Investigations: borderline real, not patch-shipped this pass
|
||||
|
||||
### symfony `PropertyAccessor::writeCollection` — O(P × C) collection diff
|
||||
|
||||
`src/Symfony/Component/PropertyAccess/PropertyAccessor.php:580-595` does a collection diff for entity property updates:
|
||||
|
||||
```php
|
||||
foreach ($previousValue as $key => $item) {
|
||||
if (!\in_array($item, $collection, true)) { // O(C) per call
|
||||
// remove $item
|
||||
}
|
||||
}
|
||||
foreach ($collection as $item) {
|
||||
if (!$previousValue || !\in_array($item, $previousValue, true)) { // O(P) per call
|
||||
// add $item
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For P previous items × C new items, total cost is **O(P × C)**. This runs on every form submission with a CollectionType field and on every PropertyAccessor write to a collection-valued entity property.
|
||||
|
||||
**Why not patch-shipped this pass:** `\in_array(..., true)` with `$strict=true` on objects checks identity (===), but on scalars checks `===`. The collection can contain mixed types (objects, scalars, arrays). A correct fix needs:
|
||||
- Objects → `SplObjectStorage` (O(1) identity lookup)
|
||||
- Hashable scalars (strings, ints, bools) → `array_flip` map
|
||||
- Arrays / non-hashable → fall back to `\in_array` (rare in practice)
|
||||
|
||||
Single-line set hoist isn't sufficient. The fix is mechanical but needs careful type-dispatch code. Logged for a follow-up patch with type-aware lookup helper.
|
||||
|
||||
Real-world scale: a Symfony entity with a `OneToMany` association of 500 items being updated to 500 different items pays 250k strict-equality comparisons per write. For most Symfony apps with smaller collections (5-50 items), the cost is invisible. Worth fixing for ORM-heavy apps.
|
||||
|
||||
### pyright `CallHierarchyProvider._outgoingCalls.find` — O(C²) call dedup
|
||||
|
||||
`packages/pyright-internal/src/languageService/callHierarchyProvider.ts:394-396`:
|
||||
|
||||
```ts
|
||||
let outgoingCall = this._outgoingCalls.find(
|
||||
(outgoing) => outgoing.to.uri === callDest.uri && rangesAreEqual(outgoing.to.range, callDest.range)
|
||||
);
|
||||
```
|
||||
|
||||
Per discovered call expression, linear scan over already-recorded outgoing calls to dedup. For a function with C call expressions where many resolve to distinct destinations, total cost is O(C²).
|
||||
|
||||
**Why not patch-shipped this pass:** the dedup key is `(uri, range)` — a composite where `range` is a `{start, end}` struct. JS Map needs a string key. We can serialize as `${uri}|${start}|${end}`, but it's worth checking whether the existing Map representation downstream relies on object identity. Logged for follow-up after reading callers.
|
||||
|
||||
Real-world scale: typical "show outgoing calls from this function" query has 10-50 unique destinations. At 50 the constant is 2500 ops — invisible in the IDE. For glue functions with 200+ call sites, becomes measurable (40k ops). Below patch-grade today.
|
||||
|
||||
### ruff `existing_class_bases.contains` — false positive on FxHashSet
|
||||
|
||||
Already a `FxHashSet<Name>` — O(1) contains. Same scanner gap as Wave 11 (Rust HashSet declared-type awareness).
|
||||
|
||||
### ruff `InterpolatedStringFlagsInner::TRIPLE_QUOTED.contains` — bitflags FP
|
||||
|
||||
Same pattern as helix/alacritty/wezterm in Wave 9. `bitflags::bitflags!` macro generates `.contains(other)` that compiles to bitwise AND.
|
||||
|
||||
### laravel + symfony `in_array` clusters — fixed enum patterns
|
||||
|
||||
Across both PHP frameworks, `\in_array($key, $rules, true)` and similar patterns are checking against fixed enums (validation rule names, allowed mutator methods, allowed transport headers). These are PHP's idiomatic fixed-enum check — 5-50 entries — and fast at this scale.
|
||||
|
||||
## Triage backlog
|
||||
|
||||
1. **symfony PropertyAccessor::writeCollection follow-up** — write a type-aware collection-membership helper (SplObjectStorage for objects, array_flip for hashable scalars, fallback for the rest). Patch is real but needs careful type dispatch.
|
||||
2. **pyright callHierarchyProvider follow-up** — convert to Map<"uri|start|end", entry>; check downstream consumers.
|
||||
3. **Scanner enhancement: Rust FxHashSet/FxHashMap awareness** — same as Wave 11 HashSet gap; ruff reinforces.
|
||||
4. **Scanner enhancement: bitflags::bitflags! macro suppression** — already on backlog from Wave 9.
|
||||
|
||||
## Method
|
||||
|
||||
Same as Waves 3-16: shallow clone, `unmoad -s high -f json`, filter test/vendor/UI noise, manual triage of strongest source-only candidates per project. **Six projects added to clean-scan honor roll.** No new UNDF IDs assigned (no patches shipped — two real candidates logged for type-aware follow-up).
|
||||
|
||||
## References
|
||||
|
||||
- `unmoad` detection engine: `git.unturf.com/engineering/unmoad.com`
|
||||
- Earlier surveys: `/test-harness-survey/`, `/wave4-linter-ci-survey/`, `/wave5-cicd-iac-survey/`, `/wave6-docgen-webfw-tui-survey/`, `/docs-pipeline-survey/`, `/wave7-mail-dns-storage-vpn-rtos-survey/`, `/wave8-observability-streaming-survey/`, `/wave9-image-pdf-db-editors-survey/`, `/wave10-crypto-text-geo-flutter-survey/`, `/wave11-unix-search-ml-survey/`, `/wave12-sci-static-site-api-gateway-survey/`, `/wave13-vms-devtools-graphql-survey/`, `/wave14-mobile-edge-pl-wm-survey/`, `/wave15-security-data-eng-ide-survey/`, `/wave16-cms-workflow-bio-node-survey/`
|
||||
- Clean-scan honor roll cumulative: 86 projects across waves 3-17
|
||||
Loading…
Add table
Add a link
Reference in a new issue