Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ All of `src/`, namespace `RenzoFranceschini\GuardCore`:
- `Routing/`: `RouteConfig` (per-route bypasses, rate limits, required headers, auth, time windows) and `RouteResolver` (resolves by `RequestState::$guardRouteId`).
- `Logging/`: request/suspicious logging with redaction (`LogRedactor`).

Known surface gaps (fail closed or documented, never silently no-op): no route registry on the `GuardEngine` facade (route configs resolve only when a caller wires them), fixed `Too many requests` 429 body (not tunable via `customErrorResponses`), in-process auto-ban violation counters, address-header ssrf exclusion delegated to `logSensitiveHeaders`.
Known surface gaps (fail closed or documented, never silently no-op): no route registry on the `GuardEngine` facade (route configs resolve only when a caller wires them), fixed `Too many requests` 429 body (not tunable via `customErrorResponses`), in-process auto-ban violation counters.

## Boundary Rules

Expand Down
17 changes: 3 additions & 14 deletions examples/advanced_app/src/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,6 @@ function advanced_int_env(string $key, int $fallback): int
return (int) $raw;
}

/** Address headers the Python engine skips when scanning for ssrf patterns. */
function advanced_address_headers(): array
{
return [
'host', 'origin', 'via', 'x-forwarded-for', 'x-forwarded-host',
'x-forwarded-proto', 'x-real-ip', 'x-client-ip', 'x-cluster-client-ip',
'cf-connecting-ip', 'true-client-ip', 'fly-client-ip',
'x-envoy-external-address',
];
}

function advanced_config(): SecurityConfig
{
return new SecurityConfig(
Expand Down Expand Up @@ -82,9 +71,9 @@ function advanced_config(): SecurityConfig
],
// Detection: every category, default detector tuning.
enablePenetrationDetection: true,
// See advanced_address_headers() above: without this, nginx's
// forwarded headers get flagged as ssrf attempts.
logSensitiveHeaders: advanced_address_headers(),
// The engine skips the ssrf category for nginx's forwarded headers
// (host, x-forwarded-for, x-real-ip, ...) by default; use
// excludedDetectionHeaders for your own address-carrying headers.
// Edge filters.
blockedUserAgents: ['badbot', 'evil-crawler', 'sqlmap'],
blockCloudProviders: advanced_split_csv(getenv('BLOCK_CLOUD_PROVIDERS') ?: ''),
Expand Down
8 changes: 1 addition & 7 deletions examples/simple_app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,7 @@ curl -i http://localhost:8080/

## A note on address headers

The Python engine skips ssrf scanning for address headers (`host`,
`x-forwarded-for`, `x-real-ip`, ...) automatically. This PHP port does not
apply that built-in exclusion yet and has no dedicated excluded-detection-
headers knob; it does skip headers listed in `logSensitiveHeaders` during
detection scanning, so `index.php` routes the address headers through that
field. Without it, a plain `Host: localhost` request is flagged as an ssrf
attempt.
The engine skips the `ssrf` category for the proxy identity headers (`host`, `x-forwarded-for`, `x-real-ip`, `cf-connecting-ip`, `forwarded`, ...) by default, so a plain `Host: localhost` request is not flagged and `index.php` needs no workaround. Every other category still scans those headers. Pass `excludedDetectionHeaders` to extend the set with your own address-carrying headers, and `excludedSemanticDetectionHeaders` to keep a header out of the semantic analyzer only.

## Configuration knobs demonstrated

Expand Down
18 changes: 4 additions & 14 deletions examples/simple_app/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,6 @@

require __DIR__ . '/../../vendor/autoload.php';

// Address headers the Python engine skips when scanning for ssrf patterns.
// This port has no dedicated excluded-detection-headers knob yet; it does
// skip headers listed in logSensitiveHeaders during detection scanning, so
// we route the address headers through it. Without this, a plain
// "Host: localhost" request is flagged as an ssrf attempt.
const ADDRESS_HEADERS = [
'host', 'origin', 'via', 'x-forwarded-for', 'x-forwarded-host',
'x-forwarded-proto', 'x-real-ip', 'x-client-ip', 'x-cluster-client-ip',
'cf-connecting-ip', 'true-client-ip', 'fly-client-ip',
'x-envoy-external-address',
];

/**
* Engine holder. Runtimes that keep the process alive across requests
* (worker mode under FrankenPHP/RoadRunner-style loops) reuse the instance
Expand Down Expand Up @@ -73,8 +61,10 @@ function simple_engine(): GuardEngine
autoBanDuration: 300,
// Penetration detection: all categories, default thresholds.
enablePenetrationDetection: true,
// See ADDRESS_HEADERS above.
logSensitiveHeaders: ADDRESS_HEADERS,
// The engine already skips the ssrf category for the proxy identity
// headers (host, x-forwarded-for, x-real-ip, ...), so no header opt-out
// is needed here. Add excludedDetectionHeaders for your own
// address-carrying headers.
// Blocked user agents (regex patterns).
blockedUserAgents: ['badbot', 'evil-crawler', 'sqlmap'],
// Custom block bodies, keyed by status code.
Expand Down
Loading