Skip to content

Add TypeScript HTML content processor with sanitization and metadata extraction#1

Open
ocilab25 with Copilot wants to merge 3 commits into
mainfrom
copilot/initialize-html-content-processor
Open

Add TypeScript HTML content processor with sanitization and metadata extraction#1
ocilab25 with Copilot wants to merge 3 commits into
mainfrom
copilot/initialize-html-content-processor

Conversation

Copilot AI commented Apr 28, 2026

Copy link
Copy Markdown

Introduces a standalone html-processor TypeScript module providing safe HTML sanitization and metadata extraction, with a full Jest test suite.

Module structure

  • src/ContentProcessor.ts — Core class delegating sanitization to sanitize-html and metadata extraction via ReDoS-safe regexes. Exports typed interfaces (HtmlMetadata, ProcessingResult, SanitizationOptions).
  • src/index.ts — Public entry point.
  • tests/ContentProcessor.test.ts — 23 Jest tests across three method groups.

Key design decisions

  • sanitize() strips <script>, <iframe>, <object>, on* attributes, and javascript: URLs via sanitize-html; allow-list is configurable via constructor.
  • extractMetadata() uses [^<]* / [^>]* character classes instead of lazy [\s\S]*? / \s+[^>]+ to avoid polynomial ReDoS on unterminated tags.
  • Methods are independently callable; process() composes both for convenience.

Quick example

const processor = new ContentProcessor();
const { sanitizedHtml, metadata } = processor.process(`
  <html><head><title>Demo</title><meta name="author" content="Alice"></head>
  <body><p>Hello</p><script>alert(1)</script><a href="/x">Link</a></body>
  </html>
`);

console.log(sanitizedHtml);       // '<p>Hello</p><a href="/x">Link</a>'
console.log(metadata.title);      // 'Demo'
console.log(metadata.metaTags);   // { author: 'Alice' }
console.log(metadata.linkCount);  // 1

Test coverage

Area Cases
Empty / whitespace input empty string, whitespace-only
XSS prevention <script>, <iframe>, <object>, onclick, javascript: href, complex payloads
Malformed HTML unclosed tags, deeply nested misnesting
Metadata extraction title, empty title, <meta name>, OG <meta property>, link count
Config custom allowed-tag overrides via constructor
Original prompt

Initialize a TypeScript-based Node.js utility for processing and sanitizing HTML content. Requirements:

  1. Use TypeScript with strict type checking.
  2. Implement a 'ContentProcessor' class that handles HTML sanitization and metadata extraction.
  3. Use JSDoc for documentation.
  4. Include a Jest test suite with test cases for empty strings, malformed HTML, and scripts (XSS prevention).
  5. Ensure the structure is modular and follows SOLID principles.

Copilot AI changed the title [WIP] Initialize TypeScript utility for processing HTML content Add TypeScript HTML content processor with sanitization and metadata extraction Apr 28, 2026
Copilot AI requested a review from ocilab25 April 28, 2026 18:49
@ocilab25 ocilab25 marked this pull request as ready for review April 28, 2026 19:50
Copilot AI review requested due to automatic review settings April 28, 2026 19:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new standalone html-processor/ TypeScript package that sanitizes HTML via sanitize-html and extracts basic metadata (title/meta/link count) via pluggable strategies (regex or DOM), along with Jest tests and CI wiring to build/type-check/test the module.

Changes:

  • Introduces ContentProcessor plus regex/DOM metadata extractor strategies and shared public types.
  • Adds a Jest test suite covering sanitization, metadata extraction, and end-to-end processing.
  • Adds Node.js CI workflow and TypeScript/Jest project configuration for the new module.

Reviewed changes

Copilot reviewed 9 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
html-processor/src/ContentProcessor.ts Core API for sanitize/process/extractMetadata with default allow-lists.
html-processor/src/RegexMetadataExtractor.ts Regex-based metadata extraction implementation.
html-processor/src/DomMetadataExtractor.ts Cheerio-based metadata extraction implementation.
html-processor/src/types.ts Public interfaces for metadata and processor options/results.
html-processor/src/index.ts Module entrypoint re-exporting public API.
html-processor/tests/ContentProcessor.test.ts Jest tests for sanitization, extraction strategies, and process().
html-processor/tsconfig.json TypeScript compiler configuration for the module build.
html-processor/package.json Declares dependencies, scripts, and Jest configuration.
html-processor/package-lock.json Locks dependency graph (notably cheerio/undici Node engine requirements).
.github/workflows/ci.yml CI job to install, type-check, and test html-processor.
.gitignore Adds Node/TS ignores and some local-tooling ignores.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/ci.yml
Comment on lines +16 to +19
- name: Use Node.js 20.x
uses: actions/setup-node@v4
with:
node-version: 20.x

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow sets up node-version: 20.x, but cheerio@1.2.0 (via the lockfile) requires Node >=20.18.1. Pin the workflow to at least 20.18.1 (or the exact version you declare in package.json#engines) to prevent CI failures if 20.x ever resolves to an older patch in another environment or cache.

Suggested change
- name: Use Node.js 20.x
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Use Node.js 20.18.1
uses: actions/setup-node@v4
with:
node-version: 20.18.1

Copilot uses AI. Check for mistakes.
}

private countLinks(html: string): number {
const matches = html.match(/<a\s/gi);

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

countLinks() only matches anchors that have whitespace after <a (e.g. <a href=...>). It will undercount links for valid markup like <a>Text</a> or <a/>. Consider switching to a boundary-based pattern (e.g. <a\b) so attribute-less anchors are counted too (and keep it case-insensitive).

Suggested change
const matches = html.match(/<a\s/gi);
const matches = html.match(/<a\b/gi);

Copilot uses AI. Check for mistakes.

/** Default set of attributes permitted on allowed tags. */
const DEFAULT_ALLOWED_ATTRIBUTES: Record<string, string[]> = {
a: ["href", "title", "target", "rel"],

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allowing target on <a> without enforcing a safe rel value can enable reverse-tabnabbing when consumers render the sanitized output. Either disallow target by default, or add a sanitize-html transform that ensures rel includes noopener noreferrer whenever target="_blank" is present.

Suggested change
a: ["href", "title", "target", "rel"],
a: ["href", "title", "rel"],

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +25
"dependencies": {
"cheerio": "^1.2.0",
"sanitize-html": "^2.13.0"
},

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This package depends on cheerio@1.2.0 (and its undici dependency), which declares engines.node >=20.18.1 in the lockfile. To avoid consumers installing with an unsupported Node version (and hitting runtime/install issues), add an explicit engines field (and optionally engineStrict) reflecting the minimum Node version you support.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants