Skip to content

Releases: SINENSIA/markdown-security

v2.3.0

10 May 22:12
780f5cf

Choose a tag to compare

Added

  • ALLOWLIST_FILE env var: when set, the contents of the referenced JSON file replace the built-in sanitize-html configuration. Lets different consumers run with different policies without forking. Malformed input fails fast at startup.
  • lib/allowlist.js module exporting DEFAULT_ALLOWLIST (the previous hardcoded config) and loadAllowlist({ path }) for tests and programmatic use.
  • RATE_LIMIT_RPM env var: opt-in per-IP rate limiting on POST /validate (requests per minute). Disabled by default. /health and /openapi.json are never rate-limited. 429 responses include retry-after and ratelimit-* headers and are documented in the OpenAPI spec.
  • CycloneDX 1.6 SBOM (sbom.cdx.json) generated from the production lockfile via @cyclonedx/cyclonedx-npm. New npm run sbom script for local use, and a .github/workflows/sbom.yml workflow that runs on release: published (plus workflow_dispatch for backfilling) and uploads the SBOM as a release asset.

v2.2.0

10 May 16:25
b0aa92b

Choose a tag to compare

Added

  • Strict request-body validation against the OpenAPI ValidateRequest schema, powered by ajv. POST /validate now returns a structured 400 with per-field violations for missing/extra fields, wrong types, and minLength violations.
  • details array on 400 responses ([{ field, message }, ...]). Documented in the OpenAPI ErrorResponse schema.

Changed

  • The 400 error message for invalid input is now "Invalid request". The previous condition (missing/empty markdown) is still rejected, but through the schema validator rather than an ad-hoc check.
  • 2.1.0 documented additionalProperties: false and type: string in openapi.json without enforcing them. 2.2.0 closes that gap: clients that already conformed to the published schema are unaffected; clients that sent extra fields or coerced types will start receiving 400s with details.

v2.1.0

10 May 15:59
e34ff9a

Choose a tag to compare

Added

  • GET /health liveness probe returning { "status": "ok" }. The Docker HEALTHCHECK now hits this endpoint instead of POST /validate, removing the sanitizer call from the probe path.
  • GET /openapi.json serves an OpenAPI 3.1 specification covering every public endpoint, request/response schemas, status codes, and the x-request-id header.
  • Structured JSON logging via pino + pino-http. New LOG_LEVEL env var (default info, forced to silent under NODE_ENV=test).
  • x-request-id correlation header. Echoed when the incoming value matches ^[a-zA-Z0-9_.-]{1,128}$, otherwise a server-generated UUID. Surfaced on every endpoint and included in every log line.
  • Property-based fuzzing suite (fast-check) exercising sanitizer invariants: no dangerous tags ever leak to sanitized, sanitization is idempotent, the front matter never bleeds into sanitized, and HTML inside the front matter always flips safe to false.
  • CI workflow on GitHub Actions: test matrix (Node 20/22/24), npm audit --audit-level=high --omit=dev job, CodeQL with security-extended queries, concurrency cancellation on PR pushes.
  • Dependabot configuration for npm and github-actions ecosystems.
  • SECURITY.md with supported versions and private vulnerability reporting through GitHub Security Advisories.
  • engines: { node: ">=20" } in package.json to align the documented floor with the CI matrix.
  • Regression tests for the 256kb body limit (413) and javascript: / data: / vbscript: URL scheme stripping.

Changed

  • Dockerfile HEALTHCHECK switched from POSTing to /validate to GETting /health.
  • Test descriptions and code comments translated to English for end-to-end consistency.
  • README updated to document the new endpoints, the LOG_LEVEL knob, and the request-correlation header.

Removed

  • Dead PROHIBITED_TAGS constant in server.js. The allowlist passed to sanitize-html was always the source of truth.

v2.0.0

10 May 13:14

Choose a tag to compare

Breaking changes

POST /validate no longer concatenates YAML front matter back into sanitized. Front matter is now returned in a dedicated frontMatter field (raw, untrusted), and safe reflects both the body and the front matter.

Why: in 1.x, HTML inside the YAML block bypassed the sanitizer while safe still reported true.

Migration: consumers that read the YAML block from sanitized must now read it from frontMatter. If front matter is rendered as HTML, sanitize it on the consumer side.

Other changes since the initial public state

  • README rewritten as UTF-8 (#4).
  • Repo housekeeping, dependency refresh, Docker hardening (#2).
  • qs array-limit DoS mitigation via query parser=simple (#1).