package runner
import (
"fmt"
"testing"
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
"github.com/projectdiscovery/nuclei/v3/pkg/output"
"github.com/stretchr/testify/require"
)
func simulateOnResultCallback(templatePath string, events []*output.InternalWrappedEvent) (map[string]interface{}, error) {
data := map[string]interface{}{}
for _, e := range events {
if e == nil {
continue
}
if !e.HasOperatorResult() {
continue
}
for k, v := range e.OperatorsResult.DynamicValues {
for _, value := range v {
oldVal, ok := data[k]
if !ok || len(value) > len(oldVal.(string)) {
data[k] = value
}
}
}
for k, v := range e.OperatorsResult.Extracts {
if len(v) > 0 {
data[k] = v[0]
}
}
}
var finalErr error
if len(data) == 0 {
finalErr = fmt.Errorf("no extracted values found for template: %s", templatePath)
}
return data, finalErr
}
func flowEvents() []*output.InternalWrappedEvent {
return []*output.InternalWrappedEvent{
{InternalEvent: map[string]interface{}{"status_code": 200}},
{
OperatorsResult: &operators.Result{
Matched: true,
Extracts: map[string][]string{
"userid": {"USER-042"},
"customerid": {"CUST-001"},
},
},
InternalEvent: map[string]interface{}{"status_code": 200},
},
}
}
func TestCallbackWorksWithFlowTemplate(t *testing.T) {
data, err := simulateOnResultCallback("login.yaml", flowEvents())
require.Equal(t, "USER-042", data["userid"])
require.Equal(t, "CUST-001", data["customerid"])
require.NoError(t, err, "should not error when data is extracted from a later event")
}
func TestCallbackSingleRequest(t *testing.T) {
events := []*output.InternalWrappedEvent{
{
OperatorsResult: &operators.Result{
Matched: true,
Extracts: map[string][]string{
"token": {"abc123"},
},
},
InternalEvent: map[string]interface{}{},
},
}
data, err := simulateOnResultCallback("auth.yaml", events)
require.NoError(t, err)
require.Equal(t, "abc123", data["token"])
}
func TestCallbackNoExtraction(t *testing.T) {
events := []*output.InternalWrappedEvent{
{InternalEvent: map[string]interface{}{"status_code": 404}},
}
data, err := simulateOnResultCallback("auth.yaml", events)
require.Error(t, err)
require.Contains(t, err.Error(), "no extracted values")
require.Empty(t, data)
}
func TestCallbackNilEvent(t *testing.T) {
events := []*output.InternalWrappedEvent{nil}
data, err := simulateOnResultCallback("auth.yaml", events)
require.Error(t, err)
require.Contains(t, err.Error(), "no extracted values")
require.Empty(t, data)
}
func TestCallbackDynamicValues(t *testing.T) {
events := []*output.InternalWrappedEvent{
{
OperatorsResult: &operators.Result{
Matched: true,
DynamicValues: map[string][]string{
"session": {"sess-xyz-123", "sess-a"},
},
},
InternalEvent: map[string]interface{}{},
},
}
data, err := simulateOnResultCallback("auth.yaml", events)
require.NoError(t, err)
require.Equal(t, "sess-xyz-123", data["session"], "should pick the longest value")
}