Back to skill

Security audit

motion-graphics

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly aligned with making motion graphics, but it grants broad network, package-execution, browser, and persistent skill-update authority that users should review before installing.

Install only if you are comfortable running HyperFrames tooling that can update global skills, fetch registry blocks, contact external search/media providers, and bake maps through a networked headless browser. Use a restricted workspace or container, avoid exposing sensitive environment variables, do not use the Gemini auto locator on confidential images without explicit approval, and prefer pinned/vendored dependencies and validated map/output parameters.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
Findings (4)

T08 · Insecure Dependencies

Error
Location
SKILL.md:14
Finding
Unpinned npm Package Execution and Automatic Global Skill Updates<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:14`, `SKILL.md:67`, `SKILL.md:84-90`, `catalog-map.md:6-16`, `agents/builder.md:9-10` **Vulnerability Type**: Unpinned third-party package execution and mutable dependency retrieval **Risk Level**: High ### Vulnerable Code ```markdown > **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update motion-graphics`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them. ``` ```bash PROJECT_DIR="${MOTION_GRAPHICS_DIR:-videos/<project-name>}" mkdir -p "$(dirname "$PROJECT_DIR")" npx hyperframes init "$PROJECT_DIR" --non-interactive --example=blank --skill=motion-graphics ``` ```markdown `init` checks the installed skills against the latest on GitHub and updates the global set if any are out of date. ``` The same unpinned package is used for catalog searches, block installation, validation, preview, rendering, and feedback: ```markdown npx hyperframes catalog --query "<the move, in plain English>" --json npx hyperframes add <block> npx hyperframes feedback --search-miss "<query>" --wanted "<the move>" --tier <tier from the envelope> ``` ### Technical Analysis The workflow repeatedly executes `npx hyperframes` without an exact version, lockfile, integrity hash, or package signature. When the package is not already available in an appropriate local installation, `npx` can download and execute the package currently published under that registry name. The resulting execution is therefore not restricted to the implementation reviewed during this audit. The effective package can change after review because of a new release, compromised publisher account, registry compromise, dependency compromise, or package-name takeover. The initialization operation adds further exposure because it is documented as updating a global set of installed Skills. Downloaded catalog blocks are ...[truncated 1898 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every `npx` invocation to an audited exact version, for example: ```bash npx --yes hyperframes@X.Y.Z ... ``` 2. Install dependencies through a project-level manifest and committed lockfile rather than resolving them dynamically for each execution. 3. Use `npm ci --ignore-scripts` where lifecycle scripts are unnecessary, and explicitly audit scripts before allowing them. 4. Verify package provenance, signatures, and registry integrity. Record expected package tarball hashes. 5. Separate Skill updates from normal initialization. Never implicitly update global Skills as a side effect of creating a project. 6. Require explicit user approval that identifies the exact old and new versions before updating global content. 7. Pin catalog blocks by immutable version or content digest and verify their hashes before insertion. 8. Run third-party tooling in a restricted container or sandbox with minimal filesystem access, no unnecessary credentials, and controlled network egress. 9. Re-audit downloaded Skill and catalog content before executing or rendering it. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
categories/maps/bake-basemap.mjs:117
Finding
Runtime Remote JavaScript Execution in an Unsandboxed Browser<![CDATA[ ## Vulnerability Details **File Location**: `categories/maps/bake-basemap.mjs:117-119`, `categories/maps/bake-basemap.mjs:181-199` **Vulnerability Type**: Remote active-content retrieval and execution with browser sandbox disabled **Risk Level**: High ### Vulnerable Code The generated page loads executable JavaScript directly from a third-party CDN: ```js const PAGE = `<!doctype html><html><head> <link href="https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js"></script> <script src="https://cdn.jsdelivr.net/npm/topojson-client@3.1.0/dist/topojson-client.min.js"></script> <style>*{margin:0}html,body{width:1920px;height:1080px;overflow:hidden;background:#05070d}#map{width:1920px;height:1080px}.maplibregl-control-container{display:none!important}</style> </head><body><div id="map"></div><script> ``` The page is then run in Chrome with the browser sandbox disabled: ```js // --no-sandbox is intentional: trusted Source-time bake, headless, often root/CI; deps are version-pinned above. const browser = await puppeteer.launch({ executablePath: resolveChrome(), headless: true, args: [ "--no-sandbox", "--hide-scrollbars", "--use-gl=angle", "--use-angle=swiftshader", "--enable-unsafe-swiftshader", "--enable-webgl", "--window-size=1920,1080", ], }); try { const page = await browser.newPage(); await page.setViewport({ width: 1920, height: 1080, deviceScaleFactor: 1 }); await page.setContent(PAGE, { waitUntil: "load" }); ``` ### Technical Analysis Although exact dependency versions appear in the CDN URLs, the files are still retrieved at runtime and are not protected by Subresource Integrity attributes, a locally verified content digest, or an immutable vendored copy. Consequently, the code that executes can differ from the code reviewed in this project. A CDN compromise, package-publisher compromise, ...[truncated 2017 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Vendor audited copies of MapLibre and TopoJSON inside the project or a trusted immutable dependency bundle. 2. Verify vendored files against committed SHA-256 or stronger hashes before launching the browser. 3. If remote resources remain unavoidable, add Subresource Integrity metadata and a restrictive Content Security Policy. Vendoring is still preferable. 4. Remove `--no-sandbox`. Configure the environment so Chrome can use its normal namespace and seccomp sandbox. 5. Never run this helper as root. Use a dedicated unprivileged account or rootless container. 6. Remove `--enable-unsafe-swiftshader` unless it is strictly required and the execution environment is strongly isolated. 7. Apply network egress rules that permit only required map endpoints and deny access to private, loopback, metadata, and link-local networks. 8. Run the bake in an ephemeral container with a read-only root filesystem, a narrowly mounted output directory, no sensitive environment variables, and resource limits. 9. Cache and verify all runtime inputs so the claimed deterministic and offline rendering properties are actually enforced. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
categories/maps/bake-basemap.mjs:61
Finding
Directory Traversal Through the Environment-Controlled Output Name<![CDATA[ ## Vulnerability Details **File Location**: `categories/maps/bake-basemap.mjs:61`, `categories/maps/bake-basemap.mjs:111-113`, `categories/maps/bake-basemap.mjs:211`, `categories/maps/bake-basemap.mjs:226-230`, `categories/maps/bake-basemap.mjs:257` **Vulnerability Type**: Path traversal and arbitrary file creation or overwrite **Risk Level**: High ### Vulnerable Code The output name is accepted directly from the environment: ```js const NAME = process.env.NAME || "basemap"; ``` It is then concatenated into multiple filesystem destinations without filename validation or containment checks: ```js const OUT = process.env.OUT || process.cwd(); // artifacts → workspace (cwd), NOT the installed skill dir const framesDir = join(OUT, "frames-" + NAME); mkdirSync(framesDir, { recursive: true }); ``` ```js await page.screenshot({ path: join(framesDir, `f${String(i).padStart(4, "0")}.png`), clip: { x: 0, y: 0, width: 1920, height: 1080 }, optimizeForSpeed: true, }); ``` ```js const mp4 = join(OUT, NAME + ".mp4"), pat = join(framesDir, "f%04d.png"); const ff = spawnSync( "ffmpeg", [ "-y", "-framerate", String(FPS), "-i", pat, "-c:v", "libx264", "-pix_fmt", "yuv420p", "-g", "1", "-crf", "16", "-movflags", "+faststart", mp4, ], { stdio: "ignore" }, ); ``` ```js writeFileSync(join(OUT, NAME + "-coords.json"), JSON.stringify(coords)); ``` ### Technical Analysis `NAME` is treated as a simple filename but is not restricted to filename-safe characters. Node.js `join()` normalizes path components, including `..`. A value containing path separators and enough parent-directory segments can therefore cause the normalized destination to escape `OUT`. The MP4 path and coordinate JSON path are directly derived from `join(OUT, NAME + suffix)`. FFmpeg is invoked with `-y`, which authorizes overwriting an existing destination without prompting. The frame directory also incorporates `NAME`, ...[truncated 1492 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict `NAME` to a basename-only allowlist: ```js if (!/^[A-Za-z0-9_-]+$/.test(NAME)) { throw new Error("NAME must contain only letters, digits, underscores, and hyphens"); } ``` 2. Reject absolute paths, path separators, `.` components, `..` components, null bytes, and platform-specific separators. 3. Resolve and verify every output destination before use: ```js import { resolve, relative, isAbsolute } from "node:path"; const outRoot = resolve(OUT); const destination = resolve(outRoot, `${NAME}.mp4`); const rel = relative(outRoot, destination); if (rel.startsWith("..") || isAbsolute(rel)) { throw new Error("Output path escapes OUT"); } ``` 4. Apply the containment check independently to frame, MP4, and JSON paths. 5. Create outputs with non-overwrite semantics where practical. Avoid unconditional FFmpeg `-y`, or require explicit approval before replacing an existing file. 6. Run the helper with a filesystem sandbox that permits writes only beneath a dedicated output directory. 7. Treat environment variables as untrusted configuration and validate all of them at the program boundary. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
categories/maps/bake-basemap.mjs:62
Finding
Server-Side Request Forgery Through an Arbitrary Map Tile Template<![CDATA[ ## Vulnerability Details **File Location**: `categories/maps/bake-basemap.mjs:62`, `categories/maps/bake-basemap.mjs:104-109`, `categories/maps/bake-basemap.mjs:124-127` **Vulnerability Type**: Unrestricted outbound requests to attacker-selected URLs **Risk Level**: Medium ### Vulnerable Code The tile style can be supplied through an environment variable: ```js const STYLE = process.env.STYLE || "satellite"; // satellite | dark | light | raw {z}/{x}/{y} template ``` Unknown values are used directly as a tile URL template: ```js const TILES = { satellite: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", dark: "https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png", light: "https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png", }[STYLE] || STYLE; // STYLE may also be a raw {z}/{x}/{y} template ``` The resulting value is passed to MapLibre as a network source: ```js var map=new maplibregl.Map({container:"map",style:{version:8,projection:{type:"mercator"}, sources:{s:{type:"raster",tiles:[${JSON.stringify(TILES)}],tileSize:256,maxzoom:19}}, layers:[{id:"bg",type:"background",paint:{"background-color":"#05070d"}},{id:"s",type:"raster",source:"s"}]}, center:CENTER,zoom:ZSTART,pitch:0,bearing:0,interactive:false,attributionControl:false,fadeDuration:0,preserveDrawingBuffer:true,maxTileCacheSize:6000}); ``` ### Technical Analysis Any `STYLE` value not equal to one of the three predefined names becomes a browser-requested tile template. The program performs no validation of: - URL scheme. - Destination hostname. - Resolved IP address. - Port. - Redirect target. - Access to loopback, private, link-local, or cloud metadata networks. When the generated page initializes MapLibre, the headless browser requests tiles derived from this attacker-controlled template. This creates a server-side request forgery primitive from the perspective of the machine running the Skill. T ...[truncated 1744 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer a closed allowlist containing only the predefined tile providers. 2. If custom providers are required, parse them with the standard `URL` class and permit only `https:`. 3. Resolve hostnames and reject every loopback, private, carrier-grade NAT, link-local, multicast, reserved, and cloud metadata address for both IPv4 and IPv6. 4. Revalidate every redirect destination and every DNS resolution to mitigate redirect-based and DNS-rebinding bypasses. 5. Restrict permitted ports to 443 unless a documented provider requires another port. 6. Require custom providers to match an administratively configured hostname allowlist. 7. Apply process-level or container-level egress controls so the browser cannot reach internal or metadata networks even if application validation fails. 8. Disable access to `file:`, `data:`, `javascript:`, and other non-HTTPS schemes. 9. Log the validated final hostname and resolved address without logging credentials or sensitive query parameters. 10. Add security tests covering IPv6 loopback, integer and alternate IP representations, redirects, DNS rebinding, and metadata endpoints. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The `auto` command adds a detector-backed network inference path that is outside the stated purpose of a motion-graphics skill and bypasses the local-only grid workflow described in the header. In practice, this creates an undocumented data egress capability where arbitrary input images can be transmitted to a third-party API, expanding the trust boundary and attack surface.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The code reads `GEMINI_API_KEY` from the environment and uses it to send image contents to an external Google API, which is unrelated to the declared motion-graphics rendering scope. This creates a real confidentiality and supply-chain risk: sensitive user images may be exfiltrated off-host without clear disclosure, and the presence of credential-driven network behavior makes the skill materially more dangerous.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
The header explicitly claims operation 'WITHOUT any detector API or key,' yet the file implements an API-key-based `auto` detector path. This misleading documentation can cause reviewers and users to underestimate privacy and network risks, making it easier for sensitive image uploads to occur without informed approval.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The `fetch` call transmits base64-encoded image data to an external API without any user-facing warning, consent prompt, or in-code disclosure at the point of use. For a motion-graphics skill, this is especially concerning because uploaded assets may contain proprietary creative work, client data, or internal visuals that users reasonably expect to remain local.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The guide explicitly instructs the skill to use external search providers and to rehost remote assets, but it does not require user disclosure or consent before transmitting project-derived queries or fetching third-party URLs. This can expose confidential project context, create privacy/compliance issues, and cause the agent to contact untrusted remote infrastructure based on user work products.

Missing User Warnings

Low
Confidence
89% confidence
Finding
The file pulls GSAP from a third-party CDN at runtime, which introduces a supply-chain and availability risk outside the control of the skill author. If the CDN response is tampered with, blocked, or unexpectedly changed, arbitrary script would execute in the page context and affect any environment rendering this template.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
grounding/locate.mjs:192