Skip to content

Commit a6e8828

Browse files
fix: bounded config fixes for the PHP CI gates (#49)
Keeps the library **PHP** (defensive PHP-security tooling — we protect PHP devs). Config-only, PHP-native fixes for the failing gates: **(a) PHPUnit — undefined aegis_* functions.** Added `autoload.files` for `src/WordPress/Adapter.php` (PSR-4 only autoloads classes, never free functions). Also moved the ~20 `aegis_*` helpers from `PhpAegis\WordPress` into the **global** namespace: they are WordPress-style globals invoked unqualified (`aegis_html(...)`) and depend on PHP's global-function fallback — while namespaced they were unresolvable from the test namespaces (`PhpAegis\Tests\*`). `use` imports retained so the underlying class refs (`Sanitizer::html`, etc.) still resolve. **(b) composer.lock — NOT included.** Cannot be generated without a Composer runtime in this environment; deliberately not fabricated. Needs `composer install` / `composer update` to commit. **(c) PHP-CS-Fixer exit 16 (invalid v3 config).** Removed bogus `no_eval` (no such fixer exists) and renamed `no_unneeded_curly_braces` → `no_unneeded_braces` (renamed in php-cs-fixer 3.x). **(d) Security Pattern Check false positives.** Added `\b` word boundaries so `curl_exec()` no longer matches `\bexec\(`, and anchored the backtick check (plus a quoted-string exclusion) so the library's own escaped backtick character inside its Turtle/URI validators isn't flagged. Verified: zero matches on `src/` now, while a bare `exec(` is still caught. **(e) PHPStan L9 — NOT attempted here** (out of scope per the request). No baseline added: generating an accurate `phpstan-baseline.neon` requires running PHPStan (no PHP runtime here), and `reportUnmatchedIgnoredErrors: true` would fail on a fabricated/stale baseline. Remains for a PHP-runtime follow-up if L9 surfaces genuine strict errors. Verified statically (no PHP runtime in env): `composer.json` is valid JSON; the new security-pattern regexes match nothing in `src/` yet still flag a bare `exec(`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent cb06935 commit a6e8828

4 files changed

Lines changed: 22 additions & 8 deletions

File tree

.github/workflows/php-lint.yml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,17 +177,25 @@ jobs:
177177
run: |
178178
FOUND=0
179179
180-
# Dangerous execution functions
181-
DANGEROUS=$(grep -rEn 'eval\s*\(|exec\s*\(|system\s*\(|passthru\s*\(|shell_exec\s*\(|proc_open\s*\(|popen\s*\(' \
180+
# Dangerous execution functions.
181+
# Word boundaries (\b) are required so legitimate functions whose
182+
# names merely END in a dangerous token are not flagged, e.g.
183+
# curl_exec() (HTTP, not command execution) must NOT match \bexec\(.
184+
DANGEROUS=$(grep -rEn '\beval\s*\(|\bexec\s*\(|\bsystem\s*\(|\bpassthru\s*\(|\bshell_exec\s*\(|\bproc_open\s*\(|\bpopen\s*\(' \
182185
--include="*.php" src/ 2>/dev/null || true)
183186
if [ -n "$DANGEROUS" ]; then
184187
echo "::error::Dangerous execution functions found:"
185188
echo "$DANGEROUS"
186189
FOUND=1
187190
fi
188191
189-
# Backtick execution
190-
BACKTICKS=$(grep -rEn '`[^`]*\$' --include="*.php" src/ 2>/dev/null || true)
192+
# Backtick (shell) execution with variable interpolation.
193+
# Anchor on the OPENING backtick of a shell-exec operator, then drop
194+
# lines where the backtick is a literal character inside a quoted
195+
# string/regex (this security library escapes the backtick char in
196+
# its Turtle/URI validators, which is legitimate, not shell exec).
197+
BACKTICKS=$(grep -rEn '(=|\(|\breturn\b|\becho\b)\s*`[^`]*\$[^`]*`' --include="*.php" src/ 2>/dev/null \
198+
| grep -vE "['\"][^'\"]*\`" || true)
191199
if [ -n "$BACKTICKS" ]; then
192200
echo "::error::Backtick execution with variables found:"
193201
echo "$BACKTICKS"

.php-cs-fixer.dist.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171

7272
// Control structures
7373
'no_unneeded_control_parentheses' => true,
74-
'no_unneeded_curly_braces' => true,
74+
'no_unneeded_braces' => true,
7575
'yoda_style' => [
7676
'equal' => false,
7777
'identical' => false,
@@ -205,7 +205,6 @@
205205
],
206206

207207
// Security-conscious rules
208-
'no_eval' => true, // Warn about eval() usage
209208
'random_api_migration' => true, // Use random_int/random_bytes
210209
'native_function_invocation' => [
211210
'include' => ['@compiler_optimized'],

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@
4141
"autoload": {
4242
"psr-4": {
4343
"PhpAegis\\": "src/"
44-
}
44+
},
45+
"files": [
46+
"src/WordPress/Adapter.php"
47+
]
4548
},
4649
"autoload-dev": {
4750
"psr-4": {

src/WordPress/Adapter.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77

88
declare(strict_types=1);
99

10-
namespace PhpAegis\WordPress;
10+
// NOTE: These WordPress-style helpers live in the GLOBAL namespace on purpose,
11+
// mirroring WordPress's own global functions (esc_html(), esc_attr(), ...).
12+
// Callers invoke them unqualified (aegis_html(...)) and rely on PHP's global
13+
// function fallback, so they must not be namespaced. The `use` imports below
14+
// keep the underlying class references (Sanitizer::html, etc.) resolvable.
1115

1216
use PhpAegis\Validator;
1317
use PhpAegis\Sanitizer;

0 commit comments

Comments
 (0)