Repository navigation
fix(loader): warn when templates are excluded by .nuclei-ignore tags - #7452
Conversation
WalkthroughAdded ChangesTemplate Exclusion Tracking by Tags
🎯 2 (Simple) | ⏱️ ~8 minutes
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In `@pkg/catalog/loader/loader.go`:
- Around line 790-802: The noteExcludedByTag closure currently treats any tag in
indexFilter.ExcludeTags as coming from .nuclei-ignore; change it to only
increment templates.TemplatesExcludedStats and log the “excluded from default
run using .nuclei-ignore” message when the matching exclude tag actually came
from the ignore file: obtain the ignore-file tags (e.g., via
config.ReadIgnoreFile().Tags or a new store field like ignoreFileTags) and
replace the slices.ContainsFunc check against indexFilter.ExcludeTags with a
check against that ignore-file tag set (or check intersection between
metadata.HasTag and ignore-file tags) so CLI-provided exclude tags
(store.config.ExcludeTags / options.ExcludeTags) are not counted or logged as
.nuclei-ignore exclusions; keep all other behavior (stats/logging) unchanged and
only gate them on the ignore-file tag match.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4e1bd89e-1670-4893-892d-4d891d2adb5b
📒 Files selected for processing (1)
pkg/catalog/loader/loader.go
| // noteExcludedByTag surfaces a template the index filter dropped because it | ||
| // carries an excluded tag (e.g. the .nuclei-ignore defaults). Without it the | ||
| // exclusion is silent at every verbosity level. | ||
| noteExcludedByTag := func(templatePath string, metadata *index.Metadata) { | ||
| if len(indexFilter.ExcludeTags) == 0 || !slices.ContainsFunc(indexFilter.ExcludeTags, metadata.HasTag) { | ||
| return | ||
| } | ||
|
|
||
| stats.Increment(templates.TemplatesExcludedStats) | ||
| if config.DefaultConfig.LogAllEvents { | ||
| store.logger.Print().Msgf("[%v] %v excluded from default run using .nuclei-ignore\n", aurora.Yellow("WRN").String(), templatePath) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for where options.ExcludeTags is set, particularly checking if .nuclei-ignore
# tags are merged with CLI tags or kept separate
# Find where ExcludeTags field is assigned
rg -n -B3 -A3 '\.ExcludeTags\s*='
# Find references to .nuclei-ignore file loading
rg -n -B3 -A3 'nuclei-ignore'
# Check if there's a separate field or filtering for .nuclei-ignore vs CLI tags
ast-grep --pattern $'struct $_ {
$$$
ExcludeTags $$$
$$$
}'Repository: projectdiscovery/nuclei
Length of output: 9432
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Inspect loader.go around noteExcludedByTag and how indexFilter is built
echo "=== loader.go context (lines ~740-850) ==="
sed -n '740,850p' pkg/catalog/loader/loader.go | nl -ba
# 2) Find where indexFilter.ExcludeTags is assigned in loader.go
echo
echo "=== indexFilter.ExcludeTags assignments in loader.go ==="
rg -n 'indexFilter\.ExcludeTags\s*=' pkg/catalog/loader/loader.go
# 3) Trace how CLI `-exclude-tags` maps into filters.ExcludeTags / options.ExcludeTags
echo
echo "=== CLI flag parsing for exclude-tags ==="
rg -n --hidden --no-ignore-vcs 'exclude-?tags' .
# 4) Locate filters.ExcludeTags source
echo
echo "=== assignments to filters.ExcludeTags ==="
rg -n 'filters\.ExcludeTags\s*=' .
# 5) Locate flag definition (if any) for ExcludeTags and its destination
echo
echo "=== struct fields or options related to ExcludeTags ==="
rg -n 'ExcludeTags\s+(\[\]string|string)' .Repository: projectdiscovery/nuclei
Length of output: 171
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== loader.go (around noteExcludedByTag) ==="
awk 'NR>=770 && NR<=820 {print NR ":" $0}' pkg/catalog/loader/loader.go
echo
echo "=== indexFilter.ExcludeTags assignments (loader.go) ==="
rg -n 'indexFilter\.ExcludeTags\s*=' pkg/catalog/loader/loader.go
echo
echo "=== flag / parsing for exclude-tags ==="
rg -n --hidden --no-ignore-vcs 'exclude-?tags' .
echo
echo "=== assignments into filters.ExcludeTags ==="
rg -n --hidden --no-ignore-vcs 'filters\.ExcludeTags\s*=' .
echo
echo "=== places where config.ReadIgnoreFile().Tags is appended to exclude tags ==="
rg -n --hidden --no-ignore-vcs 'ReadIgnoreFile\(\)\.Tags' .Repository: projectdiscovery/nuclei
Length of output: 2069
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Find where indexFilter is defined/initialized in loader.go
echo "=== indexFilter occurrences in loader.go ==="
rg -n 'indexFilter' pkg/catalog/loader/loader.go
echo
echo "=== context around indexFilter setup/initialization (approx 650-780) ==="
awk 'NR>=600 && NR<=820 {print NR ":" $0}' pkg/catalog/loader/loader.go
# 2) Trace ExcludeTags assignment into indexFilter in the whole repo (not just loader.go)
echo
echo "=== assignments to *.ExcludeTags used for index filtering ==="
rg -n 'indexFilter\.(ExcludeTags|IncludeTags)\s*=' . || true
# 3) Find CLI flag / option mapping for exclude-tags
echo
echo "=== places defining exclude-tags flag ==="
rg -n --hidden --no-ignore-vcs 'exclude-?tags' .
# 4) Find where filters.ExcludeTags is populated from CLI/options
echo
echo "=== filters.ExcludeTags population ==="
rg -n --hidden --no-ignore-vcs 'filters\.ExcludeTags\s*[:=]' .
echo
echo "=== structs with ExcludeTags field definitions (limit to relevant hits) ==="
rg -n --hidden --no-ignore-vcs 'ExcludeTags\s+(\[\]string|[]string)' .
# 5) Narrow to command-line/options parsing for exclude tags if flag exists
# (helps connect CLI -> filters.ExcludeTags -> options.ExcludeTags -> store.config -> indexFilter)
echo
echo "=== search for '-exclude-tags' / 'ExcludeTags' in cmd/ or main package ==="
rg -n --hidden --no-ignore-vcs 'ExcludeTags' cmd . | head -n 200 || trueRepository: projectdiscovery/nuclei
Length of output: 11225
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== buildIndexFilter in loader.go ==="
rg -n 'buildIndexFilter' pkg/catalog/loader/loader.go
awk 'NR>=130 && NR<=260 {print NR ":" $0}' pkg/catalog/loader/loader.go
echo
echo "=== store.buildIndexFilter definition (full function if not too large) ==="
# Print exact function body by extracting from "func (store *Store) buildIndexFilter" to next "func"
start=$(rg -n 'func \(store \*Store\) buildIndexFilter' -n pkg/catalog/loader/loader.go | head -n1 | cut -d: -f1)
awk -v s="$start" 'NR>=s {print} NR>s && /^func / {exit}' pkg/catalog/loader/loader.go || true
echo
echo "=== index.Filter struct and ExcludeTags usage ==="
rg -n 'type Filter|ExcludeTags|HasTag|Matches\(' pkg/catalog/index/filter.go
echo
echo "=== index/filter.go around ExcludeTags handling ==="
awk 'NR>=300 && NR<=480 {print NR ":" $0}' pkg/catalog/index/filter.go
echo
echo "=== index/filter.go around struct definition ==="
awk 'NR>=1 && NR<=120 {print NR ":" $0}' pkg/catalog/index/filter.go
echo
echo "=== where config.ReadIgnoreFile().Tags gets appended into ExcludeTags ==="
rg -n 'ReadIgnoreFile\(\)\.Tags' -S .Repository: projectdiscovery/nuclei
Length of output: 13621
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== where options.ExcludeTags is used ==="
rg -n --hidden --no-ignore-vcs 'options\.ExcludeTags' .
echo
echo "=== where filters.ExcludeTags is filled from options ==="
rg -n --hidden --no-ignore-vcs 'filters\.ExcludeTags' lib pkg cmd internal .
echo
echo "=== trace store.config.ExcludeTags assignments ==="
rg -n --hidden --no-ignore-vcs 'ExcludeTags:\s*' lib pkg cmd internal .
echo
echo "=== inspect config/filter struct around ExcludeTags mapping ==="
# show relevant parts of lib/config.go
awk 'NR>=540 && NR<=660 {print NR ":" $0}' lib/config.goRepository: projectdiscovery/nuclei
Length of output: 8066
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== pkg/catalog/loader/loader.go around ExcludeTags assignment from options (~90-150) ==="
awk 'NR>=70 && NR<=160 {print NR ":" $0}' pkg/catalog/loader/loader.go
echo
echo "=== pkg/catalog/loader/loader.go call sites of noteExcludedByTag (~820-900) ==="
# noteExcludedByTag likely used shortly after load filtering loops
awk 'NR>=800 && NR<=920 {print NR ":" $0}' pkg/catalog/loader/loader.go
echo
echo "=== pkg/types/types.go around ExcludeTags wiring (~450-530) ==="
awk 'NR>=450 && NR<=530 {print NR ":" $0}' pkg/types/types.goRepository: projectdiscovery/nuclei
Length of output: 13223
Fix .nuclei-ignore exclusion stats/log to only count ignore-file tags
noteExcludedByTag (pkg/catalog/loader/loader.go:790-802) increments templates.TemplatesExcludedStats and logs “excluded from default run using .nuclei-ignore” whenever a template matches indexFilter.ExcludeTags, but indexFilter.ExcludeTags is built from store.config.ExcludeTags (store.buildIndexFilter → ExcludeTags: store.config.ExcludeTags), which is populated from options.ExcludeTags (CLI -exclude-tags) and also appended with config.ReadIgnoreFile().Tags (lib/sdk_private.go:140). There’s no check that the matched exclude tag originated from .nuclei-ignore, so CLI -exclude-tags will be misattributed/counted as .nuclei-ignore exclusions.
Separate ignore-file exclude tags from CLI exclude tags in the filter logic (e.g., check intersection only with config.ReadIgnoreFile().Tags) before incrementing/logging.
🤖 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 `@pkg/catalog/loader/loader.go` around lines 790 - 802, The noteExcludedByTag
closure currently treats any tag in indexFilter.ExcludeTags as coming from
.nuclei-ignore; change it to only increment templates.TemplatesExcludedStats and
log the “excluded from default run using .nuclei-ignore” message when the
matching exclude tag actually came from the ignore file: obtain the ignore-file
tags (e.g., via config.ReadIgnoreFile().Tags or a new store field like
ignoreFileTags) and replace the slices.ContainsFunc check against
indexFilter.ExcludeTags with a check against that ignore-file tag set (or check
intersection between metadata.HasTag and ignore-file tags) so CLI-provided
exclude tags (store.config.ExcludeTags / options.ExcludeTags) are not counted or
logged as .nuclei-ignore exclusions; keep all other behavior (stats/logging)
unchanged and only gate them on the ignore-file tag match.
nuclei 3.9.0 Created-by: HarmonybrewBot Commit-by: HarmonybrewBot Merged-by: HarmonybrewBot Description: Created by `brew bump` --- Created with `brew bump-formula-pr`.<details> <summary>release notes</summary> <pre>## What's Changed ### 🎉 New Features * Added support for protocol redirects by @Mzack9999 in projectdiscovery/nuclei#7296 * Added impacket integration by @Mzack9999 in projectdiscovery/nuclei#7356 * Added wmi, tsch, scmr, and dcom helper modules for JS by @dwisiswant0 in projectdiscovery/nuclei#7388 * Added mirroring of -config, -report-config, and -dashboard flags for SDK callers by @ShubhamRasal in projectdiscovery/nuclei#7393 ### 🐞 Bug Fixes * Fixed DNS variables resolving issue by @Mzack9999 in projectdiscovery/nuclei#7379 * Fixed expressions to prefer exact placeholders over expressions by @dwisiswant0 in projectdiscovery/nuclei#7397 * Fixed an issue in JS where runtimes that outlive the interrupt grace period are not abandoned, by @Mzack9999 in projectdiscovery/nuclei#7378 * Fixed an issue in reporting/jira where status-not was not matched against the transition target status name by @knakul853 in projectdiscovery/nuclei#7361 * Fixed runner to write unsigned-templates warning (WRN) to stderr by @ChrisJr404 in projectdiscovery/nuclei#7371 * Fixed `InternalEvent` access serialization in interactsh by @dwisiswant0 in projectdiscovery/nuclei#7322 * Fixed workflow to ensure context is properly propagated to child and race steps by @dwisiswant0 in projectdiscovery/nuclei#7383 * Fixed reporting/gitlab by bumping client-go to v1.9.1 and widening IDs to int64 by @ShubhamRasal in projectdiscovery/nuclei#7398 * Fixed memogen to skip context correctly and auto-push scope to upstream by @Mzack9999 in projectdiscovery/nuclei#7419 * Fixed a slice-bounds panic in telnetmini's ParseNTLMResponse when handling truncated NTLM challenges by @tejgokani in projectdiscovery/nuclei#7425 * Fixed SMBv1 probing in JS to occur after SMB2 negotiation by @dwisiswant0 in projectdiscovery/nuclei#7430 * Fixed templates to reject unknown fields when loading JSON templates by @tejgokani in projectdiscovery/nuclei#7453 * Fixed persistence of QueryAuthStrategy parameters in ApplyAuthStrategy by @akhilesharora in projectdiscovery/nuclei#7410 * Fixed loader to warn when templates are excluded by `.nuclei-ignore` tags by @dogancanbakir in projectdiscovery/nuclei#7452 ### Other Changes * Splitted exact matches and pattern handling in catalog for improved performance by @mikhail5555 in projectdiscovery/nuclei#7340 * Updated README_PT-BR.md to reflect current project description and features by @gugacyber in projectdiscovery/nuclei#7405 * Fixed broken cloud scanning link by @dogancanbakir in projectdiscovery/nuclei#7413 * Bumped pdsec modules + govaluate/aurora migration by @dogancanbakir in projectdiscovery/nuclei#7414 * Dropped vulnerable docker/docker dependency by @dogancanbakir in projectdiscovery/nuclei#7447 * Used path-aware filesystem containment checks by @dwisiswant0 in projectdiscovery/nuclei#7420 * Centralized runtime session handling by @dwisiswant0 in projectdiscovery/nuclei#7449 ## New Contributors * @ChrisJr404 made their first contribution in projectdiscovery/nuclei#7371 * @gugacyber made their first contribution in projectdiscovery/nuclei#7405 * @akhilesharora made their first contribution in projectdiscovery/nuclei#7410 * @tejgokani made their first contribution in projectdiscovery/nuclei#7425 **Full Changelog**: https://github.com/projectdiscovery/nuclei/compare/v3.8.0...v3.9.0</pre> <p>View the full release notes at <a href="https://github.com/projectdiscovery/nuclei/releases/tag/v3.9.0">https://github.com/projectdiscovery/nuclei/releases/tag/v3.9.0</a>.</p> </details> <hr> See merge request: Harmonybrew/homebrew-core!11229
Proposed changes
Closes #7431
Since the metadata-cache refactor (#6630), tag filtering moved into
index.Filterbut the stat increment + log the oldTagFilterpath performed were not carried over. Templates dropped by default.nuclei-ignoretags (e.g.local) now vanish with no warning at any verbosity, leaving only[FTL] no templates provided for scan.This re-feeds the existing
TemplatesExcludedStatsmachinery at the filter drop sites, guarded to actual exclude-tag exclusions so include-filter mismatches (-tags,-severity) are not mislabeled.Proof
Running
-code -t <local-tagged code template>:Before:
After:
A template dropped by an include filter (no excluded tag) prints no
.nuclei-ignoreline.Checklist
Summary by CodeRabbit
.nuclei-ignore) are now properly counted and logged with warnings for better visibility into exclusion reasons.