Back to skill

Security audit

HYFCeph

Security checks for vulnerabilities and agentic risk

Overview

This skill runs the advertised dental-imaging workflow, but it needs Review because it handles sensitive medical images, saved API keys, reused session state, and shareable reports with weak user-facing controls.

Install only if you are comfortable sending cephalometric images and any patient names to the HYFCeph service and related report destinations. Use it on a private machine, clear the saved API key when done, avoid custom portal URLs unless you operate them, and do not use it for regulated patient data without reviewing the service, retention, consent, and access-control model.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/hyfceph-service-client.mjs:342
Finding

Configurable Portal Destination Can Expose API Credentials and Medical Data

Content
View full analysis

Vulnerability Details

File Location: scripts/hyfceph-service-client.mjs, lines 342-559
Vulnerability Type: Unvalidated network destination for sensitive data
Risk Level: High

Vulnerable Code

js
const { values, positionals } = parseArgs({
  options: {
    'api-key': { type: 'string' },
    'portal-base-url': { type: 'string', default: DEFAULT_PORTAL_BASE_URL },
    // ...
  },
});

const requestedPortalBaseUrl = String(values['portal-base-url'] || '').trim();
let portalBaseUrl = ensureTrailingSlash(String(
  requestedPortalBaseUrl ||
  process.env.HYFCEPH_PORTAL_BASE_URL ||
  DEFAULT_PORTAL_BASE_URL
).trim());
js
async function validateApiKey(portalBaseUrl, apiKey) {
  const endpoint = new URL('api/validate-key', portalBaseUrl).toString();
  return requestJson(endpoint, {
    method: 'POST',
    body: { apiKey },
  });
}
js
const endpoint = new URL(
  overlapMode
    ? 'api/measure/overlap'
    : imagePath
    ? 'api/measure/image'
    : (shareUrl ? 'api/measure/share-url' : 'api/measure/current-case'),
  portalBaseUrl,
).toString();

