Skip to content

Conversation

@tarunKoyalwar
Copy link
Member

@tarunKoyalwar tarunKoyalwar commented Oct 24, 2025

Summary

This PR implements wildcard certificate detection for subdomain enumeration results. When a subdomain is covered by a wildcard certificate (e.g., *.example.com), it is now identified and marked in the JSON output with a wildcard_certificate field.

Functional Testing

Test Environment

  • Tool: subfinder v2.9.1-dev
  • Target domain: nuclei.sh
  • Test flags: -oJ -nW (JSON output with DNS resolution and wildcard filtering)

Test Results

Command:

./subfinder -d nuclei.sh -oJ -nW

Sample Output:

{"host":"api.nuclei.sh","input":"nuclei.sh","source":"crtsh","wildcard_certificate":true}
{"host":"retest-prod.api.nuclei.sh","input":"nuclei.sh","source":"crtsh"}
{"host":"identity.nuclei.sh","input":"nuclei.sh","source":"crtsh"}
{"host":"templates-api.nuclei.sh","input":"nuclei.sh","source":"virustotal"}
{"host":"retest-dev.api.nuclei.sh","input":"nuclei.sh","source":"securitytrails"}
{"host":"docs.nuclei.sh","input":"nuclei.sh","source":"virustotal"}
{"host":"testing-takeover-ec2.nuclei.sh","input":"nuclei.sh","source":"securitytrails"}
{"host":"template.nuclei.sh","input":"nuclei.sh","source":"crtsh"}
{"host":"templates.nuclei.sh","input":"nuclei.sh","source":"virustotal"}
{"host":"scan-prod.api.nuclei.sh","input":"nuclei.sh","source":"securitytrails"}
{"host":"app.nuclei.sh","input":"nuclei.sh","source":"virustotal"}
{"host":"pdcp-dev.metabase.nuclei.sh","input":"nuclei.sh","source":"securitytrails"}
{"host":"scan-dev.api.nuclei.sh","input":"nuclei.sh","source":"securitytrails"}
{"host":"www.nuclei.sh","input":"nuclei.sh","source":"crtsh"}
{"host":"tm.nuclei.sh","input":"nuclei.sh","source":"crtsh"}
{"host":"cve-dev.nuclei.sh","input":"nuclei.sh","source":"crtsh"}
{"host":"form.nuclei.sh","input":"nuclei.sh","source":"crtsh"}
{"host":"pdcp.metabase.nuclei.sh","input":"nuclei.sh","source":"securitytrails"}

Results Summary:

  • Successfully enumerated 18 subdomains for nuclei.sh
  • Execution time: 3.35 seconds
  • All results properly formatted in JSON
  • DNS resolution and wildcard filtering performed correctly

Wildcard Certificate Detection

When a source returns a subdomain with a wildcard certificate pattern (e.g., *.api.nuclei.sh), the JSON output will include:

{"host":"api.nuclei.sh","input":"nuclei.sh","source":"virustotal","wildcard_certificate":true}

The wildcard_certificate field is omitted when false (using omitempty tag), keeping output clean for standard subdomains.

Behavior Validation

Standard Mode (without -cs flag)

✅ JSON output includes wildcard_certificate field when applicable
✅ Field is omitted when value is false (clean output)
✅ Works correctly with -nW flag (DNS resolution + wildcard filtering)
✅ Works correctly with -oJ flag (JSON output)

Capture Sources Mode (-cs flag)

⚠️ Important Note: When using the -cs (capture sources) flag, the wildcard_certificate field is not included in the JSON output to avoid breaking changes to the library API. This ensures backward compatibility for existing integrations that rely on the current output format when capturing multiple sources.

Technical Implementation

Detection Logic

  • Checks if source result contains wildcard pattern: *.subdomain
  • If pattern found, marks subdomain with WildcardCertificate: true
  • Propagates flag through entire resolution pipeline
  • Includes flag in JSON output structures

