Skip to content

Fix #819: sanitize CN to prevent hang#951

Open
thanik123789-coder wants to merge 5 commits intoprojectdiscovery:mainfrom
thanik123789-coder:patch-1
Open

Fix #819: sanitize CN to prevent hang#951
thanik123789-coder wants to merge 5 commits intoprojectdiscovery:mainfrom
thanik123789-coder:patch-1

Conversation

@thanik123789-coder
Copy link

@thanik123789-coder thanik123789-coder commented Mar 10, 2026

This PR fixes the indefinite hang issue (#819) by sanitizing SubjectCN and IssuerCN length and encoding. /claim #819

Summary by CodeRabbit

  • Bug Fixes
    • Certificate issuer and subject common names are now sanitized: truncated to 256 characters and normalized to valid UTF-8 to prevent display or processing issues.
    • Responses now use the sanitized subject and issuer names and include those sanitized values in the response path.
    • Domain name handling and client-certificate error behavior remain unchanged.

This PR fixes the indefinite hang issue (projectdiscovery#819) by sanitizing SubjectCN and IssuerCN length and encoding. /claim projectdiscovery#819
@neo-by-projectdiscovery-dev
Copy link

neo-by-projectdiscovery-dev bot commented Mar 10, 2026

Analysis Report

Found: 2 critical | 1 low/info

Critical

DoS (Unchanged Code) - pkg/tlsx/ztls/utils.go:54,57
❌ Lines 54 and 57 assign IssuerCN and SubjectCN directly from cert.Issuer.CommonName and cert.Subject.CommonName WITHOUT sanitization. This file was NOT modified in this commit.

Export sanitizeCN as SanitizeCN in clients/utils.go and apply it: IssuerCN: clients.SanitizeCN(cert.Issuer.CommonName), SubjectCN: clients.SanitizeCN(cert.Subject.CommonName)

DoS (Unchanged Code) - pkg/ctlogs/ctlogs.go:409,413
❌ Lines 409 and 413 assign SubjectCN and IssuerCN directly from cert.Subject.CommonName and cert.Issuer.CommonName WITHOUT sanitization. This file was NOT modified in this commit.

Import and apply clients.SanitizeCN: SubjectCN: clients.SanitizeCN(cert.Subject.CommonName), IssuerCN: clients.SanitizeCN(cert.Issuer.CommonName)

Low / Info

Fixed Code Path - pkg/tlsx/clients/utils.go:18-19
✅ The clients.Convertx509toResponse function correctly applies sanitizeCN to both SubjectCN and IssuerCN, limiting them to 256 UTF-8 characters. > This path is secure.

Prompt for AI Agents

Fix dos (unchanged code):

In @pkg/tlsx/ztls/utils.go:54,57, Export sanitizeCN as SanitizeCN in clients/utils.go and apply it: IssuerCN: clients.SanitizeCN(cert.Issuer.CommonName), SubjectCN: clients.SanitizeCN(cert.Subject.CommonName)

Fix dos (unchanged code):

In @pkg/ctlogs/ctlogs.go:409,413, Import and apply clients.SanitizeCN: SubjectCN: clients.SanitizeCN(cert.Subject.CommonName), IssuerCN: clients.SanitizeCN(cert.Issuer.CommonName)

@coderabbitai
Copy link

coderabbitai bot commented Mar 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 015cd3a6-c3d9-4d0d-a16d-2217b46fc42a

📥 Commits

Reviewing files that changed from the base of the PR and between 8febee1 and 4773d4d.

📒 Files selected for processing (1)
  • pkg/tlsx/clients/utils.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/tlsx/clients/utils.go

Walkthrough

Adds a private helper sanitizeCN and uses it when populating IssuerCN, SubjectCN, and response path entries; the helper enforces valid UTF-8 and truncates CommonName values to 256 characters, and unicode/utf8 import was added.

Changes

Cohort / File(s) Summary
Certificate CN Sanitization
pkg/tlsx/clients/utils.go
Added private sanitizeCN(s string) string (valid UTF-8, truncate to 256 chars). Replaced raw cert.Subject.CommonName and cert.Issuer.CommonName uses with sanitized values for SubjectCN, IssuerCN, domainNames initialization, and response path. Added unicode/utf8 import.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 I nibble bytes both long and stout,
I trim the CN till none stick out,
I check each rune, make UTF‑8 right,
256 bounds keep names polite,
Hoppity-hop, certs sleep tight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix #819: sanitize CN to prevent hang' directly and clearly describes the main change: sanitizing CN values to resolve an indefinite hang issue, matching the file-level summaries.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
pkg/tlsx/clients/utils.go (1)

156-161: Consider rune-safe truncation to avoid splitting multi-byte characters.

Using len(s) and s[:256] operates on bytes, not characters. If a multi-byte UTF-8 character spans the 256-byte boundary, slicing will create an invalid sequence that ToValidUTF8 then removes. This could unintentionally drop a character that started within the limit.

♻️ Proposed rune-aware truncation
 func sanitizeCN(s string) string {
+	const maxLen = 256
-	if len(s) > 256 {
-		s = s[:256]
+	s = strings.ToValidUTF8(s, "")
+	if len(s) > maxLen {
+		// Truncate at rune boundary
+		truncated := []rune(s)
+		if len(truncated) > maxLen {
+			s = string(truncated[:maxLen])
+		}
 	}
-	return strings.ToValidUTF8(s, "")
+	return s
 }

Alternatively, if byte-length is the hard constraint (e.g., for downstream buffer limits), the current approach is acceptable—just be aware of the edge case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/tlsx/clients/utils.go` around lines 156 - 161, sanitizeCN uses byte-based
len and slicing which can split multi-byte UTF-8 runes; change sanitizeCN to
perform rune-aware truncation: convert s to []rune, if len(runes) > 256 set
runes = runes[:256], then return string(runes) (and you can still pass through
strings.ToValidUTF8 if desired). Update the function sanitizeCN to use the
rune-slicing approach instead of s[:256] to avoid dropping or corrupting
multi-byte characters while preserving the existing trimming behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/tlsx/clients/utils.go`:
- Around line 156-161: sanitizeCN uses byte-based len and slicing which can
split multi-byte UTF-8 runes; change sanitizeCN to perform rune-aware
truncation: convert s to []rune, if len(runes) > 256 set runes = runes[:256],
then return string(runes) (and you can still pass through strings.ToValidUTF8 if
desired). Update the function sanitizeCN to use the rune-slicing approach
instead of s[:256] to avoid dropping or corrupting multi-byte characters while
preserving the existing trimming behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d94e61d9-2576-4d7b-9a04-d02e2528bde5

📥 Commits

Reviewing files that changed from the base of the PR and between d13b67f and 7749745.

📒 Files selected for processing (1)
  • pkg/tlsx/clients/utils.go

Updated sanitizeCN to use rune-aware truncation to prevent multi-byte character corruption as suggested by CodeRabbit.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/tlsx/clients/utils.go`:
- Around line 31-33: The code seeds domainNames with the raw
cert.Subject.CommonName so downstream checks process attacker-controlled input;
fix by applying sanitizeCN to cert.Subject.CommonName before any use (e.g., when
populating domainNames) and propagate that sanitized value to all callers
instead of the raw field, ensuring IsMisMatchedCert and IsWildCardCert receive
the sanitized name; repeat the same change in the ZTLS builder and CT log
builder code paths so every place that currently reads cert.Subject.CommonName
uses sanitizeCN(cert.Subject.CommonName) (or a local sanitizedSubjectCN
variable) before further processing.
- Around line 156-162: The sanitizeCN function currently does []rune(s) which
decodes the entire input at once and can be exploited with large
attacker-controlled CNs; replace that eager decode with a bounded decode loop
using utf8.DecodeRuneInString to read at most 256 runes, appending each decoded
rune to a buffer/string builder and breaking once 256 runes are collected (thus
avoiding scanning the rest), then pass the resulting truncated string to
strings.ToValidUTF8 before returning; reference sanitizeCN and use
utf8.DecodeRuneInString and strings.ToValidUTF8 in the fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8c06ac77-b623-4d2a-bcb6-22d40a8a9ec1

📥 Commits

Reviewing files that changed from the base of the PR and between eaae1f3 and f704516.

📒 Files selected for processing (1)
  • pkg/tlsx/clients/utils.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
pkg/tlsx/clients/utils.go (1)

19-22: ⚠️ Potential issue | 🔴 Critical

Use the sanitized CN variables everywhere in this builder.

domainNames is still seeded from cert.Subject.CommonName, so IsMisMatchedCert and IsWildCardCert continue to process attacker-controlled input. Also, subjectCN and issuerCN are currently unused, which is a compile-time error in Go.

Proposed fix
-	subjectCN := sanitizeCN(cert.Subject.CommonName)
+	subjectCN := sanitizeCN(cert.Subject.CommonName)
 	issuerCN := sanitizeCN(cert.Issuer.CommonName)
-
-	domainNames := []string{cert.Subject.CommonName}
+
+	domainNames := []string{subjectCN}
 	domainNames = append(domainNames, cert.DNSNames...)
@@
-		IssuerCN:     sanitizeCN(cert.Issuer.CommonName),
+		IssuerCN:     issuerCN,
@@
-		SubjectCN:    sanitizeCN(cert.Subject.CommonName),
+		SubjectCN:    subjectCN,
#!/bin/bash
# Verify that the sanitized locals are only declared once and that domainNames
# still starts from the raw CommonName in the current patch.
sed -n '18,40p' pkg/tlsx/clients/utils.go
rg -nP '\b(subjectCN|issuerCN)\b' pkg/tlsx/clients/utils.go
rg -n 'domainNames := \[]string\{cert\.Subject\.CommonName\}' pkg/tlsx/clients/utils.go

Also applies to: 34-36

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/tlsx/clients/utils.go` around lines 19 - 22, The cert CommonName is
sanitized into subjectCN and issuerCN but domainNames is still seeded from the
raw cert.Subject.CommonName; update the builder so domainNames is initialized
from the sanitized subjectCN (i.e. domainNames := []string{subjectCN}) so
downstream functions like IsMisMatchedCert and IsWildCardCert only see sanitized
input, and either use issuerCN where intended or remove the unused issuerCN
variable to fix the compile error; ensure all references that should use the
sanitized value (sanitizeCN, subjectCN, issuerCN, domainNames, IsMisMatchedCert,
IsWildCardCert) are updated accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/tlsx/clients/utils.go`:
- Around line 159-160: The function sanitizeCN uses a non-existent identifier
"strings.builder"; update it to use the correct exported type "strings.Builder"
in the sanitizeCN function (and any related uses) so the code compiles; locate
the sanitizeCN function and replace strings.builder with strings.Builder and
ensure any methods called on the builder match the exported API.

---

Duplicate comments:
In `@pkg/tlsx/clients/utils.go`:
- Around line 19-22: The cert CommonName is sanitized into subjectCN and
issuerCN but domainNames is still seeded from the raw cert.Subject.CommonName;
update the builder so domainNames is initialized from the sanitized subjectCN
(i.e. domainNames := []string{subjectCN}) so downstream functions like
IsMisMatchedCert and IsWildCardCert only see sanitized input, and either use
issuerCN where intended or remove the unused issuerCN variable to fix the
compile error; ensure all references that should use the sanitized value
(sanitizeCN, subjectCN, issuerCN, domainNames, IsMisMatchedCert, IsWildCardCert)
are updated accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: be0975c7-9bb2-4b43-9f6c-8efda5f0b292

📥 Commits

Reviewing files that changed from the base of the PR and between f704516 and 8febee1.

📒 Files selected for processing (1)
  • pkg/tlsx/clients/utils.go

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.

1 participant