appsec

package
v1.8.0-rc1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 27, 2026 License: MIT Imports: 38 Imported by: 0

Documentation

Index

Constants

View Source
const (
	BanRemediation       = "ban"
	CaptchaRemediation   = "captcha"
	AllowRemediation     = "allow"
	ChallengeRemediation = "challenge"
)
View Source
const (
	// BodySizeActionDrop drops the request when the body exceeds the maximum size.
	BodySizeActionDrop = "drop"
	// BodySizeActionPartial reads the body up to the maximum size and processes it.
	BodySizeActionPartial = "partial"
	// BodySizeActionAllow processes the request without inspecting the body.
	BodySizeActionAllow = "allow"

	// DefaultMaxBodySize is the default maximum body size (10MB).
	DefaultMaxBodySize = int64(10 * 1024 * 1024)
)
View Source
const (
	PhaseInBand phase = iota
	PhaseOutOfBand
)
View Source
const (
	// ModuleName is the acquisition module name carried on events produced by
	// the appsec datasource.
	ModuleName = "appsec"

	// SourceWAF is the `source` field set on events generated by a WAF rule match.
	SourceWAF = "crowdsec-appsec"
	// SourceChallenge is the `source` field set on events generated by the
	// challenge lifecycle. It is intentionally distinct from SourceWAF so that
	// challenge scenarios can filter on it without colliding with WAF scenarios.
	SourceChallenge = "crowdsec-appsec-challenge"
)
View Source
const (
	URIHeaderName           = "X-Crowdsec-Appsec-Uri"
	VerbHeaderName          = "X-Crowdsec-Appsec-Verb"
	HostHeaderName          = "X-Crowdsec-Appsec-Host"
	IPHeaderName            = "X-Crowdsec-Appsec-Ip"
	APIKeyHeaderName        = "X-Crowdsec-Appsec-Api-Key"
	UserAgentHeaderName     = "X-Crowdsec-Appsec-User-Agent"
	HTTPVersionHeaderName   = "X-Crowdsec-Appsec-Http-Version"
	TransactionIDHeaderName = "X-Crowdsec-Appsec-Transaction-Id"
)
View Source
const APPSEC_RULE = "appsec-rule"

Variables

View Source
var AppsecRulesDetails = make(map[int]RulesDetails)

FIXME: this shouldn't be a global Is using the id is a good idea ? might be too specific to coraza and not easily reusable

View Source
var DebugRules = map[int]bool{}

Functions

func ChallengeEventFromRequest

func ChallengeEventFromRequest(r *ParsedRequest, labels map[string]string, txUuid string, info ChallengeEventInfo) pipeline.Event

ChallengeEventFromRequest builds a LOG event for a challenge lifecycle moment. It carries SourceChallenge so it never collides with WAF scenarios, plus the reason, difficulty and (when available) fingerprint scalars for filtering.

func DumpFingerprint

func DumpFingerprint(dir, label string, fp *challenge.FingerprintData, req *ParsedRequest) string

DumpFingerprint allows to dump the fingerprint + some context (ip, host, timestamp etc.) to as JSONL file for later analysis.

func EventFromRequest

func EventFromRequest(r *ParsedRequest, labels map[string]string, txUuid string) (pipeline.Event, error)

func GetOnChallengeEnv

func GetOnChallengeEnv(ctx context.Context, w *AppsecRuntimeConfig, state *AppsecRequestState, request *ParsedRequest) map[string]interface{}

func GetOnChallengeSubmitEnv

func GetOnChallengeSubmitEnv(w *AppsecRuntimeConfig, state *AppsecRequestState, request *ParsedRequest) map[string]interface{}

GetOnChallengeSubmitEnv is the env exposed to on_challenge_submit hooks. Deliberately narrow: the hook fires once during the challenge submission JSON response, so anything that would change the response shape (SendChallenge, SetRemediation, SetReturnCode, SetChallengeDifficulty, DropRequest) is intentionally omitted to avoid breaking the client-side JS handler. Operators wanting to escalate or block at the next request should do so via pre_eval.

func GetOnLoadEnv

func GetOnLoadEnv(w *AppsecRuntimeConfig) map[string]interface{}

func GetOnMatchEnv

func GetOnMatchEnv(w *AppsecRuntimeConfig, state *AppsecRequestState, request *ParsedRequest, evt pipeline.Event) map[string]interface{}

func GetPostEvalEnv

func GetPostEvalEnv(ctx context.Context, w *AppsecRuntimeConfig, state *AppsecRequestState, request *ParsedRequest) map[string]interface{}

func GetPreEvalEnv

func GetPreEvalEnv(ctx context.Context, w *AppsecRuntimeConfig, state *AppsecRequestState, request *ParsedRequest) map[string]interface{}

func GetRuleDebug

func GetRuleDebug(id int) bool

func LoadAppsecRules

func LoadAppsecRules(hub *cwhub.Hub) error

func NewCrzLogger

func NewCrzLogger(logger *log.Entry) *crzLogger

func SetRuleDebug

func SetRuleDebug(id int, debug bool)

Types

type AppsecCollection

type AppsecCollection struct {
	Rules       []string
	NativeRules []string
}

func LoadCollection

func LoadCollection(pattern string, logger *log.Entry, hub *cwhub.Hub) ([]AppsecCollection, error)

func (AppsecCollection) String

func (w AppsecCollection) String() string

type AppsecCollectionConfig

type AppsecCollectionConfig struct {
	Type              string                   `yaml:"type"`
	Name              string                   `yaml:"name"`
	Debug             bool                     `yaml:"debug"`
	Description       string                   `yaml:"description"`
	SecLangFilesRules []string                 `yaml:"seclang_files_rules"`
	SecLangRules      []string                 `yaml:"seclang_rules"`
	Rules             []appsec_rule.CustomRule `yaml:"rules"`
	Severity          string                   `yaml:"severity"`

	Labels map[string]any `yaml:"labels"` // Labels is K:V list aiming at providing context the overflow

	Data []*enrichment.DataProvider `yaml:"data"`
	// contains filtered or unexported fields
}

to be filled w/ seb update

type AppsecConfig

type AppsecConfig struct {
	Name                   string   `yaml:"name"`
	OutOfBandRules         []string `yaml:"outofband_rules"`
	InBandRules            []string `yaml:"inband_rules"`
	DefaultRemediation     string   `yaml:"default_remediation"`
	DefaultPassAction      string   `yaml:"default_pass_action"`
	BouncerBlockedHTTPCode int      `yaml:"blocked_http_code"`      // returned to the bouncer
	BouncerPassedHTTPCode  int      `yaml:"passed_http_code"`       // returned to the bouncer
	UserBlockedHTTPCode    int      `yaml:"user_blocked_http_code"` // returned to the user
	UserPassedHTTPCode     int      `yaml:"user_passed_http_code"`  // returned to the user

	OnLoad            []Hook              `yaml:"on_load"`
	PreEval           []Hook              `yaml:"pre_eval"`
	PostEval          []Hook              `yaml:"post_eval"`
	OnMatch           []Hook              `yaml:"on_match"`
	OnChallenge       []Hook              `yaml:"on_challenge"`
	OnChallengeSubmit []Hook              `yaml:"on_challenge_submit"`
	VariablesTracking []string            `yaml:"variables_tracking"`
	InbandOptions     AppsecSubEngineOpts `yaml:"inband_options"`
	OutOfBandOptions  AppsecSubEngineOpts `yaml:"outofband_options"`

	InBand    *AppsecPhaseConfig `yaml:"inband"`
	OutOfBand *AppsecPhaseConfig `yaml:"outofband"`

	// Data declares datafiles (e.g. bot lists queried by MatchKnownBot) that
	// cwhub downloads and Build() loads into the expr datafile registry — the
	// same `data:` mechanism parsers/scenarios/appsec-rules use.
	Data []*enrichment.DataProvider `yaml:"data"`

	// Challenge carries the WAF challenge / bot-detection runtime tuning.
	// All fields are optional; unset fields fall back to the runtime
	// defaults at NewChallengeRuntime time. When multiple appsec-configs
	// are loaded, each later config's non-nil fields override the earlier
	// values (see LoadByPath).
	Challenge *challenge.Config `yaml:"challenge"`

	LogLevel *log.Level `yaml:"log_level"`
	Logger   *log.Entry `yaml:"-"`
}

func (*AppsecConfig) Build

func (wc *AppsecConfig) Build(ctx context.Context, hub *cwhub.Hub) (*AppsecRuntimeConfig, error)

func (*AppsecConfig) Load

func (wc *AppsecConfig) Load(configName string, hub *cwhub.Hub) error

func (*AppsecConfig) LoadByPath

func (wc *AppsecConfig) LoadByPath(file string) error

func (*AppsecConfig) SetUpLogger added in v1.6.5

func (wc *AppsecConfig) SetUpLogger()

type AppsecDropInfo added in v1.7.4

type AppsecDropInfo struct {
	Reason       string
	Interruption *corazatypes.Interruption
}

type AppsecPhaseConfig added in v1.7.8

type AppsecPhaseConfig struct {
	Rules             []string            `yaml:"rules"`
	OnMatch           []Hook              `yaml:"on_match"`
	PreEval           []Hook              `yaml:"pre_eval"`
	PostEval          []Hook              `yaml:"post_eval"`
	OnChallenge       []Hook              `yaml:"on_challenge"`
	OnChallengeSubmit []Hook              `yaml:"on_challenge_submit"`
	Options           AppsecSubEngineOpts `yaml:"options"`
	VariablesTracking []string            `yaml:"variables_tracking"`
}

AppsecPhaseConfig holds configuration scoped to a specific phase (inband or outofband). Hooks defined here are automatically dispatched only during the corresponding phase. on_challenge and on_challenge_submit are in-band only; setting them under `outofband:` is rejected at Build() time.

type AppsecRequestState added in v1.7.4