Data Flow

  1. Source returns result with value containing *.example.com
  2. Detection logic identifies wildcard pattern
  3. HostEntry created with WildcardCertificate: true
  4. Flag passed to resolver and maintained through resolution
  5. Final JSON output includes wildcard_certificate field

Files Modified

  • pkg/resolve/resolve.go - Added WildcardCertificate to HostEntry and Result structs
  • pkg/runner/enumerate.go - Implemented wildcard detection logic
  • pkg/runner/outputter.go - Added wildcard_certificate to JSON output structs
  • pkg/runner/banners.go - Updated version to v2.9.1-dev
  • .goreleaser.yml - Fixed 386 architecture quoting
  • .gitignore - Added /subfinder binary

Test Plan

  • Build succeeds without errors
  • JSON output format remains valid
  • Standard enumeration works without regression
  • DNS resolution with -nW flag functions correctly
  • Wildcard certificate detection activates when pattern found
  • Field omitted when false (omitempty behavior)
  • Multiple sources handled correctly
  • Backward compatibility maintained for -cs flag

Closes

#1656

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added wildcard certificate detection and tracking for discovered subdomains.
    • Wildcard status now travels with host results and is included in all JSON outputs.
    • Duplicate handling updated to preserve wildcard information across result merges.
  • Chores

    • Bumped tool version to v2.9.1-dev.
    • Updated gitignore patterns to exclude the distribution and subfinder paths.

This commit implements wildcard certificate detection for subdomains that
are covered by wildcard certificates (e.g., *.example.com). When sources
return results containing wildcard patterns, the subdomain is now marked
with a wildcard_certificate field in JSON output mode.

Key changes:
- Added WildcardCertificate field to HostEntry and Result structs
- Detection logic: checks if result.Value contains "*.subdomain" pattern
- Propagates wildcard flag through resolution pipeline
- JSON output includes wildcard_certificate field (omitted if false)
- Updates version to v2.9.1-dev
- Fix .goreleaser.yml 386 architecture quoting
- Add /subfinder to .gitignore

Note: wildcard_certificate field is not included when using -cs flag to
avoid breaking changes to the library API.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@coderabbitai
Copy link

coderabbitai bot commented Oct 24, 2025

Walkthrough

Adds a boolean WildcardCertificate field propagated from enumeration through resolution to JSON outputs; bumps internal version to v2.9.1-dev; updates .gitignore to ignore dist and /subfinder.

Changes

Cohort / File(s) Change Summary
Configuration
\.gitignore
Replaced negated ignore for dist with direct dist ignore and added explicit ignore for /subfinder.
Core Data Model
pkg/resolve/resolve.go
Added WildcardCertificate bool to HostEntry and Result; propagated the field in result emissions.
Version Bump
pkg/runner/banners.go
Updated tool version constant from v2.9.0 to v2.9.1-dev.
Enumeration Logic
pkg/runner/enumerate.go
Detects wildcard subdomains, sets isWildcard, stores/merges WildcardCertificate into HostEntry, and ensures propagation through resolution and callbacks.
Output Formatting
pkg/runner/outputter.go
Added WildcardCertificate bool (json:"wildcard_certificate,omitempty") to JSON result structs and populated it in all JSON write paths.

Sequence Diagram(s)

sequenceDiagram
    participant Enum as Enumerate (pkg/runner/enumerate.go)
    participant Res as Resolve (pkg/resolve/resolve.go)
    participant Out as Output (pkg/runner/outputter.go)

    Enum->>Enum: compute isWildcard for subdomain
    Enum->>Res: create HostEntry with WildcardCertificate
    Note right of Res: WildcardCertificate propagated\ninside Result emissions
    Res->>Out: emit Result with WildcardCertificate to callback
    Out->>Out: populate JSON result structs\nwith wildcard_certificate
    Out->>Client: write JSON outputs
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

🐰
A small flag hops from find to file,
Marked wild or meek with gentle style,
Through resolve, through output bright,
It travels home by morning light,
A nimble change — a rabbit's smile!

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Add wildcard certificate detection in JSON output" accurately reflects the primary change in the changeset. The core of this PR implements wildcard certificate detection by adding the WildcardCertificate field to data structures (resolve.go), implementing detection logic (enumerate.go), and exposing the flag in JSON output via new fields in result structures (outputter.go). While the PR includes minor changes like a version bump and .gitignore updates, the title correctly captures the main objective. The title is concise, specific, and clear enough that a teammate reviewing history would understand this PR adds wildcard detection capability to the JSON output format.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue-1656-wildcard

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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
pkg/runner/enumerate.go (2)

70-73: Wildcard detection heuristic is brittle; prefer normalized, anchored check

strings.Contains(result.Value, "*."+subdomain) may miss/false-positive across varied source formats. Normalize once and check a left-most wildcard prefix. Also fix typo “furthur” → “further”.

-                // check if this subdomain is actually a wildcard subdomain
-                // that may have furthur subdomains associated with it
-                isWildcard := strings.Contains(result.Value, "*."+subdomain)
+                // check if this subdomain came from a wildcard certificate entry
+                // that may have further subdomains associated with it
+                isWildcard := isWildcardCert(result.Value, subdomain)
+
+// add near the top of this file (after replacer) or as a small helper:
+func isWildcardCert(value, subdomain string) bool {
+    raw := strings.TrimSpace(value)
+    raw = strings.TrimPrefix(raw, "http://")
+    raw = strings.TrimPrefix(raw, "https://")
+    // tolerate bullet prefixes some sources use
+    raw = strings.TrimPrefix(raw, "•.")
+    raw = strings.TrimPrefix(raw, "•")
+    // prefer anchored wildcard at left-most label; keep legacy contains as fallback
+    return strings.HasPrefix(raw, "*.") || strings.Contains(value, "*."+subdomain)
+}

96-103: Good: duplicate entries upgrade to wildcard

Upgrading existing uniqueMap entries to WildcardCertificate=true avoids losing metadata. Note: ResultCallback is not re-fired on this upgrade; if downstream consumers rely on callback for real-time metadata, consider re-emitting or documenting this behavior.

Would you like a guarded re-emit (e.g., only when flag flips from false→true) to keep callbacks in sync?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ba3645 and 5356ebe.

⛔ Files ignored due to path filters (1)
  • .goreleaser.yml is excluded by !**/*.yml
📒 Files selected for processing (5)
  • .gitignore (1 hunks)
  • pkg/resolve/resolve.go (3 hunks)
  • pkg/runner/banners.go (1 hunks)
  • pkg/runner/enumerate.go (4 hunks)
  • pkg/runner/outputter.go (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
pkg/runner/enumerate.go (1)
pkg/resolve/resolve.go (1)
  • HostEntry (27-32)
pkg/runner/outputter.go (1)
pkg/resolve/resolve.go (1)
  • HostEntry (27-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Analyze (go)
  • GitHub Check: Lint Test
  • GitHub Check: release-test
🔇 Additional comments (11)
pkg/runner/banners.go (1)

20-20: Version bump looks good

No functional changes; aligns banner/update callback usage.

.gitignore (1)

11-12: Ignore additions are sensible

Ignoring dist and the top-level subfinder binary reduces accidental commits.

pkg/resolve/resolve.go (3)

94-123: WildcardCertificate propagation through all resolve paths: LGTM

Flag is carried on pass-through, error, and resolved-subdomain emissions.


28-32: No breaking changes found; all composite literals already use keyed field syntax

All instances of resolve.HostEntry construction in the codebase (outputter.go:136, enumerate.go:106, enumerate.go:143) already use keyed field syntax. Adding new fields like WildcardCertificate poses no compilation risk since keyed literals don't depend on field order. No resolve.Result unkeyed literals were detected.

Likely an incorrect or invalid review comment.


64-71: Range-over-int is supported; no compatibility issue exists

The go.mod specifies go 1.24.0, which fully supports range-over-int (introduced in Go 1.22). Since the minimum supported Go version is 1.24.0, the code is compatible with the toolchain. No changes required.

pkg/runner/enumerate.go (3)

106-118: Propagation into HostEntry and initial callback: LGTM

HostEntry carries WildcardCertificate; callback gated on !RemoveWildcard is consistent with existing flow.


143-144: Callback on resolved results includes wildcard flag: LGTM

Keeps parity between non-resolve and resolve paths.


122-124: Task channel closure timing: LGTM

Close after draining passive results to let workers terminate cleanly; paired with pool’s close(Results) after wg.Wait.

pkg/runner/outputter.go (3)

22-26: JSON schema extension is sound

wildcard_certificate added with ,omitempty across host/IP and source variants. For -cs path, field stays omitted (no assignment), preserving backward compatibility.

Please confirm docs/examples for JSON (-oJ) mention the new field.

Also applies to: 29-34, 37-41


123-124: HostIP JSON propagation: LGTM

Flag wired into jsonSourceIPResult.


181-182: Host JSON propagation: LGTM

Flag wired into jsonSourceResult.

Fixed a bug where the wildcard_certificate field was being lost when using
the -nW flag (DNS resolution with wildcard filtering). The issue occurred
because when multiple sources find the same subdomain, only the first
occurrence is sent to the resolution pool. If a later source marks the
subdomain as having a wildcard certificate, that information was stored
in uniqueMap but never propagated to foundResults.

Solution: After resolution completes, merge wildcard certificate information
from uniqueMap into foundResults. This ensures that if any source marked a
subdomain as having a wildcard certificate, that flag is preserved in the
final output.

This ensures consistent behavior - wildcard_certificate field appears in
JSON output regardless of whether -nW flag is used.

Validation:
- Without -nW: api.nuclei.sh shows wildcard_certificate:true ✓
- With -nW: api.nuclei.sh shows wildcard_certificate:true ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5356ebe and 144d6e7.

📒 Files selected for processing (1)
  • pkg/runner/enumerate.go (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
pkg/runner/enumerate.go (1)
pkg/resolve/resolve.go (1)
  • HostEntry (27-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test Builds (macOS-latest)
  • GitHub Check: Test Builds (ubuntu-latest)
  • GitHub Check: Test Builds (windows-latest)
  • GitHub Check: Analyze (go)
🔇 Additional comments (4)
pkg/runner/enumerate.go (4)

96-102: Good handling of duplicate wildcard status updates.

The logic correctly updates an existing subdomain entry to mark it as a wildcard when a later source identifies it as such, even if it was initially discovered without the wildcard flag. The conditional check prevents unnecessary updates when the entry is already marked as a wildcard.


106-106: LGTM!

The wildcard status is correctly propagated into the HostEntry structure at creation time, consistent with the enhanced struct definition.


143-143: LGTM!

The wildcard certificate status is correctly propagated through the ResultCallback in RemoveWildcard mode, ensuring consistency across both enumeration paths.


149-157: Good defensive merge of wildcard status.

This merge correctly handles the edge case where a subdomain is sent to the resolution pool before a subsequent source marks it as a wildcard. The iteration over foundResults is safe here because it occurs after wg.Wait() ensures the enumeration goroutine has completed, preventing race conditions.

@tarunKoyalwar tarunKoyalwar self-assigned this Oct 24, 2025
@tarunKoyalwar tarunKoyalwar linked an issue Oct 24, 2025 that may be closed by this pull request
@ehsandeep ehsandeep merged commit 3cb054d into dev Nov 5, 2025
10 checks passed
@ehsandeep ehsandeep deleted the issue-1656-wildcard branch November 5, 2025 16:19
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.

Return wildcard_certificate metadata for CT-derived subdomains

4 participants