Skip to content

fix: truncate input before sanitization to fix 816s JS test timeout - #50810

Merged
pelikhan merged 1 commit into
mainfrom
copilot/investigate-slow-javascript-tests
Aug 6, 2026
Merged

fix: truncate input before sanitization to fix 816s JS test timeout#50810
pelikhan merged 1 commit into
mainfrom
copilot/investigate-slow-javascript-tests

Conversation

Copilot AI commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

sanitizeContentCore was running all expensive operations — Unicode normalization, entity decoding, homoglyph mapping, 3× applyToNonCodeRegions passes, mention neutralization, URL policy — on the full raw input before truncating. A 600k-char input caused the "should truncate long content" test to hang for 816 seconds.

Change

  • Move applyTruncation to the top of sanitizeContentCore, before any other processing. Inputs exceeding maxLength (default 524,288) are cut immediately.
  • Retain the existing truncation call later in the pipeline for cases where normalization (stripping invisible chars) reduces length after the fact.
function sanitizeContentCore(content, maxLength, maxBotMentions) {
  if (!content || typeof content !== "string") return "";

  // Early truncation — avoid running expensive ops on oversized input
  content = applyTruncation(content, maxLength);

  // ... hardenUnicodeText, neutralizeAllMentions, applyToNonCodeRegions, etc.

  // Second pass after normalization (may further reduce length)
  sanitized = applyTruncation(sanitized, maxLength);
  ...
}

Result: compute_text.test.cjs (51 tests) drops from 816,365 ms (1 failed) → 387 ms (all pass).

…ocessing of large inputs

The "should truncate long content" test was timing out at ~816 seconds because
sanitizeContentCore ran all expensive operations (hardenUnicodeText, neutralizeAllMentions,
applyToNonCodeRegions, applyURLSanitizationPolicy, etc.) on the full 600,000-character input
before truncating at line 1382.

Fix: add an early applyTruncation call at the start of sanitizeContentCore so oversized
inputs are cut down to maxLength (default 524,288) before any other processing begins.
A second truncation pass is still applied later (after normalization may reduce length
by stripping invisible chars). This drops the test from ~816s to under 1s.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title fix: apply truncation early in sanitizeContentCore to prevent slow JS tests fix: truncate input before sanitization to fix 816s JS test timeout Aug 6, 2026
Copilot AI requested a review from pelikhan August 6, 2026 07:01
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Triage Summary

Category: bug (perf fix) | Risk: low | Priority score: 55/100

Score breakdown: impact 25 + urgency 15 + quality 15

Recommended action: fast_track

Small, well-scoped fix (5 additions, 1 file) that resolves an 816s test timeout by truncating input before expensive sanitization passes. No CI data available yet; no reviews posted. Low blast radius, high test-suite value.

Generated by 🔧 PR Triage Agent · auto · 33.2 AIC · ⌖ 2.45 AIC · ⊞ 7.9K ·

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Hey @copilot-swe-agent 👋 — thanks for fixing the performance issue in the sanitization pipeline! This early-truncation approach is solid and delivers a massive improvement (816s → 387ms).

One small thing to consider for completeness:

  • Add test coverage for the early truncation — while the existing test suite now runs much faster and passes, it would be good to add a unit test specifically for the early-truncation optimization. This ensures the behavior is explicitly covered if the function is refactored later.

If you'd like to add test coverage, here's a prompt:

Add a unit test to actions/setup/js/sanitize_content_core.test.cjs (or appropriate test file) that verifies:
1. When input exceeds maxLength (e.g., 600k chars), early truncation cuts it to maxLength before expensive operations.
2. The output matches the expected truncated length.
3. Performance: processing a 600k-char input completes in <1 second.

Otherwise, this is ready to go! 🚀

Generated by ✅ Contribution Check · auto · 51.7 AIC · ⊞ 8.7K ·

@pelikhan
pelikhan marked this pull request as ready for review August 6, 2026 12:10
Copilot AI balanced review requested due to automatic review settings August 6, 2026 12:10
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

No ADR enforcement needed: PR does not have the implementation label and has 0 new lines of code in business logic directories (threshold: 100).

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

No test files were added or modified in this PR. This PR only modifies the production file actions/setup/js/sanitize_content_core.cjs (a performance optimization for early truncation). Test Quality Sentinel skipped per guidelines.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Moves truncation earlier in content sanitization to avoid expensive processing of oversized inputs.

Changes:

  • Adds a raw-input truncation pre-pass.
  • Retains post-normalization truncation.
Show a summary per file
File Description
actions/setup/js/sanitize_content_core.cjs Adds early input truncation to the core sanitizer.

Review details

Tip

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

  • Files reviewed: 1/1 changed files
  • Comments generated: 2
  • Review effort level: Balanced

// Apply truncation early to avoid running expensive operations on oversized inputs.
// This is a pre-pass truncation on raw content; a second truncation pass is applied
// later after normalization (which may reduce length via stripping invisible chars).
content = applyTruncation(content, maxLength);
// Apply truncation early to avoid running expensive operations on oversized inputs.
// This is a pre-pass truncation on raw content; a second truncation pass is applied
// later after normalization (which may reduce length via stripping invisible chars).
content = applyTruncation(content, maxLength);

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

The fix is correct and well-scoped. Moving applyTruncation before expensive processing is the right approach to avoid the 816s timeout on oversized inputs. The dual-pass design (pre-normalization + post-normalization) is sound.> 🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 14.5 AIC · ⊞ 5.3K

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Skills-Based Review 🧠

