Skip to content

fix(runner): write unsigned-templates WRN to stderr - #7371

Merged
dwisiswant0 merged 3 commits into
projectdiscovery:devfrom
ChrisJr404:unsigned-warning-stderr
May 6, 2026
Merged

fix(runner): write unsigned-templates WRN to stderr#7371
dwisiswant0 merged 3 commits into
projectdiscovery:devfrom
ChrisJr404:unsigned-warning-stderr

Conversation

@ChrisJr404

@ChrisJr404 ChrisJr404 commented May 3, 2026

Copy link
Copy Markdown
Contributor

Closes #7314.

Background

When the user runs nuclei with -j / -jsonl, the JSON output stream is corrupted by the [WRN] Loading N unsigned templates for scan message:

$ ./nuclei -target http://example.test -j | jq .
[WRN] Loading 17 unsigned templates for scan. Use with caution.    # ← bare line breaks jq
{"template":"http/cves/2019/CVE-2019-1010287.yaml",...}
parse error: Invalid numeric literal at line 1, column 4

The cause is the call site at internal/runner/runner.go:931:

r.Logger.Print().Msgf("[%v] Loading %d unsigned templates for scan. Use with caution.", r.colorizer.BrightYellow("WRN"), value)

gologger's (*Logger).Print() returns a LevelSilent event — its docstring says "Print prints a string on stdout without any extra labels." It's used here to bypass level filtering (so the warning shows even at quiet verbosity), but the side-effect is that it pollutes the JSON output stream.

Change

Write the unsigned-templates warning straight to os.Stderr. That preserves the original intent — always emit, regardless of log level, with the colorized [WRN] prefix — while keeping os.Stdout clean for JSON / JSONL consumers.

fmt.Fprintf(os.Stderr, "[%v] Loading %d unsigned templates for scan. Use with caution.\n", r.colorizer.BrightYellow("WRN"), value)

fmt and os are already imported in this file.

Verification

go build ./... and go vet ./internal/runner/ both clean.

The integration suite already has filterUnsignedTemplatesWarnings() (internal/tests/integration/integration_test.go:269) that defensively strips this message — so no test churn is needed; the helper just becomes a no-op for stdout-only captures.

Notes

  • Diff is +5 / -1 lines, single file, no public-API change.
  • I deliberately scoped this PR to the unsigned-templates warning the issue calls out. There are three other r.Logger.Print().Msgf("[WRN] ...") call sites in the same file (lines 400, 619, 889) with the same pattern; happy to follow up with those in a separate PR if you'd like consistent stderr routing across all four.

Summary by CodeRabbit

  • Bug Fixes
    • Startup/execution warnings that were previously written to standard output (deprecated rate-limit, concurrency-above-max, missing DAST templates, and unsigned-template notices) now emit as proper warning-level messages to standard error to avoid contaminating JSON/streamed stdout. The unsigned-template notice now includes a formatted count inline.

…#7314)

`r.Logger.Print()` is `gologger`'s LevelSilent helper — it bypasses
level filtering but routes to stdout. Using it for the unsigned-
templates "Use with caution" warning means the line collides with the
JSON / JSONL stream when the user runs with `-j`/`-jsonl`, breaking
downstream `jq` and any other consumer that expects clean JSON on
stdout.

Write the warning to os.Stderr directly (keeping the colorized [WRN]
prefix and the always-emitted semantics) so the JSON output stays
parseable. The integration suite's filterUnsignedTemplatesWarnings
helper already strips this message defensively, so no test churn is
needed.

Closes projectdiscovery#7314.
@auto-assign
auto-assign Bot requested a review from Mzack9999 May 3, 2026 15:30
@neo-by-projectdiscovery-dev

neo-by-projectdiscovery-dev Bot commented May 3, 2026

Copy link
Copy Markdown

Neo - PR Security Review

No security issues found

Hardening Notes
  • The PR description mentioned the first review addressed only the unsigned-templates warning at line 931. This commit extends the same fix to 3 additional warning call sites (lines 400, 619, 889) for consistency, which aligns with the note in the original PR about following up with the other call sites.

Comment @pdneo help for available commands. · Open in Neo

@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Startup/execution warnings in internal/runner/runner.go previously emitted via r.Logger.Print().Msgf were changed to use the logger’s warning API (r.Logger.Warning().Msg / .Msgf), moving those messages to the logger warning level instead of the prior Print-style output.

Changes

Logging level and call-site updates