const requestBody = overlapMode
  ? await (async () => {
      const resolvedBaseImagePath = path.resolve(imagePath);
      const resolvedCompareImagePath = path.resolve(compareImagePath);
      const [baseImageBuffer, compareImageBuffer] = await Promise.all([
        fs.readFile(resolvedBaseImagePath),
        fs.readFile(resolvedCompareImagePath),
      ]);
      return {
        baseFileName: path.basename(resolvedBaseImagePath),
        baseMimeType: mimeTypeFromPath(resolvedBaseImagePath),
        baseImageBase64: baseImageBuffer.toString('base64'),
        compareFileName: path.basename(resolvedCompareImagePath),
        compareMimeType: mimeTypeFromPath(resolvedCompareImagePath),
        compareImageBase64: compareImageBuffer.toString('base64'),
        alignMode,
        genera
...[truncated 3009 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin production requests to https://hyfceph.52ortho.com/.
  2. Normalize the URL and require the protocol to be https:.
  3. Enforce an exact hostname allowlist and reject URLs containing unexpected ports, credentials, or alternate hosts.
  4. Remove the custom portal option from normal public operation. If it is needed for development, require an explicit development flag and interactive warning.
  5. Store the portal origin with the credential and refuse to send a saved key to any different origin.
  6. Require explicit credential entry when changing destinations.
  7. Apply redirect restrictions so that authentication headers and request bodies cannot be forwarded to a different origin.
  8. Document clearly that images and patient names leave the local system and obtain user confirmation before the first upload.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/hyfceph-service-client.mjs:129
Finding

HYFCeph API Key Is Persisted in Plaintext Without Explicit File Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/hyfceph-service-client.mjs, lines 98-143 and 281-288
Vulnerability Type: Insecure local credential storage
Risk Level: Medium

Vulnerable Code

js
async function writeText(filePath, content) {
  await fs.mkdir(path.dirname(filePath), { recursive: true });
  await fs.writeFile(filePath, content, 'utf8');
  return path.resolve(filePath);
}

async function writeJson(filePath, value) {
  return writeText(filePath, JSON.stringify(value, null, 2));
}
js
async function writeAuthState(state) {
  await writeJson(AUTH_STATE_PATH, state);
  return AUTH_STATE_PATH;
}
js
const directApiKey = String(explicitApiKey || '').trim();
if (directApiKey) {
  const validation = await validateApiKey(portalBaseUrl, directApiKey);
  await writeAuthState({
    apiKey: directApiKey,
    portalBaseUrl,
    owner: validation?.owner || null,
    expiresAt: validation?.expiresAt || null,
    validatedAt: nowIso(),
  });
  return {
    apiKey: directApiKey,
    authSource: 'provided',
    validation,
  };
}

Technical Analysis

A successfully validated API key is stored in full at ~/.codex/state/hyfceph-auth.json. The JSON file is not encrypted, and the code does not explicitly create the state directory with mode 0700 or the credential file with mode 0600.

The resulting permissions therefore depend on the operating system, process umask, and permissions of any pre-existing directory or file. On a permissively configured or shared system, another local account or process may be able to read the credential.

Persistent reuse is documented by the Skill and supports its convenience requirements, but storing a reusable secret as ordinary plaintext JSON without enforced access controls is not the minimum-risk implementation.

Attack Path

  1. A user provides a valid API key and completes validation.
  2. The cli ...[truncated 888 chars]
Remediation
View remediation

Remediation Suggestions

  1. Store the API key in the operating system's credential manager or keychain instead of a plaintext JSON file.
  2. If file storage must be retained, create ~/.codex/state with mode 0700.
  3. Create and rewrite the credential file with mode 0600.
  4. Check and correct permissions on pre-existing state directories and files before reading or writing credentials.
  5. Use an atomic write to a securely permissioned temporary file, then rename it into place.
  6. Keep owner metadata and non-secret settings separate from the credential.
  7. Make persistent credential storage opt-in and provide a session-only mode.
  8. Retain the existing key-removal command and document credential revocation procedures.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/hyfceph-report-pdf.mjs:1555
Finding

PDF Generation Leaves Sensitive Temporary Medical Artifacts on Disk

Content
View full analysis

Vulnerability Details

File Location: scripts/hyfceph-report-pdf.mjs, lines 1555-1585 and 1950-1961
Vulnerability Type: Unsafe temporary-file lifecycle and sensitive-data retention
Risk Level: Low

Vulnerable Code

js
async function renderHtmlToPdfWithChrome(html, outputPath) {
  const chromeBinary = await findChromeBinary();
  if (!chromeBinary) {
    throw new Error('Chrome not found');
  }

  const tempDir = await fs.mkdtemp(path.join(process.cwd(), 'hyfceph-html-pdf-'));
  const htmlPath = path.join(tempDir, 'report.html');
  await fs.writeFile(htmlPath, html, 'utf8');

  const args = [
    '--headless=new',
    '--disable-gpu',
    '--allow-file-access-from-files',
    '--no-pdf-header-footer',
    '--print-to-pdf-no-header',
    `--print-to-pdf=${outputPath}`,
    `file://${htmlPath}`,
  ];

  try {
    await execFileAsync(chromeBinary, args);
  } catch {
    const fallbackArgs = [
      '--headless',
      '--disable-gpu',
      '--allow-file-access-from-files',
      '--no-pdf-header-footer',
      '--print-to-pdf-no-header',
      `--print-to-pdf=${outputPath}`,
      `file://${htmlPath}`,
    ];
    await execFileAsync(chromeBinary, fallbackArgs);
  }

  return path.resolve(outputPath);
}
js
const svgPages = pages.map((item) => makeSvgPage(item));
const tempDir = await fs.mkdtemp(path.join(process.cwd(), 'hyfceph-pdf-'));
const rasterPages = await renderPagesToJpegs(svgPages, tempDir);
const pdfBuffer = buildJpegPdf(rasterPages);
await fs.mkdir(path.dirname(resolvedOutputPath), { recursive: true });
await fs.writeFile(resolvedOutputPath, pdfBuffer);
return path.resolve(resolvedOutputPath);

Technical Analysis

Both PDF rendering paths create temporary directories beneath the current working directory. These directories can contain a complete HTML report or per-page SVG and JPEG files. The report content can include ...[truncated 1554 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create temporary directories beneath os.tmpdir() rather than the project working directory.
  2. Wrap each temporary-directory lifecycle in try and finally.
  3. In the finally block, call:
js
await fs.rm(tempDir, {
  recursive: true,
  force: true,
});
  1. Apply restrictive permissions to temporary directories and files.
  2. Remove partial output files when report generation fails.
  3. Retain intermediates only behind an explicit debugging option that warns the user that sensitive medical data will remain on disk.
  4. Document the retention policy for final reports, result JSON files, and temporary artifacts separately.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (22)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Do not try to protect a public skill by encrypting `SKILL.md` or local scripts. That only creates client-side obfuscation. Anyone who can run the public skill c

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/hyfceph-service-client.mjs --image /path/to/ceph.png

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/hyfceph-service-client.mjs --image /path/to/ceph.png

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/hyfceph-service-client.mjs --image /path/to/ceph.png

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/hyfceph-service-client.mjs --image /path/to/ceph.png

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/hyfceph-service-client.mjs --image /path/to/ceph.png

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The prompt explicitly directs the client to persist a validated API key locally and reuse it across new conversations without any clear user-facing consent, retention policy, or storage-security warning. Because this skill also routes user uploads through a reused owner-side browser session, silent credential persistence materially increases the risk of cross-session misuse, unauthorized access by other local users/processes, and unexpected continued access to a third-party medical portal.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill declares no explicit tool scope while its behavior clearly requires network access and access to local state/files. Without a permissions boundary, an agent platform may invoke it with broader capabilities than users expect, increasing the chance of unintended data access or exfiltration during image upload, key reuse, and file reads/writes.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
98% confidence
Finding

The skill description states that the public user's request is serviced by reusing the owner's synced browser session behind the scenes. This is a serious session-persistence and session-confusion risk: one user's medical-image uploads and analysis requests may be processed under another party's authenticated session, potentially exposing account privileges, stored context, or data across trust boundaries.

Content

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

md
---
name: hyfceph
description: Run the HYFCeph cephalometric workflow through the HYFCeph portal with an API key by uploading one or two local lateral ceph images. The public user only sends images; the server reuses the owner's synced browser session behind the scenes. Save a local result JSON, write an annotated PNG, and return supported cephalometric metrics or overlap traces. Use when the user wants HYFCeph analysis, mentions HYFCeph, provides an API key, or sends ceph images.
---

# HYFCeph

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The activation text is overly broad: the skill may trigger when a user merely mentions HYFCeph, provides an API key, or sends ceph images, even without clearly requesting this workflow. In this skill, unintended invocation is more dangerous because it can reuse a saved API key, access persisted local artifacts, and upload sensitive medical images to a remote portal.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill explicitly persists a validated API key to ~/.codex/state/hyfceph-auth.json and stores result JSON/image artifacts locally, but it does not present an explicit user warning or consent step about that persistence. Because the workflow handles medical images and analysis outputs, silent local retention increases privacy, credential theft, and cross-conversation misuse risk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The reply-style section requires a fixed output language and exact Chinese phrases for core user interactions, including the clinical summary and follow-up lines. This forces a specific language without documenting user choice or opt-in, which fits the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
83% confidence
Finding

The skill is instructed to autonomously decide when to reuse stored credentials, choose operating modes based on the number of uploaded images, generate reports, and proactively disclose report links/QR artifacts in the first result reply. In a medical-imaging context, this increases the chance of acting on sensitive data and sharing access artifacts without an explicit just-in-time confirmation from the user, especially given the hidden reuse of a synced browser session behind the scenes.

Content

Scanner excerpt · agents/openai.yaml (reported line 4)May include surrounding context.

yaml
interface:
  display_name: "HYFCeph"
  short_description: "Guide the user to register, get an API key, then run Ceph by single-image upload or two-image overlap"
  default_prompt: "Use $hyfceph. Prefer the locally saved HYFCeph API key from a previous successful run before asking the user for a new one. The bundled service client persists the validated key locally and can reuse it across new conversations. If the user has not yet provided an API key in the current conversation, first try the bundled service client without --api-key. Only if the client reports that the API key is missing, invalid, or expired should you reply with a short Chinese registration prompt telling them to register at https://hyfceph.52ortho.com/ and obtain an API key. After the user provides an API key and asks to start, pass it once with --api-key so the client can save it locally. Use image-upload mode for one image, or overlap mode when the user provides two ceph images. For single-image measurement, rely on the server-side report generation and prefer the beautified portal report as the main link plus the Feishu document as the backup link. If reportQrPngPath or prettyReportQrPngPath or feishuDocQrPngPath exists, treat them as report QR images for WeChat-friendly access and show them directly after the links. If the patient name is still unknown at that moment, it is acceptable to generate the first report without a patient name and later regenerate it if the user provides the name. In overlap mode, treat the first image as the base trace and the second as the comparison trace, defaulting to SN alignment unless the user explicitly asks for FH. Do not mention current-case, browser plugin, token, share-link, or upstream login flows unless the user explicitly asks for troubleshooting. If the server reports that the remote session is temporarily unavailable, tell the user to retry later without exposing internal bridge details. Return a concise Chinese interpretation and metric summary, avoi
...[truncated 26 chars]

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

This manifest instructs the agent to use "$hyfceph" but does not explain when that invocation should occur, what exact user phrases should trigger it, or what situations should not trigger it. Without explicit scope or negative examples, the activation condition is ambiguous and could cause unintended invocation in broader contexts.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The prompt requires a short Chinese registration prompt, a concise Chinese interpretation, and specific Chinese output lines, which forces a language choice regardless of user preference. This is a natural-language policy issue because no opt-in or alternative locale behavior is offered.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code forces zh-CN locale formatting and extensively emits Chinese-only report text, which imposes a specific language/locale regardless of user preference. The policy allows locale constraints only when users are given a choice or the constraint is clearly documented and justified; neither is visible in this file.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code writes a PDF report to a caller-controlled output path and earlier generates temporary working directories under the current working directory while processing patient-identifying and medical analysis data. In a healthcare context, persisting PHI to disk without clear consent, minimization, retention controls, or secure temp handling increases the risk of local disclosure through shared directories, backups, logs, or leftover temp artifacts.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
81% confidence
Finding

The script renders user-derived HTML and image content by launching external browser and image-conversion binaries such as Chrome, sips, ImageMagick, or convert. Although execFile avoids shell injection and the HTML text is escaped, passing attacker-influenced SVG/HTML/images into complex third-party renderers can expose the host to parser/RCE vulnerabilities in those tools, which is more concerning because this skill handles untrusted uploaded files and sensitive medical content.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The code can generate local PDFs, request upload tickets, upload reports to signed URLs, and expose share/public URLs and Feishu document links that are not described in the manifest. Undisclosed secondary export and sharing paths increase the chance that sensitive medical results are redistributed or retained in external systems without the user understanding that additional copies and links are being created.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The API key is written to ~/.codex/state/hyfceph-auth.json and silently reused later, without any visible warning, consent, or file-permission hardening. Persisting reusable credentials on disk increases the blast radius of local compromise, accidental disclosure, or multi-user workstation access, especially because the key authorizes access to a medical portal backed by the owner's session.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The implementation supports api/measure/current-case and api/measure/share-url, which exceeds the declared workflow of only uploading one or two local ceph images. In this skill context, that is dangerous because it allows access to server-side browser-session data and remotely referenced cases outside the user’s explicit upload action, creating a material risk of unauthorized patient-data access through the owner's synced session.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The client base64-encodes local images and submits them, along with optional patient identifiers and report-generation flags, to remote portal endpoints without a clear runtime disclosure. In a medical-imaging workflow, silent transmission of protected health information to remote services is especially sensitive and can violate user expectations, organizational policy, or privacy obligations.

Content

No source excerpt is available for this finding.