java-topology/defects/openscad-0003/test/test_openscad_0003.py
russell@unturf.com 597b345da7 openscad: 5-MOAD scan; openscad-0001 CWE-407 AMF vertex dedup 85x, openscad-0002 CWE-407 PolySetBuilder color dedup 69x, openscad-0003 CWE-312 OctoPrint app_token logged
Restructure openscad-0001 (was in wrong dir, Java test) into proper
openscad-0001/ with TICKET.md and Python test. New openscad-0002 patches
PolySetBuilder::endPolygon + appendPolySet std::find on colors_ vector ->
unordered_map; Color4f already has std::hash. New openscad-0003 patches
OctoPrint::requestApiKey and getJsonData to stop logging API responses
verbatim (app_token + api responses exposed under --debug).
MOADs 0002/0003/0005 CLEAN.
2026-04-03 14:10:05 -04:00

107 lines
3.7 KiB
Python

"""
test_openscad_0003.py — CWE-312 OctoPrint requestApiKey logs app_token verbatim
Defect: OctoPrint.cc requestApiKey() calls PRINTDB with the full JSON response
body including app_token. When --debug is active, the temporary authentication
token is written to the debug log in plain text (CWE-312).
Fix: extract the token from the parsed JSON, log only a redacted presence
indicator, never the token value.
"""
import json
import sys
# --- Simulate defective logging: returns what gets logged ---
def request_api_key_defective(response_json: dict) -> tuple:
"""
Simulates the defective path: logs the full JSON response.
Returns (token, logged_string).
"""
doc_str = json.dumps(response_json)
logged = f"Response: {doc_str}" # PRINTDB("Response: %s", ...)
token = response_json.get("app_token", "")
return token, logged
# --- Fixed: logs only presence, not value ---
def request_api_key_fixed(response_json: dict) -> tuple:
"""
Simulates the fixed path: extracts token, logs only presence indicator.
Returns (token, logged_string).
"""
token = response_json.get("app_token", "")
if token:
logged = "requestApiKey: app_token received (redacted)"
else:
logged = "requestApiKey: app_token absent"
return token, logged
def test_token_not_in_log():
"""Defect: app_token appears in log. Fix: token absent from log."""
response = {
"app_token": "secret-token-abc123xyz",
"status": "pending"
}
# Defective path: token in log
token_def, log_def = request_api_key_defective(response)
assert token_def == "secret-token-abc123xyz", "token extraction failed"
assert "secret-token-abc123xyz" in log_def, "defect: token should appear in defective log"
# Fixed path: token NOT in log
token_fix, log_fix = request_api_key_fixed(response)
assert token_fix == "secret-token-abc123xyz", "fixed: token extraction failed"
assert "secret-token-abc123xyz" not in log_fix, \
f"CWE-312: token still appears in fixed log: {log_fix}"
assert "redacted" in log_fix, "fixed log should mention redaction"
print(f"PASS defective log contains token: True")
print(f"PASS fixed log contains token: False")
print(f"PASS fixed log: '{log_fix}'")
def test_empty_token():
"""Edge case: absent token should be handled gracefully."""
response = {"status": "pending"}
token_fix, log_fix = request_api_key_fixed(response)
assert token_fix == "", "expected empty token"
assert "absent" in log_fix, f"expected 'absent' in log, got: {log_fix}"
print(f"PASS empty token handled: '{log_fix}'")
def test_get_json_data_redaction():
"""
getJsonData() also logs full response verbatim — may include api_key,
printer config, etc. Fixed: logs only a presence indicator.
"""
sensitive_responses = [
{"api": "0.1", "server": "1.8.6"},
{"api_key": "master-key-xyz"},
{"slicers": {"cura": {"default": True, "key": "secret-profile-key"}}},
]
for resp in sensitive_responses:
resp_str = json.dumps(resp)
# Defective: log full response
defective_log = f"Response: {resp_str}"
# Fixed: never log body
fixed_log = "Response received for endpoint (body redacted)"
# Check no keys from response body appear in fixed log
for key in resp:
assert key not in fixed_log, \
f"CWE-312: key '{key}' from response appears in fixed log"
print(f"PASS getJsonData redaction for response with keys: {list(resp.keys())}")
if __name__ == "__main__":
test_token_not_in_log()
test_empty_token()
test_get_json_data_redaction()
print("ALL TESTS PASSED")