DeadLinkFinder 🔗🔍
DeadLinkFinder is a zero-dependency Markdown integrity checker for local links, images, heading anchors, and optional external URLs.
Prevent broken links, missing image assets, and dead#anchorfragments in your documentation before they reach production.
$ deadlinkfinder docs/
🔗 DeadLinkFinder v0.2.1 — Verifying documentation integrity...
[!] DLF-001 ERROR docs/getting-started.md:28
Target not found: '../guides/setup.md'
Suggestion: Did you mean '../guides/installation.md'?
[!] DLF-002 WARNING docs/api-reference.md:104
Heading anchor not found: '#authentication-keys'
[✗] 2 issues detected across 14 scanned files (exit 1).
Emitted SARIF report to 'deadlinks.sarif' (OASIS v2.1.0 compliant).
🌟 Architecture & Capabilities
Core Engine (100% Offline, Zero Network)
- Zero External Dependencies: Built strictly using the Python Standard Library (
re,pathlib,argparse,difflib,json,urllib). Requires no third-party package installation (dependencies = []). - GitHub-style Anchor Engine: Generates GitHub-compatible heading anchors for common GFM Markdown headings, including punctuation, Unicode text, emojis, and duplicate headings (
#section,#section-1). The anchor behavior is covered by compatibility fixtures for common GitHub Markdown cases. - Local Asset Verification: Distinguishes between document pages and image assets (
.png,.jpg,.svg), reporting missing assets with dedicated rule IDs. - Cross-File Anchor Verification: Validates complex references like
[API Reference](docs/api.md#authentication)by inspecting the target file's heading tree. - Code Block Isolation: Automatically ignores fenced code blocks (
```...```) and inline backtick spans so tutorial examples never trigger false alarms. - Fuzzy "Did You Mean?" Suggestions: Suggests closest matching file paths when a relative link has a typo or moved location.
- Deterministic Exit Codes:
0(clean),1(broken references found),2(CLI error or target not found).
Optional Network Mode
- External URL Validation: When explicitly enabled via
--check-external, validates remotehttp://andhttps://links. - Timeout Management: Configurable network timeout thresholds (
--timeout 5.0) with isolated timeout error reporting (DLF-007). - Offline By Default: Fast, safe local execution by default without unexpected network calls.
🔍 Validation Rules
Rule IDs are permanent and deterministic for stable CI/CD and SARIF triage:
| Rule ID | Name | Default Level | Description |
|---|---|---|---|
DLF-001 |
Broken Local File Link | error |
Referenced relative file path does not exist on disk |
DLF-002 |
Missing Anchor Slug | warning |
Target #heading-anchor not found in target Markdown document |
DLF-003 |
Broken External URL | warning |
Remote HTTP/HTTPS URL returned an error status ($\ge 400$) |
DLF-004 |
Missing Local Image | error |
Referenced graphic or screenshot asset does not exist on disk |
DLF-005 |
Invalid Markdown Target | error |
Target destination is empty or contains malformed syntax |
DLF-006 |
Skipped Code Block Reference | note |
Informational rule indicating ignored code snippet URLs |
DLF-007 |
External URL Timeout | warning |
Remote URL did not respond within configured timeout |
🚀 Quick Start
Installation
Recommended via pipx for isolated CLI usage:
pipx install deadlinkfinder
Or install via pip:
pip install .
Or run directly from source without installation:
git clone https://github.com/umutgungorr/deadlinkfinder.git
cd deadlinkfinder
python -m deadlinkfinder .
🛠️ Usage & Examples
1. Scan Local Documentation (Core Offline Mode)
Scans local Markdown files quickly with no runtime dependencies. On standard documentation repositories, local scans typically complete in milliseconds:
deadlinkfinder
2. Export SARIF for GitHub Code Scanning
SARIF findings include stable partial fingerprints (partialFingerprints.primaryLocationLineHash) to prevent duplicate Code Scanning alerts across repeated workflow runs:
deadlinkfinder --format sarif -o results.sarif
3. Machine-Parseable JSON Output
deadlinkfinder --format json -o deadlinks.json
4. Optional External URL Checking
deadlinkfinder --check-external --timeout 5.0
🤖 CI/CD Integration (GitHub Actions)
Option A: PR-Blocking Mode (Fails build on broken links)
Use this when documentation errors should prevent pull requests from merging:
name: Documentation Integrity
on: [push, pull_request]
permissions:
contents: read
security-events: write
jobs:
deadlinkfinder:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Verify Documentation Links
run: |
python -m deadlinkfinder --format sarif -o results.sarif .
- name: Upload SARIF to GitHub Code Scanning
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: results.sarif
Option B: Advisory / Reporting-Only Mode
Use continue-on-error: true if you only want alerts reported in the GitHub Security tab without blocking PR merges:
- name: Verify Documentation Links (Advisory)
run: |
python -m deadlinkfinder --format sarif -o results.sarif .
continue-on-error: true
Note: Remove
continue-on-error: trueif broken documentation should fail the workflow.
📋 Scope & Known Limitations
Supported
- Standard Markdown links:
[Text](path/to/doc.md) - Relative file references across directories:
../guide.md - Relative image links:
 - Same-file heading anchors:
[Jump](#quick-start) - Cross-file heading anchors:
[Guide](docs/setup.md#installation) - ATX headings with custom punctuation, emojis, and duplicate suffixes (
-1,-2) - Fenced code block isolation (
```...```and~~~...~~~) - Inline backtick span isolation (
...)
Known Limitations
- Complex inline raw HTML tags (
<a href="...">) receive best-effort regex parsing. - Reference-style links (
[text][ref]) with distant definitions have limited support. - JavaScript-rendered client-side Single Page Application (SPA) links are not executed.
- External URL checks require network access and are skipped by default.
🧪 Test Coverage
DeadLinkFinder is verified against a comprehensive fixture suite:
- Unicode and multi-language heading slugs
- Emoji-prefixed headings (
## 🚀 Quick Start$\rightarrow$#quick-start) - Duplicate heading counters (
#section,#section-1) - Missing image asset detection (
DLF-004) - Cross-file anchor resolution
- Mixed POSIX and Windows backslash paths
- Empty and malformed Markdown targets (
DLF-005) - SARIF v2.1.0 schema validity and partial fingerprints
Run tests locally:
python -m pytest tests contract_tests -v
📄 License
MIT License. See LICENSE for details.
Release files for deadlinkfinder 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| deadlinkfinder-0.2.1.tar.gz | 646.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| deadlinkfinder-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 663.1 kB
Release files / deadlinkfinder-0.2.1.tar.gz
| Download URL | deadlinkfinder-0.2.1.tar.gz |
|---|---|
| Size | 646.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f3998f8ebcf3ed8ebd042e4591b6c82fcf675b571aabe44747cfd2d125f3d9f3
|
|
BLAKE2b-256 checksum How to use checksums |
26b4e8f7a71689c956dfb631a874a2e373e2b54bdf9fe64054409ff29641fcf4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Release files / deadlinkfinder-0.2.1-py3-none-any.whl
| Download URL | deadlinkfinder-0.2.1-py3-none-any.whl |
|---|---|
| Size | 16.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
92a97adcc20d6c4e39427934b4eb119229895677debceb3c00f08620fd9f398f
|
|
BLAKE2b-256 checksum How to use checksums |
00c896a71a82627a2fef0799961836698386b65fa24d5d626bba75c51a8cdf92
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|