Fix black formatting and target Python 3.12 in CI
Some checks failed
CI / lint (push) Failing after 36s
CI / typecheck (push) Failing after 37s
CI / test (push) Failing after 24s

Reformat 8 files and add --target-version py312 to avoid
3.13 AST parsing issues with Python 3.12 container.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aaron D. Lee 2026-04-01 18:26:32 -04:00
parent 6d6a626b6b
commit 5c74a5f4aa
9 changed files with 448 additions and 394 deletions

View File

@ -18,7 +18,7 @@ jobs:
git clone --depth=1 --branch="${GITHUB_REF_NAME}" https://git.golfcards.club/alee/soosef.git "$GITHUB_WORKSPACE" || git clone --depth=1 https://git.golfcards.club/alee/soosef.git "$GITHUB_WORKSPACE"
- run: pip install ruff black
- name: Check formatting
run: black --check src/ tests/ frontends/
run: black --check --target-version py312 src/ tests/ frontends/
- name: Lint
run: ruff check src/ tests/ frontends/

View File

@ -477,7 +477,9 @@ def _register_stegasoo_routes(app: Flask) -> None:
detail=None if success else message,
)
if success:
flash(f"User '{username}' created with temporary password: {temp_password}", "success")
flash(
f"User '{username}' created with temporary password: {temp_password}", "success"
)
else:
flash(message, "error")
return redirect(url_for("admin_users"))
@ -535,7 +537,9 @@ def _register_stegasoo_routes(app: Flask) -> None:
if not use_pin and not use_rsa:
flash("You must select at least one security factor (PIN or RSA Key)", "error")
return render_template("stego/generate.html", generated=False, has_qrcode=_HAS_QRCODE)
return render_template(
"stego/generate.html", generated=False, has_qrcode=_HAS_QRCODE
)
pin_length = int(request.form.get("pin_length", 6))
rsa_bits = int(request.form.get("rsa_bits", 2048))
@ -569,7 +573,11 @@ def _register_stegasoo_routes(app: Flask) -> None:
temp_storage.save_temp_file(
qr_token,
creds.rsa_key_pem.encode(),
{"filename": "rsa_key.pem", "type": "rsa_key", "compress": qr_needs_compression},
{
"filename": "rsa_key.pem",
"type": "rsa_key",
"compress": qr_needs_compression,
},
)
return render_template(
@ -594,7 +602,9 @@ def _register_stegasoo_routes(app: Flask) -> None:
)
except Exception as e:
flash(f"Error generating credentials: {e}", "error")
return render_template("stego/generate.html", generated=False, has_qrcode=_HAS_QRCODE)
return render_template(
"stego/generate.html", generated=False, has_qrcode=_HAS_QRCODE
)
return render_template("stego/generate.html", generated=False, has_qrcode=_HAS_QRCODE)
@ -633,8 +643,10 @@ def _register_stegasoo_routes(app: Flask) -> None:
compress = file_info.get("compress", False)
qr_png = generate_qr_code(key_pem, compress=compress)
return send_file(
io.BytesIO(qr_png), mimetype="image/png",
as_attachment=True, download_name="soosef_rsa_key_qr.png",
io.BytesIO(qr_png),
mimetype="image/png",
as_attachment=True,
download_name="soosef_rsa_key_qr.png",
)
except Exception as e:
return f"Error generating QR code: {e}", 500
@ -656,8 +668,10 @@ def _register_stegasoo_routes(app: Flask) -> None:
key_id = secrets.token_hex(4)
filename = f"soosef_key_{private_key.key_size}_{key_id}.pem"
return send_file(
io.BytesIO(encrypted_pem), mimetype="application/x-pem-file",
as_attachment=True, download_name=filename,
io.BytesIO(encrypted_pem),
mimetype="application/x-pem-file",
as_attachment=True,
download_name=filename,
)
except Exception as e:
flash(f"Error creating key file: {e}", "error")
@ -667,12 +681,15 @@ def _register_stegasoo_routes(app: Flask) -> None:
from stego_routes import register_stego_routes
register_stego_routes(app, **{
"login_required": login_required,
"subprocess_stego": subprocess_stego,
"temp_storage": temp_storage,
"has_qrcode_read": _HAS_QRCODE_READ,
})
register_stego_routes(
app,
**{
"login_required": login_required,
"subprocess_stego": subprocess_stego,
"temp_storage": temp_storage,
"has_qrcode_read": _HAS_QRCODE_READ,
},
)
# /about route is in stego_routes.py
@ -683,22 +700,26 @@ def _register_stegasoo_routes(app: Flask) -> None:
def api_channel_status():
result = subprocess_stego.get_channel_status(reveal=False)
if result.success:
return jsonify({
"success": True,
"mode": result.mode,
"configured": result.configured,
"fingerprint": result.fingerprint,
"source": result.source,
})
return jsonify(
{
"success": True,
"mode": result.mode,
"configured": result.configured,
"fingerprint": result.fingerprint,
"source": result.source,
}
)
else:
status = get_channel_status()
return jsonify({
"success": True,
"mode": status["mode"],
"configured": status["configured"],
"fingerprint": status.get("fingerprint"),
"source": status.get("source"),
})
return jsonify(
{
"success": True,
"mode": status["mode"],
"configured": status["configured"],
"fingerprint": status.get("fingerprint"),
"source": status.get("source"),
}
)
@app.route("/api/compare-capacity", methods=["POST"])
@login_required
@ -711,23 +732,25 @@ def _register_stegasoo_routes(app: Flask) -> None:
result = subprocess_stego.compare_modes(carrier_data)
if not result.success:
return jsonify({"error": result.error or "Comparison failed"}), 500
return jsonify({
"success": True,
"width": result.width,
"height": result.height,
"lsb": {
"capacity_bytes": result.lsb["capacity_bytes"],
"capacity_kb": round(result.lsb["capacity_kb"], 1),
"output": result.lsb.get("output", "PNG"),
},
"dct": {
"capacity_bytes": result.dct["capacity_bytes"],
"capacity_kb": round(result.dct["capacity_kb"], 1),
"output": result.dct.get("output", "JPEG"),
"available": result.dct.get("available", True),
"ratio": round(result.dct.get("ratio_vs_lsb", 0), 1),
},
})
return jsonify(
{
"success": True,
"width": result.width,
"height": result.height,
"lsb": {
"capacity_bytes": result.lsb["capacity_bytes"],
"capacity_kb": round(result.lsb["capacity_kb"], 1),
"output": result.lsb.get("output", "PNG"),
},
"dct": {
"capacity_bytes": result.dct["capacity_bytes"],
"capacity_kb": round(result.dct["capacity_kb"], 1),
"output": result.dct.get("output", "JPEG"),
"available": result.dct.get("available", True),
"ratio": round(result.dct.get("ratio_vs_lsb", 0), 1),
},
}
)
except Exception as e:
return jsonify({"error": str(e)}), 500
@ -751,11 +774,13 @@ def _register_stegasoo_routes(app: Flask) -> None:
def api_generate_credentials():
try:
creds = generate_credentials(use_pin=True, use_rsa=False)
return jsonify({
"success": True,
"passphrase": creds.passphrase,
"pin": creds.pin,
})
return jsonify(
{
"success": True,
"passphrase": creds.passphrase,
"pin": creds.pin,
}
)
except Exception as e:
return jsonify({"error": str(e)}), 500

File diff suppressed because it is too large Load Diff

View File

@ -433,9 +433,7 @@ class ChainStore:
if prev_record is not None:
expected_hash = compute_record_hash(prev_record)
if record.prev_hash != expected_hash:
raise ChainIntegrityError(
f"Record {record.chain_index}: prev_hash mismatch"
)
raise ChainIntegrityError(f"Record {record.chain_index}: prev_hash mismatch")
elif record.chain_index == 0:
if record.prev_hash != ChainState.GENESIS_PREV_HASH:
raise ChainIntegrityError("Genesis record has non-zero prev_hash")

View File

@ -47,9 +47,7 @@ class DeadmanSwitch:
state["grace_hours"] = grace_hours
state["last_checkin"] = datetime.now(UTC).isoformat()
self._save_state(state)
logger.info(
"Dead man's switch armed: %dh interval, %dh grace", interval_hours, grace_hours
)
logger.info("Dead man's switch armed: %dh interval, %dh grace", interval_hours, grace_hours)
def disarm(self) -> None:
"""Disarm the dead man's switch."""
@ -83,9 +81,7 @@ class DeadmanSwitch:
if not state["armed"] or not state["last_checkin"]:
return False
last = datetime.fromisoformat(state["last_checkin"])
deadline = last + timedelta(
hours=state["interval_hours"] + state["grace_hours"]
)
deadline = last + timedelta(hours=state["interval_hours"] + state["grace_hours"])
return datetime.now(UTC) > deadline
def status(self) -> dict:

View File

