Ctrl K
ComunidadGuías…

dynamic_rules_config_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package main

import (
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"netgoat.xyz/agent/internal/config"
	"netgoat.xyz/agent/internal/dynamicrules"
)

func TestConfigureDynamicRulesEvaluatesAndRestoresRequestBody(t *testing.T) {
	cfg := &config.Config{}
	cfg.DynamicRules.Enabled = true
	cfg.DynamicRules.MaxInputBytes = 512
	cfg.DynamicRules.Rules = []config.DynamicRule{{
		Name:     "block-secret",
		Language: "typescript",
		Source: `export function evaluate(request) {
			return request.body === "secret" ? { action: "block", reason: "secret body" } : null;
		}`,
	}}
	engine, err := configureDynamicRules(cfg)
	if err != nil {
		t.Fatalf("configureDynamicRules(): %v", err)
	}
	if engine == nil {
		t.Fatal("configureDynamicRules() returned nil engine")
	}

	request := httptest.NewRequest(http.MethodPost, "http://api.example.test/submit", strings.NewReader("secret"))
	decision, err := evaluateDynamicRules(request, engine, "203.0.113.7")
	if err != nil || decision.Action != dynamicrules.ActionBlock || decision.Rule != "block-secret" {
		t.Fatalf("evaluateDynamicRules() = %#v, %v", decision, err)
	}
	body, err := io.ReadAll(request.Body)
	if err != nil {
		t.Fatalf("read restored body: %v", err)
	}
	if string(body) != "secret" {
		t.Fatalf("restored body = %q", body)
	}
	retryBody, err := request.GetBody()
	if err != nil {
		t.Fatalf("GetBody(): %v", err)
	}
	defer retryBody.Close()
	if copied, _ := io.ReadAll(retryBody); string(copied) != "secret" {
		t.Fatalf("GetBody() = %q", copied)
	}
}

func TestDynamicRulesRejectInvalidConfigAndBoundBodies(t *testing.T) {
	cfg := &config.Config{}
	cfg.DynamicRules.Enabled = true
	cfg.DynamicRules.Rules = []config.DynamicRule{{Name: "broken", Source: "export function"}}
	if _, err := configureDynamicRules(cfg); err == nil {
		t.Fatal("configureDynamicRules() accepted invalid source")
	}

	cfg.DynamicRules.Rules = []config.DynamicRule{{Name: "disabled", Source: "export function evaluate() { return null; }", Enabled: boolPointer(false)}}
	if engine, err := configureDynamicRules(cfg); err != nil || engine != nil {
		t.Fatalf("disabled rules engine = %v, %v", engine, err)
	}

	request := httptest.NewRequest(http.MethodPost, "http://api.example.test/", strings.NewReader("12345"))
	if _, err := copyRequestBodyForDynamicRules(request, 4); !errors.Is(err, dynamicrules.ErrLimitExceeded) {
		t.Fatalf("copyRequestBodyForDynamicRules() error = %v, want limit error", err)
	}
}

func TestDynamicRulesRuntimeRetainsLastKnownGoodEngine(t *testing.T) {
	cfg := &config.Config{}
	cfg.DynamicRules.Enabled = true
	cfg.DynamicRules.Rules = []config.DynamicRule{{
		Name:   "stable",
		Source: `export function evaluate(request) { return request.path === "/blocked" ? { action: "block" } : null; }`,
	}}
	runtime, err := newDynamicRulesRuntime(cfg)
	if err != nil {
		t.Fatalf("newDynamicRulesRuntime(): %v", err)
	}

	cfg.DynamicRules.Rules = []config.DynamicRule{{Name: "broken", Source: "export function"}}
	if err := runtime.Update(cfg); err == nil {
		t.Fatal("Update() accepted invalid replacement rules")
	}
	request := httptest.NewRequest(http.MethodGet, "http://api.example.test/blocked", nil)
	decision, err := evaluateDynamicRules(request, runtime.Load(), "203.0.113.7")
	if err != nil || decision.Action != dynamicrules.ActionBlock || decision.Rule != "stable" {
		t.Fatalf("last-known-good evaluation = %#v, %v", decision, err)
	}
}

func boolPointer(value bool) *bool {
	return &value
}