*** 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