Zum Inhalt springen

Open source / Threat-intel / defang-sentinel

defang-sentinel

Defang and refang IOCs (hxxps://evil[.]com) in text, logs and reports with several styles, and a CI check that fails when threat reports, tickets or docs contain live, clickable or half-defanged URLs, domains, IPs or emails. CLI, library and GitHub Action. Zero deps.

Auf npm
$ npm install defang-sentinel

README

defang-sentinel

npm version CI Node License: MIT

Defang and refang indicators of compromise, and stop live malicious links from getting into threat reports, tickets and docs.

Threat-intel write-ups, incident tickets and detection repos are full of malicious URLs, domains and IPs. A single clickable https:// link to a C2 server in a Markdown report is an accident waiting to happen. defang-sentinel does three jobs:

  1. defang: rewrite live indicators as safe text (https://evil.com → hxxps://evil[.]com) in four styles.
  2. refang: turn defanged indicators back into usable ones for tooling (hxxps[://]evil[.]com → https://evil.com).
  3. check (default): scan files or directories and fail CI when a report contains live, clickable or half-defanged indicators.

CLI, library and GitHub Action. Node.js 18+, zero runtime dependencies.

Features

  • Detects URLs (http/https/ftp/sftp/ws), domains (validated against gTLD/ccTLD lists, with file names such as setup.py ignored), public IPv4/IPv6 addresses (private, loopback and documentation ranges ignored unless --include-private; version strings like v1.2.3.4 skipped) and email addresses.
  • Clickable link detection (live-link, high): Markdown links [text](https://…), reference links, autolinks <https://…> and HTML href attributes. Defanged link text doesn't help if the href is still live.
  • Half-defanged detection (partial-defang): hxxp://evil.com or evil[.]com.badhost.ru look safe at a glance but still contain a live host.
  • Reference allowlist: links to MITRE ATT&CK, NVD, CVE.org, CISA, NCSC, VirusTotal, abuse.ch, urlscan, GitHub and others are expected in reports and allowed by default (--no-reference-domains turns this off). Add your own with --ignore-domain or ignoreDomains in the config.
  • Defang styles (--style): brackets (default) hxxps://www[.]evil[.]com/a.php, full hxxps[://]www[.]evil[.]com/a[.]php, dot hxxps://www[dot]evil[dot]com, minimal hxxps://www.evil[.]com. Emails become user[@]domain[.]com; IPv6 colons become [:].
  • Refang understands hxxp, hXXps, h[xx]p, fxp, [.], (.), {.}, [dot], (dot), \., [:], [://], [@], (at), [at].
  • defang/refang can rewrite files in place (--write) and are idempotent. Defanging already-defanged text changes nothing.
  • check walks directories for text formats (.md, .txt, .html, .csv, .json, .yml, .eml, .log, .yar…; change with --ext), skips binaries and node_modules, and can ignore fenced code blocks (--skip-code).

Fix mode, code-aware rewriting and hooks

defang-sentinel check --fix --skip-code docs/      # defang live IOCs in place, leave code blocks alone
defang-sentinel defang --style paren report.md     # hxxps://evil(.)com
defang-sentinel hook install                        # git pre-commit hook

Styles: brackets, full, dot, minimal, paren, colon. refang(defangValue(x, type, style)) === x for every style.

Library: import { defangText, refangText, refangValue, defangValue, fix, installHook, splitCode } from 'defang-sentinel' (or require). This is the stable defang/refang API that other packages (e.g. ioc-sentinel) build on.

pre-commit framework users can add repo: https://github.com/MrLouisWright/defang-sentinel with hook id defang-sentinel or defang-sentinel-fix.

Module formats and types

Ships ESM and CommonJS (import and require both work, via exports), with TypeScript declarations generated from the source at build time. Tests run on Node 18/20/22 in GitHub Actions (test/), including require/import checks against the packed tarball; releases are published with npm provenance.

Rules

RuleDefaultMeaning
live-linkhighClickable Markdown/HTML link or autolink to a live URL
live-urlhighURL not defanged
partial-defangmediumToken carries defang markers but still has a live host
live-domainmediumDomain not defanged
live-ipmediumPublic IPv4/IPv6 not defanged
live-emaillowEmail address not defanged

Install and run

echo 'C2: https://evil.com/gate.php 45.9.148.3' | npx defang-sentinel defang -
# C2: hxxps://evil[.]com/gate.php 45[.]9[.]148[.]3

npx defang-sentinel refang iocs.txt > iocs-live.txt
npx defang-sentinel defang --write reports/2026-10-apt.md

npx defang-sentinel reports/ --skip-code --ignore-domain 'mycompany.com,*.mycompany.com'

Library

import { defangText, refang, defangValue, check } from 'defang-sentinel';

defangText('see https://evil.com', { style: 'brackets' }).text; // 'see hxxps://evil[.]com'
refang('hxxps[://]evil[.]com');                                  // 'https://evil.com'
defangValue('bad@phish.ru');                                     // 'bad[@]phish[.]ru'
const report = check(['reports/'], { skipCode: true });

Options

Full CLI reference (defang-sentinel --help):

defang-sentinel 1.1.0 — defang/refang IOCs and keep live indicators out of reports

Usage:
  defang-sentinel defang [file|-] [--style s] [--write]   make URLs, domains, IPs, emails safe to share
  defang-sentinel refang [file|-] [--write]               turn hxxps://evil[.]com back into https://evil.com
  defang-sentinel [check] <file|dir...> [options]         fail on live indicators (default command)
  defang-sentinel check --fix <file|dir...>               defang live indicators in place, then re-check
  defang-sentinel hook install [--force]                  install a git pre-commit hook

Defang/refang
      --style <s>            brackets | full | dot | minimal | paren | colon (default brackets)
                               brackets: hxxps://evil[.]com/a.php   full: hxxps[://]evil[.]com/a[.]php
                               dot: hxxps://evil[dot]com            minimal: hxxps://www.evil[.]com
                               paren: hxxps://evil(.)com            colon: hxxps[:]//evil[.]com
      --skip-code            Leave Markdown code blocks / inline code and HTML <pre>/<code> untouched
  -w, --write                Rewrite the file in place instead of printing
Check
      --ignore-domain <list> Domains that may stay live (globs ok, repeatable)
      --no-reference-domains Also flag links to mitre.org, nvd.nist.gov, virustotal.com, github.com etc.
      --include-private      Also flag private/loopback IPs
      --skip-code            Ignore fenced code blocks, inline code and HTML <pre>/<code>
      --fix                  Defang findings in place (Markdown/HTML code-aware) before reporting
      --force                (hook install) overwrite an existing pre-commit hook
      --ext <list>           Extensions to scan in directories (default .md,.markdown,.txt,.rst,.adoc,.html,...)
Policy and output
  -c, --config <file>        JSON config (default .defang-sentinel.json if present)
  -f, --format <fmt>         text (default) | json | sarif | markdown
      --json/--sarif/--markdown <file>  Also write that format to a file
      --fail-on <sev>        critical | high | medium | low | info | none (default medium)
      --list-rules           Print rules and default severities
      --no-color             Disable colours
  -v, --version / -h, --help

Exit codes: 0 = passed, 1 = findings at or above --fail-on, 2 = usage error or a target could not be checked.

Configuration

Put a .defang-sentinel.json in the working directory (or pass --config <file>). Every key is optional; CLI flags override the file.

{
  "failOn": "medium",
  "severity": { "live-url": "high" },
  "allow": [
    { "rule": "live-url", "match": "*cdn.example.net*", "reason": "Accepted risk, ticket SEC-123", "expires": "2027-03-31" }
  ]
}
  • severity changes the severity of any rule, or turns it off.
  • allow suppresses findings. Each entry needs a rule (globs such as live-* work) and a reason, and can be narrowed with target and match (a glob tested against the target, location and value). Suppressed findings are listed in the JSON report. When expires (YYYY-MM-DD) has passed, the finding comes back together with an allow-expired finding, so exceptions can't be forgotten. Entries that match nothing are reported as allow-unused (info).

Output and exit codes

  • --format text|json|sarif|markdown for stdout, plus --json, --sarif and --markdown <file> to write extra copies in one run. SARIF 2.1.0 works with GitHub code scanning.
  • --fail-on critical|high|medium|low|info|none (default medium).
  • Exit code 0: passed. 1: at least one finding at or above --fail-on. 2: usage or config error, or a target could not be checked.
  • --list-rules prints every rule with its default severity.

GitHub Action

permissions:
  contents: read
  security-events: write   # only for the SARIF upload
steps:
  - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
    with: { persist-credentials: false }
  - uses: MrLouisWright/defang-sentinel@v1
    with:
      targets: 'reports/'
      fail-on: medium
      sarif: defang-sentinel.sarif
  - uses: github/codeql-action/upload-sarif@v3
    if: always()
    with: { sarif_file: defang-sentinel.sarif }

Inputs: targets, fail-on, config, sarif, json, job-summary (Markdown report in the job summary, default true), args (extra CLI flags), ignore-domains. Outputs: markdown-file, exit-code.

Requirements

Node.js 18 or newer. No runtime dependencies.

Support

If this project is useful to you, you can support its development with a donation: Buy me a coffee or GitHub Sponsors. It helps keep these tools maintained, documented and free for everyone.

Sponsor

Credit (optional)

Free to use under MIT, no attribution required. If defang-sentinel helps you, a link back is appreciated:

<a href="https://louiswright.co.uk">Built with defang-sentinel by Louis Wright</a>
[![Built with defang-sentinel by Louis Wright](https://img.shields.io/badge/built%20with-defang--sentinel-blue)](https://louiswright.co.uk)

License

MIT © Louis Wright (Louis Wright)