type AppsecRequestState struct {
	Tx           ExtendedTransaction
	CurrentPhase phase
	Response     AppsecTempResponse

	InBandDrop    *AppsecDropInfo
	OutOfBandDrop *AppsecDropInfo

	PendingAction   *string
	PendingHTTPCode *int

	RequireChallenge    bool
	Fingerprint         *challenge.FingerprintData
	CookiePowDifficulty int  // PoW difficulty proven by the client for the current cookie (0 if no/invalid cookie)
	ChallengeDifficulty *int // per-request PoW difficulty override (nil = use runtime default)

	// SubmissionRejection is set by RejectSubmission inside an
	// on_challenge_submit hook to refuse cookie issuance for the current
	// challenge submission. nil for any other phase / outcome.
	SubmissionRejection *SubmissionRejectInfo

	// ChallengeBypassed is set by GrantChallengeCookie to suppress later
	// SendChallenge calls in the same request. Per-request only; cleared
	// on ResetResponse. The bypass for subsequent requests is carried by
	// the allowlist cookie itself, not by this flag.
	ChallengeBypassed bool

	// ChallengeExempt is set by the ExemptFromChallenge expr helper to exempt
	// the current request from the bot challenge: SendChallenge becomes a no-op
	// once it is set. It only affects the current request and doesn't mint a
	// cookie (unlike GrantChallengeCookie). Deliberately not cleared by
	// ResetResponse so a pre_eval exemption survives into post_eval.
	ChallengeExempt bool

	// HooksHalted is flipped by terminal hook actions (currently
	// RejectSubmission and the inline GrantChallengeCookie variant
	// exposed in on_challenge_submit) to short-circuit later rules in
	// the same phase. Without this, a `LogAccepted` rule following a
	// `RejectSubmission` rule with `filter: "true"` would emit a
	// contradictory accept-log line for an already-rejected submission.
	// Per-request only; cleared by ResetResponse.
	HooksHalted bool

	// LastMismatchReport caches the result of the EvaluateMismatches expr
	// closure for the current request, so repeated calls from a single
	// rule expression don't redo the work (or re-emit observability).
	// nil until the first call.
	LastMismatchReport *challenge.MismatchReport

	// HookVars is a per-request scratch space exposed to expr hooks as
	// `hook_vars`. Helpers (e.g. ValidateRequestWithSchema) publish string
	// values here so that later hook expressions — including the `apply`
	// block of the same hook — can read them. The map is allocated once in
	// NewRequestState, persists across in-band/out-of-band phases, and is
	// copied into pipeline.AppsecEvent.HookVars when an event is emitted.
	HookVars              map[string]string
	DisableBodyInspection bool
}

func (*AppsecRequestState) ApplyPendingResponse added in v1.7.4

func (s *AppsecRequestState) ApplyPendingResponse()

func (*AppsecRequestState) DropInfo added in v1.7.4

func (s *AppsecRequestState) DropInfo(request *ParsedRequest) *AppsecDropInfo

func (*AppsecRequestState) ResetResponse added in v1.7.4

func (s *AppsecRequestState) ResetResponse(cfg *AppsecConfig)

type AppsecRuntimeConfig

type AppsecRuntimeConfig struct {
	Name           string
	OutOfBandRules []AppsecCollection

	InBandRules []AppsecCollection

	DefaultRemediation string
	RemediationByTag   map[string]string // Also used for ByName, as the name (for modsec rules) is a tag crowdsec-NAME
	RemediationById    map[int]string

	CompiledOnLoad            []Hook     // runs once at startup, not phase-scoped
	CompiledOnChallenge       []Hook     // in-band only; runs before pre_eval
	CompiledOnChallengeSubmit []Hook     // in-band only; runs at /submit POST after validation
	CommonHooks               PhaseHooks // apply to both phases
	InBandHooks               PhaseHooks // only run during in-band
	OutOfBandHooks            PhaseHooks // only run during out-of-band

	CompiledVariablesTracking []*regexp.Regexp
	Config                    *AppsecConfig

	Logger *log.Entry

	// Set by on_load to ignore some rules on loading
	DisabledInBandRuleIds   []int
	DisabledInBandRulesTags []string // Also used for ByName, as the name (for modsec rules) is a tag crowdsec-NAME

	DisabledOutOfBandRuleIds   []int
	DisabledOutOfBandRulesTags []string // Also used for ByName, as the name (for modsec rules) is a tag crowdsec-NAME

	// True if at least one of the hooks use `RequireValidChallenge`
	NeedWASMVM       bool
	ChallengeRuntime *challenge.ChallengeRuntime

	// OutChan is the pipeline output channel challenge lifecycle events are sent
	// on, and Labels are the datasource labels stamped on those events. Both are
	// wired by the appsec datasource at startup; nil OutChan disables emission
	// (e.g. in unit tests with no datasource).
	OutChan chan pipeline.Event
	Labels  map[string]string

	// FingerprintDumpDir is the on-disk directory the DumpFingerprint
	FingerprintDumpDir string

	RequestValidator *apivalidation.RequestValidator
	DataDir          string
	// BodySettings controls how oversized request bodies are handled. Settable via on_load hooks.
	BodySettings BodySettings
}

runtime version of AppsecConfig

func (*AppsecRuntimeConfig) CancelAlert

func (w *AppsecRuntimeConfig) CancelAlert(state *AppsecRequestState) error

func (*AppsecRuntimeConfig) CancelEvent

func (w *AppsecRuntimeConfig) CancelEvent(state *AppsecRequestState) error

func (*AppsecRuntimeConfig) ClearResponse

func (w *AppsecRuntimeConfig) ClearResponse(state *AppsecRequestState)

func (*AppsecRuntimeConfig) DisableBodyInspection added in v1.7.8

func (w *AppsecRuntimeConfig) DisableBodyInspection(state *AppsecRequestState) error

DisableBodyInspection prevents Coraza from processing the request body for the current request. Intended for use in pre_eval hooks.

func (*AppsecRuntimeConfig) DisableInBandRuleByID

func (w *AppsecRuntimeConfig) DisableInBandRuleByID(id int) error

Disable a rule at load time, meaning it will not run for any request

func (*AppsecRuntimeConfig) DisableInBandRuleByName

func (w *AppsecRuntimeConfig) DisableInBandRuleByName(name string) error

Disable a rule at load time, meaning it will not run for any request

func (*AppsecRuntimeConfig) DisableInBandRuleByTag

func (w *AppsecRuntimeConfig) DisableInBandRuleByTag(tag string) error

Disable a rule at load time, meaning it will not run for any request

func (*AppsecRuntimeConfig) DisableOutBandRuleByID

func (w *AppsecRuntimeConfig) DisableOutBandRuleByID(id int) error

Disable a rule at load time, meaning it will not run for any request

func (*AppsecRuntimeConfig) DisableOutBandRuleByName

func (w *AppsecRuntimeConfig) DisableOutBandRuleByName(name string) error

Disable a rule at load time, meaning it will not run for any request

func (*AppsecRuntimeConfig) DisableOutBandRuleByTag

func (w *AppsecRuntimeConfig) DisableOutBandRuleByTag(tag string) error

Disable a rule at load time, meaning it will not run for any request

func (*AppsecRuntimeConfig) DropRequest added in v1.7.4

func (w *AppsecRuntimeConfig) DropRequest(state *AppsecRequestState, request *ParsedRequest, reason string) error

func (*AppsecRuntimeConfig) EvaluateMismatches

func (w *AppsecRuntimeConfig) EvaluateMismatches(state *AppsecRequestState, request *ParsedRequest) *challenge.MismatchReport

SendChallenge issues a challenge HTML page for the current request. Cookie and submission handling live in ProcessOnChallengeRules; by the time this runs, state.Fingerprint has already been populated if a valid cookie was presented. If the client already proved a PoW at least as hard as the target difficulty for this request, SendChallenge is a no-op. When the target difficulty is raised (e.g. on_challenge calls SetChallengeDifficulty to punish a suspect fingerprint), the stored difficulty is lower than the target and a fresh challenge is issued. EvaluateMismatches runs all library-native + custom fingerprint mismatch checks, caches the result on state, and emits one structured Debug log line + one metric bump per fired signal on the first call of a given request. Subsequent calls return the cached pointer so rules can reference the report multiple times without redoing the work.

func (*AppsecRuntimeConfig) ExemptFromChallenge

func (*AppsecRuntimeConfig) ExemptFromChallenge(state *AppsecRequestState, request *ParsedRequest, reason string) error

ExemptFromChallenge flags the current request as exempt from the bot challenge and records why. The flag is set once and honored by SendChallenge, so challenge configs no longer gate the challenge on an explicit bot check. The counter is bumped only on the first exemption so multiple matching configs don't inflate the count.

func (*AppsecRuntimeConfig) GenerateResponse

func (w *AppsecRuntimeConfig) GenerateResponse(response AppsecTempResponse, logger *log.Entry) (int, BodyResponse)

func (*AppsecRuntimeConfig) GrantAllowlistCookieInline

func (w *AppsecRuntimeConfig) GrantAllowlistCookieInline(state *AppsecRequestState, request *ParsedRequest, reason string, ttlOverride *time.Duration) error

GrantAllowlistCookieInline mints an allowlist-bypass cookie and attaches it to the in-flight challenge-submit envelope. Used from on_challenge_submit, where the client awaits the submit JSON envelope and a redirect would break its state machine (the envelope is already a ChallengeRemediation, so UserCookies is serialized). Same precedence/ttl/error semantics as mintAllowlistCookie.

func (*AppsecRuntimeConfig) GrantChallengeCookie

func (w *AppsecRuntimeConfig) GrantChallengeCookie(state *AppsecRequestState, request *ParsedRequest, reason string, ttlOverride *time.Duration) error

GrantChallengeCookie mints an allowlist-bypass cookie and issues an HTTP 307 redirect carrying it back to the visitor. Used from pre_eval/post_eval.

Why a 307 and not a silent allow: the bouncer only serializes cookies on a ChallengeRemediation response (see GenerateResponse), so a plain allow drops the Set-Cookie. The redirect to the same URL preserves method+body and bounces the visitor back through the WAF with the cookie present, so ProcessOnChallengeRules' allowlist branch lets them through on the next hop.

Precedence: see mintAllowlistCookie (operator allowlist overwrites a real fingerprint). ttlOverride overrides cookie_ttl. Returns ErrAllowlistReasonSize if reason is too long.

func (*AppsecRuntimeConfig) LoadAPISchemaWithName added in v1.7.8

func (w *AppsecRuntimeConfig) LoadAPISchemaWithName(ref string, filename string) error

func (*AppsecRuntimeConfig) LoadAPISchemaWithOptions added in v1.7.8

func (w *AppsecRuntimeConfig) LoadAPISchemaWithOptions(ref string, filename string, opts map[string]any) error

LoadAPISchemaWithOptions behaves like LoadAPISchemaWithName but accepts a map of policy overrides. Supported keys:

  • "on_route_not_found": "drop" | "ignore" (default: "drop")
  • "on_method_not_allowed": "drop" | "ignore" (default: "drop")
  • "on_unsupported_security_scheme": "drop" | "ignore" (default: "drop")

func (*AppsecRuntimeConfig) NewRequestState added in v1.7.4

func (w *AppsecRuntimeConfig) NewRequestState() AppsecRequestState

func (*AppsecRuntimeConfig) ProcessOnChallengeRules

func (w *AppsecRuntimeConfig) ProcessOnChallengeRules(ctx context.Context, state *AppsecRequestState, request *ParsedRequest) error

ProcessOnChallengeRules is the in-band-only challenge entry point. It handles the PoW worker JS path and the challenge submission path internally, validates any existing challenge cookie to populate state.Fingerprint, and runs the user-defined on_challenge hook expressions ONLY when there is a fingerprint to inspect — i.e. on a valid submission or when a valid cookie was presented. Requests with no cookie / invalid cookie / invalid submission skip user hooks entirely (there's nothing to evaluate).

func (*AppsecRuntimeConfig) ProcessOnLoadRules

func (w *AppsecRuntimeConfig) ProcessOnLoadRules() error

func (*AppsecRuntimeConfig) ProcessOnMatchRules

func (w *AppsecRuntimeConfig) ProcessOnMatchRules(state *AppsecRequestState, request *ParsedRequest, evt pipeline.Event) error

func (*AppsecRuntimeConfig) ProcessPostEvalRules

func (w *AppsecRuntimeConfig) ProcessPostEvalRules(ctx context.Context, state *AppsecRequestState, request *ParsedRequest) error

func (*AppsecRuntimeConfig) ProcessPreEvalRules

func (w *AppsecRuntimeConfig) ProcessPreEvalRules(ctx context.Context, state *AppsecRequestState, request *ParsedRequest) error

func (*AppsecRuntimeConfig) RegisterAPISchemaBodyDecoder added in v1.7.8

func (w *AppsecRuntimeConfig) RegisterAPISchemaBodyDecoder(contentType, decoderName string) error

RegisterAPISchemaBodyDecoder allows a user's on_load hook to add a Content-Type to the set the API schema validator can decode. decoderName must be one of the stable built-in identifiers exported by the api_validation package ("json", "urlencoded", "multipart", "yaml", "csv", "plain", "file"). Note that the underlying kin-openapi decoder registry is process-global: today all appsec datasources in the same process share the same set of registered body decoders.

func (*AppsecRuntimeConfig) RejectSubmission

func (*AppsecRuntimeConfig) RejectSubmission(state *AppsecRequestState, reason string) error

RejectSubmission flags the in-flight challenge submission so the ProcessOnChallengeRules submit-path branch refuses to issue a cookie and returns bodyChallengeRejected. Exposed ONLY in the on_challenge_submit hook env — calling it from any other phase is a no-op (the field is inspected only at submit time).

The reason string is logged server-side and NOT echoed to the client.

func (*AppsecRuntimeConfig) RemoveInbandRuleByID

func (w *AppsecRuntimeConfig) RemoveInbandRuleByID(state *AppsecRequestState, id int) error

func (*AppsecRuntimeConfig) RemoveInbandRuleByName

func (w *AppsecRuntimeConfig) RemoveInbandRuleByName(state *AppsecRequestState, name string) error

func (*AppsecRuntimeConfig) RemoveInbandRuleByTag

func (w *AppsecRuntimeConfig) RemoveInbandRuleByTag(state *AppsecRequestState, tag string) error

func (*AppsecRuntimeConfig) RemoveOutbandRuleByID

func (w *AppsecRuntimeConfig) RemoveOutbandRuleByID(state *AppsecRequestState, id int) error

func (*AppsecRuntimeConfig) RemoveOutbandRuleByName

func (w *AppsecRuntimeConfig) RemoveOutbandRuleByName(state *AppsecRequestState, name string) error

func (*AppsecRuntimeConfig) RemoveOutbandRuleByTag

func (w *AppsecRuntimeConfig) RemoveOutbandRuleByTag(state *AppsecRequestState, tag string) error

func (*AppsecRuntimeConfig) SendAlert

func (w *AppsecRuntimeConfig) SendAlert(state *AppsecRequestState) error

func (*AppsecRuntimeConfig) SendChallenge

func (w *AppsecRuntimeConfig) SendChallenge(ctx context.Context, state *AppsecRequestState, request *ParsedRequest) error

func (*AppsecRuntimeConfig) SendEvent

func (w *AppsecRuntimeConfig) SendEvent(state *AppsecRequestState) error

func (*AppsecRuntimeConfig) SetAction

func (w *AppsecRuntimeConfig) SetAction(state *AppsecRequestState, action string) error

func (*AppsecRuntimeConfig) SetActionByID

func (w *AppsecRuntimeConfig) SetActionByID(id int, action string) error

func (*AppsecRuntimeConfig) SetActionByName

func (w *AppsecRuntimeConfig) SetActionByName(name string, action string) error

func (*AppsecRuntimeConfig) SetActionByTag

func (w *AppsecRuntimeConfig) SetActionByTag(tag string, action string) error

func (*AppsecRuntimeConfig) SetBodySizeExceededAction added in v1.7.8

func (w *AppsecRuntimeConfig) SetBodySizeExceededAction(action string) error

SetBodySizeExceededAction sets what happens when the body exceeds the maximum size. Valid values: "drop" (block request), "partial" (inspect up to max size), "allow" (skip body inspection). Intended for use in on_load hooks.

func (*AppsecRuntimeConfig) SetChallengeBody

func (w *AppsecRuntimeConfig) SetChallengeBody(state *AppsecRequestState, content string) error

func (*AppsecRuntimeConfig) SetChallengeCookie

func (w *AppsecRuntimeConfig) SetChallengeCookie(state *AppsecRequestState, cookie cookie.AppsecCookie) error

func (*AppsecRuntimeConfig) SetChallengeDifficulty

func (w *AppsecRuntimeConfig) SetChallengeDifficulty(level string) error

SetChallengeDifficulty sets the default PoW difficulty on the runtime (used from on_load).

func (*AppsecRuntimeConfig) SetChallengeDifficultyPerRequest

func (*AppsecRuntimeConfig) SetChallengeDifficultyPerRequest(state *AppsecRequestState, level string) error

SetChallengeDifficultyPerRequest sets a per-request PoW difficulty override (used from pre_eval/post_eval).

func (*AppsecRuntimeConfig) SetChallengeHeader

func (w *AppsecRuntimeConfig) SetChallengeHeader(state *AppsecRequestState, name string, value string) error

func (*AppsecRuntimeConfig) SetHTTPCode

func (w *AppsecRuntimeConfig) SetHTTPCode(state *AppsecRequestState, code int) error

func (*AppsecRuntimeConfig) SetMaxBodySize added in v1.7.8

func (w *AppsecRuntimeConfig) SetMaxBodySize(size int64) error

SetMaxBodySize sets the maximum allowed body size in bytes. Intended for use in on_load hooks.

func (*AppsecRuntimeConfig) ValidateRequestWithSchema added in v1.7.8

func (w *AppsecRuntimeConfig) ValidateRequestWithSchema(ctx context.Context, state *AppsecRequestState, request *ParsedRequest, ref string) bool

ValidateRequestWithSchema validates r against the OpenAPI schema registered under ref. It returns true when the request is valid, false when it is not (or when no schema is registered for ref). On failure, structured error details are published into state.HookVars under the "validation_error*" keys so that subsequent hook expressions (typically the `apply` block of the same hook) can build a drop reason or enrich an event. Each call also increments the AppsecValidationOKCounter / AppsecValidationFailedCounter metric.

type AppsecSubEngineOpts

type AppsecSubEngineOpts struct {
	DisableBodyInspection    bool `yaml:"disable_body_inspection"`
	RequestBodyInMemoryLimit *int `yaml:"request_body_in_memory_limit"`
}

type AppsecTempResponse

type AppsecTempResponse struct {
	InBandInterrupt         bool
	OutOfBandInterrupt      bool
	Action                  string                // allow, deny, captcha, challenge, log
	UserHTTPResponseCode    int                   // The response code to send to the user
	UserHTTPBodyContent     string                // The body content to send to the user, only for challenge response
	UserHTTPCookies         []cookie.AppsecCookie // Raw Set-Cookie headers to send to the user.
	UserHeaders             map[string][]string   // Headers to send to the user
	BouncerHTTPResponseCode int                   // The response code to send to the remediation component
	SendEvent               bool                  // do we send an internal event on rule match
	SendAlert               bool                  // do we send an alert on rule match
}

func (AppsecTempResponse) Clone

type BodyResponse

type BodyResponse struct {
	Action          string              `json:"action"`
	HTTPStatus      int                 `json:"http_status"`
	UserBodyContent string              `json:"user_body_content,omitempty"`
	UserCookies     []string            `json:"user_cookies,omitempty"`
	UserHeaders     map[string][]string `json:"user_headers,omitempty"`
}

type BodySettings added in v1.7.8

type BodySettings struct {
	// MaxSize is the maximum allowed body size in bytes. Defaults to DefaultMaxBodySize (10MB).
	MaxSize int64 `yaml:"max_body_size"`
	// Action controls what happens when a body exceeds MaxSize:
	// "drop" (default) - block the request, "partial" - inspect up to MaxSize bytes, "allow" - skip body inspection.
	Action string `yaml:"body_size_exceeded_action"`
}

BodySettings controls how oversized request bodies are handled.

type ChallengeEventInfo

type ChallengeEventInfo struct {
	Reason      ChallengeReason            // requested | submitted | failed | rejected | solved
	FailReason  string                     // set on failed (raw error message) or rejected (operator-supplied reason)
	FailErr     error                      // set on failed; carries the sentinel-wrapped error so metrics can classify via errors.Is.
	Difficulty  int                        // target PoW difficulty for this moment
	Fingerprint *challenge.FingerprintData // nil when none available (e.g. requested w/o cookie)
}

ChallengeEventInfo describes a single challenge lifecycle moment to be turned into a pipeline.Event by ChallengeEventFromRequest.

type ChallengeReason

type ChallengeReason string

ChallengeReason identifies a challenge lifecycle moment. Its string value is set as the `challenge_event` field on events produced by ChallengeEventFromRequest.

const (
	ChallengeReasonRequested ChallengeReason = "requested" // challenge page served to the client
	ChallengeReasonSubmitted ChallengeReason = "submitted" // a challenge validation attempt was received
	ChallengeReasonFailed    ChallengeReason = "failed"    // submission failed crypto/PoW validation
	ChallengeReasonRejected  ChallengeReason = "rejected"  // submission decoded fine but an on_challenge_submit hook called RejectSubmission
	ChallengeReasonSolved    ChallengeReason = "solved"    // validation passed, cookie issued
)

type ExtendedTransaction

type ExtendedTransaction struct {
	Tx experimental.FullTransaction
}

func NewExtendedTransaction

func NewExtendedTransaction(engine coraza.WAF, uuid string) ExtendedTransaction

func (*ExtendedTransaction) AddGetRequestArgument

func (t *ExtendedTransaction) AddGetRequestArgument(name string, value string)

func (*ExtendedTransaction) AddRequestHeader

func (t *ExtendedTransaction) AddRequestHeader(name string, value string)

func (*ExtendedTransaction) Close added in v1.6.6

func (t *ExtendedTransaction) Close() error

func (*ExtendedTransaction) ID

func (t *ExtendedTransaction) ID() string

func (*ExtendedTransaction) Interrupt added in v1.7.4

func (t *ExtendedTransaction) Interrupt(interruption *types.Interruption)

func (*ExtendedTransaction) Interruption

func (t *ExtendedTransaction) Interruption() *types.Interruption

func (*ExtendedTransaction) IsInterrupted

func (t *ExtendedTransaction) IsInterrupted() bool

func (*ExtendedTransaction) IsRuleEngineOff

func (t *ExtendedTransaction) IsRuleEngineOff() bool

func (*ExtendedTransaction) MatchedRules

func (t *ExtendedTransaction) MatchedRules() []types.MatchedRule

func (*ExtendedTransaction) ProcessConnection

func (t *ExtendedTransaction) ProcessConnection(client string, cPort int, server string, sPort int)

func (*ExtendedTransaction) ProcessLogging

func (t *ExtendedTransaction) ProcessLogging()

func (*ExtendedTransaction) ProcessRequestBody

func (t *ExtendedTransaction) ProcessRequestBody() (*types.Interruption, error)

func (*ExtendedTransaction) ProcessRequestHeaders

func (t *ExtendedTransaction) ProcessRequestHeaders() *types.Interruption

func (*ExtendedTransaction) ProcessURI

func (t *ExtendedTransaction) ProcessURI(uri string, method string, httpVersion string)

func (*ExtendedTransaction) RemoveRuleByIDWithError

func (t *ExtendedTransaction) RemoveRuleByIDWithError(id int) error

func (*ExtendedTransaction) RemoveRuleByTagWithError

func (t *ExtendedTransaction) RemoveRuleByTagWithError(tag string) error

func (*ExtendedTransaction) SetServerName

func (t *ExtendedTransaction) SetServerName(name string)

func (*ExtendedTransaction) Variables

func (*ExtendedTransaction) WriteRequestBody

func (t *ExtendedTransaction) WriteRequestBody(body []byte) (*types.Interruption, int, error)

type Hook

type Hook struct {
	Filter     string      `yaml:"filter"`
	FilterExpr *vm.Program `yaml:"-"`

	OnSuccess string        `yaml:"on_success"`
	Apply     []string      `yaml:"apply"`
	ApplyExpr []*vm.Program `yaml:"-"`
}

func (*Hook) Build

func (h *Hook) Build(ctx context.Context, stage hookStage, patcher *appsecExprPatcher) error

type ParsedRequest

type ParsedRequest struct {
	RemoteAddr           string                  `json:"remote_addr,omitempty"`
	Host                 string                  `json:"host,omitempty"`
	ClientIP             string                  `json:"client_ip,omitempty"`
	URI                  string                  `json:"uri,omitempty"`
	Args                 url.Values              `json:"args,omitempty"`
	ClientHost           string                  `json:"client_host,omitempty"`
	Headers              http.Header             `json:"headers,omitempty"`
	URL                  *url.URL                `json:"url,omitempty"`
	Method               string                  `json:"method,omitempty"`
	Proto                string                  `json:"proto,omitempty"`
	Body                 []byte                  `json:"body,omitempty"`
	TransferEncoding     []string                `json:"transfer_encoding,omitempty"`
	UUID                 string                  `json:"uuid,omitempty"`
	ResponseChannel      chan AppsecTempResponse `json:"-"`
	IsInBand             bool                    `json:"-"`
	IsOutBand            bool                    `json:"-"`
	AppsecEngine         string                  `json:"appsec_engine,omitempty"`
	RemoteAddrNormalized string                  `json:"normalized_remote_addr,omitempty"`
	HTTPRequest          *http.Request           `json:"-"`
	// BodyTruncated is true when the body was larger than the configured limit and was truncated (partial mode).
	BodyTruncated bool `json:"body_truncated,omitempty"`
	// BodySizeExceeded is true when the body exceeded the configured limit and the action is drop.
	// The body is not populated in this case; a fake interruption will be triggered in the runner.
	BodySizeExceeded bool `json:"body_size_exceeded,omitempty"`
}

func NewParsedRequestFromRequest

func NewParsedRequestFromRequest(r *http.Request, logger *log.Entry, bodySettings BodySettings) (ParsedRequest, error)

Generate a ParsedRequest from a http.Request. ParsedRequest can be consumed by the App security Engine. bodySettings controls the maximum body size and what to do when the limit is exceeded.

func (*ParsedRequest) DumpRequest

func (r *ParsedRequest) DumpRequest(params ...any) *ReqDumpFilter

type PhaseHooks added in v1.7.8

type PhaseHooks struct {
	PreEval  []Hook
	PostEval []Hook
	OnMatch  []Hook
}

PhaseHooks bundles the three phase-scoped hook lists (pre_eval, post_eval, on_match) that run during request evaluation. OnLoad is excluded because it runs once at startup and is not phase-scoped.

type ReqDumpFilter

type ReqDumpFilter struct {
	HeadersContentFilters []string
	HeadersNameFilters    []string
	HeadersDrop           bool

	BodyDrop bool

	ArgsContentFilters []string
	ArgsNameFilters    []string
	ArgsDrop           bool
	// contains filtered or unexported fields
}

func (*ReqDumpFilter) FilterArgs

func (r *ReqDumpFilter) FilterArgs(out *ParsedRequest) error

func (*ReqDumpFilter) FilterBody

func (r *ReqDumpFilter) FilterBody(out *ParsedRequest) error

func (*ReqDumpFilter) FilterHeaders

func (r *ReqDumpFilter) FilterHeaders(out *ParsedRequest) error

func (*ReqDumpFilter) GetFilteredRequest

func (r *ReqDumpFilter) GetFilteredRequest() *ParsedRequest

func (*ReqDumpFilter) NoFilters

func (r *ReqDumpFilter) NoFilters() *ReqDumpFilter

clear filters

func (*ReqDumpFilter) ToJSON

func (r *ReqDumpFilter) ToJSON() error

func (*ReqDumpFilter) WithArgsContentFilter

func (r *ReqDumpFilter) WithArgsContentFilter(filter string) *ReqDumpFilter

func (*ReqDumpFilter) WithArgsNameFilter

func (r *ReqDumpFilter) WithArgsNameFilter(filter string) *ReqDumpFilter

func (*ReqDumpFilter) WithBody

func (r *ReqDumpFilter) WithBody() *ReqDumpFilter

func (*ReqDumpFilter) WithEmptyArgsFilters

func (r *ReqDumpFilter) WithEmptyArgsFilters() *ReqDumpFilter

func (*ReqDumpFilter) WithEmptyHeadersFilters

func (r *ReqDumpFilter) WithEmptyHeadersFilters() *ReqDumpFilter

func (*ReqDumpFilter) WithHeaders

func (r *ReqDumpFilter) WithHeaders() *ReqDumpFilter

func (*ReqDumpFilter) WithHeadersContentFilter

func (r *ReqDumpFilter) WithHeadersContentFilter(filter string) *ReqDumpFilter

func (*ReqDumpFilter) WithHeadersNameFilter

func (r *ReqDumpFilter) WithHeadersNameFilter(filter string) *ReqDumpFilter

func (*ReqDumpFilter) WithNoBody

func (r *ReqDumpFilter) WithNoBody() *ReqDumpFilter

func (*ReqDumpFilter) WithNoHeaders

func (r *ReqDumpFilter) WithNoHeaders() *ReqDumpFilter

type RulesDetails

type RulesDetails struct {
	LogLevel log.Level
	Hash     string
	Version  string
	Name     string
}

type SubmissionRejectInfo

type SubmissionRejectInfo struct {
	Reason string
}

SubmissionRejectInfo signals that an on_challenge_submit hook called RejectSubmission to refuse cookie issuance for a cryptographically valid submission. ProcessOnChallengeRules inspects this on the submit-path branch and, if set, serves bodyChallengeRejected with no Set-Cookie.

Directories

Path Synopsis
Package challenge implements the AppSec WAF challenge mode: a PoW-gated landing page, a fingerprint collection bundle, and the surrounding key rotation + cookie machinery.
Package challenge implements the AppSec WAF challenge mode: a PoW-gated landing page, a fingerprint collection bundle, and the surrounding key rotation + cookie machinery.
js
js/cmd/bundle command
js/cmd/initialbundle command
initialbundle is a build-time tool that produces a pre-obfuscated initial challenge bundle, embedded into the Go binary.
initialbundle is a build-time tool that produces a pre-obfuscated initial challenge bundle, embedded into the Go binary.
pb

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL