Back to skill

Security audit

amphp

Security checks for vulnerabilities and agentic risk

Overview

This AMPHP coding skill is coherent and not malicious, but it includes copyable examples that can teach unsafe file upload and credential-handling patterns.

Review this skill before installing if you expect it to generate production PHP. The main risk is not hidden malware; it is that some examples may lead an agent to generate insecure code unless you explicitly require sanitized upload filenames, environment-based secrets, localhost-only development binds, safe logging, and confirmation before destructive filesystem or database examples are run.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
references/class-examples.md:691
Finding
Untrusted Upload Filenames Allow Path Traversal and Arbitrary File Writes<![CDATA[ ## Vulnerability Details **File Location**: `references/class-examples.md:691-700` and `references/class-examples.md:716-723` **Vulnerability Type**: Unrestricted file path construction from attacker-controlled upload filenames **Risk Level**: High ### Vulnerable Code ```php class UploadHandler implements RequestHandler { public function handleRequest(Request $request): Response { $form = Form::fromRequest($request); $username = $form->getValue('username'); $file = $form->getFile('avatar'); if ($file !== null) { \Amp\File\write('/uploads/' . $file->getFilename(), $file->getContents()); } return new Response(HttpStatus::OK, [], "Hello, $username"); } } ``` ```php class StreamingUploadHandler implements RequestHandler { public function handleRequest(Request $request): Response { $parser = new StreamingFormParser(); $parser->parseForm($request, function (mixed $field) { if ($field instanceof FileField) { $dest = File\openFile('/uploads/' . $field->getFilename(), 'w'); ByteStream\pipe($field->getStream(), $dest); } }); return new Response(HttpStatus::OK, [], 'Uploaded'); } } ``` ### Technical Analysis Both examples use the client-supplied multipart filename directly when constructing a destination path. Multipart filenames are attacker-controlled and may contain traversal sequences such as `../`, platform-specific separators, absolute path components, or names of existing files. Concatenating such a filename with `/uploads/` does not guarantee that the resulting normalized path remains inside the intended upload directory. Opening the destination with write mode may also truncate an existing file. The examples do not: - Remove directory components from the supplied filename. - Generate a server-controlled filename. - Resolve and validate the canonical destination path. ...[truncated 1683 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not use the client-provided filename as the storage filename. - Generate a cryptographically random server-side name, for example with `bin2hex(random_bytes(16))`. - If retaining part of the original filename is necessary, first apply `basename()`, normalize platform separators, and enforce a strict allowlist. - Resolve the canonical upload root and destination parent, then verify that the resulting path remains within the upload root. - Store uploads outside the document root whenever possible. - Prevent overwrites by using exclusive creation semantics or rejecting destinations that already exist. - Enforce file-size, MIME-type, extension, and content validation appropriate to the application. - Configure the upload directory as non-executable. - Run the PHP process with filesystem permissions limited to the specific upload directory. A safer storage pattern is: ```php $uploadRoot = '/uploads'; $extension = validatedExtension($field); $filename = bin2hex(random_bytes(16)) . '.' . $extension; $destination = $uploadRoot . DIRECTORY_SEPARATOR . $filename; $dest = File\openFile($destination, 'x'); ByteStream\pipe($field->getStream(), $dest); ``` The implementation should additionally verify that `$uploadRoot` is the intended canonical directory and that `validatedExtension()` uses a strict application-specific allowlist. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
examples/database.md:13
Finding
Hardcoded Credential Patterns Encourage Secrets to Be Embedded in Generated Source Code<![CDATA[ ## Vulnerability Details **File Location**: `examples/database.md:13-20`, `examples/database.md:103-106`, `examples/http-server.md:134-142`, `examples/http-client.md:121-126`, `references/class-examples.md:862-867`, and `references/class-examples.md:887-891` **Vulnerability Type**: Hardcoded credentials and authentication secrets **Risk Level**: Low ### Vulnerable Code ```php $pool = new MysqlConnectionPool( MysqlConfig::fromString('host=127.0.0.1 user=root password=secret db=myapp') ); // Alternatively, DSN-style named params: $config = MysqlConfig::fromString('host=db.example.com port=3306 user=app password=s3cr3t db=prod charset=utf8mb4'); $pool = new MysqlConnectionPool($config, maxConnections: 10); ``` ```php // With password: $redis = createRedisClient(RedisConfig::fromUri('redis://:password@127.0.0.1:6379/0')); ``` ```php private function isAuthorized(Request $request): bool { return $request->getHeader('Authorization') === 'Bearer secret'; } ``` ```php $tunnelConnector = new Http1TunnelConnector( proxyAddress: '127.0.0.1:3128', proxyHeaders: [ 'Proxy-Authorization' => 'Basic ' . base64_encode('user:secret'), ], ); ``` ### Technical Analysis The identified values appear to be illustrative placeholders, not verified live credentials. However, the Skill presents these patterns as copyable implementation examples. This can cause generated applications to embed real database passwords, Redis passwords, proxy credentials, or bearer tokens directly in source files. Base64 encoding a Basic authentication value does not protect it; it is a reversible transport encoding. A hardcoded bearer token also creates a shared static credential and may encourage deployment with a predictable example value. Source-embedded credentials can be exposed through: - Version-control history. - Source archives and backups. - Generated responses or debugging output. - Error reports and stack traces. - Code review systems and CI arti ...[truncated 1719 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace literal credentials with environment variables, a secrets manager, or an injected configuration provider. - Clearly label all placeholder values as non-production examples. - Avoid demonstrating complete credential-bearing DSNs directly in source. - Do not use privileged accounts such as `root` for application connections. - Assign each application credential only the minimum required permissions. - Rotate any credential that has been committed to version control, including removal from repository history where appropriate. - Avoid logging DSNs, authorization headers, or configuration objects containing secrets. - Use TLS for remote database, Redis, proxy, and HTTP authentication traffic. - Compare fixed bearer tokens with `hash_equals()` after validating that the header has the expected format. For example: ```php $dbPassword = getenv('DB_PASSWORD'); if ($dbPassword === false || $dbPassword === '') { throw new RuntimeException('DB_PASSWORD is not configured'); } $config = MysqlConfig::fromString(sprintf( 'host=127.0.0.1 user=app password=%s db=myapp', $dbPassword, )); ``` For production systems, prefer a configuration API that accepts credentials as separate parameters so that escaping and secret handling do not depend on string interpolation. ]]>
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
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (26)

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Session Persistence

Medium
Category
Rogue Agent
Content
# 🔥 AMPHP v3 Skill for AI Coding Assistants

> Teach AI agents to write **correct non-blocking async PHP using AMPHP v3**

[![Claude Code](https://img.shields.io/badge/Claude-Code-blue)]()
[![Cursor](https://img.shields.io/badge/Cursor-Compatible-purple)]()
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Skill Enumeration

Medium
Category
Agent Snooping
Content
## 2️⃣ Claude Code (per project)

```bash
mkdir -p .claude/skills
git clone https://github.com/opencck/skill-amphp .claude/skills/amphp
```
Confidence
85% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
Place the skill file manually:

```
skills/amphp/SKILL.md
```

Example:
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The README states the skill activates automatically when broad tokens such as 'Amp\Future', 'Amp\async', 'Amp\delay', 'Revolt\EventLoop', or 'amphp/*' are detected, plus vague 'async server patterns'. Overbroad trigger conditions can cause the skill to load in unintended contexts, influencing agent behavior when merely discussing, auditing, or comparing these technologies rather than requesting the skill's guidance.

Session Persistence

Medium
Category
Rogue Agent
Content
Good prompt:

```
Write async AMPHP v3 code for this API client
```

Bad prompt:
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The Russian section says the skill 'activates automatically' without restating any trigger constraints, which reinforces an unconditional or overly broad activation model. In multilingual documentation, inconsistent trigger descriptions can cause implementers or agents to apply the skill more broadly than intended, increasing prompt-routing and instruction-contamination risk.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
// File\getSize(string $path): int
// File\deleteFile(string $path): void
// File\deleteDirectory(string $path): void
// File\createDirectoryRecursively(string $path, int $mode = 0777): void
// File\listFiles(string $path): string[]   — FILENAMES ONLY, not full paths!
// File\openFile(string $path, string $mode): File\File   — streaming I/O
```
Confidence
80% confidence
Finding
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The natural-language prompts are written as direct instructions in Russian, e.g. "Напиши...", which imposes a specific language/locale on the generated output. The file does not offer any user opt-in or language choice, and there is no documented region-specific reason for this constraint.

Unbounded Output

Medium
Category
Output Handling
Content
use Amp\Cache\LocalCache;

// Default constructor: no size limit, no TTL
$cache = new LocalCache();

// Optional: $sizeLimit caps the number of entries (LRU eviction)
Confidence
75% confidence
Finding
Output size or generation rate is not bounded. Unbounded output enables denial-of-service through resource exhaustion, log flooding, or context-window stuffing.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The documentation embeds plaintext credentials directly in MySQL connection strings, including realistic hostnames and passwords, without warning against hardcoding secrets. This can normalize insecure secret handling, lead users to copy the pattern into production code, and increase the risk of credential leakage through source control, logs, or screenshots.

Behavior Manipulation

Medium
Category
Prompt Injection
Content
use function Amp\Redis\createRedisClient;

// createRedisClient() is a FACTORY FUNCTION — not a constructor!
// Always use it instead of new RedisClient(...)
$redis = createRedisClient(RedisConfig::fromUri('redis://127.0.0.1:6379'));

// With password:
Confidence
70% confidence
Finding
Subtle instructions detected that may alter agent decision-making or introduce hidden biases.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The Redis example includes a password-bearing URI inline, which exposes the secret in code and encourages insecure credential handling patterns. If copied into real applications, the password may be committed to repositories, leaked in logs, or exposed through process inspection and debugging output.

External Transmission

Medium
Category
Data Exfiltration
Content
$client = HttpClientBuilder::buildDefault();

// GET
$response = $client->request(new HttpRequest('https://api.example.com/data'));
$body     = $response->getBody()->buffer(); // MUST buffer or fully iterate!
echo $response->getStatus(); // 200
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
$client = HttpClientBuilder::buildDefault();

// GET
$response = $client->request(new HttpRequest('https://api.example.com/data'));
$body     = $response->getBody()->buffer(); // MUST buffer or fully iterate!
echo $response->getStatus(); // 200
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
$client = HttpClientBuilder::buildDefault();

// GET
$response = $client->request(new HttpRequest('https://api.example.com/data'));
$body     = $response->getBody()->buffer(); // MUST buffer or fully iterate!
echo $response->getStatus(); // 200
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The proxy example includes an explicit Proxy-Authorization header built from a username and secret and demonstrates transmitting credentials to a proxy without any warning about secret handling, logging exposure, or safe credential sourcing. In documentation, hardcoded credential patterns are risky because readers may copy them into production code, leak them to source control, or normalize unsafe handling of sensitive values.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The example binds the HTTP server to 0.0.0.0:8080, which exposes it on all network interfaces. In documentation, this is risky because users often copy-paste examples into development or even production-like environments without realizing the service will be reachable from other hosts, increasing the chance of unintended exposure of test endpoints or incomplete auth controls.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- `WebsocketClientHandler`, `Websocket`, `AllowOriginAcceptor` → `Amp\Websocket\Server\*`
- Push-only handlers **must** drain incoming messages to prevent deadlock
- After the receive loop exits, call `$sender->ignore()` to suppress abandoned-fiber errors
- `addClient()` in `WebsocketClientGateway` automatically removes clients on disconnect
Confidence
80% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- `WebsocketClientHandler`, `Websocket`, `AllowOriginAcceptor` → `Amp\Websocket\Server\*`
- Push-only handlers **must** drain incoming messages to prevent deadlock
- After the receive loop exits, call `$sender->ignore()` to suppress abandoned-fiber errors
- `addClient()` in `WebsocketClientGateway` automatically removes clients on disconnect
Confidence
80% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Missing User Warnings

Low
Confidence
77% confidence
Finding
This markdown file documents transactional INSERT operations and later shows Redis delete operations, but it does not include any warning that running these examples will create, modify, or remove live data. For markdown files, the rule asks for warnings when described behavior can affect user data or system integrity.

Missing User Warnings

Low
Confidence
83% confidence
Finding
This markdown file demonstrates multiple filesystem-modifying operations such as writing files, truncating files, and deleting files/directories, but it does not include any warning that the examples change local filesystem state. Under the markdown-file criteria, descriptions that affect user data or system integrity should disclose that behavior.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The deleteDirectory section demonstrates removal of files and directories, including deleting contents before deleting the directory, but the surrounding prose does not warn users that these operations are destructive. For markdown documentation, destructive behaviors should be clearly disclosed so users do not run examples against important paths unintentionally.

Missing User Warnings

Low
Confidence
76% confidence
Finding
The example logs the full request URI, which may include sensitive query parameters or identifiers depending on deployment. The markdown does not mention this privacy consideration or advise sanitizing logged request data.

Static analysis

No suspicious patterns detected.