Back to skill

Security audit

Phy Content Safety Guard

Security checks for vulnerabilities and agentic risk

Overview

The skill is transparent about using Gemini as a safety judge, but it needs review because it sends full outgoing messages to a third party and defaults to letting messages through when judging fails.

Install only if it is acceptable for eligible outbound chat messages to be sent to Google Gemini. For confidential, child-facing, financial, health, HR, or regulated use, configure fail-closed behavior, add local redaction or deterministic secret checks before remote judging, restrict and rotate the Google API key, and pin any dependency versions.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

other

Error
Location
SKILL.md:149
Finding
Unredacted outbound messages are disclosed to a third-party model provider<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:149-166` **Vulnerability Type**: Third-Party Data Disclosure **Risk Level**: High ### Vulnerable Code ```javascript async function evaluateWithGemini(apiKey, messageContent) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS); const url = `${API_URL}/${GEMINI_MODEL}:generateContent?key=${apiKey}`; try { const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ systemInstruction: { parts: [{ text: GUARD_SYSTEM_PROMPT }], }, contents: [{ role: "user", parts: [{ text: `Evaluate this outbound message:\n\n${messageContent}` }], }], ``` ### Technical Analysis The handler transmits the complete outbound message to the Google Gemini API before determining whether it is safe. No local redaction, data classification, user-consent check, destination allowlist, or sensitive-data suppression is performed. The guard is specifically designed to identify leaked API keys, system prompts, model details, and internal filenames. Consequently, sensitive data that the guard is expected to block is first disclosed to the external model provider. Messages may also contain personal information, confidential business data, regulated records, or proprietary prompts. Third-party model access is relevant to the declared cloud-based classification function, but transmitting the entire message exceeds minimum data privileges when local preprocessing or a self-hosted judge could reduce disclosure. ### Attack Path 1. A user, prompt-injection payload, or compromised upstream agent causes a generated response to contain confidential data. 2. The `message:sending` hook receives the response as `data.content`. 3. The complete content is passed to `evaluateWithGemini`. 4. The handler embeds the content unchanged in ...[truncated 774 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Perform local secret and personal-data detection before any external request. 2. Redact credentials, tokens, system prompts, identifiers, and unnecessary metadata. 3. Send only the minimum text or derived features required for classification. 4. Obtain explicit operator consent and document the third-party processor, retention policy, data residency, and logging behavior. 5. Provide a self-hosted or local judge option for confidential and regulated deployments. 6. Allow administrators to disable remote classification by data class or communication channel. 7. Encrypt traffic in transit, restrict outbound connectivity to the intended API hostname, and monitor unexpected egress. 8. Avoid logging original blocked messages unless logs are access-controlled, encrypted, and governed by a retention policy. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:149
Finding
Google API credential is embedded in the request URL<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:149-156` **Vulnerability Type**: Credential Exposure Through Query String **Risk Level**: Medium ### Vulnerable Code ```javascript async function evaluateWithGemini(apiKey, messageContent) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS); const url = `${API_URL}/${GEMINI_MODEL}:generateContent?key=${apiKey}`; try { const response = await fetch(url, { ``` ### Technical Analysis The Google API key is interpolated into the URL query string. Although the request uses HTTPS, query strings can be recorded by application-performance monitoring, HTTP tracing, debugging tools, reverse proxies, exception reports, or other URL-level telemetry. A credential in the URL is therefore more likely to be unintentionally persisted than one supplied through an authentication header. The Skill correctly reads the key from an environment variable, but this benefit is weakened when the value is subsequently embedded in a URL. ### Attack Path 1. An operator configures `GOOGLE_GENAI_API_KEY`. 2. The handler constructs a URL containing the plaintext key in its query string. 3. A proxy, tracing service, debugger, monitoring agent, or error-reporting system records the request URL. 4. An attacker or unauthorized user with access to those records extracts the key. 5. The attacker submits requests using the exposed credential until it is revoked, restricted, or exhausted. ### Impact Assessment A recovered key may permit unauthorized consumption of the associated Google API quota and cause financial charges or service exhaustion. The exact scope depends on the key's API, project, referrer, IP, and quota restrictions. This issue does not directly expose host privileges, but compromise of an insufficiently restricted key may grant access to other Google APIs enabled for that credential. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use Google's supported `x-goog-api-key` authentication header instead of a query parameter where supported: ```javascript const response = await fetch(`${API_URL}/${GEMINI_MODEL}:generateContent`, { method: "POST", headers: { "Content-Type": "application/json", "x-goog-api-key": apiKey, }, // Remaining request options }); ``` 2. Configure logging and telemetry systems to redact authentication headers and sensitive URL parameters. 3. Restrict the key to the required Generative Language API, expected source IP addresses, and minimum necessary quota. 4. Use a dedicated key for this service rather than sharing a project-wide credential. 5. Rotate the key if credential-bearing URLs may already have been logged. 6. Prevent error messages and debugging output from including complete request objects or URLs. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:138
Finding
Content safety enforcement fails open when the remote judge is unavailable<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:138-144` **Vulnerability Type**: Fail-Open Security Control **Risk Level**: High ### Vulnerable Code ```javascript } catch (err) { // Fail-open: if judge errors or times out, let the message through // Change to fail-closed (return fallback) for higher-security contexts console.error(`[message-guard] Error (fail-open): ${err.message}`); return; } } ``` ### Technical Analysis The handler returns without replacing the outbound message when the Gemini request throws an exception. Under the documented hook contract, returning `undefined` allows the original response to be delivered. Failures may result from a timeout, DNS or network outage, invalid or revoked credentials, quota exhaustion, API rate limits, malformed responses, or service disruption. As a result, the safety boundary becomes least effective precisely when its dependency is unavailable. The documentation mentions a fail-closed alternative, but the provided default implementation is fail-open. This is inconsistent with using the component as a meaningful enforcement control in children's applications, regulated industries, or other high-risk environments. ### Attack Path 1. An attacker induces the upstream agent to generate prohibited or sensitive output. 2. The attacker causes or waits for judge failure, such as by exhausting quota, triggering rate limits, exploiting a service outage, or creating sufficient latency to exceed the three-second timeout. 3. `evaluateWithGemini` throws an exception. 4. The catch block logs the failure and returns `undefined`. 5. The hook interprets the undefined return value as approval to send the original message. 6. The prohibited output reaches the user without evaluation. ### Impact Assessment An attacker does not gain operating-system privileges through this flaw. However, the attacker may bypass the Skill's complete content-control policy during dependency failure and delive ...[truncated 284 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Default to fail-closed for deployments where the guard is a security boundary: ```javascript } catch (err) { console.error(`[message-guard] Evaluation failed: ${err.message}`); return { content: SAFE_FALLBACK_EN }; } ``` 2. Select the fallback language independently of the failed remote evaluation. 3. Distinguish transient failures from permanent configuration errors, but do not release unreviewed content in high-risk contexts. 4. Add retry limits, exponential backoff, quota monitoring, and a circuit breaker that continues returning safe fallback content. 5. Alert operators when evaluation availability falls below an established threshold. 6. Add local deterministic checks for secrets and critical prohibited categories as an independent layer. 7. Test timeout, DNS failure, HTTP error, malformed JSON, quota exhaustion, and absent-key scenarios. 8. If availability requirements demand fail-open behavior, explicitly label the component as advisory rather than an enforcement boundary and obtain documented risk acceptance. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:44
Finding
Dependency installation instruction does not pin an audited version<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:44-47` **Vulnerability Type**: Unpinned Third-Party Dependency **Risk Level**: Low ### Vulnerable Code ```bash npm install node-fetch # if not using native fetch ``` ### Technical Analysis The installation command resolves the current package release rather than an explicitly reviewed version. This makes installation results dependent on registry state at execution time and reduces build reproducibility. The named `node-fetch` package is not shown to be malicious, and the instruction is optional. Nevertheless, installing an unpinned package can introduce incompatible, vulnerable, or compromised future releases. npm lifecycle scripts may execute during installation with the privileges of the account running npm. ### Attack Path 1. An operator follows the documented dependency installation instruction. 2. npm resolves the package version available under the active semver/default resolution state at that time. 3. A future release or transitive dependency contains a vulnerability or is compromised. 4. The package is downloaded into the project; applicable lifecycle scripts may execute during installation. 5. Malicious code could act with the installer account's filesystem and network privileges, or vulnerable runtime code could affect the deployed guard. ### Impact Assessment No current malicious package or dependency-confusion event was demonstrated. The risk is prospective supply-chain exposure and non-reproducible deployment. If a resolved package or transitive dependency were compromised, impact could include code execution with the installer or application account's privileges, access to environment variables such as `GOOGLE_GENAI_API_KEY`, modification of project files, and outbound network access. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer the native `fetch` implementation on supported Node.js versions, eliminating the optional dependency. 2. If `node-fetch` is required, pin an audited exact version rather than installing the latest release implicitly. 3. Commit and review `package-lock.json`, then use `npm ci` in deployment pipelines. 4. Run dependency vulnerability and license scanning in continuous integration. 5. Review transitive dependencies and package lifecycle scripts before upgrades. 6. Use a controlled registry, integrity verification, and automated update tooling with mandatory review. 7. Avoid running package installation as root or with access to production secrets. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (1)

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The skill defines fixed English and Chinese fallback messages and later selects Chinese solely by detecting Chinese characters in the outbound content. This imposes a locale policy in natural-language behavior without explicit user opt-in or documentation that the skill is limited to English/Chinese contexts.

Static analysis

Detected: suspicious.prompt_injection_instructions

Prompt-injection style instruction pattern detected.

Warn
Code
suspicious.prompt_injection_instructions
Location
SKILL.md:331