> headers = Map.of("x-vercel-mitigated", List.of("challenge"));
+ assertFalse(WafEvasion.isJsChallenge(200, headers, ""),
+ "200 nao deve ser JS challenge mesmo com header de challenge");
+ }
+
+ @Test
+ @Order(22)
+ void isJsChallengeFalseOn200EvenWithBody() {
+ assertFalse(WafEvasion.isJsChallenge(200, Map.of(), "Vercel Security Checkpoint"),
+ "200 com body match nao deve ser JS challenge (status code guard)");
+ }
+
+ // -------------------------------------------------
+ // isJsChallenge -- body-based detection
+ // -------------------------------------------------
+
+ @Test
+ @Order(23)
+ void isJsChallengeVercelBody403() {
+ assertTrue(WafEvasion.isJsChallenge(403, Map.of(), "Vercel Security Checkpoint"),
+ "403 + body 'Vercel Security Checkpoint' deve ser JS challenge");
+ }
+
+ @Test
+ @Order(24)
+ void isJsChallengeCloudflareBodyCfChlOpt503() {
+ assertTrue(WafEvasion.isJsChallenge(503, Map.of(), "test
"),
+ "503 + body com cf_chl_opt deve ser JS challenge");
+ }
+
+ @Test
+ @Order(25)
+ void isJsChallengeCloudflareBodyCfChallengeDash503() {
+ assertTrue(WafEvasion.isJsChallenge(503, Map.of(), ""),
+ "503 + body com cf-challenge- deve ser JS challenge");
+ }
+
+ // -------------------------------------------------
+ // isJsChallenge -- negative / edge cases
+ // -------------------------------------------------
+
+ @Test
+ @Order(26)
+ void isJsChallengeFalse403NoSignals() {
+ assertFalse(WafEvasion.isJsChallenge(403, Map.of(), "Access Denied"),
+ "403 sem sinais de JS challenge nao deve ser detectado");
+ }
+
+ @Test
+ @Order(27)
+ void isJsChallengeNullBodyWithHeader() {
+ Map> headers = Map.of("x-vercel-mitigated", List.of("challenge"));
+ assertTrue(WafEvasion.isJsChallenge(403, headers, null),
+ "403 + vercel header + null body deve ser JS challenge");
+ }
+
+ @Test
+ @Order(28)
+ void isJsChallengeNullBodyNoHeader() {
+ assertFalse(WafEvasion.isJsChallenge(403, Map.of(), null),
+ "403 + no header + null body nao deve ser JS challenge");
+ }
+
+ @Test
+ @Order(29)
+ void isJsChallengeHeaderValueNotChallenge() {
+ Map> headers = Map.of("x-vercel-mitigated", List.of("blocked"));
+ assertFalse(WafEvasion.isJsChallenge(403, headers, ""),
+ "x-vercel-mitigated: blocked (nao 'challenge') nao deve ser JS challenge");
+ }
+
+ // -------------------------------------------------
+ // detectChallengeType
+ // -------------------------------------------------
+
+ @Test
+ @Order(30)
+ void detectChallengeTypeVercel() {
+ Map> headers = Map.of("x-vercel-mitigated", List.of("challenge"));
+ assertEquals("Vercel Security Checkpoint", WafEvasion.detectChallengeType(403, headers, ""),
+ "Deve retornar 'Vercel Security Checkpoint'");
+ }
+
+ @Test
+ @Order(31)
+ void detectChallengeTypeCloudflare() {
+ Map> headers = Map.of("cf-mitigated", List.of("challenge"));
+ assertEquals("Cloudflare Managed Challenge", WafEvasion.detectChallengeType(503, headers, ""),
+ "Deve retornar 'Cloudflare Managed Challenge'");
+ }
+
+ @Test
+ @Order(32)
+ void detectChallengeTypeEmptyOn200() {
+ Map> headers = Map.of("x-vercel-mitigated", List.of("challenge"));
+ assertEquals("", WafEvasion.detectChallengeType(200, headers, "Vercel Security Checkpoint"),
+ "200 deve retornar vazio mesmo com sinais de challenge");
+ }
+
+ @Test
+ @Order(33)
+ void detectChallengeTypeEmptyNoSignals() {
+ assertEquals("", WafEvasion.detectChallengeType(403, Map.of(), "some body"),
+ "403 sem sinais deve retornar vazio");
+ }
+
+ @Test
+ @Order(34)
+ void detectChallengeTypePrioritizesVercel() {
+ Map> headers = Map.of("x-vercel-mitigated", List.of("challenge"), "cf-mitigated",
+ List.of("challenge"));
+ assertEquals("Vercel Security Checkpoint", WafEvasion.detectChallengeType(403, headers, ""),
+ "Com ambos headers, Vercel deve ter prioridade");
+ }
+
+ @Test
+ @Order(35)
+ void detectChallengeTypeByBodyOnly() {
+ assertEquals("Cloudflare Managed Challenge",
+ WafEvasion.detectChallengeType(503, Map.of(), "cf_chl_opt content"),
+ "Body-only Cloudflare detection deve funcionar em 503");
+ }
+
+ // -------------------------------------------------
+ // isJsChallenge <-> detectChallengeType consistency
+ // -------------------------------------------------
+
+ @Test
+ @Order(36)
+ void isJsChallengeConsistentWithDetectType() {
+ Map> vercelHeaders = Map.of("x-vercel-mitigated", List.of("challenge"));
+ Map> cfHeaders = Map.of("cf-mitigated", List.of("challenge"));
+
+ // Every case where detectChallengeType returns non-empty, isJsChallenge must
+ // return true
+ assertTrue(WafEvasion.isJsChallenge(403, vercelHeaders, ""));
+ assertFalse(WafEvasion.detectChallengeType(403, vercelHeaders, "").isEmpty());
+
+ assertTrue(WafEvasion.isJsChallenge(503, cfHeaders, ""));
+ assertFalse(WafEvasion.detectChallengeType(503, cfHeaders, "").isEmpty());
+
+ // Every case where isJsChallenge returns false, detectChallengeType must return
+ // empty
+ assertFalse(WafEvasion.isJsChallenge(200, vercelHeaders, ""));
+ assertTrue(WafEvasion.detectChallengeType(200, vercelHeaders, "").isEmpty());
+ }
+}