Back to skill

Security audit

PHP

Security checks for vulnerabilities and agentic risk

Overview

This PHP skill is a coherent documentation and coding guide with no executable payload, though production configuration advice should be reviewed before applying it.

Safe to install as a PHP guidance skill. Before applying generated production FPM configuration, review environment-variable exposure, especially clear_env = no, and prefer explicitly forwarding only the variables the application needs.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
fpm.md:45
Finding

PHP-FPM Workers Inherit the Complete Service Environment

Content
View full analysis

Vulnerability Details

File Location: fpm.md:45,53
Vulnerability Type: Excessive environment-variable exposure
Risk Level: Medium

Vulnerable Configuration

ini
clear_env = no                     ; FPM clears the environment by default

The accompanying guidance states:

text
- `clear_env = no` is required for 12-factor configuration — by default FPM starts workers with a scrubbed environment and `getenv()` returns nothing. The alternative is explicit `env[KEY] = value` lines in the pool.

Technical Analysis

PHP-FPM normally scrubs inherited environment variables before starting application workers. Setting clear_env = no makes the entire environment inherited from the service manager available to PHP application code.

This configuration violates least privilege when an application requires only a limited set of configuration values. The inherited environment may contain database passwords, API tokens, cloud credentials, deployment secrets, or variables intended for service-management components rather than the PHP application.

The assertion that disabling environment scrubbing is required for 12-factor configuration is overly broad. PHP-FPM can retain environment isolation while explicitly forwarding only the variables required by the application through narrowly scoped env[KEY] = value directives.

The Skill does not itself read or exfiltrate environment variables. Exploitation requires an application-level vulnerability, malicious application code, or an information-disclosure endpoint capable of reading the worker environment.

Attack Path

  1. An operator adopts the documented PHP-FPM pool configuration and sets clear_env = no.
  2. The FPM service inherits sensitive environment variables from systemd, a container runtime, deployment tooling, or a parent process.
  3. Every PHP worker receives the complete inherited environment.
  4. An attacker exploits an unrelated PHP v ...[truncated 1010 chars]
Remediation
View remediation

Remediation Suggestions

  1. Retain PHP-FPM's default environment scrubbing:

    ini
    clear_env = yes
    
  2. Explicitly forward only variables required by the application:

    ini
    env[APP_ENV] = production
    env[DATABASE_URL] = $DATABASE_URL
    
  3. Use a dedicated, application-specific service environment rather than inheriting deployment-agent, host-wide, or unrelated service credentials.

  4. Prefer a secret manager with narrowly scoped identities and short-lived credentials where supported.

  5. Ensure production deployments disable public phpinfo() pages and prevent error handlers, debug endpoints, and logs from dumping $_ENV, $_SERVER, or the process environment.

  6. Run each application in a separate FPM pool and operating-system identity so one compromised application cannot access another application's environment or files.

  7. Update the documentation to present clear_env = no as an explicit risk-bearing exception rather than a general requirement for 12-factor configuration.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (9)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 50)May include surrounding context.

md
ly fails | `mb_*` for text, `/u` on every pattern, check `preg_last_error()` → `strings.md` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 145)May include surrounding context.

md
ly fails | `mb_*` for text, `/u` on every pattern, check `preg_last_error()` → `strings.md` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 175)May include surrounding context.

md
ly fails | `mb_*` for text, `/u` on every pattern, check `preg_last_error()` → `strings.md` |

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
80% confidence
Finding

Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Content

Scanner excerpt · datetime.md (reported line 7)May include surrounding context.

md
- `DateTimeImmutable` for everything. `DateTime::modify()`, `add()`, `sub()`, and `setDate()` mutate in place, so a helper that "returns next month" also moves the caller's date — the reference is shared (`oop.md`).
- An explicit IANA zone (`new DateTimeZone('Europe/Madrid')`), never an abbreviation. `EST` is a fixed offset with no DST; `America/New_York` is the actual rule set.
- Set `date.timezone` in php.ini. Unset, PHP uses UTC with no warning, and the CLI and FPM ini files can disagree (`php-ini.md`).
- Store UTC, convert at the edges. `->setTimezone(new DateTimeZone($user->tz))` for display, `->setTimezone(new DateTimeZone('UTC'))` before writing.
- Elapsed time is measured with `hrtime(true)` (monotonic nanoseconds), never by subtracting two wall-clock timestamps — NTP can step the clock backwards and produce a negative duration.

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
80% confidence
Finding

Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Content

Scanner excerpt · php-ini.md (reported line 40)May include surrounding context.

