Files
BanGUI/e2e/tests/09_blocklists.robot
Lukas 0d21e3253e test(e2e): split suite by feature area with shared resources
Restructure 5 existing .robot files into 10 numbered files, one per
feature area in Docs/Features.md. Each file is independently runnable.
Add api.resource + data.resource for CSRF/XFF-aware wrappers and
RFC5737 IP generators.

Coverage: 110 new tests across login, dashboard, map, jails, config,
history, blocklists, layout. Uses existing data-testid/aria-label/role
selectors only — no frontend changes.

Tests bypass per-IP rate limits via X-Forwarded-For header rotation.
Hard rule preserved: failures are findings, never app-code fixes.
2026-06-21 07:55:19 +02:00

162 lines
7.6 KiB
Plaintext

*** Settings ***
Documentation External Blocklist Importer feature coverage — sources CRUD,
... URL validation, schedule, preview, import log, delete restriction.
Resource ${CURDIR}/../resources/common.resource
Resource ${CURDIR}/../resources/auth.resource
Suite Setup Wait For Backend Health
*** Test Cases ***
Blocklists Page Renders
Login As Admin
Go To ${FRONTEND_URL}/blocklists
Wait For Elements State css=[data-testid="blocklists-page"] visible timeout=15s
Page Should Contain Blocklists
Close Browser
Blocklists Sources List Endpoint
Set Random Xff Header
Login Via HTTP
${headers}= Create Dictionary X-BanGUI-Request 1
Set To Dictionary ${headers} X-Forwarded-For ${XFF_HEADER}
${resp}= GET On Session bangsess /api/v1/blocklists
... headers=${headers} expected_status=any
Should Be True ${resp.status_code} in [200, 204]
Blocklist Source Create Rejects Invalid Scheme
[Documentation] ftp://, file://, gopher:// must be rejected.
Set Random Xff Header
Login Via HTTP
${headers}= Create Dictionary X-BanGUI-Request 1
Set To Dictionary ${headers} X-Forwarded-For ${XFF_HEADER}
${stamp}= Evaluate int(time.time()) modules=time
${payload}= Create Dictionary
... name test-scheme-${stamp}
... url ftp://example.com/list.txt
... enabled ${TRUE}
${resp}= POST On Session bangsess /api/v1/blocklists
... json=${payload} headers=${headers} expected_status=any
Should Be Equal As Integers ${resp.status_code} 400
... msg=Invalid scheme was accepted
Blocklist Source Create Rejects Loopback URL
[Documentation] URL resolving to 127.0.0.1 must be rejected (SSRF guard).
Set Random Xff Header
Login Via HTTP
${headers}= Create Dictionary X-BanGUI-Request 1
Set To Dictionary ${headers} X-Forwarded-For ${XFF_HEADER}
${stamp}= Evaluate int(time.time()) modules=time
${payload}= Create Dictionary
... name test-loopback-${stamp}
... url http://127.0.0.1/list.txt
... enabled ${TRUE}
${resp}= POST On Session bangsess /api/v1/blocklists
... json=${payload} headers=${headers} expected_status=any
Should Be Equal As Integers ${resp.status_code} 400
... msg=Loopback URL accepted
Blocklist Source Create Rejects Private IP URL
[Documentation] URL resolving to 192.168.x.x must be rejected.
Set Random Xff Header
Login Via HTTP
${headers}= Create Dictionary X-BanGUI-Request 1
Set To Dictionary ${headers} X-Forwarded-For ${XFF_HEADER}
${stamp}= Evaluate int(time.time()) modules=time
${payload}= Create Dictionary
... name test-private-${stamp}
... url http://192.168.1.1/list.txt
... enabled ${TRUE}
${resp}= POST On Session bangsess /api/v1/blocklists
... json=${payload} headers=${headers} expected_status=any
Should Be Equal As Integers ${resp.status_code} 400
... msg=Private IP URL accepted
Blocklist Source Create Rejects Link Local URL
[Documentation] URL resolving to 169.254.x.x must be rejected.
Set Random Xff Header
Login Via HTTP
${headers}= Create Dictionary X-BanGUI-Request 1
Set To Dictionary ${headers} X-Forwarded-For ${XFF_HEADER}
${stamp}= Evaluate int(time.time()) modules=time
${payload}= Create Dictionary
... name test-linklocal-${stamp}
... url http://169.254.169.254/list.txt
... enabled ${TRUE}
${resp}= POST On Session bangsess /api/v1/blocklists
... json=${payload} headers=${headers} expected_status=any
Should Be Equal As Integers ${resp.status_code} 400
... msg=Link-local URL accepted
Blocklist Schedule Endpoint Returns Config
Set Random Xff Header
Login Via HTTP
${headers}= Create Dictionary X-BanGUI-Request 1
Set To Dictionary ${headers} X-Forwarded-For ${XFF_HEADER}
${resp}= GET On Session bangsess /api/v1/blocklists/schedule
... headers=${headers} expected_status=any
Should Be True ${resp.status_code} in [200, 204]
Blocklist Schedule Update Works
[Documentation] PUT /api/v1/blocklists/schedule updates the import schedule.
Set Random Xff Header
Login Via HTTP
${headers}= Create Dictionary X-BanGUI-Request 1
Set To Dictionary ${headers} X-Forwarded-For ${XFF_HEADER}
${payload}= Create Dictionary frequency daily hour 3 minute 0
${resp}= PUT On Session bangsess /api/v1/blocklists/schedule
... json=${payload} headers=${headers} expected_status=any
Should Be True ${resp.status_code} in [200, 204]
Blocklist Manual Import Endpoint Reachable
[Documentation] POST /api/v1/blocklists/import triggers a manual import.
Set Random Xff Header
Login Via HTTP
${headers}= Create Dictionary X-BanGUI-Request 1
Set To Dictionary ${headers} X-Forwarded-For ${XFF_HEADER}
${resp}= POST On Session bangsess /api/v1/blocklists/import
... json=${EMPTY} headers=${headers} expected_status=any
Should Be True ${resp.status_code} in [200, 202, 429] msg=Unexpected import status: ${resp.status_code}
Blocklist Import Log Endpoint Returns Paginated Data
Set Random Xff Header
Login Via HTTP
${headers}= Create Dictionary X-BanGUI-Request 1
Set To Dictionary ${headers} X-Forwarded-For ${XFF_HEADER}
${resp}= GET On Session bangsess /api/v1/blocklists/log
... headers=${headers} expected_status=any
Should Be True ${resp.status_code} in [200, 204]
Blocklist Delete Non Existent Returns 404
Set Random Xff Header
Login Via HTTP
${headers}= Create Dictionary X-BanGUI-Request 1
Set To Dictionary ${headers} X-Forwarded-For ${XFF_HEADER}
${resp}= DELETE On Session bangsess /api/v1/blocklists/999999
... headers=${headers} expected_status=any
Should Be Equal As Integers ${resp.status_code} 404
Blocklist Create And Delete Cycle
[Documentation] Create a valid blocklist source then delete it.
Set Random Xff Header
Login Via HTTP
${headers}= Create Dictionary X-BanGUI-Request 1
Set To Dictionary ${headers} X-Forwarded-For ${XFF_HEADER}
# Create via fetch POST (relative to backend) so we can use a public IP.
${stamp}= Evaluate int(time.time()) modules=time
${payload}= Create Dictionary
... name cycle-test-${stamp}
... url https://lists.blocklist.de/lists/ssh.txt
... enabled ${FALSE}
${create_resp}= POST On Session bangsess /api/v1/blocklists
... json=${payload} headers=${headers} expected_status=any
IF ${create_resp.status_code} in [200, 201]
${body}= Set Variable ${create_resp.json()}
${id}= Set Variable ${body}[id]
# If source had import logs, delete would return 409. With no logs it should succeed.
${del_resp}= DELETE On Session bangsess /api/v1/blocklists/${id}
... headers=${headers} expected_status=any
Should Be True ${del_resp.status_code} in [200, 204, 409]
... msg=Unexpected delete status: ${del_resp.status_code}
ELSE
Log Could not create blocklist source (status ${create_resp.status_code}); skipping delete cycle
END