Skip to content

feat(templating): add vars templating into yaml inputs (ytt)#6261

Merged
Mzack9999 merged 14 commits intoprojectdiscovery:devfrom
alban-stourbe-wmx:feature/ytt-yaml-templating
Sep 11, 2025
Merged

feat(templating): add vars templating into yaml inputs (ytt)#6261
Mzack9999 merged 14 commits intoprojectdiscovery:devfrom
alban-stourbe-wmx:feature/ytt-yaml-templating

Conversation

@alban-stourbe-wmx
Copy link
Contributor

@alban-stourbe-wmx alban-stourbe-wmx commented Jun 12, 2025

Proposed changes

Checklist

  • Pull request is created against the dev branch
  • All checks passed (lint, unit/integration/regression tests etc.) with my changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Summary by CodeRabbit

  • New Features

    • Added variable injection and optional YTT text templating for YAML inputs via new CLI flags, plus loading variables from external YAML files.
  • Bug Fixes

    • Improved handling, warnings, and error messages when loading/parsing YAML configs and variables.
  • Tests

    • Added tests validating YAML parsing with variables and templating.
  • Chores

    • Updated dependencies and Go toolchain directives.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 12, 2025

Walkthrough

Adds Carvel ytt-based variable templating for YAML input: new CLI flags and Options fields, propagation through input provider, ytt integration utilities, YAML parser templating step, tests, and dependency updates.

Changes

Cohort / File(s) Change Summary
CLI & config
cmd/nuclei/main.go
Added flags --vars-text-templating (-vtt) and --var-file-paths (-vfp) and YAML config decoding for vars.
Options / types
pkg/types/types.go
Added public fields VarsTextTemplating and VarsFilePaths and updated Copy() to propagate them.
Input format options
pkg/input/formats/formats.go
Added VarsTextTemplating bool and VarsFilePaths []string to InputFormatOptions.
Provider wiring
pkg/input/provider/interface.go
Propagated the new templating fields into input provider initialization.
YAML parsing & templating
pkg/input/formats/yaml/multidoc.go
Optional pre-processing with ytt when templating enabled; adjusted decoding/error handling and debug logging.
ytt integration
pkg/input/formats/yaml/ytt.go
New helpers to run Carvel ytt programmatically (templates, data-values, var files, noop UI).
Tests
pkg/input/formats/yaml/multidoc_test.go
Added TestYamlFormatterParseWithVariables exercising ytt templating end-to-end.
Module deps
go.mod
Updated Go toolchain directive and dependencies; added carvel.dev/ytt and related packages; several dependency version changes.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant CLI
    participant ConfigLoader
    participant InputProvider
    participant YamlMultiDocFormat
    participant yttEngine

    User->>CLI: invoke with flags (--vars-text-templating / --var-file-paths)
    CLI->>ConfigLoader: parse flags & config file
    ConfigLoader->>InputProvider: initialize with Options (includes templating fields)
    InputProvider->>YamlMultiDocFormat: Parse(input)
    alt VarsTextTemplating enabled
        YamlMultiDocFormat->>yttEngine: render templates (templates, data-values, var files)
        yttEngine-->>YamlMultiDocFormat: rendered YAML
    end
    YamlMultiDocFormat->>YamlMultiDocFormat: decode YAML docs -> raw requests
    YamlMultiDocFormat-->>InputProvider: return parsed requests
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • dogancanbakir
  • ehsandeep

Pre-merge checks (3 passed)

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "feat(templating): add vars templating into yaml inputs (ytt)" succinctly and accurately captures the primary change—adding ytt-based variable templating for YAML input files—using conventional commit style; it is specific, concise, and directly related to the changes in the PR.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Poem

In a burrow of bytes the rabbit did say,
"I'll ytt-mix your YAML and hop it away."
Vars stitched like carrots in rows neat and tidy,
Tests hum a chorus — the output looks mighty.
🐇✨ Hop, template, and play!

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

❤️ Share

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

@alban-stourbe-wmx
Copy link
Contributor Author

heyyyy 🙋 @dogancanbakir @tarunKoyalwar @ehsandeep

@alban-stourbe-wmx alban-stourbe-wmx changed the title feat(templating): add vars templating into yaml inputs feat(templating): add vars templating into yaml inputs (ytt) Jun 12, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (10)
go.mod (1)

120-123: Heavy new indirect dependency – confirm necessity

carvel.dev/ytt v0.52.0 brings in ~25 MiB of transitive deps.
Double-check that:

  1. It is only required at build time for the YAML templating helper and not leaked into user-facing binaries via go:generate.
  2. There is no lighter alternative (e.g. Go template) that would satisfy the feature.

If retention is confirmed, consider guarding the import behind build tags or a tiny wrapper package to avoid dragging the whole tree into non-YAML builds.

pkg/types/types.go (1)

422-424: Field added but not surfaced in defaults / docs

VarsTextTemplating bool defaults to false, which is fine, but:

  1. DefaultOptions() should include a comment mentioning the default so conf‐generating code picks it up.
  2. Consider grouping it with the existing Vars field in the struct to keep related knobs together – the file is already huge.

No functional issue, just maintainability.

pkg/input/formats/formats.go (1)

31-34: Doc comment inaccurate & missing YAML guard

The comment says “Only available for Yaml formats” yet nothing enforces this at type level.
Add a runtime guard in the YAML parser (if not already there) and reword the comment:

- // VarsTextTemplating uses Variables and inject it into the input
- // this is used for text templating of variables based on carvel ytt
- // Only available for Yaml formats
+ // VarsTextTemplating enables Carvel-ytt variable interpolation.
+ // Supported only by YAML input formats; other formats ignore the flag.
pkg/input/provider/interface.go (1)

115-122: Propagation OK – but missing unit-test coverage

The new flag is correctly forwarded into InputFormatOptions, nice.
Add a small provider test ensuring the boolean travels end-to-end (options → provider → format).

cmd/nuclei/main.go (1)

263-264: CLI flag naming could be clearer

-vtt / --vars-text-templating is non-obvious; users might read it as “verbose test?”

Consider --vars-tt or simply --ytt:

- flagSet.BoolVarP(&options.VarsTextTemplating, "vars-text-templating", "vtt", false, ...
+ flagSet.BoolVarP(&options.VarsTextTemplating, "vars-text-templating", "ytt", false, ...

Short option collision check: -v, -vv already exist; -ytt is unique.

pkg/input/formats/yaml/multidoc_test.go (1)

44-50: Prefer table-driven tests for broader coverage

Right now the test hard-codes a single variable map (foo, bar). Turning this into a table-driven test would let you exercise multiple variable sets / edge-cases (empty map, numeric values, nested objects) with very little extra code and keeps the test scalable.

pkg/input/formats/yaml/multidoc.go (2)

82-84: strings.TrimSpace may strip intentional leading/trailing CRLF

Trimming could alter request bodies that legitimately start or end with whitespace (e.g., JSON " key": "value "). Consider limiting the trim to \r\n surrounding delimiters or dropping it altogether—types.ParseRawRequestWithURL already tolerates surrounding blank lines.


67-68: Debug log may leak secrets

Dumping fully-rendered YAML—including headers, tokens, and bodies—into debug logs can inadvertently expose credentials when users enable -debug. Mask sensitive fields or gate the log behind an explicit --verbose-templating flag.

pkg/input/formats/yaml/ytt.go (2)

45-55: Generated filenames hurt error diagnostics

Every inline template is named tpl*.yml, so ytt error messages don’t map back to real file paths. Pass the original filePath (if available) or a caller-supplied descriptor to improve debuggability.


25-29: Use bytes.Buffer instead of noopWriter for unit-testability

With a bytes.Buffer you can assert on ytt’s stdout/stderr in tests, and you still avoid polluting user terminals. Consider exposing the buffer behind a debug flag.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4859df and a0bd3b8.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • pkg/input/formats/testdata/ginandjuice.ytt.yaml is excluded by !**/*.yaml
📒 Files selected for processing (8)
  • cmd/nuclei/main.go (1 hunks)
  • go.mod (4 hunks)
  • pkg/input/formats/formats.go (1 hunks)
  • pkg/input/formats/yaml/multidoc.go (1 hunks)
  • pkg/input/formats/yaml/multidoc_test.go (2 hunks)
  • pkg/input/formats/yaml/ytt.go (1 hunks)
  • pkg/input/provider/interface.go (1 hunks)
  • pkg/types/types.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
pkg/input/provider/interface.go (1)
pkg/types/types.go (1)
  • Options (30-454)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Lint
🔇 Additional comments (1)
go.mod (1)

188-199: Version bumps: verify API surface

github.com/hashicorp/go-versionv1.7.0 and github.com/k14s/starlark-go pin.
Make sure nothing in the codebase relies on behaviours removed in go-version v1.6.x (e.g. Prerelease() semver semantics).
CI green? Good. If not, pin back or adjust callers.

Comment on lines 63 to 69
finalData, err := io.ReadAll(finalInput)
if err != nil {
return errors.Wrap(err, "could not read templated input")
}
gologger.Debug().Msgf("Templated YAML content: %s", string(finalData))
finalInput = strings.NewReader(string(finalData))

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Unconditional double-buffering wastes memory

io.ReadAll(finalInput) is executed only for debug logging but forces a second full copy of the templated YAML even when debug is disabled, doubling peak memory.
Wrap the debug block in if gologger.DefaultLogger.IsLevelEnabled(gologger.LevelDebug) { … } (or equivalent) and re-use the original bs slice returned from ytt() instead of re-reading the stream.

🤖 Prompt for AI Agents
In pkg/input/formats/yaml/multidoc.go around lines 63 to 69, the code reads the
entire finalInput stream into memory unconditionally for debug logging, causing
unnecessary memory usage. To fix this, wrap the debug logging block inside a
conditional that checks if debug level logging is enabled using
gologger.DefaultLogger.IsLevelEnabled(gologger.LevelDebug). Also, avoid
re-reading finalInput by reusing the original byte slice returned from ytt() for
logging instead of reading from the stream again.

@sacha-athias-wmx
Copy link

that are amazing features well done @alban-stourbe-wmx !!!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3eb3f66 and 248548e.

⛔ Files ignored due to path filters (4)
  • integration_tests/fuzz/fuzz-body.yaml is excluded by !**/*.yaml
  • pkg/input/formats/testdata/ytt/ginandjuice.ytt.yaml is excluded by !**/*.yaml
  • pkg/input/formats/testdata/ytt/ytt-profile.yaml is excluded by !**/*.yaml
  • pkg/input/formats/testdata/ytt/ytt-vars.yaml is excluded by !**/*.yaml
📒 Files selected for processing (6)
  • cmd/nuclei/main.go (4 hunks)
  • pkg/input/formats/formats.go (1 hunks)
  • pkg/input/formats/yaml/multidoc.go (1 hunks)
  • pkg/input/formats/yaml/ytt.go (1 hunks)
  • pkg/input/provider/interface.go (1 hunks)
  • pkg/types/types.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • pkg/input/formats/formats.go
  • pkg/input/provider/interface.go
  • pkg/types/types.go
  • pkg/input/formats/yaml/multidoc.go
  • pkg/input/formats/yaml/ytt.go
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Lint
🔇 Additional comments (2)
cmd/nuclei/main.go (2)

21-21: LGTM: Import addition is appropriate.

The gopkg.in/yaml.v2 import is correctly added to support YAML config file parsing functionality.


264-265: LGTM: CLI flags are well-defined.

The new flags are appropriately named and documented:

  • vars-text-templating enables the feature with clear scope limitation (yaml input mode only)
  • var-file-paths accepts multiple file paths for variable injection

@alban-stourbe-wmx
Copy link
Contributor Author

alban-stourbe-wmx commented Jun 24, 2025

I've added the ability to populate a yaml file containing variables directly from a yaml file. Why? Because it's possible to inject data structures directly into ytt, whereas via the var cli you can only inject strings.

As shown in the example above, this makes it possible to create queries that mix the encoding of the form body with other data structures (sending a json to a form variable), or even to inject arrays.

Command to test it:
nuclei -config pkg/input/formats/testdata/ytt/ytt-profile.yaml -proxy http://127.0.0.1:8080 -v

Screenshot 2025-06-24 at 18 48 44

@dogancanbakir dogancanbakir requested a review from Mzack9999 June 26, 2025 09:41
@alban-stourbe-wmx
Copy link
Contributor Author

fix: #6260

Copy link
Member

@Mzack9999 Mzack9999 left a comment

Choose a reason for hiding this comment

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

I've fixed a few issues with tests and Request parsing. Very interesting addition - Thanks for the PR!

@ehsandeep ehsandeep linked an issue Jul 3, 2025 that may be closed by this pull request
@alban-stourbe-wmx
Copy link
Contributor Author

alban-stourbe-wmx commented Jul 8, 2025

I've fixed a few issues with tests and Request parsing. Very interesting addition - Thanks for the PR!

Thanks ! ;)
Do you know if this feature will be included in the next version?
@Mzack9999

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
pkg/input/formats/yaml/multidoc_test.go (5)

39-41: Run this test in parallel to speed up CI.
Safe here and consistent with other table-free tests.

 func TestYamlFormatterParseWithVariables(t *testing.T) {
-	format := New()
+	t.Parallel()
+	format := New()

55-56: Prefer require.NoError over Nilf for clearer failure output.

-	require.Nilf(t, err, "error opening proxify ytt input file: %v", err)
+	require.NoError(t, err, "error opening proxify ytt input file")

63-74: Make raw-request assertion less brittle (ignore header order; keep strict body and request line).
Exact equality on the entire raw string can be fragile across minor header-ordering or middleware changes. Assert critical parts with contains.

-		expectedRaw := `POST /users/3 HTTP/1.1
-Host: ginandjuice.shop
-Authorization: Bearer 3x4mpl3t0k3n
-Accept-Encoding: gzip
-Content-Type: application/x-www-form-urlencoded
-Connection: close
-User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
-
-foo="catalog"&bar=product&debug=false`
-		normalised := strings.ReplaceAll(request.Request.Raw, "\r\n", "\n")
-		require.Equal(t, expectedRaw, strings.TrimSuffix(normalised, "\n"), "request raw does not match expected value")
+		normalized := strings.ReplaceAll(request.Request.Raw, "\r\n", "\n")
+		require.Contains(t, normalized, "POST /users/3 HTTP/1.1\n", "request line mismatch")
+		require.Contains(t, normalized, "\nHost: ginandjuice.shop\n", "missing Host header")
+		require.Contains(t, normalized, "\nContent-Type: application/x-www-form-urlencoded\n", "missing Content-Type header")
+		require.Contains(t, normalized, "\n\nfoo=\"catalog\"&bar=product&debug=false", "body mismatch")

78-78: Same here: use require.NoError for parse error.

-	require.Nilf(t, err, "error parsing yaml file: %v", err)
+	require.NoError(t, err, "error parsing yaml file")

47-53: Add a companion subtest for VarsFilePaths to exercise file-based variables.
Covers the other half of the new API (ytt vars from file), aligning tests with the PR scope.

I can draft a subtest that loads a small YAML file with foo/bar and asserts the same request/body as this test. Would you like me to add it?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d55ab2f and c487e59.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • cmd/nuclei/main.go (4 hunks)
  • go.mod (3 hunks)
  • pkg/input/formats/yaml/multidoc_test.go (2 hunks)
  • pkg/input/provider/interface.go (1 hunks)
  • pkg/types/types.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/input/provider/interface.go
  • pkg/types/types.go
  • cmd/nuclei/main.go
  • go.mod
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Format Go code using go fmt
Run static analysis with go vet

Files:

  • pkg/input/formats/yaml/multidoc_test.go
🧬 Code graph analysis (1)
pkg/input/formats/yaml/multidoc_test.go (3)
pkg/input/formats/yaml/multidoc.go (1)
  • New (21-23)
pkg/input/formats/formats.go (1)
  • InputFormatOptions (20-37)
pkg/input/types/http.go (1)
  • RequestResponse (28-43)
⏰ 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 (macOS-latest)
  • GitHub Check: Tests (windows-latest)
  • GitHub Check: Tests (ubuntu-latest)
🔇 Additional comments (3)
pkg/input/formats/yaml/multidoc_test.go (3)

5-5: LGTM: newline normalization helper import is appropriate.


8-8: LGTM: formats import is correct for SetOptions usage.


39-82: Fix gofmt issues, then re-run go vet and the targeted tests.

  • gofmt -s -l reported unformatted files: pkg/fuzz/component/path.go, pkg/fuzz/component/path_test.go, pkg/testutils/fuzzplayground/sqli_test.go
  • Action: run gofmt -s -w . then go vet ./... and re-run go test ./pkg/input/formats/yaml -run 'TestYamlFormatterParse($|WithVariables$)' -v

@Mzack9999
Copy link
Member

@alban-stourbe-wmx I'm merging in dev, it will be available in next release!

@Mzack9999 Mzack9999 merged commit a21bfc4 into projectdiscovery:dev Sep 11, 2025
20 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.

[FEATURE] Templating variables in YAML inputs

3 participants