Back to skill

Security audit

Youtube Podcast Generator

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but its local web app exposes generated files and destructive cleanup actions too broadly and has exploitable handling of AI-produced content.

Install only if you are comfortable running a local Node server that sends transcript and script content to Gemini and OpenAI, stores generated files locally, and exposes those files through local web routes. The author should fix the unsafe innerHTML rendering, strict session ID validation, session authorization, delete behavior, server timeouts, and lockfile mismatch before broad use.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T09 · Insecure Skill Coding Practices

Error
Location
public/script.js:264
Finding

AI-Controlled Content Rendered Through Unsafe innerHTML

Content
View full analysis
` `).join(''); } else log("❌ " + data.error); ``` ### Technical Analysis The values `r.text`, `r.timestamp`, and `r.seconds` are returned by Gemini after the model processes transcript content. They are interpolated directly into an HTML template and assigned to `innerHTML` without HTML escaping, sanitization, or schema validation. AI output must be treated as untrusted. An attacker who controls or influences the source video captions can insert prompt-injection content intended to make Gemini return HTML markup. If the returned value contains an executable element or event-handler attribute, the browser may execute it in the localhost application's origin. The backend only parses the model response as JSON: ```javascript const json = JSON.parse(result.text.replace(/```json|```/g, '')); res.json({ results: json.map(r => ({ ...r, seconds: timestampToSeconds(r.timestamp) })) }); ``` JSON parsing does not make embedded HTML safe, and the returned object is not checked against a strict schema. ### Attack Path 1. An attacker publishes or controls a YouTube video whose captions contain prompt-injection instructions. 2. The user transcribes that video through the Skill. 3. The user performs semantic search, causing the VTT content to be sent to Gemini. 4. The embedded instructions influence Gemini to return a JSON field containing malicious HTML. 5. The frontend interpolates that field into `search-results.innerHTML`. 6. ...[truncated 829 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
index.js:448
Finding

Empty Sanitized Identifier Can Delete the Entire Downloads Directory

Content
View full analysis
{ const rawId = req.body.id || req.query.id; if (!rawId) return res.status(400).json({ error: "Missing ID" }); const safeId = sanitizeId(rawId); if (jobs[safeId]) { if (jobs[safeId].process) jobs[safeId].process.kill('SIGKILL'); jobs[safeId].status = 'cancelled'; delete jobs[safeId]; } const folder = path.join(downloadsDir, safeId); if (fs.existsSync(folder)) { try { fs.rmSync(folder, { recursive: true, force: true }); res.json({ success: true, message: "Folder safely removed." }); } catch (e) { res.status(500).json({ error: "Failed to delete" }); } } else res.status(404).json({ error: "Folder not found" }); }); ``` ### Technical Analysis `sanitizeId` removes all characters outside its allowlist but does not verify that any characters remain. A nonempty identifier composed entirely of rejected characters, such as `../`, becomes an empty string. Node.js resolves: ```javascript path.join(downloadsDir, '') ``` to `downloadsDir` itself. The deletion route then executes: ```javascript fs.rmSync(folder, { recursive: true, force: true }); ``` Consequently, an identifier intended to select one session can instead select and recursively delete the root directory containing every session. This is not conventional directory traversal because traversal characters are removed. It is a path-collapse vulnerability caused by accepting an empty sanitized result. ### Attack Path 1. A local caller sends a request such as: ```http DELETE /api/ ...[truncated 830 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
index.js:25
Finding

Generated Assets and Destructive Session APIs Lack Authorization

Content
View full analysis
{ const safeId = sanitizeId(req.query.id); if (!jobs[safeId]) return res.json({ status: 'not_found' }); const { process, ...safeData } = jobs[safeId]; res.json(safeData); }); ``` ```javascript app.delete('/api/delete-folder', (req, res) => { const rawId = req.body.id || req.query.id; if (!rawId) return res.status(400).json({ error: "Missing ID" }); const safeId = sanitizeId(rawId); if (jobs[safeId]) { if (jobs[safeId].process) jobs[safeId].process.kill('SIGKILL'); jobs[safeId].status = 'cancelled'; delete jobs[safeId]; } const folder = path.join(downloadsDir, safeId); if (fs.existsSync(folder)) { try { fs.rmSync(folder, { recursive: true, force: true }); res.json({ success: true, message: "Folder safely removed." }); } catch (e) { res.status(500).json({ error: "Failed to delete" }); } } else res.status(404).json({ error: "Folder not found" }); }); ``` ```javascript app.get('/api/download-zip', (req, res) => { const rawId = req.query.id; if (!rawId) return res.status(400).send("Missing ID"); const safeId = sanitizeId(rawId); if (jobs[safeId] && (jobs[safeId].status === 'processing' || jobs[safeId].status === 'queued')) { return res.status(409).send("Podcast is still rendering. Please try again when complete."); } const folderPath = path.join(downloadsDir, safeId); if (!fs.existsSync(folderPath)) return res.status(404).send("Files not found."); res.attachment(`podcast_assets_${safeId}.zip`); const archive = archiver('zi ...[truncated 2502 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
package-lock.json:7
Finding

OpenAI Runtime Dependency Is Missing from the Dependency Lockfile

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • 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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (40)

Known Vulnerable Dependency: protobufjs==7.5.4 — 12 advisory(ies): CVE-2026-44294 (protobuf.js: Denial of service from crafted field names in generated code); CVE-2026-44293 (protobuf.js: Code injection through bytes field defaults in generated toObject c); CVE-2026-44289 (protobuf.js: Denial of service through unbounded protobuf recursion) +9 more

Critical
Category
Supply Chain
Confidence
94% confidence
Finding

protobufjs 7.5.4 is a real critical-risk dependency with multiple advisories including code generation and denial-of-service issues. In this skill, protobufjs is brought in by @google/genai, so untrusted model/service data passing through serialization logic increases the importance of keeping this stack patched even if the lockfile alone cannot prove a specific exploit path.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The documented purpose omits materially sensitive behaviors: semantic search over transcript content, deletion endpoints, ZIP packaging, and persistent static serving of generated files. This mismatch prevents informed consent and can expose users to unexpected data retention, local file serving, and destructive actions that are more powerful than a simple transcript-to-podcast workflow suggests.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · index.js (reported line 199)May include surrounding context.

js
app.post('/api/draft-script', async (req, res) => {
    const { id, host1 = 'Alex', host2 = 'Sam', targetLanguage = 'English' } = req.body;
    const apiKey = getApiKey(req);
    if (!apiKey) return res.status(401).json({ error: "API Key required in .env or header" });

    const safeId = sanitizeId(id);
    const txtPath = path.join(downloadsDir, safeId, 'original.txt');

Known Vulnerable Dependency: brace-expansion==2.0.2 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
92% confidence
Finding

brace-expansion 2.0.2 has multiple reported expansion-related denial-of-service issues, and this is a real vulnerable version in the lockfile. While exploitation depends on attacker influence over glob or brace patterns, archive/glob-related dependencies make this more relevant than a purely unused package because malicious patterns could trigger CPU or memory exhaustion.

Content

No source excerpt is available for this finding.

Possible Typosquatting: 'gaxios' resembles popular package 'axios'

High
Category
Supply Chain
Confidence
70% confidence
Finding

Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: ip-address==10.1.0 — 2 advisory(ies): CVE-2026-69192 (ip-address: Address4 decodes leading-zero octets as decimal while resolvers deco); CVE-2026-42338 (ip-address has XSS in Address6 HTML-emitting methods)

High
Category
Supply Chain
Confidence
80% confidence
Finding

ip-address 10.1.0 is a real flagged dependency pulled in by express-rate-limit. The listed XSS issue is probably irrelevant unless the library's HTML-emitting helpers are used, but the parsing inconsistency advisory could affect IP normalization or rate-limit logic if attacker-supplied addresses are trusted behind proxies.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: lodash==4.17.23 — 2 advisory(ies): CVE-2025-13465 (lodash vulnerable to Prototype Pollution via array path bypass in `_.unset` and ); CVE-2021-23337 (lodash vulnerable to Code Injection via `_.template` imports key names)

High
Category
Supply Chain
Confidence
90% confidence
Finding

lodash 4.17.23 is a genuinely vulnerable version with known prototype-pollution and template-related issues. Even though the lockfile alone does not show direct use of dangerous APIs like _.template or path-based object mutation, the package is present and old utility libraries commonly become reachable through application data handling.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: path-to-regexp==8.3.0 — 2 advisory(ies): CVE-2026-4923 (path-to-regexp vulnerable to Regular Expression Denial of Service via multiple w); CVE-2026-4926 (path-to-regexp vulnerable to Denial of Service via sequential optional groups)

High
Category
Supply Chain
Confidence
86% confidence
Finding

path-to-regexp 8.3.0 is present through Express router components, and the cited issues are ReDoS/DoS conditions from crafted route patterns. This is a real dependency risk, though practical exploitability usually depends on whether the application defines complex vulnerable route expressions rather than attackers supplying arbitrary patterns at runtime.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: ws==8.19.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
84% confidence
Finding

ws 8.19.0 is a real vulnerable version with reported memory disclosure and memory exhaustion issues. Because it is pulled in by @google/genai, risk depends on whether websocket features are actually used, but if enabled, malformed peer input could affect confidentiality or availability.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill advertises capabilities that require environment variable access and outbound network access, but it does not declare an explicit tool scope such as permissions or allowed-tools. That weakens containment and user awareness, making it easier for the agent to invoke broader capabilities than the skill description clearly authorizes, including access to API keys and external services.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The description explains how to configure API keys and run the workflow but does not clearly warn that transcript-derived content, generated scripts, and possibly related metadata are sent to Gemini and OpenAI. Users may assume processing is local because the server binds to localhost, when in fact third-party services receive substantive content.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
79% confidence
Finding

The invocation phrase is broad enough that ordinary user requests about creating a podcast from a YouTube video could unintentionally trigger the skill. In a skill that can start a local server, call external APIs, and generate files, accidental activation can lead to unintended network use, secret consumption, and content processing.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
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.

Content

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

md
*(The agent will execute `kill $(cat .podcaster.pid)` or `pkill -f "node index.js"` to target the specific process safely).*

## Storage & File Outputs
Files are saved to `downloads/<session_id>/` inside the skill directory. The server includes an hourly garbage collector that automatically deletes inactive sessions.
* **Audio:** `podcast.m4a`
* **Captions:** `podcast.vtt`
* **Scripts:** `script.txt` and `original.txt`

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The code silently reads credentials from process environment variables or request headers to call external AI services, but there is no confirmation prompt, visible user-facing log, or explanatory comment/docstring near the credential handling for the user. Because this route behavior involves sensitive credential use and outbound service access, some form of disclosure is expected under the code-file warning criteria.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

Transcript content is sent to Gemini for semantic search without any visible consent, disclosure, minimization, or tenant isolation controls. Users may assume transcripts remain local, but this route transfers potentially sensitive video-derived text to a third party, creating privacy and compliance risk.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The search query is interpolated directly into an LLM prompt alongside transcript content, allowing a user to phrase the 'query' as instructions to summarize, reveal, or transform transcript data outside the intended search semantics. This is a prompt-injection and scope-control weakness that can bypass product expectations and expose more transcript content than intended.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The full transcript is transmitted to Gemini for script generation with no user-facing notice or consent mechanism. Because transcripts may contain sensitive or copyrighted content, undisclosed transfer to a third-party model increases confidentiality, privacy, and policy risk.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The transcript text is inserted verbatim into the script-generation prompt, so any adversarial or instruction-like content in the transcript can steer the model to ignore formatting rules, reproduce sensitive text, or inject undesirable output. Since the skill processes untrusted transcript content from external videos, prompt injection is realistically reachable in this context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

User-provided script text is sent to OpenAI TTS without any visible disclosure about third-party processing. If scripts contain personal, confidential, or proprietary information, this can cause unintended external data exposure.

Content

No source excerpt is available for this finding.

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
97% confidence
Finding

Setting server.timeout to 0 disables request timeouts, allowing connections to remain open indefinitely. Even on localhost, a local process or proxied client can exploit this to tie up worker resources and contribute to denial of service.

Content

Scanner excerpt · index.js (reported line 507)May include surrounding context.

js
console.log(`🚀 Hardened Studio running securely at http://${host}:${port}`);
    console.log(`🔒 Bound exclusively to 127.0.0.1 (Local Access Only)`);
});
server.timeout = 0;
server.keepAliveTimeout = 0; 
server.headersTimeout = 0;

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
97% confidence
Finding

Setting keepAliveTimeout to 0 permits persistent connections to remain open without bound, increasing susceptibility to connection-hoarding and resource exhaustion. This weakens resilience against slow-client and local DoS scenarios.

Content

Scanner excerpt · index.js (reported line 508)May include surrounding context.

js
console.log(`🔒 Bound exclusively to 127.0.0.1 (Local Access Only)`);
});
server.timeout = 0;
server.keepAliveTimeout = 0; 
server.headersTimeout = 0;

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
98% confidence
Finding

Disabling headersTimeout allows clients to send headers arbitrarily slowly, a classic slowloris-style resource exhaustion condition. Localhost binding reduces exposure, but any local user, container peer, or trusted proxy path can still abuse it.

Content

Scanner excerpt · index.js (reported line 509)May include surrounding context.

js
});
server.timeout = 0;
server.keepAliveTimeout = 0; 
server.headersTimeout = 0;

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This code triggers a DELETE request to /api/delete-folder during beforeunload, which can remove server-side data as soon as the tab is closed. There is no visible user disclosure at that point such as a prompt, alert, or explanatory comment aimed at the user, so users may not realize closing the page destroys their session data.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The manifest describes extracting YouTube text, converting it into a multi-voice AI podcast, and showing the podcast text in WebVTT format. This route adds a separate semantic search capability over transcript captions by sending VTT content to Gemini, which is a materially different user-facing behavior not reflected in the description.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The prompt instructs the model that the entire script must be written seamlessly in the provided target language, with a default of English elsewhere in the route. This creates a language constraint without any documented user-choice policy explanation or explicit opt-in handling beyond the raw parameter default.

Content

No source excerpt is available for this finding.