SHA256
fix(android): retain HTTPS sessions with upstream default cookie flags
Reproduce browser-success/native-401 with unmodified upstream SESSION_SECURE=false behind HTTPS. Keep cookies in the existing HTTPS-only origin-bound encrypted jar regardless of the server Secure attribute; preserve TLS, redirect, expiry and origin protections. Distinguish credential/2FA rejection from missing/rejected session handoff and clear stale login errors on retry. Verify 13 JVM tests plus the 7-test real-server suite in both Secure/default-cookie modes, including bad-password then successful native login. Bump Android preview to 0.2.1/code 3; extend CI and document the defect without claiming access to the user's production server.
This commit is contained in:
1 parent
129eac675c
commit
4b483c20e3
16 files changed
+220
-22
No files matched your search
@@ -13,7 +13,10 @@ process.env.PORT = '0';
|
||||
process.env.BIND_ADDRESS = '127.0.0.1';
|
||||
process.env.DB_PATH = path.join(directory, 'fixture.db');
|
||||
process.env.SESSION_SECRET = randomBytes(48).toString('hex');
|
||||
process.env.SESSION_SECURE = 'true';
|
||||
// Exercise both explicit Secure cookies and upstream's default behind HTTPS.
|
||||
const sessionSecure = process.env.YUVOMI_TEST_SESSION_SECURE ?? 'true';
|
||||
if (!['true', 'false'].includes(sessionSecure)) throw new Error('Invalid fixture cookie mode');
|
||||
process.env.SESSION_SECURE = sessionSecure;
|
||||
process.env.DB_ENCRYPTION_KEY = '';
|
||||
process.env.NODE_ENV = 'test';
|
||||
process.env.LOG_LEVEL = 'warn';
|
||||
|
||||
@@ -41,7 +41,7 @@ def collect_native_screenshot(additional, screenshots):
|
||||
(screenshots / "native-dashboard.png").write_bytes(capture)
|
||||
|
||||
|
||||
def run(upstream, android):
|
||||
def run(upstream, android, cookie_without_secure=False):
|
||||
revision = subprocess.check_output(["git", "-C", str(upstream), "rev-parse", "HEAD"], text=True).strip()
|
||||
assert revision == REVISION, f"Expected pinned upstream {REVISION}, got {revision}"
|
||||
base_temp = Path("/tmp/opencode") if Path("/tmp/opencode").exists() else Path(tempfile.gettempdir())
|
||||
@@ -56,7 +56,8 @@ def run(upstream, android):
|
||||
with (directory / "server.log").open("w") as log:
|
||||
process = subprocess.Popen(["node", str(ROOT / "scripts/integration-server.mjs"), str(upstream), temporary],
|
||||
cwd=upstream, stdout=log, stderr=log,
|
||||
env={**os.environ, "NODE_EXTRA_CA_CERTS": str(directory / "cert.pem")})
|
||||
env={**os.environ, "NODE_EXTRA_CA_CERTS": str(directory / "cert.pem"),
|
||||
"YUVOMI_TEST_SESSION_SECURE": "false" if cookie_without_secure else "true"})
|
||||
try:
|
||||
for _ in range(200):
|
||||
if (directory / "ready.json").exists():
|
||||
@@ -102,7 +103,7 @@ def run(upstream, android):
|
||||
status, body, headers = request("/api/v1/auth/login", {"username": "mobiletest", "password": password})
|
||||
assert status == 200
|
||||
session = [cookie for cookie in cookies if cookie.name == "yuvomi.sid"][0]
|
||||
assert session.secure and session.has_nonstandard_attr("HttpOnly")
|
||||
assert session.secure == (not cookie_without_secure) and session.has_nonstandard_attr("HttpOnly")
|
||||
assert "SameSite=Lax" in " ".join(headers.get_all("Set-Cookie"))
|
||||
csrf = json.loads(body)["csrfToken"]
|
||||
assert request("/api/v1/auth/me")[0] == 200
|
||||
@@ -110,7 +111,8 @@ def run(upstream, android):
|
||||
assert request("/api/v1/auth/logout", {})[0] == 403
|
||||
assert request("/api/v1/auth/logout", {}, {"X-CSRF-Token": csrf})[0] == 200
|
||||
assert request("/api/v1/auth/me")[0] == 401
|
||||
print("PASS: login, Secure/HttpOnly/SameSite cookies, persistence, CSRF rejection and logout/revocation")
|
||||
cookie_mode = "HTTPS with upstream-default non-Secure session cookie" if cookie_without_secure else "Secure session cookie"
|
||||
print(f"PASS: login, {cookie_mode}, HttpOnly/SameSite, persistence, CSRF rejection and logout/revocation")
|
||||
assert request("/api/v1/auth/oidc/start")[0] == 200
|
||||
status, body, _ = request("/api/v1/auth/me")
|
||||
identity = json.loads(body)
|
||||
@@ -168,5 +170,7 @@ if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("upstream", type=Path)
|
||||
parser.add_argument("--android", action="store_true")
|
||||
parser.add_argument("--cookie-without-secure", action="store_true",
|
||||
help="Test upstream's default session-cookie attributes over HTTPS; never enables HTTP")
|
||||
arguments = parser.parse_args()
|
||||
run(arguments.upstream.resolve(), arguments.android)
|
||||
run(arguments.upstream.resolve(), arguments.android, arguments.cookie_without_secure)
|
||||
Reference in new issue
Block a user