You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(appsec): keep first redirect hop for redirection derivatives (#4972)
### What does this PR do?
Merges WAF attribute derivatives with **first-write-wins** in `AbsorbDerivatives` so a uniform-status redirect chain reports the **first** hop's `appsec.api.redirection.*` value instead of the last.
It also enables the `APPSEC_RASP_NON_BLOCKING` scenario in the system-tests CI matrix (across all weblog variants, matching `APPSEC_RASP`) so this stays covered.
### Motivation
#4938 moved per-hop downstream request/response WAF evaluation from the persistent request context to per-roundtrip **ephemeral subcontexts**. The persistent context deduplicates attribute generation within a request, so a redirection attribute rule matching the same status (e.g. 302) on every hop produced its derivative only once (the first hop). Ephemeral subcontexts don't share that dedup, so the rule re-fires on every hop and `AbsorbDerivatives`' last-write-wins kept the **final** hop.
This regressed system-tests `Test_API10_redirect_status::test_api10_redirect` on `golang@2.10.0-dev` (bisected to `40280d189`): `appsec.api.redirection.move_target` became `/mirror/200` (last hop) instead of `/redirect?totalRedirects=2` (first hop).
The fix restores the pre-#4938 "first discovery wins" invariant at the derivative merge boundary while preserving #4938's per-hop ephemeral evaluation (RASP SSRF still fires on every hop). The existing blocked-response-schema skip is kept ahead of the new guard.
### Testing
- New `TestAppsecHTTP30XRedirectChainKeepsFirstHop` (uniform-302 chain) — verified it FAILS without the fix and PASSES with it. The existing `TestAppsecHTTP30X` used distinct 308/307 per hop, so it never collided on a single key.
- New `AbsorbDerivatives` unit tests (first-write-wins + blocked-response-schema skip preserved).
- Full `contrib/net/http` appsec suite and `internal/appsec/emitter/waf` package pass; `go vet` clean.
### Reviewer's Checklist
- [x] Changed code has unit tests for its functionality at or near 100% coverage.
- [x] [System-Tests](https://github.com/DataDog/system-tests/) covering this feature have been enabled (`APPSEC_RASP_NON_BLOCKING` added to the CI matrix).
- [ ] There is a benchmark for any new code, or changes to existing code.
- [ ] If this interacts with the agent in a new way, a system test has been added.
- [x] New code is free of linting errors.
- [x] New code doesn't break existing tests.
- [ ] Add an appropriate team label so this PR gets put in the right place for the release notes.
- [x] All generated files are up to date.
- [ ] Non-trivial go.mod changes reviewed by @DataDog/dd-trace-go-guild (no go.mod changes).
Co-authored-by: eliott.bouhana <eliott.bouhana@datadoghq.com>
// 1 handler span + 5 downstream requests (4x 302 + the final 200).
384
+
require.Len(t, spans, 6)
385
+
serviceSpan:=spans[len(spans)-1]
386
+
387
+
assert.Equal(t, "/redirect?totalRedirects=2", serviceSpan.Tags()["appsec.api.redirection.redirect_target"], "redirect_target must reflect the FIRST redirect hop, not the last")
388
+
assert.Equal(t, float64(5), serviceSpan.Tags()["_dd.appsec.downstream_request"], "unexpected or missing _dd.appsec.downstream_request tag")
0 commit comments