Layer / File(s) Summary
Logging call updates
internal/runner/runner.go
Replaced r.Logger.Print().Msgf uses with r.Logger.Warning().Msg / r.Logger.Warning().Msgf for several startup/execution notices.
Affected warnings (call sites)
internal/runner/runner.go
Updated warnings: deprecated rate-limit-per-minute, concurrency > max-host-error, No DAST templates found, and Loading N unsigned templates....
Minor formatting change
internal/runner/runner.go
Removed prior WRN-prefixed stdout-style formatting in favor of the logger Warning calls (message formatting adjusted where .Msgf retained).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 I spotted warnings hopping in the stream,
I nudged them gently—now they whisper, not scream.
JSON stays pure, no clutter in sight,
Nighttime scans hum, all output polite. 🥕✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR addresses the core requirement from issue #7314: redirecting warning messages from stdout to stderr to prevent JSON output corruption.
Out of Scope Changes check ✅ Passed Changes are limited to converting Logger.Print().Msgf calls to Logger.Warning().Msg, directly addressing the stdout pollution issue without introducing unrelated modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title accurately describes the main change—fixing the unsigned-templates warning to write to stderr instead of stdout to prevent JSON output pollution.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
internal/runner/runner.go (1)

399-401: ⚡ Quick win

Other r.Logger.Print() WRN calls are still stdout-bound.

If this warning was indeed reaching stdout (corrupting -j output), the same issue exists at:

  • Line 400: r.Logger.Print().Msgf("[%v] %v", aurora.BrightYellow("WRN"), "rate limit per minute is deprecated...")
  • Line 619: r.Logger.Print().Msgf("[%v] The concurrency value is higher than max-host-error", r.colorizer.BrightYellow("WRN"))
  • Line 889: r.Logger.Print().Msgf("[%v] No DAST templates found", aurora.BrightYellow("WRN"))

The PR author acknowledges scoping this PR to one site, but all three emit WRN-prefixed messages via Print() that would equally corrupt JSON output in -j mode.

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

