Aller au contenu

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.

Sur 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 });

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. It helps keep these tools maintained, documented and free for everyone.

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)