feat(loader): implement persistent metadata cache#6630
Conversation
WalkthroughAdds an otter-backed in-memory Metadata Index with Gob snapshot persistence, a Filter system (include/exclude/forced rules with wildcard matching), integrates index-based pre-filtering into the loader to avoid unnecessary YAML parsing, and includes unit tests and benchmarks for index, filter, and loader behaviors. Changes
sequenceDiagram
participant User
participant Loader as Store/Loader
participant Index as MetadataIndex
participant Disk
participant Parser as YAMLParser
User->>Loader: LoadTemplates(filter)
Loader->>Index: loadTemplatesIndex()
alt snapshot exists
Index->>Disk: Load snapshot (Gob)
Disk-->>Index: Snapshot metadata
else no snapshot
Index-->>Index: Initialize empty cache
end
Loader->>Index: Filter(filter_criteria)
Index-->>Loader: candidate paths
loop for each candidate path
alt metadata indicates parsing needed
Loader->>Parser: Parse template YAML
Parser-->>Loader: Template + metadata
Loader->>Index: SetFromTemplate(path, metadata)
Index-->>Index: Update cache
else skip parsing
Loader-->>Index: (no-op)
end
end
Loader->>Index: Save() (deferred)
Index->>Disk: Atomic write (temp -> final)
Disk-->>Index: Persisted snapshot
Loader-->>User: Loaded templates
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~35 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
pkg/catalog/loader/loader_bench_test.go (1)
47-187: Verify benchmark isolation and state management.Each sub-benchmark calls
LoadTemplatesrepeatedly within the loop. Ensure that:
- Template state doesn't accumulate across iterations (which could skew timing)
- The store is properly reset or isolated between iterations
- The benchmark measures the intended operation without pollution from previous iterations
Consider whether each iteration should create a fresh store or if the current approach correctly measures incremental loading.
pkg/catalog/index/filter_test.go (1)
148-156: Clarify the filter logic explanation in the test comment.The comment on line 154 says "With OR logic, matches because author AND severity match" which is contradictory. Based on the test setup and the PR objectives, it appears the filtering uses:
- OR logic within each field (e.g., multiple authors)
- AND logic across fields (e.g., must match author AND severity)
Consider revising the comment to clearly state: "With AND logic across fields, matches because both author and severity match (tag mismatch is ignored since another field matches)."
pkg/catalog/loader/loader.go (2)
372-383: IncludeConditions bypass cache optimization.When
tagFilteris present (for IncludeConditions), the code performs full template parsing even for cached entries. This is expected per the PR objectives but means the cache provides limited benefit when using advanced-tcfilters.Consider documenting this trade-off more prominently, or exploring future optimizations to cache IncludeConditions evaluation results.
683-707: Consider adding inline comments to clarify the metadata caching flow.The logic correctly handles both cached and uncached metadata scenarios, but it's complex:
- If cached metadata exists and matches filter, proceed to LoadTemplate (for tagFilter check)
- If not cached, LoadTemplate first, then cache metadata and verify filter match
- Early return if newly cached metadata doesn't match filter
Adding brief inline comments at lines 686, 693, and 703 would help future maintainers understand the flow.
pkg/catalog/index/index.go (1)
58-83: Cache weigher provides reasonable approximation.The weigher function calculates memory usage by summing string and slice content lengths. While it doesn't account for all Go runtime overhead (slice headers ~24B, bool 1B, pointers 8B), this approximation is acceptable for LRU eviction decisions.
If cache sizing becomes problematic in production, consider adding overhead estimates:
- ~24B per slice header (Authors, Tags)
- ~1B for Verified bool
- ~8B for pointer fields
pkg/catalog/index/filter.go (1)
257-295: Suggestion to expose direct parsing methods on Holder types is valid but optional.The verification confirms there are no
ParseStringorFromStringmethods onseverity.Holderortypes.TypeHolder. The private functionstoSeverity(severity.go:49) andtoProtocolType(types.go:84) exist but are intentionally kept private. The current code correctly uses the publicUnmarshalYAMLAPI, which is the canonical interface for these types.While the suggestion to expose these parsing functions as public methods (e.g.,
ParseStringorFromString) on the Holder types would improve maintainability, the current approach is not fragile—it uses a stable public interface. Exposing the private parsing functions as public methods would be a reasonable refactoring that simplifies future uses of these types.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
.github/workflows/generate-pgo.yamlis excluded by!**/*.yaml.github/workflows/perf-regression.yamlis excluded by!**/*.yaml.github/workflows/tests.yamlis excluded by!**/*.yamlgo.sumis excluded by!**/*.sum
📒 Files selected for processing (8)
go.mod(1 hunks)pkg/catalog/index/filter.go(1 hunks)pkg/catalog/index/filter_test.go(1 hunks)pkg/catalog/index/index.go(1 hunks)pkg/catalog/index/index_test.go(1 hunks)pkg/catalog/index/metadata.go(1 hunks)pkg/catalog/loader/loader.go(9 hunks)pkg/catalog/loader/loader_bench_test.go(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-16T21:27:14.937Z
Learnt from: hdm
Repo: projectdiscovery/nuclei PR: 6322
File: pkg/templates/compile.go:79-81
Timestamp: 2025-07-16T21:27:14.937Z
Learning: To make the template caching mechanism in pkg/templates/compile.go production-ready, DSLs need to be updated to use runtime options instead of cached variables, rather than restoring the Compile() calls on each request.
Applied to files:
pkg/catalog/index/index.gopkg/catalog/loader/loader.go
🧬 Code graph analysis (2)
pkg/catalog/index/index.go (5)
pkg/catalog/index/metadata.go (1)
Metadata(13-54)pkg/catalog/config/constants.go (2)
Version(34-34)BinaryName(40-40)pkg/model/types/severity/severity.go (1)
Severity(11-11)pkg/templates/types/types.go (1)
ProtocolType(15-15)pkg/catalog/index/filter.go (2)
Filter(21-57)FilterFunc(236-236)
pkg/catalog/index/index_test.go (3)
pkg/catalog/index/index.go (3)
NewIndex(46-97)IndexFileName(18-18)IndexVersion(22-22)pkg/catalog/index/metadata.go (1)
Metadata(13-54)pkg/model/types/stringslice/stringslice.go (1)
StringSlice(29-31)
⏰ 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). (1)
- GitHub Check: Lint
🔇 Additional comments (12)
pkg/catalog/loader/loader_bench_test.go (1)
212-213: LGTM! Pre-warming approach is appropriate for cache benchmarks.The pre-warming step correctly ensures the benchmark measures warm cache performance, which aligns with the metadata caching feature being tested.
Also applies to: 233-234
pkg/catalog/loader/loader.go (4)
94-102: LGTM! Index initialization and lifecycle management are well-designed.The use of
sync.OnceFuncforsaveMetadataIndexOnceensures atomic save operations, and graceful error handling with warnings prevents cache failures from blocking normal operations.Also applies to: 172-185
318-337: LGTM! Filter construction correctly maps all configuration fields.The direct type casts for
SeveritiesandProtocolTypesare safe given they're slice type aliases.
339-353: LGTM! Graceful degradation pattern for index loading.Returning
nilon errors and logging warnings ensures the system continues to function even if the cache is unavailable, which aligns with the PR's goal of non-breaking fallback behavior.
608-609: LGTM! Pre-allocating slice capacity is a nice optimization.Using
makewith capacity avoids reallocation duringappendoperations.pkg/catalog/index/metadata.go (1)
12-85: LGTM! Metadata struct and helper methods are well-designed.The
IsValidmethod correctly usesModTime.Equalfor timezone-aware comparison, and the matching helpers are straightforward and efficient. The NOTE about future extensions is valuable guidance for maintainers.pkg/catalog/index/filter.go (1)
206-233: LGTM! Path and ID wildcard matching logic is sound.Both functions correctly handle exact matches and shell-style wildcards. The directory prefix matching in
matchesPath(line 226) appropriately handles directory-based include/exclude patterns.pkg/catalog/index/index.go (3)
105-122: LGTM! Cache Get/validation with async deletion is well-designed.The asynchronous deletion on invalid entries (line 116) is safe because
IsValid()checks would consistently fail, and multipleDeletecalls are idempotent. This approach avoids blocking readers on write locks.
202-273: LGTM! Persistence implementation is robust with proper atomic writes and error handling.The Save method correctly implements atomic writes using temp file + rename. The Load method appropriately handles missing files, corruption, and version mismatches by removing invalid cache files and falling back gracefully.
275-353: LGTM! Query methods are thread-safe and efficient.All methods properly use
RLockfor concurrent reads, andAll()preallocates capacity to avoid reallocation. The early returns for empty filters are good optimizations.pkg/catalog/index/index_test.go (1)
1-689: LGTM! Comprehensive test coverage for the Index implementation.The test suite covers happy paths, error paths, edge cases, concurrency, and persistence. The tests properly use temp directories, clean up resources, and verify both positive and negative cases.
go.mod (1)
90-90: No issues found.The version v2.2.1 is the latest stable release, and no security vulnerabilities have been detected for this version. The dependency is appropriately versioned and secure.
50a83ea to
c177839
Compare
|
e3ad221 to
d626ddc
Compare
for template filtering optimization. Introduce a new template metadata indexing system with persistent caching to dramatically improve template loading perf when filters are applied. The implementation adds a new index pkg that caches lightweight template metadata (ID, tags, authors, severity, .etc) and enables filtering templates before expensive YAML parsing occurs. The index uses an in-memory LRU cache backed by `otter` pkg for efficient memory management with adaptive sizing based on entry weight, defaulting to approx. 40MB for 50K templates. Metadata is persisted to disk using gob encoding at "~/.cache/nuclei/index.gob" with atomic writes to prevent corruption. The cache automatically invalidates stale entries using `ModTime` to detect file modifications, ensuring metadata freshness w/o manual intervention. Filtering has been refactored from the previous `TagFilter` and `PathFilter` approach into a unified `index.Filter` type that handles all basic filtering ops including severity, authors, tags, template IDs with wildcard support, protocol types, and path-based inclusion and exclusion. The filter implements OR logic within each field type and AND logic across different field types, with exclusion filters taking precedence over inclusion filters and forced inclusion via `IncludeTemplates` and `IncludeTags` overriding exclusions. The `loader` integration creates an index filter from store configuration via `buildIndexFilter` and manages the cache lifecycle through `loadTemplatesIndex` and `saveTemplatesIndex` methods. When `LoadTemplatesOnlyMetadata` or `LoadTemplatesWithTags` is called, the system first checks the metadata cache for each template path. If cached metadata exists and passes validation, the filter is applied directly against the metadata without parsing. Only templates matching the filter criteria proceed to full YAML parsing, resulting in significant performance gains. Advanced filtering via "-tc" flag (`IncludeConditions`) still requires template parsing as these are expression-based filters that cannot be evaluated from metadata alone. The `TagFilter` has been simplified to handle only `IncludeConditions` while all other filtering ops are delegated to the index-based filtering system. Cache management is fully automatic with no user configuration required. The cache gracefully handles errors by logging warnings & falling back to normal op w/o caching. Cache files use schema versioning to invalidate incompatible cache formats across nuclei updates (well, specifically `Index` and `Metadata` changes). This optimization particularly benefits repeated scans with the same filters, CI/CD pipelines running nuclei regularly, development and testing workflows with frequent template loading, and any scenario with large template collections where filtering would exclude most templates.
Signed-off-by: Dwi Siswanto <git@dw1.io>
Signed-off-by: Dwi Siswanto <git@dw1.io>
Signed-off-by: Dwi Siswanto <git@dw1.io>
for proper template matching. The `filter.matchesIncludes()` was using OR logic across different filter types, causing incorrect template matching. Additionally, ID matching was case-sensitive, failing to match patterns like 'CVE-2021-*'. The filter now correctly implements: (author1 OR author2) AND (tag1 OR tag2) AND (severity1 OR severity2) - using OR within each filter type and AND across different types. Signed-off-by: Dwi Siswanto <git@dw1.io>
in CI environments. Some test was failing in CI due to filesystem timestamp resolution limitations. On filesystems with 1s ModTime granularity (common in CI), modifying a file immediately after capturing its timestamp resulted in identical ModTime values, causing IsValid() to incorrectly return true. Signed-off-by: Dwi Siswanto <git@dw1.io>
d626ddc to
f990e5b
Compare
Signed-off-by: Dwi Siswanto <git@dw1.io>
f990e5b to
e659c88
Compare
during cache save/load. Explicitly close file handles before performing rename/remove ops in `Save` and `Load` methods. * In `Save`, close temp file before rename. * In `Load`, close file before remove during error handling/version mismatch. Signed-off-by: Dwi Siswanto <git@dw1.io>
Fix path separator mismatch in `TestCacheSize` and `TestCachePersistenceWithLargeDataset` by using `filepath.Join` consistently instead of hardcoded forward slashes. Signed-off-by: Dwi Siswanto <git@dw1.io>
The integration tests were panicking with a nil pointer dereference in `pkg/catalog/loader` because the logger was not init'ed. When `store.saveMetadataIndexOnce` attempted to log the result of the metadata cache op, it dereferenced the nil logger, causing a crash. Signed-off-by: Dwi Siswanto <git@dw1.io>
for metadata cache filter. The `indexFilter` was previously init'ed using raw relative paths from the config for `IncludeTemplates` and `ExcludeTemplates`. But the persistent metadata cache stores templates using their absolute paths. This mismatch caused the `matchesPath` check to fail, leading to templates being incorrectly excluded even when explicitly included via flags (e.g., "-include-templates loader/excluded-template.yaml"). This commit updates `buildIndexFilter` to resolve these paths to their absolute versions using `store.config.Catalog.GetTemplatesPath` before creating the filter, ensuring consistent path matching against the metadata cache. Signed-off-by: Dwi Siswanto <git@dw1.io>
|
Yay! Everything's green now. |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pkg/catalog/loader/loader.go (1)
631-713: Same “no-index” semantics gap exists inLoadTemplatesWithTags
LoadTemplatesWithTagsmirrorsLoadTemplatesOnlyMetadata: all checks againstindexFilterare guarded byif store.metadataIndex != nil. If the index can’t be created/loaded, templates are still parsed and then filtered only bytagFilterand execution-time options (DAST, headless, code, self-contained, signatures), but all basic metadata filters (authors/tags/IDs/severity/protocols/path) are skipped.To keep CLI behavior independent of cache availability, it would be safer to:
- Either compute a
Metadataview fromparsedand callindexFilter.Matcheseven whenmetadataIndexisnil, or- Detect a
nilmetadataIndexearly and route this code path through the previous non-index filtering mechanism.Currently, users could see different template sets under the same flags depending solely on whether the metadata cache file can be used.
🧹 Nitpick comments (1)
pkg/catalog/index/filter.go (1)
285-344: Consider including more fields inFilter.Stringfor easier debugging
Filter.Stringcurrently only renders authors, tags, exclude-tags, IDs, severities, and protocol types. It omits other influential fields likeIncludeTags,IncludeTemplates,ExcludeTemplates,ExcludeIDs,ExcludeSeverities, andExcludeProtocolTypes, which can make logs less informative when debugging complex filter combinations.If log readability becomes an issue, consider extending
String()to include these additional fields (possibly abbreviated) to give a fuller picture of the active filter.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
.github/workflows/generate-pgo.yamlis excluded by!**/*.yaml.github/workflows/perf-regression.yamlis excluded by!**/*.yaml.github/workflows/tests.yamlis excluded by!**/*.yamlgo.sumis excluded by!**/*.sum
📒 Files selected for processing (9)
cmd/integration-test/library.go(2 hunks)go.mod(1 hunks)pkg/catalog/index/filter.go(1 hunks)pkg/catalog/index/filter_test.go(1 hunks)pkg/catalog/index/index.go(1 hunks)pkg/catalog/index/index_test.go(1 hunks)pkg/catalog/index/metadata.go(1 hunks)pkg/catalog/loader/loader.go(9 hunks)pkg/catalog/loader/loader_bench_test.go(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- pkg/catalog/index/metadata.go
- pkg/catalog/index/filter_test.go
- pkg/catalog/loader/loader_bench_test.go
- pkg/catalog/index/index.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: Format Go code usinggo fmt ./...
Run static analysis usinggo vet ./...
Files:
pkg/catalog/index/filter.gocmd/integration-test/library.gopkg/catalog/loader/loader.gopkg/catalog/index/index_test.go
🧬 Code graph analysis (3)
pkg/catalog/index/filter.go (4)
pkg/model/types/severity/severities.go (1)
Severities(13-13)pkg/model/types/severity/severity.go (2)
Severity(11-11)Holder(70-72)pkg/templates/types/types.go (3)
ProtocolTypes(142-142)ProtocolType(15-15)TypeHolder(103-105)pkg/catalog/index/metadata.go (1)
Metadata(13-54)
pkg/catalog/loader/loader.go (5)
pkg/catalog/index/index.go (2)
Index(32-37)NewDefaultIndex(101-103)pkg/catalog/index/filter.go (1)
Filter(20-56)pkg/model/types/severity/severities.go (1)
Severities(13-13)pkg/model/types/severity/severity.go (1)
Severity(11-11)pkg/templates/types/types.go (2)
ProtocolTypes(142-142)ProtocolType(15-15)
pkg/catalog/index/index_test.go (2)
pkg/catalog/index/index.go (4)
NewIndex(46-97)IndexFileName(18-18)IndexVersion(22-22)NewDefaultIndex(101-103)pkg/catalog/index/metadata.go (1)
Metadata(13-54)
🔇 Additional comments (6)
cmd/integration-test/library.go (1)
18-19: Initialize logger ondefaultOptsto avoid nil-logger pathsWiring
defaultOpts.Logger = gologger.DefaultLoggerhere looks correct and aligns this integration path with the main CLI behavior, which should keep the new loader/index code from ever encountering a nil logger ontypes.Options.Only thing to double‑check is whether the additional logging in these integration tests is acceptable for your CI output; if not, you may want to configure
DefaultLogger(or an alternative logger instance) to be quieter in this context.Please run
go test ./cmd/integration-test(plusgo vet ./...andgo fmt ./...as per guidelines) and confirm the logging output is as expected in your test/CI environment.Also applies to: 72-75
go.mod (1)
48-100: Newotter/v2dependency looks appropriate; just ensure tooling is updatedThe addition of
github.com/maypok86/otter/v2 v2.2.1for the metadata cache is reasonable and consistent with the new index package. Please just make surego mod tidyand your usualgo vet ./.../ CI checks are run so the module graph and diagnostics stay in sync with this dependency.pkg/catalog/loader/loader.go (2)
141-186: Index initialization & save hook are clean and lifecycle-awareUsing
loadTemplatesIndex,buildIndexFilter, andsync.OnceFuncforsaveMetadataIndexOncegives a clear lifecycle: lazy load at store creation, single save after first load operation, and graceful logging on errors. This aligns well with the “automatic cache lifecycle, non-fatal failures” objective.
318-356: Index filter construction matches config fields and types
buildIndexFiltercorrectly maps loaderConfigfields (authors/tags/IDs/severities/protocols and include/exclude templates) into theindex.Filter, with proper type conversions for severities and protocol types. This keeps the filtering semantics centralized and avoids duplicating parsing logic in the loader.pkg/catalog/index/filter.go (1)
58-176: Filter matching semantics now align with the documented AND/OR behaviorThe combination of
Matches,isForcedInclude,isExcluded, andmatchesIncludescorrectly implements:
- Forced include via
IncludeTemplates/IncludeTags- Exclusions taking precedence via
Exclude*- AND across different include fields (authors, tags, IDs, severities, protocol types)
- OR within each field (any author/tag/ID/severity/protocol type value can match)
The wildcard and case-insensitive handling in
matchesIDand flexible path rules inmatchesPathalso look appropriate for typical template ID/path patterns.pkg/catalog/index/index_test.go (1)
21-708: Index test suite is comprehensive and exercises critical behaviorsThe tests here do a solid job covering the new index: basic cache ops, versioning, persistence (including corrupted files and atomic saves), metadata validation, SetFromTemplate extraction (including multi-protocol and nil-classification cases), concurrency on Set/Has, and large datasets. This should give good confidence that the index behaves correctly under both normal and adverse conditions.
Signed-off-by: Dwi Siswanto <git@dw1.io>
Signed-off-by: Dwi Siswanto <git@dw1.io>
93dd108 to
8e0a55d
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
pkg/catalog/loader/loader.go (2)
358-417: Good: basic filters now apply even when the index is unavailable (for metadata-only load)In
LoadTemplatesOnlyMetadata, the fallback branch that builds aMetadataviaindex.NewMetadataFromTemplatewhenmetadataIndexis nil ensuresindexFilter.Matchesis still enforced, addressing the previous issue where basic filters were skipped if the index couldn’t be initialized. The tagFilter/IncludeConditions path is also preserved.
635-787: Basic filters are still skipped in LoadTemplatesWithTags when the metadata index fails to initializeWhen
store.metadataIndexisnil(e.g., default cache dir can’t be created),LoadTemplatesWithTagsnever evaluatesindexFilter.Matches:
- The early
if store.metadataIndex != nil { … }block is skipped entirely.- The later
if store.metadataIndex != nil && parsed != nil && !metadataCachedblock is also skipped.- There is no
index.NewMetadataFromTemplatefallback here as you added inLoadTemplatesOnlyMetadata.As a result, when the index can’t be created, CLI options backed solely by
index.Filter(authors, tags/exclude-tags/include-tags, include/exclude IDs, severities, protocols, include/exclude templates) become no-ops for this main loading path, contradicting the “graceful fallback without caching” requirement. OnlytagFilter/IncludeConditions continue to be honored.You can mirror the
LoadTemplatesOnlyMetadatabehavior by always deriving aMetadatafrom the parsed template and applyingindexFilter.Matches, caching it viaSetFromTemplateonly whenmetadataIndexis available. For example:@@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templates.Template { - if loaded { - parsed, err := templates.Parse(templatePath, store.preprocessor, store.config.ExecutorOptions) - - if store.metadataIndex != nil && parsed != nil && !metadataCached { - metadata, _ := store.metadataIndex.SetFromTemplate(templatePath, parsed) - if metadata != nil && !indexFilter.Matches(metadata) { - return - } - } + if loaded { + parsed, err := templates.Parse(templatePath, store.preprocessor, store.config.ExecutorOptions) + + if parsed != nil && indexFilter != nil { + var metadata *index.Metadata + if store.metadataIndex != nil { + // Warm/populate the index when available. + metadata, _ = store.metadataIndex.SetFromTemplate(templatePath, parsed) + } else { + // Fallback: still honor filters without persisting. + metadata = index.NewMetadataFromTemplate(templatePath, parsed) + } + if metadata != nil && !indexFilter.Matches(metadata) { + return + } + }This keeps the cache optional while ensuring filter semantics are identical whether the index is usable or not.
🧹 Nitpick comments (3)
pkg/catalog/index/index_test.go (1)
199-218: Consider deduplicating the corrupted-cache tests
TestCacheCorruptedFileandTestCacheLoadCorruptedRemovalexercise nearly the same behavior (loading a corrupted gob file removes it and results in an empty cache). This is great coverage, but you can likely merge them or factor out a helper to avoid duplication and keep future changes to the corruption behavior localized to one place.Also applies to: 566-589
pkg/catalog/loader/loader.go (1)
318-356: Optional: surface include/exclude template resolution errors in buildIndexFilter
buildIndexFilterresolvesIncludeTemplates/ExcludeTemplatesviaCatalog.GetTemplatesPathbut ignores the returned error maps, unlike other call sites where you log vialogErroredTemplates. If resolution fails here, the filter silently drops those include/exclude constraints.If you want stricter behavior, you could log those errors or at least reuse
logErroredTemplatesfor consistency; otherwise this is fine as a best-effort optimization.pkg/catalog/index/metadata.go (1)
13-55: Metadata shape and helpers align well with index usageThe
Metadatafields and gob tags are well-chosen for lightweight filtering, andNewMetadataFromTemplatecleanly mirrors the template’s core info (withModTimedelegated toSetFromTemplate).IsValid’sModTime.Equalcheck is a simple and cheap freshness guard; if you ever hit edge cases on low-resolution filesystems, you might consider allowing a small skew, but for now this is a reasonable trade-off.The
MatchesSeverity,MatchesProtocol,HasTag, andHasAuthorhelpers are straightforward and keep filter code tidy.Also applies to: 57-73, 75-104
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
pkg/catalog/index/index.go(1 hunks)pkg/catalog/index/index_test.go(1 hunks)pkg/catalog/index/metadata.go(1 hunks)pkg/catalog/loader/loader.go(9 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: Format Go code usinggo fmt ./...
Run static analysis usinggo vet ./...
Files:
pkg/catalog/index/metadata.gopkg/catalog/index/index_test.gopkg/catalog/loader/loader.gopkg/catalog/index/index.go
🧬 Code graph analysis (4)
pkg/catalog/index/metadata.go (2)
pkg/model/types/severity/severity.go (1)
Severity(11-11)pkg/templates/types/types.go (1)
ProtocolType(15-15)
pkg/catalog/index/index_test.go (2)
pkg/catalog/index/index.go (3)
NewIndex(46-97)IndexFileName(18-18)IndexVersion(22-22)pkg/catalog/index/metadata.go (2)
Metadata(14-55)NewMetadataFromTemplate(58-73)
pkg/catalog/loader/loader.go (3)
pkg/catalog/index/index.go (2)
Index(32-37)NewDefaultIndex(101-103)pkg/catalog/index/filter.go (1)
Filter(20-56)pkg/catalog/index/metadata.go (2)
Metadata(14-55)NewMetadataFromTemplate(58-73)
pkg/catalog/index/index.go (5)
pkg/catalog/index/metadata.go (2)
Metadata(14-55)NewMetadataFromTemplate(58-73)pkg/catalog/config/constants.go (2)
Version(34-34)BinaryName(40-40)pkg/model/types/severity/severity.go (1)
Severity(11-11)pkg/templates/types/types.go (1)
ProtocolType(15-15)pkg/catalog/index/filter.go (2)
Filter(20-56)FilterFunc(212-212)
🔇 Additional comments (2)
pkg/catalog/loader/loader.go (1)
94-185: Index initialization and one-time persistence look solidThe addition of
metadataIndex,indexFilter, andsaveMetadataIndexOnceis wired cleanly: you initialize the index (with graceful logging on failure), build a reusableindex.Filterfrom config, and gateSave()behind async.OnceFunc, so the gob snapshot is written at most once per process. This matches the “automatic cache lifecycle with graceful fallback” goal without adding complexity to callers.pkg/catalog/index/index.go (1)
16-37: Index implementation matches the intended persistent LRU metadata cache
NewIndexandNewDefaultIndexset up an otter-backed cache with a reasonableMaximumWeightand a weigher that tracks key and metadata field sizes, then persist it via gob toindex.gobwith a versionedcacheSnapshot.Get'sIsValidcheck and asyncDelete, combined withSave's atomic temp-file rename andLoad's "delete on corruption or version mismatch" behavior, give you a robust, low-friction cache that degrades safely when something goes wrong.Filter/FilterFunc/All/GetAll/Count are straightforward wrappers over
cache.All()under RW locks and line up with how the loader uses them. The external RWMutex correctly synchronizes multi-call sequences (e.g., Load's decode + Set loop, Save's snapshot construction, Filter's iteration) for consistency guarantees that single otter operations alone cannot provide. Just keep in mind to update both the weigher here and the note inMetadataif you add more fields so eviction behavior remains roughly proportional to actual memory use.
|
@coderabbitai wdym by "if you ever hit edge cases on low-resolution filesystems, you might consider allowing a small skew"? |
|
Tip For best results, initiate chat on the files or code changes. @dwisiswant0, great question! Let me clarify that comment about low-resolution filesystems. Low-resolution filesystems are filesystems that store file modification times with limited precision. For example:
The potential edge case happens in
The cache would incorrectly consider the entry valid even though the file changed. "Small skew" means adding a tolerance window to the comparison, something like: // Instead of exact equality
if !m.ModTime.Equal(fileInfo.ModTime()) {
return false
}
// Allow ±1 second tolerance
timeDiff := m.ModTime.Sub(fileInfo.ModTime()).Abs()
if timeDiff > time.Second {
return false
}That said, your current implementation is perfectly fine for Nuclei's use case:
This was just a theoretical edge case to be aware of—I wouldn't recommend changing anything unless you actually encounter issues in the field! 🙂 |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
pkg/catalog/loader/loader.go (2)
318-340: Consider surfacing or logging include/exclude resolution errors when building the index filter
buildIndexFiltercurrently discards errors fromCatalog.GetTemplatesPathforIncludeTemplates/ExcludeTemplates, which means bad IDs/paths in those flags silently degrade to “no-op” behavior for the force-include/exclude semantics. Similarly,loadTemplatesIndexonly logs creation/load failures and returnsnil, which is fine for graceful fallback but makesmetadataIndex == nila special behavior case elsewhere.If you want better debuggability without changing behavior, consider:
- Logging the
errsfromGetTemplatesPathhere (reusinglogErroredTemplates) so misconfigured include/exclude templates are visible even before any templates are loaded.- Optionally documenting that
NewDefaultIndexfailures are the only reasonmetadataIndexisnil, so future uses don’t inadvertently assume “index always present”.This is non-blocking but would make filter/debug behavior more transparent.
Also applies to: 342-356
475-495: Validation now ignores index-based filters; consider aligning with load-time behavior
ValidateTemplatesnow builds simplemap[string]struct{}sets for alltemplatePathsandworkflowPaths, then validates everything viaareTemplatesValid/areWorkflowsValid. This is simpler than the previous path‑filtering, but it means validation no longer respects the index‑based filters (authors, tags, severities, protocols, include/exclude templates).If users run validation with the same CLI filters they use for execution (e.g.
-tagsor-severity), they might expect the validated set to match the loaded set.Not strictly required for correctness, but for consistency and performance you might want to:
- Apply
indexFilter.Matches(and any path include/exclude logic) when buildingtemplatePathsMap/workflowPathsMap, using cached or on‑the‑fly metadata similarly to the loading paths; or- Document that
ValidateTemplatesvalidates all resolved templates/workflows regardless of filter flags.Given the cost of full validation, aligning it with the index filter would be a nice future improvement.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
pkg/catalog/loader/loader.go(9 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: Format Go code usinggo fmt ./...
Run static analysis usinggo vet ./...
Files:
pkg/catalog/loader/loader.go
🧬 Code graph analysis (1)
pkg/catalog/loader/loader.go (3)
pkg/catalog/index/index.go (2)
Index(32-37)NewDefaultIndex(101-103)pkg/catalog/index/filter.go (1)
Filter(20-56)pkg/catalog/index/metadata.go (2)
Metadata(14-55)NewMetadataFromTemplate(58-73)
⏰ 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: Tests (ubuntu-latest)
- GitHub Check: Tests (windows-latest)
- GitHub Check: Tests (macOS-latest)
🔇 Additional comments (2)
pkg/catalog/loader/loader.go (2)
17-17: Metadata index wiring and save lifecycle look soundThe introduction of
metadataIndex,indexFilter, andsaveMetadataIndexOnceplus their initialization inNewis cohesive: index/load happen once per store, andsync.OnceFuncprevents redundant saves even when multiple loading paths calldefer saveMetadataIndexOnce(). Early-return in the save closure whenmetadataIndexisnilalso ensures graceful fallback when the cache cannot be created. No functional issues spotted here.Also applies to: 94-103, 141-143, 152-158, 172-185
611-629: Workflow loading preallocation is finePreallocating
loadedWorkflowswithlen(includedWorkflows)is a small but sensible optimization; the control flow and error handling aroundLoadWorkflow/Parseremain unchanged. No issues here.
Signed-off-by: Dwi Siswanto <git@dw1.io>
8e0a55d to
0002035
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
pkg/catalog/loader/loader.go (1)
360-367: Nil-pointer risk inLoadTemplatesOnlyMetadatawhen index is warm but parser cache is coldWhen the metadata index is warm and
tagFilterdoes not force a load,validPathscan contain entries for which no template has been parsed in this run:if store.metadataIndex != nil { if metadata, found := store.metadataIndex.Get(templatePath); found { if !indexFilter.Matches(metadata) { continue } if store.tagFilter != nil { loaded, err := Parser.LoadTemplate(...) // ... } validPaths[templatePath] = struct{}{} continue } }Those paths are later processed via:
templatesCache := store.parserCacheOnce() for templatePath := range validPaths { template, _, _ := templatesCache.Has(templatePath) if len(template.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless { // ... } // ... if template != nil { // ... } }If the parser cache has no entry for
templatePath(a normal case when you rely solely on the persisted metadata cache),templateisniland the firstlen(template.RequestsHeadless)dereference will panic. This is exactly the “warm metadata / cold parser cache” scenario.To keep the short‑circuiting benefit while avoiding the panic, ensure you load the template on demand when
templateisnilbefore dereferencing any fields. For example:- for templatePath := range validPaths { - template, _, _ := templatesCache.Has(templatePath) + for templatePath := range validPaths { + template, _, _ := templatesCache.Has(templatePath) + + if template == nil { + loaded, err := store.config.ExecutorOptions.Parser.LoadTemplate(templatePath, store.tagFilter, nil, store.config.Catalog) + if !loaded { + if err != nil { + if strings.Contains(err.Error(), templates.ErrExcluded.Error()) { + stats.Increment(templates.TemplatesExcludedStats) + if config.DefaultConfig.LogAllEvents { + store.logger.Print().Msgf("[%v] %v\n", aurora.Yellow("WRN").String(), err.Error()) + } + } else { + store.logger.Warning().Msg(err.Error()) + } + } + continue + } + + if template, _, _ = templatesCache.Has(templatePath); template == nil { + continue + } + } - if len(template.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless { + if len(template.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless { continue } // ... rest of gating and duplicate-ID logic unchanged ...This keeps metadata‑based skipping when possible, but guarantees that any template you inspect for headless/code/DAST/self‑contained/file checks is non‑nil, preventing a panic and preserving gating semantics.
Also applies to: 368-413, 435-468
🧹 Nitpick comments (3)
pkg/catalog/loader/loader.go (3)
94-103: Metadata index lifecycle wiring looks sound; be aware of single-save semanticsThe
metadataIndex,indexFilter, andsaveMetadataIndexOncewiring is clean and guarded againstnilindexes, so failures in creating/loading the index degrade gracefully.One behavioral wrinkle:
saveMetadataIndexOncewill only ever persist once perStoreinstance. If, in any workflow, you end up calling bothLoadTemplatesOnlyMetadataandLoadTemplatesWithTags(or other future callers that mutate metadata) on the sameStore, only the first completion will flush index changes. If that’s not intentional, consider either:
- moving persistence behind an explicit
Close()/Flush()onStore, or- dropping
OnceFuncand saving at each high‑level load, letting the index implementation manage incremental writes or cheap no‑ops.Also applies to: 172-185
318-340: Index filter construction is correct; consider surfacing include/exclude resolution errorsMapping config fields into
index.Filterlooks consistent (authors, tags, (exclude|include) IDs/templates, severities, protocols). One thing you might want to improve for UX:GetTemplatesPatherrors forIncludeTemplates/ExcludeTemplatesare currently discarded.If users typo a path in
-include-templates/-exclude-templates, they’ll get silent misconfiguration. You could reuselogErroredTemplateshere, for example:func (store *Store) buildIndexFilter() *index.Filter { - includeTemplates, _ := store.config.Catalog.GetTemplatesPath(store.config.IncludeTemplates) - excludeTemplates, _ := store.config.Catalog.GetTemplatesPath(store.config.ExcludeTemplates) + includeTemplates, includeErrs := store.config.Catalog.GetTemplatesPath(store.config.IncludeTemplates) + excludeTemplates, excludeErrs := store.config.Catalog.GetTemplatesPath(store.config.ExcludeTemplates) + store.logErroredTemplates(includeErrs) + store.logErroredTemplates(excludeErrs)
342-356: Graceful fallback on index creation/load; consider dropping a bad index instead of reusing itCreating the default index and logging both creation and load failures gives you a clean fallback (no caching, normal behavior) when the cache directory or gob file is unusable.
If
Load()returns an error due to a corrupt on-disk cache, you currently keep usingmetadataIdx. Depending onIndex.Loadsemantics, that may leave you with partially initialized state. IfLoad()doesn’t already reset to an empty cache on failure, consider:idx, err := index.NewDefaultIndex() if err != nil { store.logger.Warning().Msgf("Could not create metadata cache: %v", err) } else { metadataIdx = idx if err := metadataIdx.Load(); err != nil { - store.logger.Warning().Msgf("Could not load metadata cache: %v", err) + store.logger.Warning().Msgf("Could not load metadata cache: %v", err) + metadataIdx = nil } }so a corrupt file behaves like “no index” instead of “possibly inconsistent index”.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
pkg/catalog/loader/loader.go(9 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: Format Go code usinggo fmt ./...
Run static analysis usinggo vet ./...
Files:
pkg/catalog/loader/loader.go
🧬 Code graph analysis (1)
pkg/catalog/loader/loader.go (3)
pkg/catalog/index/index.go (1)
Index(32-37)pkg/catalog/index/filter.go (1)
Filter(20-56)pkg/catalog/index/metadata.go (2)
Metadata(14-55)NewMetadataFromTemplate(58-73)
🔇 Additional comments (4)
pkg/catalog/loader/loader.go (4)
141-144: Separation betweentagFilter(advanced) andindexFilter(basic) is clear and maintainableThe comments and initialization make the division of responsibility explicit:
tagFilterforIncludeConditions,indexFilterfor all basic include/exclude criteria. This makes the filtering pipeline easier to reason about and aligns with the new index package’s intent.Also applies to: 151-158
481-490: Path map construction for validation is straightforward and efficientBuilding
templatePathsMap/workflowPathsMaponce and passing them intoareTemplatesValid/areWorkflowsValidis a clear way to remove the old pathFilter dependency while preserving uniqueness semantics. The map preallocation withlen(...)is also a nice touch for avoiding extra allocations.Also applies to: 491-494
614-615: Minor optimization inLoadWorkflowsslice initializationPreallocating
loadedWorkflowswithlen(includedWorkflows)capacity is a sensible micro‑optimization on this hot path and doesn’t change behavior.
635-638: Index-backed fast path and fallback behavior inLoadTemplatesWithTagslook correctThe new flow correctly:
- Uses cached metadata (
metadataIndex.Get) to short‑circuit templates that obviously don’t matchindexFilter.- Still runs
LoadTemplatewhen advancedIncludeConditionsor per-calltagsmay affect inclusion.- Recomputes metadata from
parsedtemplates and re-appliesindexFiltereven whenmetadataIndexisnil, so basic CLI filters (tags, authors, severities, IDs, protocol types, include/exclude templates) continue to work without a usable cache.- Only updates the index via
SetFromTemplatewhen available, otherwise falls back toNewMetadataFromTemplate.This preserves pre-index semantics while giving you the intended metadata-based pruning, and the concurrency pattern (shared read-only
indexFilter, internally synchronizedmetadataIndex) is safe.Also applies to: 684-707, 708-723
|
@Mzack9999 - heads up: I just added the memory comparison table from the gctrace results in the PR body. :) Gonna merge this now. |
* Multi Port Support Added - JS * minor -changes * restoring basic sequential multiport support * better error handling * feat(openapi/swagger): direct fuzzing using target url * fix (openapi/swagger): improve error handling and tmpDir cleanup * fix(openapi/swagger): err shadowing on write failure * fix(openapi/swagger): remove discarded error in defer * fix(openapi/swagger): linter and url validation * fix(openapi/swagger): remove code duplication * reusing dialer * removing debug log * fix: restore parallel processing in workflow & file proto add missing `go` keyword to anonymous funcs that were intended to run as goroutines but were executing synchronously instead. Fixes #6492 Signed-off-by: Dwi Siswanto <git@dw1.io> * test: adds `Test(FileProtocol|Workflows)ConcurrentExecution` tests Signed-off-by: Dwi Siswanto <git@dw1.io> * chore(file): satisfy lints Signed-off-by: Dwi Siswanto <git@dw1.io> * refactor(integration-test): enhance debug mode detects * replace hardcoded `DEBUG` env var check with extensible helper func. * add support for GitHub Actions Runner env var. * accept multiple truthy value variants. Signed-off-by: Dwi Siswanto <git@dw1.io> * fix(core): race cond in workflow execution caused by shared context callbacks. it was exposed after adding concurrent exec to workflow processing and occurred when multiple goroutines attempted to write to the same `ctx.OnResult` callback field simultaneously, causing data races during workflow template exec. Signed-off-by: Dwi Siswanto <git@dw1.io> * introducing workflow sequential mode * Revert "introducing workflow sequential mode" This reverts commit 1093bbc. * refactor(core): keep workflow exec seq Signed-off-by: Dwi Siswanto <git@dw1.io> * test(core): rm unused tests Signed-off-by: Dwi Siswanto <git@dw1.io> * fix(sdk): configure tmpDir for SDK Closes #6595. * docs(sdk): update comment to more accurately reflect purpose * feat(sdk): add tmpDir configuration option for SDK users * fix(sdk): init default engine tmpDir when unconfigured * style(sdk): remove unnecessary else block * feat(sdk): create parent & tmp dir in WithTemporaryDirectory * test(cmd): enable `BenchmarkRunEnumeration/Default` bench Signed-off-by: Dwi Siswanto <git@dw1.io> * test(cmd): collect CPU & heap profiles Signed-off-by: Dwi Siswanto <git@dw1.io> * chore(cmd): satisfy lints Signed-off-by: Dwi Siswanto <git@dw1.io> * Merge pull request #6610 from projectdiscovery/feat-result-upload allow custom id for upload * feat: write resume file specified by flag * updating docs * chore(deps): bump the modules group with 6 updates Bumps the modules group with 6 updates: | Package | From | To | | --- | --- | --- | | [github.com/projectdiscovery/gologger](https://github.com/projectdiscovery/gologger) | `1.1.59` | `1.1.60` | | [github.com/projectdiscovery/httpx](https://github.com/projectdiscovery/httpx) | `1.7.2-0.20250911192144-fc425deb041a` | `1.7.2` | | [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) | `0.1.27` | `0.1.28` | | [github.com/projectdiscovery/utils](https://github.com/projectdiscovery/utils) | `0.6.1-0.20251030144701-ce5c4b44e1e6` | `0.6.1` | | [github.com/projectdiscovery/wappalyzergo](https://github.com/projectdiscovery/wappalyzergo) | `0.2.54` | `0.2.55` | | [github.com/projectdiscovery/cdncheck](https://github.com/projectdiscovery/cdncheck) | `1.2.9` | `1.2.10` | Updates `github.com/projectdiscovery/gologger` from 1.1.59 to 1.1.60 - [Release notes](https://github.com/projectdiscovery/gologger/releases) - [Commits](projectdiscovery/gologger@v1.1.59...v1.1.60) Updates `github.com/projectdiscovery/httpx` from 1.7.2-0.20250911192144-fc425deb041a to 1.7.2 - [Release notes](https://github.com/projectdiscovery/httpx/releases) - [Changelog](https://github.com/projectdiscovery/httpx/blob/dev/.goreleaser.yml) - [Commits](https://github.com/projectdiscovery/httpx/commits/v1.7.2) Updates `github.com/projectdiscovery/networkpolicy` from 0.1.27 to 0.1.28 - [Release notes](https://github.com/projectdiscovery/networkpolicy/releases) - [Commits](projectdiscovery/networkpolicy@v0.1.27...v0.1.28) Updates `github.com/projectdiscovery/utils` from 0.6.1-0.20251030144701-ce5c4b44e1e6 to 0.6.1 - [Release notes](https://github.com/projectdiscovery/utils/releases) - [Changelog](https://github.com/projectdiscovery/utils/blob/main/CHANGELOG.md) - [Commits](https://github.com/projectdiscovery/utils/commits/v0.6.1) Updates `github.com/projectdiscovery/wappalyzergo` from 0.2.54 to 0.2.55 - [Release notes](https://github.com/projectdiscovery/wappalyzergo/releases) - [Commits](projectdiscovery/wappalyzergo@v0.2.54...v0.2.55) Updates `github.com/projectdiscovery/cdncheck` from 1.2.9 to 1.2.10 - [Release notes](https://github.com/projectdiscovery/cdncheck/releases) - [Changelog](https://github.com/projectdiscovery/cdncheck/blob/main/.goreleaser.yaml) - [Commits](projectdiscovery/cdncheck@v1.2.9...v1.2.10) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/gologger dependency-version: 1.1.60 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/httpx dependency-version: 1.7.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/networkpolicy dependency-version: 0.1.28 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/utils dependency-version: 0.6.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/wappalyzergo dependency-version: 0.2.55 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/cdncheck dependency-version: 1.2.10 dependency-type: indirect update-type: version-update:semver-patch dependency-group: modules ... Signed-off-by: dependabot[bot] <support@github.com> * refactor(sdk): don't create parentDir when configuring tmpDir * adding test case * lint * removing unused check * adding multiport template * refactor test * chore(deps): bump golang.org/x/crypto Bumps the go_modules group with 1 update in the / directory: [golang.org/x/crypto](https://github.com/golang/crypto). Updates `golang.org/x/crypto` from 0.43.0 to 0.45.0 - [Commits](golang/crypto@v0.43.0...v0.45.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.45.0 dependency-type: indirect dependency-group: go_modules ... Signed-off-by: dependabot[bot] <support@github.com> * feat(variables): check for undefined params for lazy eval (#6618) * feat(variables): check for undefined params for lazy eval Signed-off-by: Dwi Siswanto <git@dw1.io> * test(variables): add TestCheckForLazyEval Signed-off-by: Dwi Siswanto <git@dw1.io> * fix(variables): fail safe on err compile expr Signed-off-by: Dwi Siswanto <git@dw1.io> --------- Signed-off-by: Dwi Siswanto <git@dw1.io> * chore(deps): bump github.com/projectdiscovery/fastdialer@v0.4.16 Signed-off-by: Dwi Siswanto <git@dw1.io> * fix(interactsh): skip DNS lookups on interactsh domains (#6614) * fix(interactsh): skip DNS lookups on interactsh domains to prevent false positives. Prevents nuclei from resolving interactsh domains injected in Host headers, which would cause self-interactions to be incorrectly reported as matches. Changes: * Add `GetHostname()` method to `interactsh.Client` to expose active server domain. * Skip CNAME DNS lookups in `(*http.Request).addCNameIfAvailable` when hostname matches the `(*interactsh.Client).GetHostname`. Fixes #6613 Signed-off-by: Dwi Siswanto <git@dw1.io> * fix(http): prevent false `interactshDomain` matches Signed-off-by: Dwi Siswanto <git@dw1.io> --------- Signed-off-by: Dwi Siswanto <git@dw1.io> * feat: bump dsl with deserialization helpers * chore: omit unnecessary reassignment (#6622) Signed-off-by: ledigang <shuangcui@msn.com> * disable stale workflow for enhancements * ci: cache go-rod browser (#6640) Signed-off-by: Dwi Siswanto <git@dw1.io> * chore(deps): bump actions/checkout from 5 to 6 in the workflows group Bumps the workflows group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 5 to 6 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major dependency-group: workflows ... Signed-off-by: dependabot[bot] <support@github.com> * do not exempt abandoned issues and prs * ci: apply free-disk-space on tests Signed-off-by: Dwi Siswanto <git@dw1.io> * chore: bump PD modules & update `httputil` calls (#6629) * chore(deps): bump the modules group across 1 directory with 11 updates Bumps the modules group with 11 updates in the / directory: | Package | From | To | | --- | --- | --- | | [github.com/projectdiscovery/fastdialer](https://github.com/projectdiscovery/fastdialer) | `0.4.16` | `0.4.17` | | [github.com/projectdiscovery/hmap](https://github.com/projectdiscovery/hmap) | `0.0.95` | `0.0.96` | | [github.com/projectdiscovery/retryabledns](https://github.com/projectdiscovery/retryabledns) | `1.0.108` | `1.0.109` | | [github.com/projectdiscovery/retryablehttp-go](https://github.com/projectdiscovery/retryablehttp-go) | `1.0.131` | `1.0.132` | | [github.com/projectdiscovery/gologger](https://github.com/projectdiscovery/gologger) | `1.1.60` | `1.1.61` | | [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) | `0.1.28` | `0.1.29` | | [github.com/projectdiscovery/tlsx](https://github.com/projectdiscovery/tlsx) | `1.2.1` | `1.2.2` | | [github.com/projectdiscovery/useragent](https://github.com/projectdiscovery/useragent) | `0.0.102` | `0.0.103` | | [github.com/projectdiscovery/utils](https://github.com/projectdiscovery/utils) | `0.6.1` | `0.7.1` | | [github.com/projectdiscovery/wappalyzergo](https://github.com/projectdiscovery/wappalyzergo) | `0.2.55` | `0.2.56` | | [github.com/projectdiscovery/cdncheck](https://github.com/projectdiscovery/cdncheck) | `1.2.10` | `1.2.11` | Updates `github.com/projectdiscovery/fastdialer` from 0.4.16 to 0.4.17 - [Release notes](https://github.com/projectdiscovery/fastdialer/releases) - [Commits](projectdiscovery/fastdialer@v0.4.16...v0.4.17) Updates `github.com/projectdiscovery/hmap` from 0.0.95 to 0.0.96 - [Release notes](https://github.com/projectdiscovery/hmap/releases) - [Commits](projectdiscovery/hmap@v0.0.95...v0.0.96) Updates `github.com/projectdiscovery/retryabledns` from 1.0.108 to 1.0.109 - [Release notes](https://github.com/projectdiscovery/retryabledns/releases) - [Commits](projectdiscovery/retryabledns@v1.0.108...v1.0.109) Updates `github.com/projectdiscovery/retryablehttp-go` from 1.0.131 to 1.0.132 - [Release notes](https://github.com/projectdiscovery/retryablehttp-go/releases) - [Commits](projectdiscovery/retryablehttp-go@v1.0.131...v1.0.132) Updates `github.com/projectdiscovery/gologger` from 1.1.60 to 1.1.61 - [Release notes](https://github.com/projectdiscovery/gologger/releases) - [Commits](projectdiscovery/gologger@v1.1.60...v1.1.61) Updates `github.com/projectdiscovery/networkpolicy` from 0.1.28 to 0.1.29 - [Release notes](https://github.com/projectdiscovery/networkpolicy/releases) - [Commits](projectdiscovery/networkpolicy@v0.1.28...v0.1.29) Updates `github.com/projectdiscovery/tlsx` from 1.2.1 to 1.2.2 - [Release notes](https://github.com/projectdiscovery/tlsx/releases) - [Changelog](https://github.com/projectdiscovery/tlsx/blob/main/.goreleaser.yml) - [Commits](projectdiscovery/tlsx@v1.2.1...v1.2.2) Updates `github.com/projectdiscovery/useragent` from 0.0.102 to 0.0.103 - [Release notes](https://github.com/projectdiscovery/useragent/releases) - [Commits](projectdiscovery/useragent@v0.0.102...v0.0.103) Updates `github.com/projectdiscovery/utils` from 0.6.1 to 0.7.1 - [Release notes](https://github.com/projectdiscovery/utils/releases) - [Changelog](https://github.com/projectdiscovery/utils/blob/main/CHANGELOG.md) - [Commits](projectdiscovery/utils@v0.6.1...v0.7.1) Updates `github.com/projectdiscovery/wappalyzergo` from 0.2.55 to 0.2.56 - [Release notes](https://github.com/projectdiscovery/wappalyzergo/releases) - [Commits](projectdiscovery/wappalyzergo@v0.2.55...v0.2.56) Updates `github.com/projectdiscovery/cdncheck` from 1.2.10 to 1.2.11 - [Release notes](https://github.com/projectdiscovery/cdncheck/releases) - [Changelog](https://github.com/projectdiscovery/cdncheck/blob/main/.goreleaser.yaml) - [Commits](projectdiscovery/cdncheck@v1.2.10...v1.2.11) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/fastdialer dependency-version: 0.4.17 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/hmap dependency-version: 0.0.96 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/retryabledns dependency-version: 1.0.109 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/retryablehttp-go dependency-version: 1.0.132 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/gologger dependency-version: 1.1.61 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/networkpolicy dependency-version: 0.1.29 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/tlsx dependency-version: 1.2.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/useragent dependency-version: 0.0.103 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/utils dependency-version: 0.7.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: modules - dependency-name: github.com/projectdiscovery/wappalyzergo dependency-version: 0.2.56 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/cdncheck dependency-version: 1.2.11 dependency-type: indirect update-type: version-update:semver-patch dependency-group: modules ... Signed-off-by: dependabot[bot] <support@github.com> * chore: update utils.httputil calls Signed-off-by: Dwi Siswanto <git@dw1.io> * chore(deps): bump github.com/projectdiscovery/utils => v0.7.3 Signed-off-by: Dwi Siswanto <git@dw1.io> --------- Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Dwi Siswanto <git@dw1.io> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dwi Siswanto <git@dw1.io> * chore(deps): bump the modules group with 11 updates Bumps the modules group with 11 updates: | Package | From | To | | --- | --- | --- | | [github.com/projectdiscovery/fastdialer](https://github.com/projectdiscovery/fastdialer) | `0.4.17` | `0.4.18` | | [github.com/projectdiscovery/hmap](https://github.com/projectdiscovery/hmap) | `0.0.96` | `0.0.97` | | [github.com/projectdiscovery/retryabledns](https://github.com/projectdiscovery/retryabledns) | `1.0.109` | `1.0.110` | | [github.com/projectdiscovery/retryablehttp-go](https://github.com/projectdiscovery/retryablehttp-go) | `1.0.132` | `1.0.133` | | [github.com/projectdiscovery/dsl](https://github.com/projectdiscovery/dsl) | `0.8.5` | `0.8.6` | | [github.com/projectdiscovery/gologger](https://github.com/projectdiscovery/gologger) | `1.1.61` | `1.1.62` | | [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) | `0.1.29` | `0.1.30` | | [github.com/projectdiscovery/uncover](https://github.com/projectdiscovery/uncover) | `1.1.0` | `1.2.0` | | [github.com/projectdiscovery/useragent](https://github.com/projectdiscovery/useragent) | `0.0.103` | `0.0.104` | | [github.com/projectdiscovery/wappalyzergo](https://github.com/projectdiscovery/wappalyzergo) | `0.2.56` | `0.2.57` | | [github.com/projectdiscovery/cdncheck](https://github.com/projectdiscovery/cdncheck) | `1.2.11` | `1.2.12` | Updates `github.com/projectdiscovery/fastdialer` from 0.4.17 to 0.4.18 - [Release notes](https://github.com/projectdiscovery/fastdialer/releases) - [Commits](projectdiscovery/fastdialer@v0.4.17...v0.4.18) Updates `github.com/projectdiscovery/hmap` from 0.0.96 to 0.0.97 - [Release notes](https://github.com/projectdiscovery/hmap/releases) - [Commits](projectdiscovery/hmap@v0.0.96...v0.0.97) Updates `github.com/projectdiscovery/retryabledns` from 1.0.109 to 1.0.110 - [Release notes](https://github.com/projectdiscovery/retryabledns/releases) - [Commits](projectdiscovery/retryabledns@v1.0.109...v1.0.110) Updates `github.com/projectdiscovery/retryablehttp-go` from 1.0.132 to 1.0.133 - [Release notes](https://github.com/projectdiscovery/retryablehttp-go/releases) - [Commits](projectdiscovery/retryablehttp-go@v1.0.132...v1.0.133) Updates `github.com/projectdiscovery/dsl` from 0.8.5 to 0.8.6 - [Release notes](https://github.com/projectdiscovery/dsl/releases) - [Commits](projectdiscovery/dsl@v0.8.5...v0.8.6) Updates `github.com/projectdiscovery/gologger` from 1.1.61 to 1.1.62 - [Release notes](https://github.com/projectdiscovery/gologger/releases) - [Commits](projectdiscovery/gologger@v1.1.61...v1.1.62) Updates `github.com/projectdiscovery/networkpolicy` from 0.1.29 to 0.1.30 - [Release notes](https://github.com/projectdiscovery/networkpolicy/releases) - [Commits](projectdiscovery/networkpolicy@v0.1.29...v0.1.30) Updates `github.com/projectdiscovery/uncover` from 1.1.0 to 1.2.0 - [Release notes](https://github.com/projectdiscovery/uncover/releases) - [Commits](projectdiscovery/uncover@v1.1.0...v1.2.0) Updates `github.com/projectdiscovery/useragent` from 0.0.103 to 0.0.104 - [Release notes](https://github.com/projectdiscovery/useragent/releases) - [Commits](projectdiscovery/useragent@v0.0.103...v0.0.104) Updates `github.com/projectdiscovery/wappalyzergo` from 0.2.56 to 0.2.57 - [Release notes](https://github.com/projectdiscovery/wappalyzergo/releases) - [Commits](projectdiscovery/wappalyzergo@v0.2.56...v0.2.57) Updates `github.com/projectdiscovery/cdncheck` from 1.2.11 to 1.2.12 - [Release notes](https://github.com/projectdiscovery/cdncheck/releases) - [Commits](projectdiscovery/cdncheck@v1.2.11...v1.2.12) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/fastdialer dependency-version: 0.4.18 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/hmap dependency-version: 0.0.97 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/retryabledns dependency-version: 1.0.110 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/retryablehttp-go dependency-version: 1.0.133 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/dsl dependency-version: 0.8.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/gologger dependency-version: 1.1.62 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/networkpolicy dependency-version: 0.1.30 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/uncover dependency-version: 1.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: modules - dependency-name: github.com/projectdiscovery/useragent dependency-version: 0.0.104 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/wappalyzergo dependency-version: 0.2.57 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/cdncheck dependency-version: 1.2.12 dependency-type: indirect update-type: version-update:semver-patch dependency-group: modules ... Signed-off-by: dependabot[bot] <support@github.com> * feat(loader): implement persistent metadata cache (#6630) * feat(loader): implement persistent metadata cache for template filtering optimization. Introduce a new template metadata indexing system with persistent caching to dramatically improve template loading perf when filters are applied. The implementation adds a new index pkg that caches lightweight template metadata (ID, tags, authors, severity, .etc) and enables filtering templates before expensive YAML parsing occurs. The index uses an in-memory LRU cache backed by `otter` pkg for efficient memory management with adaptive sizing based on entry weight, defaulting to approx. 40MB for 50K templates. Metadata is persisted to disk using gob encoding at "~/.cache/nuclei/index.gob" with atomic writes to prevent corruption. The cache automatically invalidates stale entries using `ModTime` to detect file modifications, ensuring metadata freshness w/o manual intervention. Filtering has been refactored from the previous `TagFilter` and `PathFilter` approach into a unified `index.Filter` type that handles all basic filtering ops including severity, authors, tags, template IDs with wildcard support, protocol types, and path-based inclusion and exclusion. The filter implements OR logic within each field type and AND logic across different field types, with exclusion filters taking precedence over inclusion filters and forced inclusion via `IncludeTemplates` and `IncludeTags` overriding exclusions. The `loader` integration creates an index filter from store configuration via `buildIndexFilter` and manages the cache lifecycle through `loadTemplatesIndex` and `saveTemplatesIndex` methods. When `LoadTemplatesOnlyMetadata` or `LoadTemplatesWithTags` is called, the system first checks the metadata cache for each template path. If cached metadata exists and passes validation, the filter is applied directly against the metadata without parsing. Only templates matching the filter criteria proceed to full YAML parsing, resulting in significant performance gains. Advanced filtering via "-tc" flag (`IncludeConditions`) still requires template parsing as these are expression-based filters that cannot be evaluated from metadata alone. The `TagFilter` has been simplified to handle only `IncludeConditions` while all other filtering ops are delegated to the index-based filtering system. Cache management is fully automatic with no user configuration required. The cache gracefully handles errors by logging warnings & falling back to normal op w/o caching. Cache files use schema versioning to invalidate incompatible cache formats across nuclei updates (well, specifically `Index` and `Metadata` changes). This optimization particularly benefits repeated scans with the same filters, CI/CD pipelines running nuclei regularly, development and testing workflows with frequent template loading, and any scenario with large template collections where filtering would exclude most templates. * test(loader): adds `BenchmarkLoadTemplates{,OnlyMetadata}` benchs Signed-off-by: Dwi Siswanto <git@dw1.io> * ci: cache nuclei-templates index Signed-off-by: Dwi Siswanto <git@dw1.io> * chore(index): satisfy lints Signed-off-by: Dwi Siswanto <git@dw1.io> * fix(index): correct metadata filter logic for proper template matching. The `filter.matchesIncludes()` was using OR logic across different filter types, causing incorrect template matching. Additionally, ID matching was case-sensitive, failing to match patterns like 'CVE-2021-*'. The filter now correctly implements: (author1 OR author2) AND (tag1 OR tag2) AND (severity1 OR severity2) - using OR within each filter type and AND across different types. Signed-off-by: Dwi Siswanto <git@dw1.io> * test(index): resolve test timing issue in CI environments. Some test was failing in CI due to filesystem timestamp resolution limitations. On filesystems with 1s ModTime granularity (common in CI), modifying a file immediately after capturing its timestamp resulted in identical ModTime values, causing IsValid() to incorrectly return true. Signed-off-by: Dwi Siswanto <git@dw1.io> * ci: cache nuclei with composite action Signed-off-by: Dwi Siswanto <git@dw1.io> * fix(index): file locking issue on Windows during cache save/load. Explicitly close file handles before performing rename/remove ops in `Save` and `Load` methods. * In `Save`, close temp file before rename. * In `Load`, close file before remove during error handling/version mismatch. Signed-off-by: Dwi Siswanto <git@dw1.io> * test(index): flaky index tests on Windows Fix path separator mismatch in `TestCacheSize` and `TestCachePersistenceWithLargeDataset` by using `filepath.Join` consistently instead of hardcoded forward slashes. Signed-off-by: Dwi Siswanto <git@dw1.io> * test(cmd): init logger to prevent nil pointer deref The integration tests were panicking with a nil pointer dereference in `pkg/catalog/loader` because the logger was not init'ed. When `store.saveMetadataIndexOnce` attempted to log the result of the metadata cache op, it dereferenced the nil logger, causing a crash. Signed-off-by: Dwi Siswanto <git@dw1.io> * fix(loader): resolve include/exclude paths for metadata cache filter. The `indexFilter` was previously init'ed using raw relative paths from the config for `IncludeTemplates` and `ExcludeTemplates`. But the persistent metadata cache stores templates using their absolute paths. This mismatch caused the `matchesPath` check to fail, leading to templates being incorrectly excluded even when explicitly included via flags (e.g., "-include-templates loader/excluded-template.yaml"). This commit updates `buildIndexFilter` to resolve these paths to their absolute versions using `store.config.Catalog.GetTemplatesPath` before creating the filter, ensuring consistent path matching against the metadata cache. Signed-off-by: Dwi Siswanto <git@dw1.io> * feat(index): adds `NewMetadataFromTemplate` func Signed-off-by: Dwi Siswanto <git@dw1.io> * refactor(index): return metadata when `(*Index).cache` is nil Signed-off-by: Dwi Siswanto <git@dw1.io> * refactor(loader): restore pre‑index behavior semantics Signed-off-by: Dwi Siswanto <git@dw1.io> --------- Signed-off-by: Dwi Siswanto <git@dw1.io> * chore: bump version Signed-off-by: Dwi Siswanto <git@dw1.io> --------- Signed-off-by: Dwi Siswanto <git@dw1.io> Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: ledigang <shuangcui@msn.com> Co-authored-by: pussycat0x <65701233+pussycat0x@users.noreply.github.com> Co-authored-by: Mzack9999 <mzack9999@protonmail.com> Co-authored-by: tvroi <roy.oswaldha@traveloka.com> Co-authored-by: Niek den Breeje <n.denbreeje@guardian360.nl> Co-authored-by: circleous <circleousdev@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ice3man <nizamulrana@gmail.com> Co-authored-by: Dogan Can Bakir <65292895+dogancanbakir@users.noreply.github.com> Co-authored-by: ledigang <shuangcui@msn.com> Co-authored-by: Doğan Can Bakır <dogancanbakir@protonmail.com>
Summary
This PR introduces a persistent metadata caching (#6626) system that significantly improves template loading performance by enabling early filtering w/o full YAML parsing.
Closes #6626
Motive
With ~11K+ templates (current) in the nuclei-templates repository, template loading performance becomes a bottleneck, especially when using filters. Currently, nuclei must parse every template's YAML to extract metadata (tags, authors, severity, etc.) before filtering, wasting time on templates that will be excluded.
p.s. this work is a continuation of #6619.
Changes
New
indexpkgotterpkg, gob serialization.Why mod time instead of checksum? Because mod time is
statsyscall, while checksumming will betouchw/o change) is negligible compared to the complexity and risks of trying to be "smarter". tl;dr: keep it strict, if the time changed, treat it as dirty.Modified
loaderpkgpathFilter(replaced byindex.Filter).LoadTemplatesWithTags()w/ two-stage filtering.LoadTemplatesOnlyMetadata()w/ cache-first approach.tagFilterto handle onlyIncludeConditions(advanced DSL filtering).Filter responsibility split:
Before:
tagFilterhandles all filtering (tags, authors, IDs, paths, conditions).After:
index.Filternow handles basic filtering (tags, authors, IDs, paths, severity, protocols).tagFilterhandles advanced filtering (IncludeConditionsDSL expressions).Proof
Benchmark results using
hyperfinewith 10 runs each, comparing before/after implementation:Note
./bin/nucleibuilt againstdevbranch, while./bin/nuclei-patchbuilt against this PR branch.Click to toggle contents of
compare.sh-s critical,high-author pdteam-tags cve,rce-id 'CVE-2021-*'-s high -author pdteam -tags cveMemory
-s critical,high-author pdteam-tags cve,rce-id 'CVE-2021-*'-s high -author pdteam -tags cveClick to toggle contents of
gctrace.shChecklist
Summary by CodeRabbit
New Features
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.