In `@internal/runner/runner.go` around lines 399 - 401, Three warning messages
currently use r.Logger.Print().Msgf (e.g., the "rate limit per minute is
deprecated" call, the "The concurrency value is higher than max-host-error"
call, and the "No DAST templates found" call) which writes to stdout and can
corrupt JSON `-j` output; change each of those calls to the logger's
warning-level API (replace r.Logger.Print().Msgf(...) with
r.Logger.Warn().Msgf(...) or equivalent structured Warn method) so the messages
are emitted at warning level (preserving the same message text and
colorizer/aurora usage) instead of going to stdout.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/runner/runner.go`:
- Around line 399-401: Three warning messages currently use
r.Logger.Print().Msgf (e.g., the "rate limit per minute is deprecated" call, the
"The concurrency value is higher than max-host-error" call, and the "No DAST
templates found" call) which writes to stdout and can corrupt JSON `-j` output;
change each of those calls to the logger's warning-level API (replace
r.Logger.Print().Msgf(...) with r.Logger.Warn().Msgf(...) or equivalent
structured Warn method) so the messages are emitted at warning level (preserving
the same message text and colorizer/aurora usage) instead of going to stdout.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bf5022c9-bd24-47b2-a07f-e0f7e4d45f5f

📥 Commits

Reviewing files that changed from the base of the PR and between a3c07e3 and 4de72f8.

📒 Files selected for processing (1)
  • internal/runner/runner.go

CodeRabbit flagged that the rate-limit-deprecated, concurrency-vs-max-host-error,
and DAST-no-templates warnings have the same stdout-pollution problem the
unsigned-templates one had. Same fix — fmt.Fprintf to os.Stderr — applied to all
three so the stdout stream stays clean for -j / -jsonl consumers regardless of
which warning fires.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
internal/runner/runner.go (1)

931-935: ⚡ Quick win

Consider centralizing WRN-to-stderr formatting in a small helper.

The same [%v] ... + os.Stderr pattern now appears in multiple places; a helper will reduce drift and future regressions.

Proposed refactor sketch
+func (r *Runner) warnStderrf(format string, args ...interface{}) {
+	_, _ = fmt.Fprintf(os.Stderr, "[%v] %s\n", r.colorizer.BrightYellow("WRN"), fmt.Sprintf(format, args...))
+}
- fmt.Fprintf(os.Stderr, "[%v] Loading %d unsigned templates for scan. Use with caution.\n", r.colorizer.BrightYellow("WRN"), value)
+ r.warnStderrf("Loading %d unsigned templates for scan. Use with caution.", value)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/runner/runner.go` around lines 931 - 935, Multiple places duplicate
the pattern fmt.Fprintf(os.Stderr, "[%v] ...", r.colorizer.BrightYellow(...))
which risks drift; create a small helper (e.g., a method on the runner receiver
like warnStderr or a package-level stderrWarn) that accepts a format string and
args, formats the prefix using r.colorizer.BrightYellow("WRN") and writes to
os.Stderr, then replace the direct fmt.Fprintf calls (including the one in
runner.go that logs "Loading %d unsigned templates for scan. Use with caution.")
with calls to that helper so all WRN-to-stderr formatting is centralized.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/runner/runner.go`:
- Around line 931-935: Multiple places duplicate the pattern
fmt.Fprintf(os.Stderr, "[%v] ...", r.colorizer.BrightYellow(...)) which risks
drift; create a small helper (e.g., a method on the runner receiver like
warnStderr or a package-level stderrWarn) that accepts a format string and args,
formats the prefix using r.colorizer.BrightYellow("WRN") and writes to
os.Stderr, then replace the direct fmt.Fprintf calls (including the one in
runner.go that logs "Loading %d unsigned templates for scan. Use with caution.")
with calls to that helper so all WRN-to-stderr formatting is centralized.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7726028d-1f18-40d5-bcf0-c47fee82b753

📥 Commits

Reviewing files that changed from the base of the PR and between 4de72f8 and 8295580.

📒 Files selected for processing (1)
  • internal/runner/runner.go

@dogancanbakir
dogancanbakir requested a review from dwisiswant0 May 6, 2026 10:08

@dwisiswant0 dwisiswant0 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You can just use runner.Logger.Warning() instead.

Per the review on projectdiscovery#7371, swap the four `fmt.Fprintf(os.Stderr, ...)`
WRN sites for `r.Logger.Warning().Msgf(...)`. Same stderr destination,
no duplicated WRN-prefix logic, follows the existing pattern used
across the runner package.
@ChrisJr404

Copy link
Copy Markdown
Contributor Author

Done in d596add — swapped all four fmt.Fprintf(os.Stderr, ...) sites for r.Logger.Warning().Msgf(...). Cleaner and matches the existing pattern in this package. Build + vet + runner tests still pass locally.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
internal/runner/runner.go (1)

438-440: ⚡ Quick win

Remaining Print() call in Close() is inconsistent with the PR's intent.

The honeypot detector summary at Line 439 still uses r.Logger.Print().Msgf(...), while all the warning sites in this PR were migrated away from Print(). If any writer behind r.Logger routes Print-level events to stdout, this would still pollute -j/-jsonl output during scanner shutdown.

♻️ Proposed fix for consistency
 if r.honeypotDetector != nil {
-    r.Logger.Print().Msgf("%s\n", r.honeypotDetector.Summary())
+    r.Logger.Info().Msgf("%s\n", r.honeypotDetector.Summary())
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/runner/runner.go` around lines 438 - 440, The Close() method still
logs the honeypot detector summary using r.Logger.Print().Msgf(...), which is
inconsistent with the PR's migration away from Print(); update the call in the
Close() function to use the same non-Print logging level used elsewhere (e.g.,
r.Logger.Warn().Msgf(...) or the project’s chosen warning method) so the summary
goes to the appropriate log level and does not write to stdout/JSONL streams;
keep the existing message text and formatting but replace the Print() call on
r.Logger with the consistent log level method.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/runner/runner.go`:
- Around line 438-440: The Close() method still logs the honeypot detector
summary using r.Logger.Print().Msgf(...), which is inconsistent with the PR's
migration away from Print(); update the call in the Close() function to use the
same non-Print logging level used elsewhere (e.g., r.Logger.Warn().Msgf(...) or
the project’s chosen warning method) so the summary goes to the appropriate log
level and does not write to stdout/JSONL streams; keep the existing message text
and formatting but replace the Print() call on r.Logger with the consistent log
level method.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 140566bb-d28b-4777-8707-abb198c23e99

📥 Commits

Reviewing files that changed from the base of the PR and between 8295580 and d596add.

📒 Files selected for processing (1)
  • internal/runner/runner.go

@ChrisJr404

ChrisJr404 commented May 6, 2026

Copy link
Copy Markdown
Contributor Author

Pushed d596add. Swapped the four fmt.Fprintf(os.Stderr, ...) calls for r.Logger.Warning().Msgf(...). Checked the gologger CLI writer first to make sure routing was the same (everything except LevelSilent already goes to stderr), so output is unchanged.

@dwisiswant0 dwisiswant0 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

@dwisiswant0 dwisiswant0 changed the title fix(runner): write unsigned-templates WRN to stderr (#7314) fix(runner): write unsigned-templates WRN to stderr May 6, 2026
@dwisiswant0
dwisiswant0 merged commit 1f8dd03 into projectdiscovery:dev May 6, 2026
2 checks passed
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.

[BUG] Warning Message Appears in Stdout Breaking Json Output

2 participants