Applied /diagnosing-bugs — approving with one suggestion on test coverage.

📋 Key Themes & Highlights

Key Themes

  • Correct root-cause fix: the expensive sanitization pipeline now never runs on inputs larger than maxLength, eliminating the timeout.
  • Dual-pass design preserved: the second applyTruncation call after normalization is correctly retained for cases where stripping invisible characters reduces content length past the limit.
  • Regression test gap: there is no test that guards the new early-truncation path. A future refactor could silently reintroduce the timeout.

Positive Highlights

  • ✅ Minimal, surgical change — 5 lines, zero deleted, no side-effects on other code paths
  • ✅ Clear inline comments explain the dual-pass rationale
  • ✅ PR description quantifies the fix (816,365 ms → 387 ms) — excellent diagnosis
> 🧠 *Reviewed using Matt Pocock's skills by [Matt Pocock Skills Reviewer](https://github.com/github/gh-aw/actions/runs/31100300255)* · sonnet46 · 22 AIC · ⊞ 7.1K > Comment /matt to run again


// Build list of allowed domains from environment and GitHub context
const allowedDomains = buildAllowedDomains();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/diagnosing-bugs] No regression test guards the early-truncation path — the 816s timeout could silently regress if applyTruncation is ever moved or conditionalised.

💡 Suggested regression test sketch
it("sanitizeContentCore completes quickly on oversized input", () => {
  const huge = "a".repeat(600_000);
  const start = Date.now();
  sanitizeContentCore(huge, 524288, 10);
  expect(Date.now() - start).toBeLessThan(1000); // must finish in <1 s
});

A timing-based test (or at minimum a test that asserts the output length equals maxLength) would lock in this performance contract and catch future regressions early.

@copilot please address this.

@pelikhan
pelikhan merged commit 5c22a87 into main Aug 6, 2026
55 of 63 checks passed
@pelikhan
pelikhan deleted the copilot/investigate-slow-javascript-tests branch August 6, 2026 12:22

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Review: performance fix is sound, one edge-case worth hardening

The core change (move applyTruncation before expensive Unicode/entity/mention processing) correctly resolves the 816s hang for oversized input and is a minimal, well-targeted diff.

💡 Themes and analysis

What was checked:

  • Confirmed via manual simulation that the new early-truncation call short-circuits pathological-length inputs before the 3x applyToNonCodeRegions passes and other O(n) regex scans run, which matches the stated fix for the 816s timeout.
  • Diff is 5 lines, scoped to a single file, low blast radius.
  • Found one non-blocking correctness/edge-case concern: double-applying applyTruncation (once pre-normalization, once post-normalization) can produce a duplicated or embedded truncation marker if normalization doesn't shrink the string below maxLength after the first pass (e.g., entity/homoglyph decoding expansion). Verified via simulation that applyTruncation run twice on already-marked content re-truncates the marker text itself rather than detecting/replacing it. This is pre-existing logic in applyTruncation, not introduced by this PR, but the new early-truncation call increases the chance the function is invoked twice on marker-bearing content — worth a regression test asserting at most one truncation marker appears in output.
  • No test coverage was added in this PR to assert single-marker output invariants after the double-truncation path; existing compute_text.test.cjs tests only check that truncation occurs, not that it's clean.

Verdict rationale: Not requesting changes — the flagged issue is a pre-existing edge case in applyTruncation's design, not a new bug introduced by this specific diff, and the primary goal (fixing the test timeout) is achieved correctly. Recommend addressing the marker-duplication hardening in a follow-up.

> 🔎 *Code quality review by [PR Code Quality Reviewer](https://github.com/github/gh-aw/actions/runs/31100300305)* · auto · 104.8 AIC · ⊞ 7.8K > Comment /review to run again

// Apply truncation early to avoid running expensive operations on oversized inputs.
// This is a pre-pass truncation on raw content; a second truncation pass is applied
// later after normalization (which may reduce length via stripping invisible chars).
content = applyTruncation(content, maxLength);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Running truncation twice on oversized input can leave the truncation marker itself embedded before the length cutoff, duplicating/garbling the trailer instead of cleanly signaling truncation once.

💡 Details

With the new early pass, content = applyTruncation(content, maxLength) truncates raw input and appends "\n[Content truncated due to length]". If normalization later still leaves the string over maxLength (e.g. it doesn't shrink, or entity/homoglyph decoding expands it back up), the second applyTruncation call at line ~1387 truncates again and appends the same marker a second time — but because substring(0, maxLength) is applied to text that already contains the first marker, the cut can land inside or immediately before the existing "[Content truncated due to length]" string, producing a confusing double-marker or a marker embedded mid-content rather than at a clean boundary.

This is a correctness/UX regression risk from adding a second truncation pass over content that may already carry a truncation marker. Consider either:

  1. Stripping any existing truncation marker before re-applying applyTruncation, or
  2. Reserving space for a single marker and asserting only one marker ever appears in output, with a regression test asserting sanitized.match(/\[Content truncated/g).length <= 1.
// Example guard
function applyTruncation(content, maxLength) {
  // Strip an already-applied marker before re-truncating
  content = content.replace(/\n\[Content truncated due to (length|line count)\]$/, "");
  ...
}

No existing test in compute_text.test.cjs currently checks for marker duplication after the double-truncation path introduced by this PR.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.86.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants