Files
Dwi Siswanto c50f92fdd3 refactor: enforce scan network restrictions in network helpers
Pass scan options through rendering, variables, matchers, and extractors
so resolve(), public_ip()/publicip(), and jarm() honor -lna and -eh.
These helpers previously performed network I/O outside the scan's network
policy.

Use the scan dialer for HTTP and JARM connections and reject restricted
IP addresses in DNS answers. Validate original and normalized hostnames
to prevent exclusion bypasses through DNS escapes and trailing dots.

Enforce -eh with or without -lna, including in unsigned templates.
Keep local access available without -lna unless excluded, and preserve
configured DNS resolvers, including local ones.

Bind helpers at evaluation time without storing scan state in cached
expressions. Reject network helper calls without initialized scan
context.

Fixes #7769

Signed-off-by: Dwi Siswanto <[email protected]>
2026-09-23 18:53:49 +07:00

137 lines
4.3 KiB
Go

// Package render centralizes rendering of template text with runtime values.
package render
import (
"fmt"
"strings"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/marker"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
)
// Values contains render values. Values are inserted as data; they are not
// scanned as new template text after insertion.
type Values = map[string]interface{}
// URLSource allocates URLs for Interactsh markers found in template text.
type URLSource interface {
NewURLWithData(data string) (string, error)
}
// Input describes a render operation. Text is template text, Values are data,
// and Interactsh is optional source-level marker handling.
type Input struct {
// Options supplies scan authority separately from template-controlled values.
Options *types.Options
Text string
Values Values
Interactsh URLSource
InteractURLs []string
}
// Result is the terminal rendered data and any Interactsh URLs allocated while
// rendering template text.
type Result struct {
Text string
InteractURLs []string
}
// MapInput describes a map render operation. Source values are template text.
// Data values are terminal data that override Source and are not rendered.
type MapInput struct {
Options *types.Options
Source Values
Data Values
// Values is the evaluation context used while rendering Source.
Values Values
// Interactsh and InteractURLs carry source-level Interactsh allocations.
Interactsh URLSource
InteractURLs []string
}
// MapResult contains the complete value map after Source rendering and Data
// overlay, plus any Interactsh URLs allocated while rendering Source values.
type MapResult struct {
Values Values
InteractURLs []string
}
// Render evaluates template text with values and returns rendered data.
//
// Interactsh markers in Text are source-level render effects and are allocated
// before DSL helper evaluation, so helper transforms apply to generated URLs.
// Values are data and rendered output is terminal data; neither is rescanned for
// DSL expressions or Interactsh markers.
func Render(input Input) (Result, error) {
prepared, err := ReplaceInteractshMarkers(input.Text, input.Interactsh, input.InteractURLs)
if err != nil {
return prepared, err
}
result, err := expressions.EvaluateWithOptions(prepared.Text, input.Values, input.Options)
if err != nil {
return prepared, err
}
return Result{Text: result, InteractURLs: prepared.InteractURLs}, nil
}
// RenderMap renders Source values as template text, overlays Data values, and
// returns a complete value map. Data keys are skipped while rendering so they
// cannot produce DSL or Interactsh side effects.
//
// It is intended for already-expanded payload iteration maps, not raw payload
// definitions containing lists.
func RenderMap(input MapInput) (MapResult, error) {
values := make(Values, len(input.Source)+len(input.Data))
urls := append([]string(nil), input.InteractURLs...)
for key, value := range input.Source {
if _, ok := input.Data[key]; ok {
continue
}
result, err := Render(Input{
Options: input.Options,
Text: fmt.Sprint(value),
Values: input.Values,
Interactsh: input.Interactsh,
InteractURLs: urls,
})
if err != nil {
return MapResult{Values: values, InteractURLs: result.InteractURLs}, err
}
values[key] = result.Text
urls = result.InteractURLs
}
for key, value := range input.Data {
values[key] = value
}
return MapResult{Values: values, InteractURLs: urls}, nil
}
// ReplaceInteractshMarkers allocates Interactsh URLs for markers that appear in
// template text. It does not evaluate DSL expressions or inspect values.
func ReplaceInteractshMarkers(text string, source URLSource, interactURLs []string) (Result, error) {
urls := append([]string(nil), interactURLs...)
if source == nil {
return Result{Text: text, InteractURLs: urls}, nil
}
for _, interactshURLMarker := range marker.FindInteractshURLMarkers(text) {
url, err := source.NewURLWithData(interactshURLMarker)
if err != nil {
return Result{Text: text, InteractURLs: urls}, fmt.Errorf("replace interactsh marker %q: %w", interactshURLMarker, err)
}
urls = append(urls, url)
text = strings.Replace(text, interactshURLMarker, url, 1)
}
return Result{Text: text, InteractURLs: urls}, nil
}