Skip to content

Security: SSRF fail-closed (#38) + pin the execute() permission contract (#39) - #48

Merged
ifahimreza merged 2 commits into
mainfrom
fix/security-hardening
Jul 12, 2026
Merged

Security: SSRF fail-closed (#38) + pin the execute() permission contract (#39)#48
ifahimreza merged 2 commits into
mainfrom
fix/security-hardening

Conversation

@ifahimreza

Copy link
Copy Markdown
Contributor

Two independent hardening items from the 2026-07-12 audit.

#38 — SSRF guard fails closed. source_url_is_safe() treated a host that resolved to zero IPs as safe (fail-open). On hosts where PHP DNS lookups are disabled but HTTP fetches still work, an agent-supplied name that only resolves at fetch time could bypass the internal-address block. Now refuses when nothing resolved; the saddle_source_url_is_safe filter stays as the trusted-env escape hatch and the WP_Error names it. 7 tests (literal metadata/private/loopback IPs, unresolvable→closed, filter override, public IP passes).

#39 — pin the permission contract. The fallback JSON-RPC transport trusts WP_Ability::execute() to run the permission_callback first — verified true on WP 6.9 core, now pinned by a regression test (write-tier tool at read tier → JSON-RPC error + nothing created). Comment updated from 'verify on first boot'.

Suite 309 green (300 base + 9 new). Production PHP is WPCS-clean; tests are out of phpcs scope by config.

Closes #38
Closes #39

🤖 Generated with Claude Code

https://claude.ai/code/session_017iV29qbQ5jCicpuJ8JAtRx

ifahimreza and others added 2 commits July 12, 2026 17:08
…hosts

source_url_is_safe() treated a host that resolved to zero IPs as safe —
on a host where PHP DNS lookups are disabled but HTTP fetches work, an
agent-supplied name that only resolves at fetch time could slip past the
internal-address block. It now refuses when nothing resolved; the
saddle_source_url_is_safe filter remains the escape hatch for trusted/
NAT'd environments and the returned WP_Error names it. Literal-IP blocks
(RFC1918, link-local metadata, loopback) are unchanged.

Closes #38

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017iV29qbQ5jCicpuJ8JAtRx
…ck transport

The built-in JSON-RPC transport trusts execute() to run the
permission_callback before the execute_callback. Verified true on WP 6.9
core (execute() calls check_permissions() first) — now pinned by a test
driving a write-tier tool at the read tier: it must return a JSON-RPC
error and create nothing. Comment updated from 'verify on first boot' to
the verified + test-pinned statement.

Closes #39

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017iV29qbQ5jCicpuJ8JAtRx
@ifahimreza
ifahimreza merged commit 50e35bc into main Jul 12, 2026
0 of 4 checks passed
@ifahimreza
ifahimreza deleted the fix/security-hardening branch July 12, 2026 13: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

1 participant