mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 03:56:15 -08:00
test(stalker): add authentication replay corpus
This commit is contained in:
1 parent
ffefe48992
commit
79729c4dfc
24 files changed
+5959
-27
No files matched your search
@@ -0,0 +1,236 @@
|
||||
{
|
||||
"description": "Synthetic blocked profile terminates without credential retry.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/c/"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "anonymous-probe",
|
||||
"method": "GET",
|
||||
"operation": "anonymous-probe",
|
||||
"origin": "portal",
|
||||
"path": "/c/",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization",
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"device_id",
|
||||
"device_id2",
|
||||
"mac",
|
||||
"password",
|
||||
"signature",
|
||||
"signature2",
|
||||
"sn",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {},
|
||||
"status": 204
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "handshake",
|
||||
"method": "GET",
|
||||
"operation": "handshake",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"token": {
|
||||
"kind": "generate",
|
||||
"symbol": "session-token",
|
||||
"valueKind": "token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "blocked-profile",
|
||||
"method": "GET",
|
||||
"operation": "blocked-profile",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "0",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"block_msg": "Synthetic blocked profile",
|
||||
"status": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "blocked-profile",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "authentication-blocked-profile",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "mac",
|
||||
"valueKind": "mac"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
{
|
||||
"description": "Synthetic noncanonical do auth success still permits the second profile.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/c/"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "anonymous-probe",
|
||||
"method": "GET",
|
||||
"operation": "anonymous-probe",
|
||||
"origin": "portal",
|
||||
"path": "/c/",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization",
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"device_id",
|
||||
"device_id2",
|
||||
"mac",
|
||||
"password",
|
||||
"signature",
|
||||
"signature2",
|
||||
"sn",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {},
|
||||
"status": 204
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "handshake",
|
||||
"method": "GET",
|
||||
"operation": "handshake",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"token": {
|
||||
"kind": "generate",
|
||||
"symbol": "session-token",
|
||||
"valueKind": "token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "profile-first",
|
||||
"method": "GET",
|
||||
"operation": "profile-first",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "0",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"status": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "do-auth-noncanonical",
|
||||
"method": "GET",
|
||||
"operation": "do-auth-noncanonical",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "do_auth",
|
||||
"login": {
|
||||
"kind": "ref",
|
||||
"symbol": "username"
|
||||
},
|
||||
"password": {
|
||||
"kind": "ref",
|
||||
"symbol": "password"
|
||||
},
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": 1
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "noncanonical-do-auth",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "authentication-noncanonical-do-auth",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "mac",
|
||||
"valueKind": "mac"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "username",
|
||||
"valueKind": "credential"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "password",
|
||||
"valueKind": "credential"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
{
|
||||
"description": "Synthetic rejected saved credentials yield a fresh challenge whose credentials can complete profile step two.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/c/"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "anonymous-probe",
|
||||
"method": "GET",
|
||||
"operation": "anonymous-probe",
|
||||
"origin": "portal",
|
||||
"path": "/c/",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization",
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"device_id",
|
||||
"device_id2",
|
||||
"mac",
|
||||
"password",
|
||||
"signature",
|
||||
"signature2",
|
||||
"sn",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {},
|
||||
"status": 204
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "handshake",
|
||||
"method": "GET",
|
||||
"operation": "handshake",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"token": {
|
||||
"kind": "generate",
|
||||
"symbol": "session-token",
|
||||
"valueKind": "token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "profile-first",
|
||||
"method": "GET",
|
||||
"operation": "profile-first",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "0",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"status": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "saved-do-auth",
|
||||
"method": "GET",
|
||||
"operation": "saved-do-auth",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "do_auth",
|
||||
"login": {
|
||||
"kind": "ref",
|
||||
"symbol": "saved-username"
|
||||
},
|
||||
"password": {
|
||||
"kind": "ref",
|
||||
"symbol": "saved-password"
|
||||
},
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": false
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "fresh-do-auth",
|
||||
"method": "GET",
|
||||
"operation": "fresh-do-auth",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "do_auth",
|
||||
"login": {
|
||||
"kind": "ref",
|
||||
"symbol": "fresh-username"
|
||||
},
|
||||
"password": {
|
||||
"kind": "ref",
|
||||
"symbol": "fresh-password"
|
||||
},
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": true
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "profile-second",
|
||||
"method": "GET",
|
||||
"operation": "profile-second",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "1",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"id": 1,
|
||||
"status": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "saved-rejected-fresh",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "authentication-saved-rejected-fresh",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "mac",
|
||||
"valueKind": "mac"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "saved-username",
|
||||
"valueKind": "credential"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "saved-password",
|
||||
"valueKind": "credential"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "fresh-username",
|
||||
"valueKind": "credential"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "fresh-password",
|
||||
"valueKind": "credential"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
{
|
||||
"description": "Synthetic status two profile requires do auth before the second profile.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/c/"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "anonymous-probe",
|
||||
"method": "GET",
|
||||
"operation": "anonymous-probe",
|
||||
"origin": "portal",
|
||||
"path": "/c/",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization",
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"device_id",
|
||||
"device_id2",
|
||||
"mac",
|
||||
"password",
|
||||
"signature",
|
||||
"signature2",
|
||||
"sn",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {},
|
||||
"status": 204
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "handshake",
|
||||
"method": "GET",
|
||||
"operation": "handshake",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"random": {
|
||||
"kind": "generate",
|
||||
"symbol": "challenge",
|
||||
"valueKind": "random"
|
||||
},
|
||||
"token": {
|
||||
"kind": "generate",
|
||||
"symbol": "session-token",
|
||||
"valueKind": "token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "profile-first",
|
||||
"method": "GET",
|
||||
"operation": "profile-first",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "0",
|
||||
"random": {
|
||||
"kind": "ref",
|
||||
"symbol": "challenge"
|
||||
},
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"status": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "do-auth",
|
||||
"method": "GET",
|
||||
"operation": "do-auth",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "do_auth",
|
||||
"login": {
|
||||
"kind": "ref",
|
||||
"symbol": "username"
|
||||
},
|
||||
"password": {
|
||||
"kind": "ref",
|
||||
"symbol": "password"
|
||||
},
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": true
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "profile-second",
|
||||
"method": "GET",
|
||||
"operation": "profile-second",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "1",
|
||||
"random": {
|
||||
"kind": "ref",
|
||||
"symbol": "challenge"
|
||||
},
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"status": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "conditional-second-step",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "authentication-status2-second-step",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "mac",
|
||||
"valueKind": "mac"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "username",
|
||||
"valueKind": "credential"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "password",
|
||||
"valueKind": "credential"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
{
|
||||
"description": "Synthetic credentials are rejected exactly three times before the connection attempt ends.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/c/"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "anonymous-probe",
|
||||
"method": "GET",
|
||||
"operation": "anonymous-probe",
|
||||
"origin": "portal",
|
||||
"path": "/c/",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization",
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"device_id",
|
||||
"device_id2",
|
||||
"mac",
|
||||
"password",
|
||||
"signature",
|
||||
"signature2",
|
||||
"sn",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {},
|
||||
"status": 204
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "handshake",
|
||||
"method": "GET",
|
||||
"operation": "handshake",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"token": {
|
||||
"kind": "generate",
|
||||
"symbol": "session-token",
|
||||
"valueKind": "token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "profile-status-two",
|
||||
"method": "GET",
|
||||
"operation": "profile-status-two",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "0",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"status": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "credential-attempt-one",
|
||||
"method": "GET",
|
||||
"operation": "credential-attempt-one",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "do_auth",
|
||||
"login": {
|
||||
"kind": "ref",
|
||||
"symbol": "username-one"
|
||||
},
|
||||
"password": {
|
||||
"kind": "ref",
|
||||
"symbol": "password-one"
|
||||
},
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": false
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "credential-attempt-two",
|
||||
"method": "GET",
|
||||
"operation": "credential-attempt-two",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "do_auth",
|
||||
"login": {
|
||||
"kind": "ref",
|
||||
"symbol": "username-two"
|
||||
},
|
||||
"password": {
|
||||
"kind": "ref",
|
||||
"symbol": "password-two"
|
||||
},
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": false
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "credential-attempt-three",
|
||||
"method": "GET",
|
||||
"operation": "credential-attempt-three",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "do_auth",
|
||||
"login": {
|
||||
"kind": "ref",
|
||||
"symbol": "username-three"
|
||||
},
|
||||
"password": {
|
||||
"kind": "ref",
|
||||
"symbol": "password-three"
|
||||
},
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": false
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "three-attempt-limit",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "authentication-three-attempt-limit",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "mac",
|
||||
"valueKind": "mac"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "username-one",
|
||||
"valueKind": "credential"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "password-one",
|
||||
"valueKind": "credential"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "username-two",
|
||||
"valueKind": "credential"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "password-two",
|
||||
"valueKind": "credential"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "username-three",
|
||||
"valueKind": "credential"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "password-three",
|
||||
"valueKind": "credential"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"description": "Synthetic HTTP 200 body denial does not imply token invalidation.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "body-denial",
|
||||
"method": "GET",
|
||||
"operation": "body-denial",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"error": "Access denied"
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "body-denial",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "classifier-access-denied-200",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"description": "Synthetic response crosses the session response-size classifier bound.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "oversized-response",
|
||||
"method": "GET",
|
||||
"operation": "oversized-response",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"byte": 97,
|
||||
"byteLength": 4194305,
|
||||
"kind": "generated"
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/octet-stream"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "oversized-response",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "classifier-oversized-response",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"description": "Synthetic HTTP 429 response preserves bounded retry evidence.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "rate-limit",
|
||||
"method": "GET",
|
||||
"operation": "rate-limit",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"error": "synthetic-rate-limit"
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
],
|
||||
"retry-after": [
|
||||
"3"
|
||||
]
|
||||
},
|
||||
"status": 429
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "rate-limit",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "classifier-rate-limit-429",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"description": "Synthetic HTTP 503 response is a retryable service failure.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "service-unavailable",
|
||||
"method": "GET",
|
||||
"operation": "service-unavailable",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"error": "synthetic-unavailable"
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 503
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "service-unavailable",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "classifier-service-unavailable-503",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"description": "Synthetic HTTP 403 WAF response is distinct from token rejection.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "waf-response",
|
||||
"method": "GET",
|
||||
"operation": "waf-response",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "text",
|
||||
"value": "Synthetic Web Application Firewall challenge"
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"text/html"
|
||||
]
|
||||
},
|
||||
"status": 403
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "waf-response",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "classifier-waf-403",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
{
|
||||
"description": "Synthetic server cookie cannot shadow the manager-owned MAC through a narrower path.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/shadow"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/managed"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "shadow-cookie",
|
||||
"method": "GET",
|
||||
"operation": "shadow-cookie",
|
||||
"origin": "portal",
|
||||
"path": "/shadow",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "managed-mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"status": "synthetic-managed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"set-cookie": [
|
||||
{
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "mac="
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "shadow-mac"
|
||||
},
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "; Path=/shadow"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "shadow-cookie",
|
||||
"nextState": "managed-cookie",
|
||||
"state": "start"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "managed-cookie",
|
||||
"method": "GET",
|
||||
"operation": "managed-cookie",
|
||||
"origin": "portal",
|
||||
"path": "/managed",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "managed-mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {},
|
||||
"status": 204
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "managed-cookie",
|
||||
"nextState": "complete",
|
||||
"state": "managed-cookie"
|
||||
}
|
||||
],
|
||||
"scenarioId": "cookies-managed-shadow",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "managed-mac",
|
||||
"valueKind": "mac"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "shadow-mac",
|
||||
"valueKind": "mac"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
{
|
||||
"description": "Synthetic cookie remains isolated to its issuing origin.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/issue"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/return"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"auth": {},
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "portal-cookie",
|
||||
"method": "GET",
|
||||
"operation": "portal-cookie",
|
||||
"origin": "portal",
|
||||
"path": "/issue",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {
|
||||
"location": [
|
||||
{
|
||||
"kind": "origin-url",
|
||||
"origin": "auth",
|
||||
"path": "/check"
|
||||
}
|
||||
],
|
||||
"set-cookie": [
|
||||
{
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "session="
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "portal-session"
|
||||
},
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "; Path=/; HttpOnly"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": 302
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "portal-cookie",
|
||||
"nextState": "cross-origin",
|
||||
"state": "start"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "cross-origin",
|
||||
"method": "GET",
|
||||
"operation": "cross-origin",
|
||||
"origin": "auth",
|
||||
"path": "/check",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {
|
||||
"location": [
|
||||
{
|
||||
"kind": "origin-url",
|
||||
"origin": "portal",
|
||||
"path": "/return"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": 302
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "cross-origin",
|
||||
"nextState": "return",
|
||||
"state": "cross-origin"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "return",
|
||||
"method": "GET",
|
||||
"operation": "return",
|
||||
"origin": "portal",
|
||||
"path": "/return",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"session": {
|
||||
"kind": "ref",
|
||||
"symbol": "portal-session"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {},
|
||||
"status": 204
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "return",
|
||||
"nextState": "complete",
|
||||
"state": "return"
|
||||
}
|
||||
],
|
||||
"scenarioId": "cookies-session-isolation",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "portal-session",
|
||||
"valueKind": "cookie"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"description": "Synthetic redirect uses typed generated authentication query fields.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/entry"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/entry"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"auth": {},
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "typed-auth-query",
|
||||
"method": "GET",
|
||||
"operation": "typed-auth-query",
|
||||
"origin": "portal",
|
||||
"path": "/entry",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization",
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"username",
|
||||
"password",
|
||||
"token"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {
|
||||
"location": [
|
||||
{
|
||||
"kind": "origin-url",
|
||||
"origin": "auth",
|
||||
"path": "/login",
|
||||
"query": {
|
||||
"auth": {
|
||||
"kind": "generate",
|
||||
"symbol": "redirect-auth",
|
||||
"valueKind": "token"
|
||||
},
|
||||
"login": {
|
||||
"kind": "ref",
|
||||
"symbol": "username"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": 302
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "typed-auth-query",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "redirect-auth-query",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "username",
|
||||
"valueKind": "credential"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
{
|
||||
"description": "Synthetic redirect pauses identity until landing approval completes.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/entry"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "landing",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"landing": {},
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "anonymous-redirect",
|
||||
"method": "GET",
|
||||
"operation": "anonymous-redirect",
|
||||
"origin": "portal",
|
||||
"path": "/entry",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization",
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"token"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {
|
||||
"location": [
|
||||
{
|
||||
"kind": "origin-url",
|
||||
"origin": "landing",
|
||||
"path": "/approve"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": 302
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "anonymous-redirect",
|
||||
"nextState": "paused",
|
||||
"state": "start"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "approval",
|
||||
"method": "GET",
|
||||
"operation": "approval",
|
||||
"origin": "landing",
|
||||
"path": "/approve",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization",
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"token"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {},
|
||||
"status": 204
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "approval",
|
||||
"nextState": "identity-ready",
|
||||
"state": "paused"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "identity-request",
|
||||
"method": "GET",
|
||||
"operation": "identity-request",
|
||||
"origin": "landing",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"token"
|
||||
],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"status": "synthetic-ready"
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "identity-request",
|
||||
"nextState": "complete",
|
||||
"state": "identity-ready"
|
||||
}
|
||||
],
|
||||
"scenarioId": "redirect-identity-pause",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "mac",
|
||||
"valueKind": "mac"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
{
|
||||
"description": "Synthetic cross-origin landing requires explicit approval before use.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/c/"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "landing",
|
||||
"path": "/approve"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"landing": {},
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "landing-redirect",
|
||||
"method": "GET",
|
||||
"operation": "landing-redirect",
|
||||
"origin": "portal",
|
||||
"path": "/c/",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization",
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"token",
|
||||
"username",
|
||||
"password"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {
|
||||
"location": [
|
||||
{
|
||||
"kind": "origin-url",
|
||||
"origin": "landing",
|
||||
"path": "/approve"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": 302
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "landing-redirect",
|
||||
"nextState": "approval",
|
||||
"state": "start"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "approved-landing",
|
||||
"method": "GET",
|
||||
"operation": "approved-landing",
|
||||
"origin": "landing",
|
||||
"path": "/approve",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization",
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"token",
|
||||
"username",
|
||||
"password"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"approval": "synthetic-required"
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "approved-landing",
|
||||
"nextState": "complete",
|
||||
"state": "approval"
|
||||
}
|
||||
],
|
||||
"scenarioId": "redirect-landing-approval",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"description": "Synthetic redirect carries typed user-info for fail-closed review.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/entry"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/entry"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"auth": {},
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "credential-bearing-redirect",
|
||||
"method": "GET",
|
||||
"operation": "credential-bearing-redirect",
|
||||
"origin": "portal",
|
||||
"path": "/entry",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization",
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"username",
|
||||
"password",
|
||||
"token"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {
|
||||
"location": [
|
||||
{
|
||||
"kind": "origin-url",
|
||||
"origin": "auth",
|
||||
"path": "/login",
|
||||
"query": {
|
||||
"continue": "synthetic"
|
||||
},
|
||||
"userInfo": {
|
||||
"password": {
|
||||
"kind": "ref",
|
||||
"symbol": "password"
|
||||
},
|
||||
"username": {
|
||||
"kind": "ref",
|
||||
"symbol": "username"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": 302
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "credential-bearing-redirect",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "redirect-malicious-user-info",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "username",
|
||||
"valueKind": "credential"
|
||||
},
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "password",
|
||||
"valueKind": "credential"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"description": "Synthetic repeated redirect reaches the bounded loop ceiling.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/loop"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/loop"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 3,
|
||||
"min": 3
|
||||
},
|
||||
"id": "bounded-loop",
|
||||
"method": "GET",
|
||||
"operation": "bounded-loop",
|
||||
"origin": "portal",
|
||||
"path": "/loop",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {
|
||||
"location": [
|
||||
"/loop"
|
||||
]
|
||||
},
|
||||
"status": 302
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "bounded-loop",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "redirect-repeated-loop",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,654 @@
|
||||
{
|
||||
"description": "Synthetic concurrent token rejections share one refresh and retry both operations exactly once.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "bootstrap",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "initial-handshake",
|
||||
"method": "GET",
|
||||
"operation": "initial-handshake",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"token": {
|
||||
"kind": "generate",
|
||||
"symbol": "old-token",
|
||||
"valueKind": "token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "initial-profile",
|
||||
"method": "GET",
|
||||
"operation": "initial-profile",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "old-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "0",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"id": 1,
|
||||
"status": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "initial-session",
|
||||
"nextState": "start",
|
||||
"state": "bootstrap"
|
||||
},
|
||||
{
|
||||
"barrier": {
|
||||
"name": "both-rejected",
|
||||
"releaseWhenMatched": 2
|
||||
},
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "first-rejection",
|
||||
"method": "GET",
|
||||
"operation": "first-rejection",
|
||||
"origin": "portal",
|
||||
"path": "/one",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "old-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_ordered_list",
|
||||
"type": "itv"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"error": "Authorization failed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 401
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "second-rejection",
|
||||
"method": "GET",
|
||||
"operation": "second-rejection",
|
||||
"origin": "portal",
|
||||
"path": "/two",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "old-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_epg_info",
|
||||
"type": "itv"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"error": "Authorization failed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 401
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "unordered",
|
||||
"name": "concurrent-rejections",
|
||||
"nextState": "refresh",
|
||||
"state": "start"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "refresh-handshake",
|
||||
"method": "GET",
|
||||
"operation": "refresh-handshake",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"token": {
|
||||
"kind": "generate",
|
||||
"symbol": "new-token",
|
||||
"valueKind": "token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "refresh-profile",
|
||||
"method": "GET",
|
||||
"operation": "refresh-profile",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "new-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "0",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"id": 1,
|
||||
"status": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "single-refresh",
|
||||
"nextState": "retry",
|
||||
"state": "refresh"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "first-retry",
|
||||
"method": "GET",
|
||||
"operation": "first-retry",
|
||||
"origin": "portal",
|
||||
"path": "/one",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "new-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_ordered_list",
|
||||
"type": "itv"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"data": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "second-retry",
|
||||
"method": "GET",
|
||||
"operation": "second-retry",
|
||||
"origin": "portal",
|
||||
"path": "/two",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "new-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_epg_info",
|
||||
"type": "itv"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"data": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "unordered",
|
||||
"name": "retried-operations",
|
||||
"nextState": "complete",
|
||||
"state": "retry"
|
||||
}
|
||||
],
|
||||
"scenarioId": "refresh-concurrent-rejections",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "mac",
|
||||
"valueKind": "mac"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,500 @@
|
||||
{
|
||||
"description": "Synthetic token rejection triggers one refresh and one terminally rejected retry without recursion.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "bootstrap",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "initial-handshake",
|
||||
"method": "GET",
|
||||
"operation": "initial-handshake",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"token": {
|
||||
"kind": "generate",
|
||||
"symbol": "old-token",
|
||||
"valueKind": "token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "initial-profile",
|
||||
"method": "GET",
|
||||
"operation": "initial-profile",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "old-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "0",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"id": 1,
|
||||
"status": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "initial-session",
|
||||
"nextState": "start",
|
||||
"state": "bootstrap"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "initial-rejection",
|
||||
"method": "GET",
|
||||
"operation": "initial-rejection",
|
||||
"origin": "portal",
|
||||
"path": "/catalog",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "old-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_ordered_list",
|
||||
"type": "itv"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"error": "Authorization failed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 401
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "initial-rejection",
|
||||
"nextState": "refresh",
|
||||
"state": "start"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "refresh-handshake",
|
||||
"method": "GET",
|
||||
"operation": "refresh-handshake",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"token": {
|
||||
"kind": "generate",
|
||||
"symbol": "new-token",
|
||||
"valueKind": "token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "refresh-profile",
|
||||
"method": "GET",
|
||||
"operation": "refresh-profile",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "new-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "0",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"id": 1,
|
||||
"status": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "single-refresh",
|
||||
"nextState": "retry",
|
||||
"state": "refresh"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "terminal-rejection",
|
||||
"method": "GET",
|
||||
"operation": "terminal-rejection",
|
||||
"origin": "portal",
|
||||
"path": "/catalog",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "new-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_ordered_list",
|
||||
"type": "itv"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"error": "Authorization failed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 401
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "terminal-retry",
|
||||
"nextState": "complete",
|
||||
"state": "retry"
|
||||
}
|
||||
],
|
||||
"scenarioId": "refresh-exhausted-retry",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "mac",
|
||||
"valueKind": "mac"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"description": "Synthetic direct load endpoint completes handshake and profile through allowlisted JSONP envelopes.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/server/load.php"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/server/load.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "direct-load-handshake",
|
||||
"method": "GET",
|
||||
"operation": "direct-load-handshake",
|
||||
"origin": "portal",
|
||||
"path": "/server/load.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"JsHttpRequest": "1-xml",
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"callback": "callback",
|
||||
"kind": "jsonp",
|
||||
"value": {
|
||||
"js": {
|
||||
"token": {
|
||||
"kind": "generate",
|
||||
"symbol": "session-token",
|
||||
"valueKind": "token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/javascript"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "direct-load-profile",
|
||||
"method": "GET",
|
||||
"operation": "direct-load-profile",
|
||||
"origin": "portal",
|
||||
"path": "/server/load.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"JsHttpRequest": "1-xml",
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "0",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"callback": "callback",
|
||||
"kind": "jsonp",
|
||||
"value": {
|
||||
"js": {
|
||||
"id": 1,
|
||||
"status": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/javascript"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "direct-load",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "resolver-direct-load-jsonp",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "mac",
|
||||
"valueKind": "mac"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
{
|
||||
"description": "Synthetic direct portal endpoint completes a handshake and recognized statusless profile.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "direct-handshake",
|
||||
"method": "GET",
|
||||
"operation": "direct-handshake",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"token": {
|
||||
"kind": "generate",
|
||||
"symbol": "session-token",
|
||||
"valueKind": "token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "statusless-profile",
|
||||
"method": "GET",
|
||||
"operation": "statusless-profile",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "0",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"id": 7,
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "direct-portal",
|
||||
"nextState": "complete",
|
||||
"state": "start"
|
||||
}
|
||||
],
|
||||
"scenarioId": "resolver-direct-portal-statusless",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "mac",
|
||||
"valueKind": "mac"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"description": "Synthetic stale learned route falls back to safe rediscovery.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/learned/load.php"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/portal.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "learned-route",
|
||||
"method": "GET",
|
||||
"operation": "learned-route",
|
||||
"origin": "portal",
|
||||
"path": "/learned/load.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"error": "synthetic-missing"
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "learned-route",
|
||||
"nextState": "rediscover",
|
||||
"state": "start"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "rediscovered-route",
|
||||
"method": "GET",
|
||||
"operation": "rediscovered-route",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"status": "synthetic-ready"
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "rediscovered-route",
|
||||
"nextState": "complete",
|
||||
"state": "rediscover"
|
||||
}
|
||||
],
|
||||
"scenarioId": "resolver-learned-rediscovery",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
{
|
||||
"description": "Synthetic root discovery records an early stateless candidate but selects the later full-session endpoint.",
|
||||
"entry": {
|
||||
"origin": "portal",
|
||||
"path": "/"
|
||||
},
|
||||
"expectedEndpoint": {
|
||||
"origin": "portal",
|
||||
"path": "/stalker_portal/server/load.php"
|
||||
},
|
||||
"failOnUnexpectedRequest": true,
|
||||
"initialState": "start",
|
||||
"origins": {
|
||||
"portal": {}
|
||||
},
|
||||
"phases": [
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "root-probe",
|
||||
"method": "GET",
|
||||
"operation": "root-probe",
|
||||
"origin": "portal",
|
||||
"path": "/",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"mac",
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization",
|
||||
"cookie"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"device_id",
|
||||
"device_id2",
|
||||
"mac",
|
||||
"password",
|
||||
"signature",
|
||||
"signature2",
|
||||
"sn",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "empty"
|
||||
},
|
||||
"headers": {},
|
||||
"status": 204
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "root-probe",
|
||||
"nextState": "stateless-candidate",
|
||||
"state": "start"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "stateless-capability",
|
||||
"method": "GET",
|
||||
"operation": "stateless-capability",
|
||||
"origin": "portal",
|
||||
"path": "/portal.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_genres",
|
||||
"type": "itv"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": [
|
||||
{
|
||||
"id": "1",
|
||||
"title": "Synthetic"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "stateless-candidate",
|
||||
"nextState": "full-candidate",
|
||||
"state": "stateless-candidate"
|
||||
},
|
||||
{
|
||||
"expectations": [
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "full-handshake",
|
||||
"method": "GET",
|
||||
"operation": "full-handshake",
|
||||
"origin": "portal",
|
||||
"path": "/stalker_portal/server/load.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [
|
||||
"authorization"
|
||||
],
|
||||
"exact": {},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"token",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "handshake",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"token": {
|
||||
"kind": "generate",
|
||||
"symbol": "full-token",
|
||||
"valueKind": "token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"cardinality": {
|
||||
"max": 1,
|
||||
"min": 1
|
||||
},
|
||||
"id": "full-profile",
|
||||
"method": "GET",
|
||||
"operation": "full-profile",
|
||||
"origin": "portal",
|
||||
"path": "/stalker_portal/server/load.php",
|
||||
"request": {
|
||||
"body": {
|
||||
"kind": "absent"
|
||||
},
|
||||
"cookies": {
|
||||
"absent": [
|
||||
"session"
|
||||
],
|
||||
"attributes": {},
|
||||
"exact": {
|
||||
"mac": {
|
||||
"kind": "ref",
|
||||
"symbol": "mac"
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"headers": {
|
||||
"absent": [],
|
||||
"exact": {
|
||||
"authorization": {
|
||||
"kind": "parts",
|
||||
"parts": [
|
||||
{
|
||||
"kind": "literal",
|
||||
"value": "Bearer "
|
||||
},
|
||||
{
|
||||
"kind": "ref",
|
||||
"symbol": "full-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"present": []
|
||||
},
|
||||
"query": {
|
||||
"absent": [
|
||||
"password",
|
||||
"username"
|
||||
],
|
||||
"exact": {
|
||||
"action": "get_profile",
|
||||
"auth_second_step": "0",
|
||||
"type": "stb"
|
||||
},
|
||||
"present": []
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"body": {
|
||||
"kind": "json",
|
||||
"value": {
|
||||
"js": {
|
||||
"id": 1,
|
||||
"status": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"content-type": [
|
||||
"application/json"
|
||||
]
|
||||
},
|
||||
"status": 200
|
||||
}
|
||||
}
|
||||
],
|
||||
"mode": "ordered",
|
||||
"name": "full-candidate",
|
||||
"nextState": "complete",
|
||||
"state": "full-candidate"
|
||||
}
|
||||
],
|
||||
"scenarioId": "resolver-root-full-wins",
|
||||
"schemaVersion": 1,
|
||||
"symbols": [
|
||||
{
|
||||
"kind": "generate",
|
||||
"symbol": "mac",
|
||||
"valueKind": "mac"
|
||||
}
|
||||
],
|
||||
"terminalState": "complete"
|
||||
}
|
||||
@@ -1,14 +1,69 @@
|
||||
/* eslint-disable max-lines -- The committed corpus matrix and its typed deterministic driver form one auditable contract. */
|
||||
import { readdirSync, readFileSync } from 'node:fs';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { createReplayRun, type ReplayRun } from './replay-run.js';
|
||||
import { parseReplayFixtureText } from './replay-schema.js';
|
||||
import type { ReplayObservedRequest } from './replay.types.js';
|
||||
import type {
|
||||
ReplayExpectation,
|
||||
ReplayFixtureV1,
|
||||
ReplayGenerateNode,
|
||||
ReplayLiteralPart,
|
||||
ReplayObservedCookie,
|
||||
ReplayObservedRequest,
|
||||
ReplayPartsNode,
|
||||
ReplayRefNode,
|
||||
ReplayRenderedResponse,
|
||||
ReplayResponseHeaderValue,
|
||||
ReplayTemplateString,
|
||||
ReplayTemplateValue,
|
||||
} from './replay.types.js';
|
||||
|
||||
const FIXTURE_ROOT = resolve(
|
||||
process.cwd(),
|
||||
'apps/stalker-mock-server/fixtures/replay'
|
||||
);
|
||||
|
||||
const EXPECTED_SCENARIOS = [
|
||||
'authentication-blocked-profile',
|
||||
'authentication-noncanonical-do-auth',
|
||||
'authentication-saved-rejected-fresh',
|
||||
'authentication-status2-second-step',
|
||||
'authentication-three-attempt-limit',
|
||||
'classifier-access-denied-200',
|
||||
'classifier-oversized-response',
|
||||
'classifier-rate-limit-429',
|
||||
'classifier-service-unavailable-503',
|
||||
'classifier-waf-403',
|
||||
'cookies-managed-shadow',
|
||||
'cookies-session-isolation',
|
||||
'redirect-auth-query',
|
||||
'redirect-identity-pause',
|
||||
'redirect-landing-approval',
|
||||
'redirect-malicious-user-info',
|
||||
'redirect-repeated-loop',
|
||||
'refresh-concurrent-rejections',
|
||||
'refresh-exhausted-retry',
|
||||
'resolver-direct-load-jsonp',
|
||||
'resolver-direct-portal-statusless',
|
||||
'resolver-learned-rediscovery',
|
||||
'resolver-root-full-wins',
|
||||
'resolver-root-landing',
|
||||
] as const;
|
||||
|
||||
const RESERVED_EARLY_QUERY_FIELDS = [
|
||||
'device_id',
|
||||
'device_id2',
|
||||
'mac',
|
||||
'password',
|
||||
'signature',
|
||||
'signature2',
|
||||
'sn',
|
||||
'token',
|
||||
'username',
|
||||
] as const;
|
||||
const RESERVED_EARLY_HEADERS = ['authorization', 'cookie'] as const;
|
||||
const RESERVED_EARLY_COOKIES = ['mac', 'session'] as const;
|
||||
|
||||
function collectFixturePaths(directory: string): string[] {
|
||||
return readdirSync(directory, { withFileTypes: true })
|
||||
.sort((left, right) => compareCodeUnits(left.name, right.name))
|
||||
@@ -24,48 +79,554 @@ function collectFixturePaths(directory: string): string[] {
|
||||
});
|
||||
}
|
||||
|
||||
function emptyGet(path: string): ReplayObservedRequest {
|
||||
function resolveString(
|
||||
value: ReplayTemplateString,
|
||||
bindings: ReadonlyMap<string, string>
|
||||
): string {
|
||||
if (typeof value === 'string') {
|
||||
return value;
|
||||
}
|
||||
if (value.kind === 'ref') {
|
||||
const resolved = bindings.get(value.symbol);
|
||||
if (resolved === undefined) {
|
||||
throw new Error(`Fixture driver symbol missing: ${value.symbol}.`);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
if (value.kind === 'parts') {
|
||||
return value.parts
|
||||
.map((part) =>
|
||||
part.kind === 'literal'
|
||||
? part.value
|
||||
: resolveString(part, bindings)
|
||||
)
|
||||
.join('');
|
||||
}
|
||||
throw new Error(
|
||||
`Fixture request cannot generate symbol: ${value.symbol}.`
|
||||
);
|
||||
}
|
||||
|
||||
function templateNodeKind(value: object): unknown {
|
||||
return (value as { kind?: unknown }).kind;
|
||||
}
|
||||
|
||||
function resolveValue(
|
||||
value: ReplayTemplateValue,
|
||||
bindings: ReadonlyMap<string, string>
|
||||
): unknown {
|
||||
if (
|
||||
value === null ||
|
||||
typeof value === 'boolean' ||
|
||||
typeof value === 'number' ||
|
||||
typeof value === 'string'
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => resolveValue(item, bindings));
|
||||
}
|
||||
const kind = templateNodeKind(value);
|
||||
if (kind === 'ref' || kind === 'parts' || kind === 'generate') {
|
||||
return resolveString(value as ReplayTemplateString, bindings);
|
||||
}
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).map(([key, child]) => [
|
||||
key,
|
||||
resolveValue(child, bindings),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
function resolveRepeatedFields(
|
||||
exact: ReplayExpectation['request']['query']['exact'],
|
||||
present: readonly string[],
|
||||
bindings: ReadonlyMap<string, string>
|
||||
): Record<string, string[]> {
|
||||
const fields = Object.create(null) as Record<string, string[]>;
|
||||
for (const [name, value] of Object.entries(exact)) {
|
||||
const values = Array.isArray(value) ? value : [value];
|
||||
fields[name] = values.map((item) =>
|
||||
resolveString(item, bindings)
|
||||
);
|
||||
}
|
||||
for (const name of present) {
|
||||
fields[name] ??= ['synthetic-present'];
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
function observedRequest(
|
||||
expectation: ReplayExpectation,
|
||||
bindings: ReadonlyMap<string, string>
|
||||
): ReplayObservedRequest {
|
||||
const cookies = Object.create(null) as Record<
|
||||
string,
|
||||
ReplayObservedCookie
|
||||
>;
|
||||
for (const [name, value] of Object.entries(
|
||||
expectation.request.cookies.exact
|
||||
)) {
|
||||
const attributes = expectation.request.cookies.attributes[name];
|
||||
cookies[name] = {
|
||||
value: resolveString(value, bindings),
|
||||
...(attributes === undefined
|
||||
? {}
|
||||
: {
|
||||
attributes: Object.fromEntries(
|
||||
Object.entries(attributes).map(
|
||||
([attribute, attributeValue]) => [
|
||||
attribute,
|
||||
typeof attributeValue === 'object' ||
|
||||
typeof attributeValue === 'string'
|
||||
? resolveString(
|
||||
attributeValue as ReplayTemplateString,
|
||||
bindings
|
||||
)
|
||||
: attributeValue,
|
||||
]
|
||||
)
|
||||
),
|
||||
}),
|
||||
};
|
||||
}
|
||||
for (const name of expectation.request.cookies.present) {
|
||||
cookies[name] ??= { value: 'synthetic-present' };
|
||||
}
|
||||
|
||||
const matcher = expectation.request.body;
|
||||
const body: ReplayObservedRequest['body'] =
|
||||
matcher.kind === 'absent'
|
||||
? { kind: 'absent' }
|
||||
: matcher.kind === 'text'
|
||||
? {
|
||||
kind: 'text',
|
||||
value: resolveString(matcher.exact, bindings),
|
||||
}
|
||||
: matcher.kind === 'form'
|
||||
? {
|
||||
kind: 'form',
|
||||
value: Object.fromEntries([
|
||||
...Object.entries(matcher.exact).map(
|
||||
([name, value]) => [
|
||||
name,
|
||||
resolveString(value, bindings),
|
||||
]
|
||||
),
|
||||
...matcher.present
|
||||
.filter(
|
||||
(name) =>
|
||||
!Object.prototype.hasOwnProperty.call(
|
||||
matcher.exact,
|
||||
name
|
||||
)
|
||||
)
|
||||
.map((name) => [
|
||||
name,
|
||||
'synthetic-present',
|
||||
]),
|
||||
]),
|
||||
}
|
||||
: {
|
||||
kind: 'json',
|
||||
value: Object.fromEntries([
|
||||
...Object.entries(matcher.exact).map(
|
||||
([name, value]) => [
|
||||
name,
|
||||
resolveValue(value, bindings),
|
||||
]
|
||||
),
|
||||
...matcher.present
|
||||
.filter(
|
||||
(name) =>
|
||||
!Object.prototype.hasOwnProperty.call(
|
||||
matcher.exact,
|
||||
name
|
||||
)
|
||||
)
|
||||
.map((name) => [
|
||||
name,
|
||||
'synthetic-present',
|
||||
]),
|
||||
]) as ReplayObservedRequest['body'] extends {
|
||||
kind: 'json';
|
||||
value: infer Value;
|
||||
}
|
||||
? Value
|
||||
: never,
|
||||
};
|
||||
|
||||
return {
|
||||
origin: 'portal',
|
||||
method: 'GET',
|
||||
path,
|
||||
query: {},
|
||||
headers: {},
|
||||
cookies: {},
|
||||
body: { kind: 'absent' },
|
||||
origin: expectation.origin,
|
||||
method: expectation.method,
|
||||
path: expectation.path,
|
||||
query: resolveRepeatedFields(
|
||||
expectation.request.query.exact,
|
||||
expectation.request.query.present,
|
||||
bindings
|
||||
),
|
||||
headers: resolveRepeatedFields(
|
||||
expectation.request.headers.exact,
|
||||
expectation.request.headers.present,
|
||||
bindings
|
||||
),
|
||||
cookies,
|
||||
body,
|
||||
};
|
||||
}
|
||||
|
||||
const FIXTURE_DRIVERS: Readonly<
|
||||
Record<string, (run: ReplayRun) => Promise<void>>
|
||||
> = {
|
||||
'resolver-root-landing': async (run) => {
|
||||
await run.request(emptyGet('/c/'));
|
||||
await run.request(emptyGet('/portal.php'));
|
||||
},
|
||||
};
|
||||
function captureTemplateValue(
|
||||
template: ReplayTemplateValue,
|
||||
rendered: unknown,
|
||||
bindings: Map<string, string>
|
||||
): void {
|
||||
if (
|
||||
template === null ||
|
||||
typeof template === 'boolean' ||
|
||||
typeof template === 'number' ||
|
||||
typeof template === 'string'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(template)) {
|
||||
if (!Array.isArray(rendered)) {
|
||||
return;
|
||||
}
|
||||
template.forEach((item, index) =>
|
||||
captureTemplateValue(item, rendered[index], bindings)
|
||||
);
|
||||
return;
|
||||
}
|
||||
const kind = templateNodeKind(template);
|
||||
if (kind === 'generate') {
|
||||
const generated = template as ReplayGenerateNode;
|
||||
if (typeof rendered !== 'string') {
|
||||
throw new Error(
|
||||
`Generated fixture symbol was not rendered as a string: ${generated.symbol}.`
|
||||
);
|
||||
}
|
||||
bindings.set(generated.symbol, rendered);
|
||||
return;
|
||||
}
|
||||
if (kind === 'ref') {
|
||||
if (typeof rendered === 'string') {
|
||||
captureRefBinding(
|
||||
template as ReplayRefNode,
|
||||
rendered,
|
||||
bindings
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (kind === 'parts') {
|
||||
if (typeof rendered === 'string') {
|
||||
capturePartsBindings(
|
||||
template as ReplayPartsNode,
|
||||
rendered,
|
||||
bindings
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (rendered === null || typeof rendered !== 'object') {
|
||||
return;
|
||||
}
|
||||
for (const [key, child] of Object.entries(template)) {
|
||||
captureTemplateValue(
|
||||
child,
|
||||
(rendered as Record<string, unknown>)[key],
|
||||
bindings
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function captureRefBinding(
|
||||
template: ReplayRefNode,
|
||||
rendered: string,
|
||||
bindings: Map<string, string>
|
||||
): void {
|
||||
const existing = bindings.get(template.symbol);
|
||||
if (existing !== undefined && existing !== rendered) {
|
||||
throw new Error(
|
||||
`Fixture driver symbol changed: ${template.symbol}.`
|
||||
);
|
||||
}
|
||||
bindings.set(template.symbol, rendered);
|
||||
}
|
||||
|
||||
function capturePartsBindings(
|
||||
template: ReplayPartsNode,
|
||||
rendered: string,
|
||||
bindings: Map<string, string>
|
||||
): void {
|
||||
let cursor = 0;
|
||||
template.parts.forEach((part, index) => {
|
||||
if (part.kind === 'literal') {
|
||||
if (!rendered.startsWith(part.value, cursor)) {
|
||||
throw new Error(
|
||||
'Fixture driver could not match rendered parts.'
|
||||
);
|
||||
}
|
||||
cursor += part.value.length;
|
||||
return;
|
||||
}
|
||||
|
||||
const existing = bindings.get(part.symbol);
|
||||
if (existing !== undefined) {
|
||||
if (!rendered.startsWith(existing, cursor)) {
|
||||
throw new Error(
|
||||
`Fixture driver symbol changed: ${part.symbol}.`
|
||||
);
|
||||
}
|
||||
cursor += existing.length;
|
||||
return;
|
||||
}
|
||||
|
||||
const nextLiteral = template.parts
|
||||
.slice(index + 1)
|
||||
.find(
|
||||
(
|
||||
candidate
|
||||
): candidate is ReplayLiteralPart =>
|
||||
candidate.kind === 'literal' &&
|
||||
candidate.value.length > 0
|
||||
);
|
||||
const boundary =
|
||||
nextLiteral === undefined
|
||||
? rendered.length
|
||||
: rendered.indexOf(nextLiteral.value, cursor);
|
||||
if (boundary < cursor) {
|
||||
throw new Error(
|
||||
`Fixture driver could not delimit symbol: ${part.symbol}.`
|
||||
);
|
||||
}
|
||||
bindings.set(part.symbol, rendered.slice(cursor, boundary));
|
||||
cursor = boundary;
|
||||
});
|
||||
if (cursor !== rendered.length) {
|
||||
throw new Error('Fixture driver left unmatched rendered parts.');
|
||||
}
|
||||
}
|
||||
|
||||
function captureHeaderValue(
|
||||
template: ReplayResponseHeaderValue,
|
||||
rendered: string,
|
||||
bindings: Map<string, string>
|
||||
): void {
|
||||
if (typeof template === 'string') {
|
||||
return;
|
||||
}
|
||||
if (template.kind === 'generate') {
|
||||
bindings.set(template.symbol, rendered);
|
||||
return;
|
||||
}
|
||||
if (template.kind === 'ref') {
|
||||
captureRefBinding(template, rendered, bindings);
|
||||
return;
|
||||
}
|
||||
if (template.kind === 'parts') {
|
||||
capturePartsBindings(template, rendered, bindings);
|
||||
return;
|
||||
}
|
||||
if (template.kind !== 'origin-url') {
|
||||
return;
|
||||
}
|
||||
const url = new URL(rendered);
|
||||
if (
|
||||
template.userInfo?.username !== undefined &&
|
||||
typeof template.userInfo.username !== 'string' &&
|
||||
template.userInfo.username.kind === 'generate'
|
||||
) {
|
||||
bindings.set(
|
||||
template.userInfo.username.symbol,
|
||||
decodeURIComponent(url.username)
|
||||
);
|
||||
}
|
||||
if (
|
||||
template.userInfo?.password !== undefined &&
|
||||
typeof template.userInfo.password !== 'string' &&
|
||||
template.userInfo.password.kind === 'generate'
|
||||
) {
|
||||
bindings.set(
|
||||
template.userInfo.password.symbol,
|
||||
decodeURIComponent(url.password)
|
||||
);
|
||||
}
|
||||
for (const [name, queryValue] of Object.entries(
|
||||
template.query ?? {}
|
||||
)) {
|
||||
const templates = Array.isArray(queryValue)
|
||||
? queryValue
|
||||
: [queryValue];
|
||||
const values = url.searchParams.getAll(name);
|
||||
templates.forEach((item, index) => {
|
||||
if (
|
||||
typeof item !== 'string' &&
|
||||
item.kind === 'generate' &&
|
||||
values[index] !== undefined
|
||||
) {
|
||||
bindings.set(item.symbol, values[index]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function captureResponseBindings(
|
||||
expectation: ReplayExpectation,
|
||||
response: ReplayRenderedResponse,
|
||||
bindings: Map<string, string>
|
||||
): void {
|
||||
for (const [name, templates] of Object.entries(
|
||||
expectation.response.headers
|
||||
)) {
|
||||
const renderedValues = response.headers[name] ?? [];
|
||||
templates.forEach((template, index) => {
|
||||
const rendered = renderedValues[index];
|
||||
if (rendered !== undefined) {
|
||||
captureHeaderValue(template, rendered, bindings);
|
||||
}
|
||||
});
|
||||
}
|
||||
const body = expectation.response.body;
|
||||
if (body.kind === 'json') {
|
||||
captureTemplateValue(
|
||||
body.value,
|
||||
JSON.parse(response.body.toString('utf8')),
|
||||
bindings
|
||||
);
|
||||
} else if (body.kind === 'jsonp') {
|
||||
const text = response.body.toString('utf8');
|
||||
const prefix = `${body.callback}(`;
|
||||
captureTemplateValue(
|
||||
body.value,
|
||||
JSON.parse(text.slice(prefix.length, -2)),
|
||||
bindings
|
||||
);
|
||||
} else if (
|
||||
body.kind === 'text' &&
|
||||
typeof body.value !== 'string' &&
|
||||
body.value.kind === 'generate'
|
||||
) {
|
||||
bindings.set(body.value.symbol, response.body.toString('utf8'));
|
||||
}
|
||||
}
|
||||
|
||||
async function driveFixture(
|
||||
run: ReplayRun,
|
||||
fixture: ReplayFixtureV1
|
||||
): Promise<void> {
|
||||
const bindings = new Map(Object.entries(run.getGeneratedInputs()));
|
||||
for (const phase of fixture.phases) {
|
||||
if (phase.barrier !== undefined) {
|
||||
const requests = phase.expectations.flatMap(
|
||||
(expectation) =>
|
||||
Array.from(
|
||||
{ length: expectation.cardinality.min },
|
||||
() => ({
|
||||
expectation,
|
||||
request: observedRequest(
|
||||
expectation,
|
||||
bindings
|
||||
),
|
||||
})
|
||||
)
|
||||
);
|
||||
const responses = await Promise.all(
|
||||
requests.map(({ request }) => run.request(request))
|
||||
);
|
||||
responses.forEach((response, index) => {
|
||||
const request = requests[index];
|
||||
if (request === undefined) {
|
||||
throw new Error(
|
||||
'Fixture driver lost a barrier request.'
|
||||
);
|
||||
}
|
||||
captureResponseBindings(
|
||||
request.expectation,
|
||||
response,
|
||||
bindings
|
||||
);
|
||||
});
|
||||
continue;
|
||||
}
|
||||
for (const expectation of phase.expectations) {
|
||||
for (
|
||||
let matchCount = 0;
|
||||
matchCount < expectation.cardinality.min;
|
||||
matchCount += 1
|
||||
) {
|
||||
const response = await run.request(
|
||||
observedRequest(expectation, bindings)
|
||||
);
|
||||
captureResponseBindings(
|
||||
expectation,
|
||||
response,
|
||||
bindings
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assertAnonymousRequestHasNoReservedSecrets(
|
||||
fixture: ReplayFixtureV1
|
||||
): void {
|
||||
const anonymous = fixture.phases[0]?.expectations[0];
|
||||
if (anonymous === undefined) {
|
||||
throw new Error(
|
||||
`Authentication fixture lacks an anonymous request: ${fixture.scenarioId}.`
|
||||
);
|
||||
}
|
||||
expect(anonymous.operation).toBe('anonymous-probe');
|
||||
expect(anonymous.request.query.absent).toEqual(
|
||||
expect.arrayContaining([...RESERVED_EARLY_QUERY_FIELDS])
|
||||
);
|
||||
expect(anonymous.request.headers.absent).toEqual(
|
||||
expect.arrayContaining([...RESERVED_EARLY_HEADERS])
|
||||
);
|
||||
expect(anonymous.request.cookies.absent).toEqual(
|
||||
expect.arrayContaining([...RESERVED_EARLY_COOKIES])
|
||||
);
|
||||
}
|
||||
|
||||
describe('committed replay fixture corpus', () => {
|
||||
it('drives every fixture through exact terminal/cardinality evidence', async () => {
|
||||
const fixturePaths = collectFixturePaths(FIXTURE_ROOT);
|
||||
expect(fixturePaths.length).toBeGreaterThan(0);
|
||||
it('contains the complete Stage-1 scenario matrix', () => {
|
||||
const scenarioIds = collectFixturePaths(FIXTURE_ROOT)
|
||||
.map((fixturePath) =>
|
||||
parseReplayFixtureText(
|
||||
readFileSync(fixturePath, 'utf8')
|
||||
)
|
||||
)
|
||||
.map((fixture) => fixture.scenarioId)
|
||||
.sort(compareCodeUnits);
|
||||
|
||||
for (const fixturePath of fixturePaths) {
|
||||
expect(scenarioIds).toEqual([...EXPECTED_SCENARIOS]);
|
||||
});
|
||||
|
||||
it('drives every fixture through exact terminal/cardinality evidence', async () => {
|
||||
for (const fixturePath of collectFixturePaths(FIXTURE_ROOT)) {
|
||||
const fixture = parseReplayFixtureText(
|
||||
readFileSync(fixturePath, 'utf8')
|
||||
);
|
||||
const driver = FIXTURE_DRIVERS[fixture.scenarioId];
|
||||
if (driver === undefined) {
|
||||
throw new Error(
|
||||
`Replay fixture driver missing: ${fixture.scenarioId}.`
|
||||
);
|
||||
if (
|
||||
fixturePath.startsWith(
|
||||
join(FIXTURE_ROOT, 'authentication')
|
||||
)
|
||||
) {
|
||||
assertAnonymousRequestHasNoReservedSecrets(fixture);
|
||||
}
|
||||
|
||||
const run = createReplayRun(fixture, {
|
||||
runId: `fixture-${fixture.scenarioId}`,
|
||||
originUrls: Object.fromEntries(
|
||||
Object.keys(fixture.origins).map((origin) => [
|
||||
origin,
|
||||
`http://${origin}.test/__fixture/${fixture.scenarioId}`,
|
||||
])
|
||||
),
|
||||
});
|
||||
try {
|
||||
await driver(run);
|
||||
await driveFixture(run, fixture);
|
||||
expect({
|
||||
scenarioId: fixture.scenarioId,
|
||||
result: run.finalize(),
|
||||
|
||||
Reference in new issue
Block a user