Back to skill

Security audit

HN Reader

Security checks for vulnerabilities and agentic risk

Overview

The skill is a read-only Hacker News helper with dependency and output-sanitization risks, but no hidden credential access, persistence, or destructive behavior was found.

Reasonable to install only if you accept a small public-network CLI skill, but update and pin dependencies before use, narrow activation phrases to HN-specific wording, and sanitize Hacker News text before printing it in terminals.

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

T09 · Insecure Skill Coding Practices

Warning
Location
src/index.js:49
Finding
Terminal Escape-Sequence Injection Through Untrusted Hacker News Content<![CDATA[ ## Vulnerability Details **File Location**: `src/index.js:49-71` and `src/index.js:161-163` **Vulnerability Type**: Unsanitized terminal output **Risk Level**: Medium ### Vulnerable Code ```js // Format story for output function formatStory(story, index) { const points = story.score || 0; const comments = story.descendants || 0; const by = story.by || 'unknown'; const time = new Date(story.time * 1000).toLocaleString('pt-BR'); let url = ''; if (story.url) { try { const urlObj = new URL(story.url); url = ` (${urlObj.hostname})`; } catch {} } return `${index + 1}. ${story.title}${url} 🔼 ${points} pontos | 💬 ${comments} comentários | por @${by} 🕐 ${time} 🔗 https://news.ycombinator.com/item?id=${story.id}`; } // Format user profile function formatUser(user) { if (!user) return 'Usuário não encontrado.'; const created = new Date(user.created * 1000).toLocaleString('pt-BR'); const about = user.about || 'Sem descrição.'; return `👤 @${user.id} 📊 Karma: ${user.karma} 📅 Criado em: ${created} 📝 Sobre: ${about}`; } ``` The item-detail path similarly prints remote text: ```js console.log(formatStory(story, 0)); if (story.text) { console.log(`\n📝 Texto: ${story.text.replace(/<[^>]*>/g, '')}`); } ``` ### Technical Analysis The application retrieves public, user-generated Hacker News data and writes fields such as `story.title`, `story.by`, `story.text`, `user.id`, and `user.about` directly to the terminal. The regular expression applied to `story.text` removes strings resembling HTML tags, but it does not remove terminal control characters or ANSI escape sequences. HTML stripping and terminal-output sanitization address different encoding contexts. If attacker-controlled content contains supported control sequences, a compatible terminal may interpret those bytes instead of displaying them literally. Relevant sequence classes include: - ANSI/CSI sequences that change colors, cursor ...[truncated 1898 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Sanitize every remotely sourced string before writing it to a terminal. The remediation should cover all Hacker News fields rather than only `story.text`. 1. Add a centralized terminal-output sanitizer that removes: - ANSI CSI escape sequences. - OSC sequences, including hyperlink and clipboard-related sequences. - Other C0 and C1 control characters, except explicitly permitted formatting characters such as newline where required. 2. Apply the sanitizer to `story.title`, `story.by`, `story.text`, `user.id`, and `user.about`. 3. Use a maintained package designed to strip ANSI sequences, pinned through the lockfile, while separately removing non-ANSI control characters. 4. Normalize or escape line breaks where remote values are expected to occupy only one output line. 5. Add tests containing CSI, OSC, carriage-return, backspace, null-byte, and multiline payloads. 6. Consider escaping remote content into a visibly quoted representation when preserving the exact value is more important than presentation. Example hardening pattern: ```js const stripAnsi = require('strip-ansi'); function sanitizeTerminal(value, { allowNewlines = false } = {}) { let result = stripAnsi(String(value ?? '')); // Remove OSC sequences not covered by basic ANSI handling. result = result.replace(/\x1B\][^\x07\x1B]*(?:\x07|\x1B\\)/g, ''); // Remove C0/C1 controls, optionally retaining CR/LF formatting. result = allowNewlines ? result.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '') : result.replace(/[\x00-\x1F\x7F-\x9F]/g, ''); return result; } ``` Use the function before interpolation: ```js const title = sanitizeTerminal(story.title); const author = sanitizeTerminal(story.by); const about = sanitizeTerminal(user.about, { allowNewlines: true }); const text = sanitizeTerminal( story.text.replace(/<[^>]*>/g, ''), { allowNewlines: true } ); ``` The sanitizer should be validated against the terminal environments sup ...[truncated 90 chars]
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Known Vulnerable Dependency: axios==1.13.6 — 16 advisory(ies): CVE-2026-44494 (axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `co); CVE-2026-44495 (axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollut); CVE-2025-62718 (Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF) +13 more

High
Category
Supply Chain
Confidence
97% confidence
Finding
The lockfile pins axios 1.13.6, and the supplied advisory set includes high-risk issues such as SSRF-related proxy bypass and prototype-pollution-enabled request/response manipulation. Because axios is the primary direct dependency in this skill, any code using it for outbound HTTP requests could inherit those weaknesses, making this a genuine supply-chain risk rather than a contextual false positive.

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
90% confidence
Finding
The presence of form-data 4.0.5 introduces a reported CRLF injection risk in multipart field names and filenames. If any part of the skill constructs multipart requests from user-controlled input, an attacker may be able to smuggle additional headers or manipulate downstream HTTP parsing, which can lead to request tampering or data exposure.

Known Vulnerable Dependency: axios==1.13.6 — 16 advisory(ies): CVE-2026-44494 (axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `co); CVE-2026-44495 (axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollut); CVE-2025-62718 (Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF) +13 more

High
Category
Supply Chain
Confidence
90% confidence
Finding
The project permits installation of axios versions that static analysis associates with multiple published advisories, including SSRF/proxy-bypass and prototype-pollution-related issues. In a skill that monitors and searches HackerNews via external API calls, a vulnerable HTTP client is materially relevant because it processes untrusted network input and outbound request configuration, increasing the chance of request tampering, credential leakage, or response hijacking if exploitable code paths are present.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger list includes very generic terms like "new", "latest", "jobs", and "best" without requiring HackerNews-specific context. In an agent environment, these phrases can cause unintended skill activation during unrelated user requests, leading to confused-deputy behavior or incorrect external API access when the user did not intend to invoke this skill.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The code hard-codes the locale 'pt-BR' for date formatting, and the surrounding CLI messages are also written in Portuguese. This imposes a specific language/locale on all users without offering opt-in, selection, or documenting that the skill is intentionally region-specific.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
Natural-language instructions and trigger descriptions are presented only in Portuguese, but there is no statement that the skill is intended specifically for Portuguese-speaking users or that other languages are supported. This can conflict with organizational language-choice expectations when no opt-in or justification is provided.

Known Vulnerable Dependency: follow-redirects==1.15.11 — 1 advisory(ies): CVE-2026-40895 (follow-redirects leaks Custom Authentication Headers to Cross-Domain Redirect Ta)

Low
Category
Supply Chain
Confidence
89% confidence
Finding
The lockfile includes follow-redirects 1.15.11, which is reported to leak custom authentication headers across cross-domain redirects. Even though the scanner labels the advisory LOW, in an agent or integration context this can expose API keys, bearer tokens, or internal auth headers to attacker-controlled redirect targets, raising practical risk.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"start": "node src/index.js"
  },
  "dependencies": {
    "axios": "^1.6.0"
  }
}
Confidence
95% confidence
Finding
The dependency is specified with a caret range (^1.6.0), which allows automatic installation of newer 1.x releases rather than a single reviewed version. This weakens supply-chain control and can unexpectedly introduce vulnerable or malicious upstream changes, especially because the separate finding indicates resolution may reach a vulnerable axios release.

Static analysis

No suspicious patterns detected.