md
- `DateTimeImmutable` for everything. `DateTime::modify()`, `add()`, `sub()`, and `setDate()` mutate in place, so a helper that "returns next month" also moves the caller's date — the reference is shared (`oop.md`).
- An explicit IANA zone (`new DateTimeZone('Europe/Madrid')`), never an abbreviation. `EST` is a fixed offset with no DST; `America/New_York` is the actual rule set.
- Set `date.timezone` in php.ini. Unset, PHP uses UTC with no warning, and the CLI and FPM ini files can disagree (`php-ini.md`).
- Store UTC, convert at the edges. `->setTimezone(new DateTimeZone($user->tz))` for display, `->setTimezone(new DateTimeZone('UTC'))` before writing.
- Elapsed time is measured with `hrtime(true)` (monotonic nanoseconds), never by subtracting two wall-clock timestamps — NTP can step the clock backwards and produce a negative duration.

Cloud Metadata Access

High
Category
Server-Side Request Forgery
Confidence
90% confidence
Finding

Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.

Content

Scanner excerpt · http.md (reported line 78)May include surrounding context.

md
- Both timeouts, always. Without them a hung upstream occupies an FPM worker until `request_terminate_timeout` kills it, and enough of those saturate the pool (`fpm.md`).
- Never `CURLOPT_SSL_VERIFYPEER => false`. A certificate error is a configuration problem (missing CA bundle, clock skew); disabling verification makes every request interceptable.
- `CURLOPT_FOLLOWLOCATION` with a user-supplied URL is an SSRF amplifier — a redirect can land on `169.254.169.254`. Keep it off, or validate every hop (`security.md`).
- `file_get_contents($url)` needs `allow_url_fopen`, honors only `default_socket_timeout` (60s) unless you build a stream context, returns `false` with a warning on a 4xx (discarding the error body), and cannot show you the response headers without `$http_response_header`. Use curl or an HTTP client.
- Retries: only for idempotent requests or with an idempotency key; exponential backoff with jitter; a cap on total elapsed time, because retries inside a web request multiply the user's wait (`concurrency.md`).
- Parallel calls with `curl_multi_*` or a client's pool turn five 200 ms calls into one 200 ms wait (`concurrency.md`).

Cloud Metadata Access

High
Category
Server-Side Request Forgery
Confidence
90% confidence
Finding

Code accesses a cloud instance metadata endpoint (e.g. 169.254.169.254). A single request can return temporary IAM credentials, making this a high-value SSRF target for credential theft.

Content

Scanner excerpt · security.md (reported line 83)May include surrounding context.

md
- Both timeouts, always. Without them a hung upstream occupies an FPM worker until `request_terminate_timeout` kills it, and enough of those saturate the pool (`fpm.md`).
- Never `CURLOPT_SSL_VERIFYPEER => false`. A certificate error is a configuration problem (missing CA bundle, clock skew); disabling verification makes every request interceptable.
- `CURLOPT_FOLLOWLOCATION` with a user-supplied URL is an SSRF amplifier — a redirect can land on `169.254.169.254`. Keep it off, or validate every hop (`security.md`).
- `file_get_contents($url)` needs `allow_url_fopen`, honors only `default_socket_timeout` (60s) unless you build a stream context, returns `false` with a warning on a 4xx (discarding the error body), and cannot show you the response headers without `$http_response_header`. Use curl or an HTTP client.
- Retries: only for idempotent requests or with an idempotency key; exponential backoff with jitter; a cap on total elapsed time, because retries inside a web request multiply the user's wait (`concurrency.md`).
- Parallel calls with `curl_multi_*` or a client's pool turn five 200 ms calls into one 200 ms wait (`concurrency.md`).

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Content

Scanner excerpt · arrays.md (reported line 55)May include surrounding context.

md
- Arrays are value types with copy-on-write. Passing a 50 MB array to a function costs nothing until either side writes — then the array is duplicated and peak memory doubles.
- The `foreach` reference trap, precisely: after `foreach ($a as &$v) {}`, `$v` still points at the last element. A following `foreach ($a as $v) {}` writes into that last element on every iteration, so the array ends with its last value replaced by the second-to-last. Always `unset($v);` right after a reference loop.
- `foreach` by value iterates a snapshot, so appending inside the loop does not extend it. By reference it iterates live, and appending inside can loop forever.
- `$row = $obj->items; $row['a'] = 1;` modifies a copy — arrays are values. The same line with an object property modifies the original, because objects are handles (`oop.md`).

## Lookup Cost

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

Line L39 explicitly says that format('Y-m-d')/PHP formatting produces English month and day names regardless of locale. This is a natural-language locale policy concern because it normalizes English output without offering user choice or opt-in.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.