feat: update plan 16 sec14-26

This commit is contained in:
thanhnv
2026-07-07 15:46:36 +09:00
parent 0c60ed33e9
commit ae4fc7112c
64 changed files with 2231 additions and 116 deletions
@@ -22,6 +22,9 @@ set -uo pipefail
# CASAN_APPROVAL_JWT (optional RS256 IdP token)
# CASAN_IDP_PUBLIC_KEY (default central-governance/idp-public.pem)
# CASAN_IDP_JWKS_URL (optional OIDC JWKS endpoint; overrides public key)
# CASAN_TRUSTED_TIME / CASAN_TRUSTED_TIME_FILE (SEC-22/ARCH-06: trusted time
# source for JWT `exp` instead of the manipulable local clock; file
# unreadable = fail-closed)
# Exit: 0 ok (prints "APPROVAL_OK role=<role>"), 3 deny (reason on stderr), 64 usage.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -109,7 +112,27 @@ header = json.loads(b64u_decode(parts[0]))
claims = json.loads(b64u_decode(parts[1]))
if header.get("alg") != "RS256":
die("jwt_alg_not_allowed")
if int(claims.get("exp", 0)) <= int(time.time()):
# SEC-22 (ARCH-06): do NOT trust the local system clock alone for expiry. When a
# trusted time source is provided (CASAN_TRUSTED_TIME seconds, or
# CASAN_TRUSTED_TIME_FILE containing seconds from a trusted timestamp authority),
# use it; an unreadable/invalid source is fail-closed (deny).
def _trusted_now():
v = os.environ.get("CASAN_TRUSTED_TIME")
if v:
try:
return int(v)
except Exception:
die("trusted_time_invalid")
f = os.environ.get("CASAN_TRUSTED_TIME_FILE")
if f:
try:
return int(open(f).read().strip())
except Exception:
die("trusted_time_file_unreadable")
return int(time.time())
if int(claims.get("exp", 0)) <= _trusted_now():
die("jwt_expired")
if claims.get("sub") != approver:
die("jwt_sub_mismatch")