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 deadlinkfinder
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) - reStructuredText hyperlinks:
`Text <path/to/doc.rst>`_and targets (.. _target: url) - Sphinx / Docutils roles:
:doc:\...`,:ref:`...`,:download:`...`` - reStructuredText images and figures:
.. image:: pathand.. figure:: path - Relative file references across directories:
../guide.md,../guide.rst - Relative image links:
 - Same-file heading anchors:
[Jump](#quick-start),`Jump <#quick-start>`_ - Cross-file heading anchors:
[Guide](docs/setup.md#installation),`Guide <docs/setup.rst#installation>`_ - ATX headings with custom punctuation, emojis, and duplicate suffixes (
-1,-2) - reStructuredText section underlines (
=,-,~, etc.) and explicit targets (.. _name:) - Fenced code block isolation (
```...```and~~~...~~~) - reStructuredText code-block and literal directive isolation (
.. code-block::,::) - Inline backtick and literal span isolation (
...and...)
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
Project case study
DeadLinkFinder is an open-source project by Umut Güngör. Read the DeadLinkFinder case study for its validation approach, supported checks, and limitations.
📄 License
MIT License. See LICENSE for details.
Metadata
Release files for deadlinkfinder 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| deadlinkfinder-0.2.2.tar.gz | 649.6 kB | Without provenance | Details for deadlinkfinder-0.2.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| deadlinkfinder-0.2.2-py3-none-any.whl | Python 3 | none | any | Details for deadlinkfinder-0.2.2-py3-none-any.whl |
Total release size: 668.5 kB
Release files / deadlinkfinder-0.2.2.tar.gz
| Download URL | deadlinkfinder-0.2.2.tar.gz |
|---|---|
| Size | 649.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f1bc4c8bb1074f80fb4e655893775503a7e04997837eb9cd7575804ae55c6348
|
|
BLAKE2b-256 checksum How to use checksums |
beb05ddcae1b6a3e3ce2d387f5fbd9e9bf16c2d7ce0cdc9c15dd61453223d1e5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / deadlinkfinder-0.2.2-py3-none-any.whl
| Download URL | deadlinkfinder-0.2.2-py3-none-any.whl |
|---|---|
| Size | 18.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ac193d6478eb187f2643dc95a2272d7fd9901b40468b81278a3e300de5dc85db
|
|
BLAKE2b-256 checksum How to use checksums |
00a95fd8d8fd593f71ab753136d9390413ab4660fd8e4b52f4bb48c21b49da29
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for deadlinkfinder 0.2.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.