@ -92,14 +92,16 @@ def execute_purge(scope: PurgeScope = PurgeScope.ALL, reason: str = "manual") ->
]
if scope == PurgeScope.ALL:
steps.extend([
("destroy_auth_db", lambda: _secure_delete_file(paths.AUTH_DB)),
("destroy_attestation_log", lambda: _secure_delete_dir(paths.ATTESTATIONS_DIR)),
("destroy_chain_data", lambda: _secure_delete_dir(paths.CHAIN_DIR)),
("destroy_temp_files", lambda: _secure_delete_dir(paths.TEMP_DIR)),
("destroy_config", lambda: _secure_delete_file(paths.CONFIG_FILE)),
("clear_journald", _clear_system_logs),
])
steps.extend(
[
("destroy_auth_db", lambda: _secure_delete_file(paths.AUTH_DB)),
("destroy_attestation_log", lambda: _secure_delete_dir(paths.ATTESTATIONS_DIR)),
("destroy_chain_data", lambda: _secure_delete_dir(paths.CHAIN_DIR)),
("destroy_temp_files", lambda: _secure_delete_dir(paths.TEMP_DIR)),
("destroy_config", lambda: _secure_delete_file(paths.CONFIG_FILE)),
("clear_journald", _clear_system_logs),
]
)
for name, action in steps:
try:

View File

@ -135,9 +135,7 @@ class KeystoreManager:
from datetime import UTC, datetime
meta_path = self._identity_meta_path()
meta_path.write_text(
json.dumps({"created_at": datetime.now(UTC).isoformat()}, indent=None)
)
meta_path.write_text(json.dumps({"created_at": datetime.now(UTC).isoformat()}, indent=None))
return self.get_identity()
@ -263,8 +261,7 @@ class KeystoreManager:
from datetime import UTC, datetime
(archive_dir / "rotation.txt").write_text(
f"Rotated at: {datetime.now(UTC).isoformat()}\n"
f"Old fingerprint: {old_fp}\n"
f"Rotated at: {datetime.now(UTC).isoformat()}\n" f"Old fingerprint: {old_fp}\n"
)
new_key = self.generate_channel_key()

View File

@ -52,8 +52,7 @@ def test_concurrent_append_no_fork(chain_dir: Path):
# Every index must be unique (no fork)
assert len(all_indices) == len(set(all_indices)), (
f"Duplicate chain indices detected — chain forked! "
f"Indices: {sorted(all_indices)}"
f"Duplicate chain indices detected — chain forked! " f"Indices: {sorted(all_indices)}"
)
# Indices should be 0..N-1 contiguous
@ -100,7 +99,7 @@ def test_truncated_chain_file(chain_dir: Path, private_key: Ed25519PrivateKey):
# Truncate the file mid-record
chain_file = chain_dir / "chain.bin"
data = chain_file.read_bytes()
chain_file.write_bytes(data[:len(data) - 50])
chain_file.write_bytes(data[: len(data) - 50])
store2 = ChainStore(chain_dir)
records = list(store2._iter_raw())

View File

@ -22,7 +22,6 @@ from pathlib import Path
import pytest
from click.testing import CliRunner
# ── Fixtures ────────────────────────────────────────────────────────────────
@ -205,7 +204,9 @@ def cli_runner():
return CliRunner()
def test_check_deadman_disarmed(tmp_path: Path, cli_runner: CliRunner, monkeypatch: pytest.MonkeyPatch):
def test_check_deadman_disarmed(
tmp_path: Path, cli_runner: CliRunner, monkeypatch: pytest.MonkeyPatch
):
"""check-deadman exits 0 and prints helpful message when not armed."""
from soosef.fieldkit import deadman as deadman_mod
from soosef.cli import main
@ -219,7 +220,9 @@ def test_check_deadman_disarmed(tmp_path: Path, cli_runner: CliRunner, monkeypat
assert "not armed" in result.output
def test_check_deadman_armed_ok(tmp_path: Path, cli_runner: CliRunner, monkeypatch: pytest.MonkeyPatch):
def test_check_deadman_armed_ok(
tmp_path: Path, cli_runner: CliRunner, monkeypatch: pytest.MonkeyPatch
):
"""check-deadman exits 0 when armed and check-in is current."""
from soosef.fieldkit import deadman as deadman_mod
from soosef.cli import main
@ -241,7 +244,9 @@ def test_check_deadman_armed_ok(tmp_path: Path, cli_runner: CliRunner, monkeypat
assert "OK" in result.output
def test_check_deadman_overdue_in_grace(tmp_path: Path, cli_runner: CliRunner, monkeypatch: pytest.MonkeyPatch):
def test_check_deadman_overdue_in_grace(
tmp_path: Path, cli_runner: CliRunner, monkeypatch: pytest.MonkeyPatch
):
"""check-deadman exits 0 but prints OVERDUE warning when past interval but in grace."""
from soosef.fieldkit import deadman as deadman_mod
from soosef.cli import main