{"id":50182057,"url":"https://github.com/ipanalytics/prefixlint","last_synced_at":"2026-05-25T07:05:15.676Z","repository":{"id":359950383,"uuid":"1248133981","full_name":"ipanalytics/PrefixLint","owner":"ipanalytics","description":"CI-native linter and normalizer for IP blocklists, allow-lists, CIDR feeds, ipset/nftables inputs, and network policy datasets.","archived":false,"fork":false,"pushed_at":"2026-05-24T09:09:24.000Z","size":27,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-24T10:22:27.726Z","etag":null,"topics":["allowlist","blocklist","ci","cidr","denylist","firewall","github-actions","go","infosec","ip","ipset","linter","network-security","nftables","threat-intelligence"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ipanalytics.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-24T08:20:43.000Z","updated_at":"2026-05-24T09:09:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ipanalytics/PrefixLint","commit_stats":null,"previous_names":["ipanalytics/prefixlint"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ipanalytics/PrefixLint","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FPrefixLint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FPrefixLint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FPrefixLint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FPrefixLint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ipanalytics","download_url":"https://codeload.github.com/ipanalytics/PrefixLint/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FPrefixLint/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33464014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-25T06:32:55.349Z","status":"ssl_error","status_checked_at":"2026-05-25T06:32:35.322Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["allowlist","blocklist","ci","cidr","denylist","firewall","github-actions","go","infosec","ip","ipset","linter","network-security","nftables","threat-intelligence"],"created_at":"2026-05-25T07:04:44.437Z","updated_at":"2026-05-25T07:05:15.668Z","avatar_url":"https://github.com/ipanalytics.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PrefixLint\n\nPrefixLint is a CI-oriented linter and normalizer for IP prefix lists. It validates deny-lists, allow-lists, ipset inputs, nftables feeds, and CIDR datasets for duplicate rules, redundant coverage, malformed entries, allow/deny conflicts, and normalization impact.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./site/banner.svg\" alt=\"PrefixLint banner\" width=\"100%\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/license-Apache%202.0-blue\" alt=\"License\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/status-active-success\" alt=\"Project status\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/ci-github%20actions-2088ff\" alt=\"CI\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/output-markdown%20%7C%20json%20%7C%20sarif-555\" alt=\"Outputs\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/go-1.22+-00ADD8\" alt=\"Go version\"\u003e\n\u003c/p\u003e\n\n---\n\n## Overview\n\nIP lists tend to accumulate operational debt: copied prefixes, overlapping ranges, stale exceptions, private space in public blocklists, and conflicting allow/deny rules. PrefixLint gives those lists the same review surface that source code already has: deterministic checks, file and line references, severity controls, normalized output, and pull request feedback.\n\nThe default workflow is intentionally small:\n\n```text\nprefixlint check --allow allow.txt blocklist.txt\n\n412 duplicate prefixes\n37 prefixes fully covered by broader rules\n18 allow/deny conflicts\n5000 rules -\u003e 1870 rules, same coverage\n```\n\nPrefixLint does not need threat-intelligence feeds or external enrichment to be useful. It operates on repository-owned files and produces artifacts that fit normal infrastructure review: terminal output, Markdown summaries, JSON, SARIF, and normalized CIDR output.\n\n---\n\n## System Behavior\n\nPrefixLint parses list files into canonical `netip.Prefix` values, preserves source locations for reporting, and evaluates each rule against the parsed prefix set. Normalization is performed as a coverage-preserving operation:\n\n1. Parse IP addresses and CIDR prefixes.\n2. Canonicalize host addresses into `/32` or `/128` prefixes.\n3. Remove duplicate prefixes.\n4. Remove prefixes fully covered by broader entries.\n5. Collapse adjacent prefixes when the resulting broader prefix has identical coverage.\n6. Emit sorted canonical CIDR output.\n\nThe linter and normalizer share the same prefix model. A `check` report explains why a list is inefficient or unsafe to merge; `fix` emits the compact representation.\n\n```mermaid\nflowchart LR\n    A[\"CIDR / IP list\"] --\u003e B[\"Parser\"]\n    B --\u003e C[\"Canonical prefix set\"]\n    C --\u003e D[\"Lint rules\"]\n    C --\u003e E[\"Coverage-preserving normalizer\"]\n    D --\u003e F[\"Text / Markdown / JSON / SARIF\"]\n    E --\u003e G[\"Normalized CIDR file\"]\n```\n\n---\n\n## Features\n\n| Area | Capability |\n| --- | --- |\n| Prefix hygiene | Detect duplicate prefixes and entries covered by broader rules. |\n| Conflict analysis | Compare deny-list and allow-list coverage. |\n| Syntax validation | Report malformed IP addresses and CIDR prefixes with line numbers. |\n| Normalization | Deduplicate, sort, remove covered rules, and collapse adjacent prefixes. |\n| CI output | Text, Markdown, JSON, and SARIF report formats. |\n| Rule control | Configure rule severity or disable rules with `.prefixlintrc.json`. |\n| GitHub-native workflow | Composite Action for pull request checks and job summaries. |\n| IPv4 / IPv6 | Uses Go `net/netip` for native IPv4 and IPv6 prefix handling. |\n\n---\n\n## Quick Start\n\nRun a local check:\n\n```sh\ngo run ./cmd/prefixlint check examples/deny.txt\n```\n\nCheck deny-list coverage against an allow-list:\n\n```sh\ngo run ./cmd/prefixlint check \\\n  --allow examples/allow.txt \\\n  --config .prefixlintrc.json \\\n  examples/deny.txt\n```\n\nNormalize a list without modifying the input:\n\n```sh\ngo run ./cmd/prefixlint fix examples/deny.txt\n```\n\nRewrite the input file in place:\n\n```sh\ngo run ./cmd/prefixlint fix -w lists/deny.txt\n```\n\n---\n\n## Installation\n\n### From Go\n\n```sh\ngo install github.com/ipanalytics/PrefixLint/cmd/prefixlint@latest\n```\n\n### From Source\n\n```sh\ngit clone https://github.com/ipanalytics/PrefixLint.git\ncd prefixlint\ngo test ./...\ngo build -o prefixlint ./cmd/prefixlint\n```\n\n### GitHub Action\n\n```yaml\nname: PrefixLint\n\non:\n  pull_request:\n    paths:\n      - \"lists/**\"\n      - \"**/*.cidr\"\n      - \"**/*.netset\"\n\njobs:\n  prefixlint:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ipanalytics/PrefixLint@v1\n        with:\n          list: lists/deny.txt\n          allow: lists/allow.txt\n          fail-on: warning\n```\n\nThe Action writes a Markdown report into the GitHub Actions job summary. SARIF output can be generated by the CLI for code scanning pipelines that upload SARIF separately.\n\n---\n\n## Usage\n\n### Check a List\n\n```sh\nprefixlint check blocklist.txt\n```\n\nExample output:\n\n```text\nprefixlint: 6 rules -\u003e 3 rules, same coverage\nduplicates: 1, covered: 1, conflicts: 1, malformed: 0\nexamples/deny.txt:3: warning duplicate: 192.0.2.0/24 duplicates line 1\nexamples/deny.txt:2: warning covered: 192.0.2.1/32 is fully covered by broader prefix 192.0.2.0/24 on line 1\nexamples/deny.txt:1: error allow-deny-conflict: deny 192.0.2.0/24 conflicts with allow 192.0.2.128/25 on line 1\n```\n\n### Compare Deny and Allow Lists\n\n```sh\nprefixlint check --allow allow.txt deny.txt\n```\n\nConflict detection is coverage-based. A deny `/24` conflicts with an allow `/25` inside that range, and an allow `/24` conflicts with a narrower deny prefix inside it.\n\n### Generate Markdown\n\n```sh\nprefixlint check \\\n  --format markdown \\\n  --markdown-out prefixlint.md \\\n  lists/deny.txt\n```\n\n### Generate SARIF\n\n```sh\nprefixlint check --format sarif lists/deny.txt \u003e prefixlint.sarif\n```\n\n### Normalize Coverage\n\n```sh\nprefixlint fix lists/deny.txt \u003e lists/deny.normalized.txt\n```\n\nNormalization emits canonical CIDR prefixes sorted by address family and address order.\n\n---\n\n## Outputs and Artifacts\n\n| Format | Use |\n| --- | --- |\n| `text` | Local terminal use and CI logs. |\n| `markdown` | GitHub job summaries and pull request comments. |\n| `json` | Automation, dashboards, and downstream analysis. |\n| `sarif` | Code scanning and security review systems. |\n| normalized CIDR | Compact list output from `prefixlint fix`. |\n\nJSON reports contain a summary and a finding list:\n\n```json\n{\n  \"summary\": {\n    \"input_rules\": 5000,\n    \"normalized_rules\": 1870,\n    \"duplicate_prefixes\": 412,\n    \"covered_prefixes\": 37,\n    \"allow_deny_conflicts\": 18,\n    \"coverage_unchanged\": true\n  },\n  \"findings\": []\n}\n```\n\n---\n\n## Data Format\n\nPrefixLint accepts plain text files containing one IP address or CIDR prefix per line:\n\n```text\n192.0.2.0/24\n192.0.2.1\n2001:db8::/32\n203.0.113.0/25 # ticket SEC-1248\n```\n\nParsing rules:\n\n| Input | Behavior |\n| --- | --- |\n| `192.0.2.1` | Treated as `192.0.2.1/32`. |\n| `2001:db8::1` | Treated as `2001:db8::1/128`. |\n| `192.0.2.0/24` | Parsed as an IPv4 prefix. |\n| `2001:db8::/32` | Parsed as an IPv6 prefix. |\n| `# comment` | Ignored. |\n| Inline `# comment` | Ignored for parsing; source line remains available for reporting. |\n\nCurrent `fix` output is canonical CIDR-only output. Comment-preserving rewrites are part of the project roadmap because they require stable attachment semantics when multiple prefixes collapse into one broader rule.\n\n---\n\n## Rule Configuration\n\nRules can be disabled or assigned a severity in `.prefixlintrc.json`:\n\n```json\n{\n  \"rules\": {\n    \"malformed\": \"error\",\n    \"duplicate\": \"warning\",\n    \"covered\": \"warning\",\n    \"allow-deny-conflict\": \"error\",\n    \"private-in-public-blocklist\": \"info\"\n  }\n}\n```\n\nRun with:\n\n```sh\nprefixlint check --config .prefixlintrc.json lists/deny.txt\n```\n\nSupported severity values:\n\n| Value | Meaning |\n| --- | --- |\n| `error` | Fails by default and should block merge in strict CI. |\n| `warning` | Fails when `--fail-on warning` is used. |\n| `info` | Reported for visibility. |\n| `off` | Rule is disabled. |\n\nThe workflow failure threshold is controlled separately:\n\n```sh\nprefixlint check --fail-on error lists/deny.txt\nprefixlint check --fail-on none lists/deny.txt\n```\n\n---\n\n## Rules\n\n| Rule | Default | Description |\n| --- | --- | --- |\n| `malformed` | `error` | Invalid IP address or CIDR prefix. |\n| `duplicate` | `warning` | Prefix appears more than once. |\n| `covered` | `warning` | Prefix is fully covered by a broader rule. |\n| `allow-deny-conflict` | `error` | Allow-list and deny-list coverage overlap. |\n| `private-in-public-blocklist` | `info` | Private, loopback, or link-local space appears in a public blocklist. |\n\n---\n\n## Operational Notes\n\nPrefixLint is designed for repositories where list changes are reviewed like code changes. The high-value CI pattern is to run `check` on pull requests and reserve `fix` for maintainers or automated cleanup branches.\n\nFor large lists, rule output should be treated as review metadata rather than an opaque pass/fail result. Duplicate and coverage findings are usually cleanup tasks. Allow/deny conflicts are operationally significant because they indicate ambiguity between enforcement policy and exception policy.\n\nRecommended CI behavior:\n\n| Repository Type | Suggested Threshold |\n| --- | --- |\n| Public community blocklist | `--fail-on warning` |\n| Internal production allow/deny policy | `--fail-on error` |\n| Migration or audit branch | `--fail-on none` with report artifact |\n\n---\n\n## Use Cases\n\n- Pull request review for public blocklist repositories.\n- CI checks for nftables, ipset, firewall, and routing policy inputs.\n- Cleanup of threat-intelligence feed exports before loading into constrained systems.\n- Validation of corporate allow/deny exception files.\n- Drift detection for manually maintained CIDR datasets.\n- SARIF-backed review of infrastructure policy repositories.\n\n---\n\n## Project Scope\n\nPrefixLint focuses on list correctness, reviewability, and coverage-preserving normalization.\n\nIn scope:\n\n- CIDR and host IP parsing.\n- IPv4 and IPv6 prefix analysis.\n- Duplicate, overlap, conflict, and malformed-entry detection.\n- Deterministic normalization.\n- CI-friendly reporting.\n- Rule severity configuration.\n\nPlanned extensions:\n\n- Comment-preserving fixes.\n- Sticky pull request comments.\n- Native SARIF upload workflow examples.\n- Optional semantic checks against bogon ranges, cloud provider ranges, and RIR delegated statistics.\n- Multi-file project configuration.\n\n---\n\n## Limitations\n\nPrefixLint does not decide whether a prefix should be blocked. It evaluates the structure and consistency of the lists supplied to it.\n\n`fix` currently emits canonical CIDR output and does not preserve comments. When adjacent prefixes collapse into a broader prefix, comment ownership can become ambiguous; preserving useful annotations requires explicit policy.\n\nThe current implementation is optimized for clear CI behavior and deterministic output. Very large datasets should be tested with representative repository workloads before enforcing strict merge gates.\n\n---\n\n## Directory Structure\n\n```text\n.\n├── action.yml                     # GitHub composite Action\n├── cmd/prefixlint/                # CLI entrypoint\n├── examples/                      # Example deny/allow lists\n├── internal/prefixlint/           # Parser, rules, normalizer, reports\n├── .github/workflows/             # Example CI workflow\n├── .prefixlintrc.json             # Example rule configuration\n├── go.mod\n└── README.md\n```\n\n---\n\n## Deployment\n\nPrefixLint can run as a developer tool, a CI step, or a scheduled repository audit.\n\n### Pull Request Gate\n\n```yaml\n- uses: ipanalytics/PrefixLint@v1\n  with:\n    list: lists/deny.txt\n    allow: lists/allow.txt\n    fail-on: warning\n```\n\n### Scheduled Audit\n\n```yaml\non:\n  schedule:\n    - cron: \"15 3 * * *\"\n```\n\nUse scheduled audits when lists are generated by external jobs and pull request checks do not cover every update path.\n\n### Artifact Collection\n\n```sh\nprefixlint check --format json lists/deny.txt \u003e prefixlint.json\nprefixlint check --format sarif lists/deny.txt \u003e prefixlint.sarif\nprefixlint fix lists/deny.txt \u003e deny.normalized.txt\n```\n\nStore JSON and SARIF with CI artifacts when list quality trends matter over time.\n\n---\n\n## Development\n\n```sh\ngo test ./...\ngo run ./cmd/prefixlint check --allow examples/allow.txt examples/deny.txt\ngo run ./cmd/prefixlint fix examples/deny.txt\n```\n\nThe codebase intentionally keeps the core prefix logic small and inspectable. Rules should produce actionable findings with stable file and line references.\n\n\u003cdetails\u003e\n\u003csummary\u003eMaintainer checklist for new rules\u003c/summary\u003e\n\n- The rule has a stable ID.\n- The default severity matches operational impact.\n- Findings include source file and line number when possible.\n- The rule can be disabled through `.prefixlintrc.json`.\n- Tests cover IPv4 and IPv6 behavior where applicable.\n- Report output remains deterministic.\n\n\u003c/details\u003e\n\n---\n\n## License\n\nPrefixLint is licensed under the Apache License, Version 2.0. See [LICENSE](./LICENSE).\n\n---\n\n## Disclaimer\n\nPrefixLint is an infrastructure quality tool. Operators remain responsible for validating the policy impact of any blocklist or allow-list before deploying it to production enforcement systems.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fipanalytics%2Fprefixlint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fipanalytics%2Fprefixlint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fipanalytics%2Fprefixlint/lists"}