Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7d64abea3 | ||
|
|
9303a5d684 |
+1
@@ -0,0 +1 @@
|
||||
{"approved_evidence_ref":{"commit":"015185d7e5191d18c5c1596160807ac228782a0a","observed_at":"2026-08-08T10:46:49Z","path":"approvals/governance-policy/AIOS-AUTONOMOUS-MERGE-GOVERNANCE-SCOPE-EXTENSION-V1.preapproval-evidence.json"},"approved_semantic_digest":"ba15d512ed8c1f43e5f72c84125a3a71a7347ef59e18000bbfca24d6e69f1b19","approved_transport_digest":"8b3370add21cd23c2b4f41d8d84d3eaaa09ddf0451549b72c7941bbd92d5d60a","classification":"SNAPSHOT_SCHEMA_DRIFT","drift_source":"UNKNOWN","field_diff_artifact":"/var/lib/aios-gitops/runtime/tasks/AIOS-PROTECTION-SNAPSHOT-DRIFT-FORENSICS-V1/protection-drift/semantic-analysis.json","governance":{"direct_gitea_db_write":false,"force_push":false,"new_scope":false,"owner_pat_used":false,"protection_mutation":false},"legacy_schema":{"created_unix":"server_metadata","merge_whitelist_user_i_ds":"JSON_STRING_ARRAY","status_check_contexts":"JSON_STRING_ARRAY","updated_unix":"server_metadata"},"live_schema":{"created_at":"server_metadata","merge_whitelist_usernames":"TYPED_ARRAY","status_check_contexts":"TYPED_ARRAY","updated_at":"server_metadata"},"observed_semantic_digest":"ba15d512ed8c1f43e5f72c84125a3a71a7347ef59e18000bbfca24d6e69f1b19","observed_transport_digest":"d4d3652cbdf012b57d7e24a066325a4d0ca779a8ca24255e0d578c1bb0276d10","production":{"unchanged":true,"version":"V2R1C7"},"protection_identity":{"branch":"main","id":2},"repository_identity":{"repo_id":1,"repository":"aios/aios"},"request_id":"AIOS-PROTECTION-SEMANTIC-SNAPSHOT-RECONCILIATION-V1","schema":"aios.semantic-governance-reconciliation-evidence.v1","security_relevant_field_diff":"none","semantic_equal":true}
|
||||
@@ -0,0 +1 @@
|
||||
{"existing_operation_rebindings":{"stage1":{"operation":"ADD_EXISTING_MACHINE_TO_MAIN_MERGE_WHITELIST","subject_user_id":153},"stage2":{"operation":"MIGRATE_LEGACY_OWNER_APPROVAL_GATE_TO_CAMPAIGN_APPROVAL_GATE"}},"new_governance_scope":false,"operation":"SEMANTIC_RECONCILIATION_REQUEST","original_governance_scope":{"pr54":{"head":"015185d7e5191d18c5c1596160807ac228782a0a","manifest_sha256":"1b11ee42d53a9f6c1646e5f5ddd8ce5b7ecf301483b9f3d47a068290d541ba4b","number":54,"owner_id":2,"review_id":104},"pr55":{"head":"9f98e15d37371af755c644d70575354099d21f76","number":55,"review_id":106}},"purpose":"migrate_execution_binding_schema_only","repository":"aios/aios","request_branch":"governance/request/protection-semantic-reconciliation-v1-20260808","request_id":"AIOS-PROTECTION-SEMANTIC-SNAPSHOT-RECONCILIATION-V1","schema":"aios.semantic-governance-reconciliation-manifest.v1","snapshot_reconciliation":{"approved_semantic_digest":"ba15d512ed8c1f43e5f72c84125a3a71a7347ef59e18000bbfca24d6e69f1b19","approved_transport_digest":"8b3370add21cd23c2b4f41d8d84d3eaaa09ddf0451549b72c7941bbd92d5d60a","canonicalizer_version":1,"classification":"SNAPSHOT_SCHEMA_DRIFT","drift_source":"UNKNOWN","observed_semantic_digest":"ba15d512ed8c1f43e5f72c84125a3a71a7347ef59e18000bbfca24d6e69f1b19","observed_transport_digest":"d4d3652cbdf012b57d7e24a066325a4d0ca779a8ca24255e0d578c1bb0276d10","protection_identity":{"id":2},"repository_identity":{"repo_id":1},"security_relevant_field_diff":"none","semantic_equal":true,"semantic_schema":"aios.semantic-protection.v1"},"stage1_predicate":{"branch":"main","mutation":{"add_merge_whitelist_user_id":153},"preserve_all_other_security_fields":true,"repository":"aios/aios","require":{"can_force_push":false,"can_push":false,"enable_status_check":true,"merge_whitelist_contains":[2],"merge_whitelist_not_contains":[153],"required_approvals":0},"semantic_precondition_digest":"ba15d512ed8c1f43e5f72c84125a3a71a7347ef59e18000bbfca24d6e69f1b19"},"stage2_predicate":{"mutation":{"add_required_context":"campaign-approval-gate","remove_required_context":"owner-approval-gate"},"preserve_all_other_security_fields":true,"require":{"campaign_active":true,"campaign_revoked":false,"can_force_push":false,"can_push":false,"merge_whitelist_contains":[2,153],"required_contexts_contains":["owner-approval-gate"],"required_contexts_not_contains":["campaign-approval-gate"]}},"target_branch":"governance/approvals"}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"approval_type": "TASK_STATUS_MERGE_BROKER_BOOTSTRAP_RFC3339_FIX",
|
||||
"schema_version": 2,
|
||||
"repository": "aios/aios",
|
||||
"expected_governance_base_oid": "8814dff20222625b60454c613b61ccf15fa20e51",
|
||||
"request_branch": "patch/codex/AIOS-V2R1C4-BOOTSTRAP-RFC3339-OFFSET-FIX",
|
||||
"target_ref": "refs/heads/governance/approvals",
|
||||
"issued_at": "2026-08-01T02:50:00Z",
|
||||
"expires_at": "2026-08-01T14:50:00Z",
|
||||
"required_human_approvals": 1,
|
||||
"allowed_human_approvers": ["owner"],
|
||||
"require_latest_head_approval": true,
|
||||
"approval_scopes": [
|
||||
"BOOTSTRAP_FIX_APPROVAL",
|
||||
"GITEA_INTERNAL_TOKEN_ROTATION_APPROVAL"
|
||||
],
|
||||
"supersedes": {
|
||||
"pr_number": 37,
|
||||
"head_oid": "57697d81cf3d1fef23798ec390418efe8b64f4e9",
|
||||
"tree_oid": "b58af1925318a0eefa6cc709e8d52ba57bb20d7f",
|
||||
"reason": "APPROVED_BOOTSTRAP_RUNTIME_CONTRACT_MISMATCH",
|
||||
"old_review_reusable": false,
|
||||
"old_pr_immutable": true
|
||||
},
|
||||
"exact_head_binding": {
|
||||
"method": "GITEA_EXACT_HEAD_OWNER_REVIEW_AND_PR_METADATA",
|
||||
"head_oid_resolved_after_commit": true,
|
||||
"tree_oid_resolved_after_commit": true,
|
||||
"manifest_blob_oid_resolved_after_commit": true,
|
||||
"manifest_sha256_resolved_after_commit": true,
|
||||
"head_change_invalidates_review": true
|
||||
},
|
||||
"timestamp_contract": {
|
||||
"id": "RFC3339_OFFSET_AWARE_V2",
|
||||
"canonical_timezone": "UTC",
|
||||
"raw_timestamp_preserved": true,
|
||||
"naive_datetime_allowed": false,
|
||||
"accepted_timezone_forms": ["Z", "+HH:MM", "-HH:MM"],
|
||||
"fractional_seconds_allowed": true,
|
||||
"lowercase_z_allowed": false,
|
||||
"expiry_boundary": "REVIEW_MUST_BE_STRICTLY_BEFORE_EXPIRY"
|
||||
},
|
||||
"internal_token_rotation": {
|
||||
"secret_class": "GITEA_INTERNAL_TOKEN",
|
||||
"old_secret_fingerprint_algorithm": "SHA256",
|
||||
"old_secret_fingerprint": "42e2af774700405ae32d55c98ca010fdb5da890b08f9e12e77372133c68e0562",
|
||||
"old_secret_reference": "gitea-config://security/INTERNAL_TOKEN",
|
||||
"affected_consumers": [
|
||||
"gitea-config-residue:/data/aios-gitops/runtime/gitea-bin/custom/conf/app.ini.bak-20260706-auth-gitops",
|
||||
"gitea-config:/data/aios-gitops/runtime/gitea-bin/custom/conf/app.ini",
|
||||
"systemd-service:aios-gitea.service"
|
||||
],
|
||||
"replacement_secret_reference": "systemd-credential-encrypted://gitea-internal-token-v2",
|
||||
"rotation_method": "ATOMIC_REPLACE_THEN_REVOKE",
|
||||
"rollback_plan_path": "evidence/governance/bootstrap-rfc3339-fix-20260801/ROLLBACK_PLAN.json",
|
||||
"rollback_plan_sha256": "3283f4c3d6bb7f34ac893789f7e9f5df6c68b7807203e6f471ad92e8e007ff3f",
|
||||
"audit_window_start": "2026-08-01T00:00:00Z",
|
||||
"allowed_operations": [
|
||||
"generate_replacement_internal_token_to_encrypted_runtime_credential",
|
||||
"validate_replacement_internal_token_without_logging_plaintext",
|
||||
"atomically_replace_exact_gitea_internal_token_config_value",
|
||||
"restart_exact_aios_gitea_service",
|
||||
"verify_exact_consumers_healthy",
|
||||
"verify_old_internal_token_authentication_rejected",
|
||||
"remove_or_redact_exact_old_secret_config_residue",
|
||||
"audit_exposure_window",
|
||||
"write_immutable_security_event_receipt"
|
||||
],
|
||||
"access_token_table_exception_applicable": false,
|
||||
"plaintext_secret_in_manifest_allowed": false,
|
||||
"plaintext_secret_in_argv_allowed": false,
|
||||
"plaintext_secret_in_logs_allowed": false,
|
||||
"plaintext_secret_in_git_allowed": false
|
||||
},
|
||||
"phase_b": {
|
||||
"approval_mode": "TWO_STAGE_REQUIRED",
|
||||
"reason": "CURRENT_SCHEMAS_DO_NOT_ATOMICALLY_BIND_BOOTSTRAP_ACTIVATION_AND_FULL_TASK_SCOPED_RELEASE_PAYLOAD",
|
||||
"old_pr_number": 36,
|
||||
"old_head_oid": "a8644fc172bd8ebbba17a16e6bf586c93fd1e793",
|
||||
"old_approval_reusable": false,
|
||||
"successor_required_after_bootstrap_activation": true,
|
||||
"frozen_release_objects_unchanged": true
|
||||
},
|
||||
"frozen_release_objects": {
|
||||
"source_commit_oid": "1857b88690cd1c0119df984534c260b6f217a21a",
|
||||
"source_tree_oid": "17c153de2338f570f70cb7407e7dda3c596b524b",
|
||||
"evidence_closure_oid": "057f5923f72cdb8d002ae40bc0cd584961825801",
|
||||
"evidence_tree_oid": "30be66d153fe9d3758f5fdba89ca4169beab38f8",
|
||||
"git_bundle_sha256": "762e280e240988282bc2c2e65fafecedd596400ab6829016947e84559508a015",
|
||||
"tag_oid": "7f82d77cf7702a6d695b4f0b74da7a9a9ecd7fa3",
|
||||
"peeled_target_oid": "057f5923f72cdb8d002ae40bc0cd584961825801",
|
||||
"release_artifact_sha256": "39523caa48f39b33e5e5df785bb568e0381d8b20301746c280442f73f294f2b2",
|
||||
"rollback_artifact_sha256": "dd0dbbe21db14722996636347ea1d22917436c688d5f0b7055b5bfb1776c4d95",
|
||||
"release_root_sha256": "16e4e881287cef4a02525a6a5b337d3e12995e510832fe04fea2ac196885f3ef",
|
||||
"evidence_manifest_sha256": "3b0b72af400997385e817e9bec677942288f422b69c2d2b52a8a00407fc395e0"
|
||||
},
|
||||
"allowed_status_contexts": [
|
||||
"aios/branch-protection",
|
||||
"aios/evidence-closure",
|
||||
"aios/secret-scan",
|
||||
"aios/state-machine",
|
||||
"aios/validator"
|
||||
],
|
||||
"allowed_identities": ["codex", "gpt"],
|
||||
"allowed_operations": [
|
||||
"install_exact_approved_bootstrap_files",
|
||||
"sign_task_grant_after_owner_exact_head_review",
|
||||
"issue_task_status_credential",
|
||||
"issue_task_merge_credential",
|
||||
"publish_exact_five_statuses",
|
||||
"fast_forward_only_merge",
|
||||
"readback_status_and_ref",
|
||||
"revoke_task_credentials",
|
||||
"remove_credential_projection",
|
||||
"sweep_completed_or_expired_task_credentials"
|
||||
],
|
||||
"operation_limits": {
|
||||
"maximum_status_writes_per_grant": 5,
|
||||
"maximum_merges_per_grant": 1,
|
||||
"maximum_grant_lifetime_seconds": 43200,
|
||||
"automatic_revocation_required": true
|
||||
},
|
||||
"denied_operations": [
|
||||
"branch_push_outside_request_branch",
|
||||
"main_push",
|
||||
"tag_write",
|
||||
"registry_write",
|
||||
"baseline_write",
|
||||
"production_write",
|
||||
"branch_protection_change",
|
||||
"force_push",
|
||||
"squash",
|
||||
"rebase",
|
||||
"merge_commit",
|
||||
"approval_impersonation",
|
||||
"commit_status_database_write",
|
||||
"database_write_outside_exact_task_access_token_revocation",
|
||||
"reuse_pr36_or_pr37_review",
|
||||
"plaintext_secret_persistence"
|
||||
],
|
||||
"runtime_controls": {
|
||||
"global_gitops_writes_enabled": false,
|
||||
"systemd_encrypted_credentials_required": true,
|
||||
"systemd_projection_mode": "0440_ROOT_ROOT_CREDENTIALS_DIRECTORY_ONLY",
|
||||
"systemd_gitea_token_embedded_name": "gitea-token",
|
||||
"sqlite_parent_directory_metadata_write_required": true,
|
||||
"sqlite_sibling_paths_read_only": true,
|
||||
"sqlite_quota_journal_mode": "WAL",
|
||||
"sqlite_quota_synchronous": "FULL",
|
||||
"status_evidence_digest_bound_in_signed_grant": true,
|
||||
"merge_method": "fast-forward-only",
|
||||
"commit_status_database_write_allowed": false
|
||||
},
|
||||
"approved_files": {
|
||||
"deploy/systemd/aios-task-credential-broker@.service": "95f8da02600b3c908013ffa723d79f6fca0c7a4332a077a7b673e97ce35bcd69",
|
||||
"deploy/systemd/aios-task-credential-expiry-sweeper.service": "72a336523983b2486300c4e52f2f01dce392aaf6ffd953041c209d204df85717",
|
||||
"deploy/systemd/aios-task-credential-expiry-sweeper.timer": "49ff9c80cbd0b44fd584dc7d29f4db0d042e33f325b1ed7af81a5be42f1cd011",
|
||||
"deploy/systemd/aios-task-merge-broker@.service": "4e6b8fcf541f225e58ea080107fc30deb0c4ac9eac70217ff9e57081245c106a",
|
||||
"deploy/systemd/aios-task-status-broker@.service": "378a2e8f658cb56cac5dcc151772ec6a00d86f6c1ce3106509b9aa87549dc9da",
|
||||
"evidence/governance/bootstrap-rfc3339-fix-20260801/ROLLBACK_PLAN.json": "3283f4c3d6bb7f34ac893789f7e9f5df6c68b7807203e6f471ad92e8e007ff3f",
|
||||
"evidence/governance/bootstrap-rfc3339-fix-20260801/TEST_SUMMARY.json": "c7d28139fddb9666e60fd62a9a27803dd00a1ebbccbbb75a5121cd8ed3d20fc2",
|
||||
"services/aios-single-owner-governance/aios_single_owner_governance/task_credential_broker.py": "3b2fbbf2698a2d8385a2bf9eccdebbea629642908cbc7a1e8251476475e0d647",
|
||||
"services/aios-single-owner-governance/aios_single_owner_governance/task_credential_expiry_sweeper.py": "8d383768eb0322c84a35282c126bee26a3e6419b125780424ffc7793a504efdf",
|
||||
"services/aios-single-owner-governance/aios_single_owner_governance/task_status_merge_broker.py": "697d8deccdf2fec902dab38a0436075d78a95c1c5b410e115ee74cf79f477e63",
|
||||
"services/aios-single-owner-governance/task-status-merge-grant-v1.schema.json": "34472624261203b5c1a215c141887ea3fed5d68fb3c503c7db2ad9a717862597",
|
||||
"tests/single_owner_governance/test_task_status_merge_broker.py": "af1c5844ee0f7d6907f8cb68d5f503f3bdbd59bb05150e5364eaf7bf2b37be1a"
|
||||
},
|
||||
"aggregate_bindings": {
|
||||
"broker_module_set_sha256": "c73ccaf8f60cf2446a5d4c0b4baf5456cb77961fda682285cb0a4f509ed49caa",
|
||||
"systemd_definition_set_sha256": "3591be96f1717e7a0a57957aafbb233eec2b1617a8fb938889ccd312d812d066",
|
||||
"grant_schema_sha256": "34472624261203b5c1a215c141887ea3fed5d68fb3c503c7db2ad9a717862597",
|
||||
"test_summary_sha256": "c7d28139fddb9666e60fd62a9a27803dd00a1ebbccbbb75a5121cd8ed3d20fc2",
|
||||
"python_executable_sha256": "e1efa562c2cc2e35521a5c9c9b9939921001ff8ca9708a13ef15ace68cc2ccd7"
|
||||
},
|
||||
"machine_gate_requirements": {
|
||||
"compileall": "PASS",
|
||||
"unit_tests": "PASS",
|
||||
"rfc3339_offset_tests": "PASS",
|
||||
"json_schema": "PASS",
|
||||
"systemd_validation": "PASS",
|
||||
"changed_path_secret_scan": "PASS",
|
||||
"fault_injection": "PASS",
|
||||
"scope_boundary_tests": "PASS"
|
||||
}
|
||||
}
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
{
|
||||
"approval_type": "TASK_STATUS_MERGE_BROKER_BOOTSTRAP",
|
||||
"schema_version": 1,
|
||||
"repository": "aios/aios",
|
||||
"expected_governance_base_oid": "8814dff20222625b60454c613b61ccf15fa20e51",
|
||||
"request_branch": "governance/bootstrap/task-status-merge-broker-20260801t0036z",
|
||||
"issued_at": "2026-08-01T00:46:50Z",
|
||||
"expires_at": "2026-08-01T12:46:50Z",
|
||||
"required_human_approvals": 1,
|
||||
"allowed_human_approvers": ["owner"],
|
||||
"require_latest_head_approval": true,
|
||||
"purpose": "Activate a task-scoped credential, status, and fast-forward-only merge broker so a new V2R1C4 Phase B renewal can satisfy existing branch protection without impersonation or long-lived credentials.",
|
||||
"phase_b_transition": {
|
||||
"old_pr_number": 36,
|
||||
"old_head_oid": "a8644fc172bd8ebbba17a16e6bf586c93fd1e793",
|
||||
"reuse_after_governance_base_change": false,
|
||||
"renewal_required_after_bootstrap_merge": true
|
||||
},
|
||||
"allowed_status_contexts": [
|
||||
"aios/branch-protection",
|
||||
"aios/evidence-closure",
|
||||
"aios/secret-scan",
|
||||
"aios/state-machine",
|
||||
"aios/validator"
|
||||
],
|
||||
"allowed_identities": ["codex", "gpt"],
|
||||
"allowed_operations": [
|
||||
"issue_task_status_credential",
|
||||
"issue_task_merge_credential",
|
||||
"sign_task_grant_after_owner_exact_head_review",
|
||||
"publish_exact_five_statuses",
|
||||
"fast_forward_only_merge",
|
||||
"readback_status_and_ref",
|
||||
"revoke_task_credentials",
|
||||
"remove_credential_projection"
|
||||
,"sweep_completed_or_expired_task_credentials"
|
||||
],
|
||||
"operation_limits": {
|
||||
"maximum_status_writes_per_grant": 5,
|
||||
"maximum_merges_per_grant": 1,
|
||||
"maximum_grant_lifetime_seconds": 43200,
|
||||
"automatic_revocation_required": true
|
||||
},
|
||||
"denied_operations": [
|
||||
"branch_push",
|
||||
"main_push",
|
||||
"tag_write",
|
||||
"registry_write",
|
||||
"baseline_write",
|
||||
"production_write",
|
||||
"branch_protection_change",
|
||||
"force_push",
|
||||
"squash",
|
||||
"rebase",
|
||||
"merge_commit",
|
||||
"approval_impersonation",
|
||||
"plaintext_secret_persistence"
|
||||
,"sign_unapproved_grant"
|
||||
,"reuse_superseded_pr"
|
||||
,"grant_signing_private_key_projection_to_broker"
|
||||
,"commit_status_database_write"
|
||||
,"database_write_outside_exact_access_token_revocation"
|
||||
],
|
||||
"runtime_controls": {
|
||||
"global_gitops_writes_enabled": false,
|
||||
"runtime_secret_only": true,
|
||||
"systemd_encrypted_credentials_required": true,
|
||||
"token_in_argv_allowed": false,
|
||||
"token_in_log_allowed": false,
|
||||
"separate_non_root_status_identity": true,
|
||||
"separate_non_root_merge_identity": true,
|
||||
"root_credential_broker_only": true,
|
||||
"sqlite_quota_and_hash_chain_audit": true,
|
||||
"hmac_audit_signature": true,
|
||||
"detached_ssh_grant_signature_required": true,
|
||||
"broker_receives_public_verification_key_only": true,
|
||||
"grant_signing_requires_live_owner_exact_head_review": true,
|
||||
"grant_signing_requires_open_pr_exact_head_base_tree_manifest": true,
|
||||
"status_evidence_digest_bound_in_signed_grant": true,
|
||||
"credential_issue_rechecks_local_pr_review_ref_tree_manifest": true,
|
||||
"gitea_write_repository_scope_compensated_by_locked_broker_sandbox": true,
|
||||
"token_revocation_exact_row_and_http_401_required": true,
|
||||
"gitea_1_22_token_delete_requires_basic_auth": true,
|
||||
"exact_access_token_row_revocation_exception": true,
|
||||
"access_token_revocation_binding_fields": ["id", "uid", "name", "scope"],
|
||||
"commit_status_database_write_allowed": false,
|
||||
"credential_completion_and_expiry_sweep_interval_seconds": 60,
|
||||
"sweeper_uses_same_per_task_audit_credential_as_issuer": true,
|
||||
"gitea_api_origin": "http://127.0.0.1:3000/api/v1",
|
||||
"merge_method": "fast-forward-only"
|
||||
},
|
||||
"approved_files": {
|
||||
"services/aios-single-owner-governance/aios_single_owner_governance/task_status_merge_broker.py": "79a7b6807dc9e6c167c94adbd3bb4a1b9ae17e54d2d6fd19bd6a38ce2f8b8e3f",
|
||||
"services/aios-single-owner-governance/aios_single_owner_governance/task_credential_broker.py": "678fccd8e61fa1806d44fbb81fe7f6e2a7b63bbd99d1323516073100b6e48ab5",
|
||||
"services/aios-single-owner-governance/aios_single_owner_governance/task_credential_expiry_sweeper.py": "8d383768eb0322c84a35282c126bee26a3e6419b125780424ffc7793a504efdf",
|
||||
"services/aios-single-owner-governance/task-status-merge-grant-v1.schema.json": "4528aa6dfeaa151deadb54bf1c462e1ff3e9526e0da3eb20144e6c01c19998f0",
|
||||
"tests/single_owner_governance/test_task_status_merge_broker.py": "65a285b29798b72d13e30562ce97529780643f3739e5d07c283dfa5995035dc4",
|
||||
"deploy/systemd/aios-task-status-broker@.service": "378a2e8f658cb56cac5dcc151772ec6a00d86f6c1ce3106509b9aa87549dc9da",
|
||||
"deploy/systemd/aios-task-merge-broker@.service": "4e6b8fcf541f225e58ea080107fc30deb0c4ac9eac70217ff9e57081245c106a",
|
||||
"deploy/systemd/aios-task-credential-broker@.service": "ec5dd2be87f3e02af89a40a615ccbd76a277f46aa29cc1a92fee5496d750cbce",
|
||||
"deploy/systemd/aios-task-credential-expiry-sweeper.service": "39726537a3b4f57614eba678309fd3009c8fa25448458827aef7ece0015be10d",
|
||||
"deploy/systemd/aios-task-credential-expiry-sweeper.timer": "49ff9c80cbd0b44fd584dc7d29f4db0d042e33f325b1ed7af81a5be42f1cd011"
|
||||
},
|
||||
"machine_gate_requirements": {
|
||||
"compileall": "PASS",
|
||||
"unit_tests": "PASS",
|
||||
"json_schema": "PASS",
|
||||
"systemd_validation": "PASS",
|
||||
"secret_scan": "PASS",
|
||||
"fault_injection": "PASS",
|
||||
"scope_boundary_tests": "PASS"
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,8 @@ PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadOnlyPaths=/data/aios-gitops/runtime/bin/gitea /data/aios-gitops/runtime/gitea-bin/custom/conf/app.ini /var/lib/aios-gitops/runtime/task-status-merge/%i/grant.json /var/lib/aios-gitops/runtime/task-status-merge/%i/grant.sig /var/lib/aios-gitops/runtime/task-status-merge/%i/credential-request.json /etc/aios-gitops/task-status-merge/allowed_signers
|
||||
ReadWritePaths=/data/aios-gitops/runtime/gitea-bin/data/gitea.db /run/aios-task-credential-broker /var/lib/aios-gitops/runtime/task-status-merge/%i/audit
|
||||
ReadOnlyPaths=/data/aios-gitops/runtime/bin/gitea /data/aios-gitops/runtime/gitea-bin/custom/conf/app.ini /var/lib/aios-gitops/runtime/task-status-merge/%i/grant.json /var/lib/aios-gitops/runtime/task-status-merge/%i/grant.sig /var/lib/aios-gitops/runtime/task-status-merge/%i/credential-request.json /etc/aios-gitops/task-status-merge/allowed_signers /data/aios-gitops/runtime/gitea-bin/data/actions_artifacts /data/aios-gitops/runtime/gitea-bin/data/actions_log /data/aios-gitops/runtime/gitea-bin/data/attachments /data/aios-gitops/runtime/gitea-bin/data/avatars /data/aios-gitops/runtime/gitea-bin/data/gitea.db.bak.20260708133830 /data/aios-gitops/runtime/gitea-bin/data/gitea.db.bak-20260710-0105 /data/aios-gitops/runtime/gitea-bin/data/home /data/aios-gitops/runtime/gitea-bin/data/indexers /data/aios-gitops/runtime/gitea-bin/data/jwt /data/aios-gitops/runtime/gitea-bin/data/packages /data/aios-gitops/runtime/gitea-bin/data/queues /data/aios-gitops/runtime/gitea-bin/data/repo-archive /data/aios-gitops/runtime/gitea-bin/data/repo-avatars /data/aios-gitops/runtime/gitea-bin/data/repositories /data/aios-gitops/runtime/gitea-bin/data/tmp
|
||||
ReadWritePaths=/data/aios-gitops/runtime/gitea-bin/data /run/aios-task-credential-broker /var/lib/aios-gitops/runtime/task-status-merge/%i/audit
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET
|
||||
IPAddressDeny=any
|
||||
IPAddressAllow=localhost
|
||||
|
||||
@@ -13,8 +13,8 @@ PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadOnlyPaths=/var/lib/aios-gitops/runtime/task-status-merge /etc/aios-gitops/task-status-merge/allowed_signers
|
||||
ReadWritePaths=/data/aios-gitops/runtime/gitea-bin/data/gitea.db /run/aios-task-credential-broker /var/lib/aios-gitops/runtime/task-status-merge
|
||||
ReadOnlyPaths=/etc/aios-gitops/task-status-merge/allowed_signers /data/aios-gitops/runtime/gitea-bin/data/actions_artifacts /data/aios-gitops/runtime/gitea-bin/data/actions_log /data/aios-gitops/runtime/gitea-bin/data/attachments /data/aios-gitops/runtime/gitea-bin/data/avatars /data/aios-gitops/runtime/gitea-bin/data/gitea.db.bak.20260708133830 /data/aios-gitops/runtime/gitea-bin/data/gitea.db.bak-20260710-0105 /data/aios-gitops/runtime/gitea-bin/data/home /data/aios-gitops/runtime/gitea-bin/data/indexers /data/aios-gitops/runtime/gitea-bin/data/jwt /data/aios-gitops/runtime/gitea-bin/data/packages /data/aios-gitops/runtime/gitea-bin/data/queues /data/aios-gitops/runtime/gitea-bin/data/repo-archive /data/aios-gitops/runtime/gitea-bin/data/repo-avatars /data/aios-gitops/runtime/gitea-bin/data/repositories /data/aios-gitops/runtime/gitea-bin/data/tmp
|
||||
ReadWritePaths=/data/aios-gitops/runtime/gitea-bin/data /run/aios-task-credential-broker /var/lib/aios-gitops/runtime/task-status-merge
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET
|
||||
IPAddressDeny=any
|
||||
IPAddressAllow=localhost
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"plan_id": "AIOS-BOOTSTRAP-RFC3339-FIX-ROLLBACK-V1",
|
||||
"schema_version": 1,
|
||||
"repository": "aios/aios",
|
||||
"approved_base_oid": "8814dff20222625b60454c613b61ccf15fa20e51",
|
||||
"governance_history_rewrite_allowed": false,
|
||||
"steps": [
|
||||
"stop_uncompleted_task_status_merge_units",
|
||||
"revoke_exact_task_tokens_and_verify_http_401",
|
||||
"remove_all_runtime_credential_projections",
|
||||
"remove_unactivated_runtime_units_and_non_root_task_accounts",
|
||||
"verify_governance_refs_and_production_refs_unchanged"
|
||||
],
|
||||
"internal_token_rotation": {
|
||||
"rollback_allowed_before_old_secret_revocation": true,
|
||||
"rollback_source": "root_only_encrypted_runtime_credential",
|
||||
"rollback_allowed_after_old_secret_revocation": false,
|
||||
"post_revocation_recovery": "issue_another_replacement_secret_under_new_governed_operation",
|
||||
"plaintext_secret_in_evidence_allowed": false
|
||||
},
|
||||
"required_closure": {
|
||||
"temporary_accounts_remaining": 0,
|
||||
"temporary_credentials_remaining": 0,
|
||||
"credential_projections_remaining": 0,
|
||||
"old_task_tokens_http_status": 401,
|
||||
"AIOS_GITOPS_WRITES_ENABLED": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"schema_id": "AIOS_BOOTSTRAP_RFC3339_FIX_TEST_SUMMARY",
|
||||
"schema_version": 1,
|
||||
"candidate_tree_before_manifest": "c72b13bfe0656de9deeff9652e3e13ada81b110e",
|
||||
"local_unittest": {
|
||||
"tests_passed": 19,
|
||||
"tests_failed": 0,
|
||||
"tests_skipped": 0
|
||||
},
|
||||
"remote_posix": {
|
||||
"pytest_tests_passed": 19,
|
||||
"pytest_subtests_passed": 16,
|
||||
"tests_failed": 0,
|
||||
"tests_skipped": 0,
|
||||
"compileall": "PASS",
|
||||
"json_schema": "PASS",
|
||||
"systemd_validation": "PASS"
|
||||
},
|
||||
"secret_scan": {
|
||||
"scanner": "gitleaks",
|
||||
"exact_changed_paths": "PASS",
|
||||
"exact_changed_path_findings": 0,
|
||||
"whole_tree_existing_findings": 10,
|
||||
"whole_tree_findings_claimed_as_new": false
|
||||
},
|
||||
"python_executable": {
|
||||
"path": "/usr/bin/python3.12",
|
||||
"sha256": "e1efa562c2cc2e35521a5c9c9b9939921001ff8ca9708a13ef15ace68cc2ccd7"
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -106,15 +106,16 @@ class TaskCredentialBroker:
|
||||
issued = subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.strip()
|
||||
if len(issued) < 32 or b"\n" in issued or b"\x00" in issued:
|
||||
raise ContractDenied("ISSUED_TOKEN_INVALID")
|
||||
subprocess.run([str(SYSTEMD_CREDS), "encrypt", "-", str(encrypted_path)], input=issued, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
|
||||
subprocess.run([str(SYSTEMD_CREDS), "encrypt", "--name=gitea-token", "-", str(encrypted_path)], input=issued, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
|
||||
os.chmod(encrypted_path, 0o400)
|
||||
connection = sqlite3.connect(f"file:{GITEA_DB}?mode=ro", uri=True)
|
||||
try:
|
||||
row = connection.execute("SELECT t.id,u.id FROM access_token t JOIN user u ON u.id=t.uid WHERE u.lower_name=? AND t.name=? AND t.scope='write:repository'", (identity, token_name)).fetchone()
|
||||
rows = connection.execute("SELECT t.id,u.id FROM access_token t JOIN user u ON u.id=t.uid WHERE u.lower_name=? AND t.name=? AND t.scope='write:repository'", (identity, token_name)).fetchall()
|
||||
finally:
|
||||
connection.close()
|
||||
if row is None:
|
||||
if len(rows) != 1:
|
||||
raise ContractDenied("TOKEN_DATABASE_BINDING_MISSING")
|
||||
row = rows[0]
|
||||
receipt = {"status": "PASS", "operation": "credential_provision", "identity": identity, "token_name": token_name, "token_id": int(row[0]), "token_uid": int(row[1]), "grant_sha256": self.grant.digest, "credential_ref": f"systemd-credential-encrypted://{encrypted_path.name}"}
|
||||
return self._audit(receipt)
|
||||
|
||||
|
||||
+47
-11
@@ -40,8 +40,12 @@ FIELDS = frozenset({
|
||||
"baseline_write_allowed", "production_write_allowed", "issued_at", "expires_at",
|
||||
"audit_key_ref", "grant_signer_identity", "grant_signature_namespace",
|
||||
"status_evidence_sha256", "supersedes_pr_number", "supersedes_head_oid",
|
||||
"renewal_required",
|
||||
"renewal_required", "timestamp_contract", "canonical_timezone",
|
||||
"raw_timestamp_preserved", "naive_datetime_allowed",
|
||||
})
|
||||
RFC3339 = re.compile(
|
||||
r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$"
|
||||
)
|
||||
|
||||
|
||||
class ContractDenied(RuntimeError):
|
||||
@@ -53,12 +57,21 @@ def canonical(value: Any) -> bytes:
|
||||
|
||||
|
||||
def _time(value: str) -> datetime:
|
||||
if not isinstance(value, str) or not value.endswith("Z"):
|
||||
if not isinstance(value, str) or value != value.strip() or not RFC3339.fullmatch(value):
|
||||
raise ContractDenied("TIMESTAMP_INVALID")
|
||||
try:
|
||||
return datetime.fromisoformat(value.replace("Z", "+00:00"))
|
||||
normalized = value[:-1] + "+00:00" if value.endswith("Z") else value
|
||||
parsed = datetime.fromisoformat(normalized)
|
||||
except ValueError as exc:
|
||||
raise ContractDenied("TIMESTAMP_INVALID") from exc
|
||||
if parsed.tzinfo is None or parsed.utcoffset() is None:
|
||||
raise ContractDenied("TIMESTAMP_TIMEZONE_REQUIRED")
|
||||
return parsed.astimezone(timezone.utc)
|
||||
|
||||
|
||||
def _utc_timestamp(value: datetime) -> str:
|
||||
normalized = value.astimezone(timezone.utc).isoformat()
|
||||
return normalized[:-6] + "Z" if normalized.endswith("+00:00") else normalized
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -109,6 +122,11 @@ class TaskGrant:
|
||||
raise ContractDenied("AUDIT_KEY_REFERENCE_INVALID")
|
||||
if value["grant_signer_identity"] != "aios-task-grant-signer" or value["grant_signature_namespace"] != "aios-task-status-merge-grant-v1":
|
||||
raise ContractDenied("GRANT_SIGNATURE_POLICY_INVALID")
|
||||
if (value["timestamp_contract"] != "RFC3339_OFFSET_AWARE_V2"
|
||||
or value["canonical_timezone"] != "UTC"
|
||||
or value["raw_timestamp_preserved"] is not True
|
||||
or value["naive_datetime_allowed"] is not False):
|
||||
raise ContractDenied("TIMESTAMP_CONTRACT_INVALID")
|
||||
issued, expires = _time(value["issued_at"]), _time(value["expires_at"])
|
||||
current = now or datetime.now(timezone.utc)
|
||||
lifetime = (expires - issued).total_seconds()
|
||||
@@ -158,7 +176,16 @@ def read_runtime_secret(path: Path) -> bytes:
|
||||
descriptor = os.open(path, flags)
|
||||
try:
|
||||
metadata = os.fstat(descriptor)
|
||||
if not stat.S_ISREG(metadata.st_mode) or stat.S_IMODE(metadata.st_mode) & 0o077:
|
||||
mode = stat.S_IMODE(metadata.st_mode)
|
||||
credential_directory = os.environ.get("CREDENTIALS_DIRECTORY")
|
||||
systemd_projection = (
|
||||
credential_directory is not None
|
||||
and path.parent == Path(credential_directory)
|
||||
and mode == 0o440
|
||||
and metadata.st_uid == 0
|
||||
and metadata.st_gid == 0
|
||||
)
|
||||
if not stat.S_ISREG(metadata.st_mode) or (mode != 0o400 and not systemd_projection):
|
||||
raise ContractDenied("CREDENTIAL_FILE_UNSAFE")
|
||||
value = os.read(descriptor, 16 * 1024 + 1)
|
||||
finally:
|
||||
@@ -209,8 +236,13 @@ class QuotaJournal:
|
||||
self.path = Path(path)
|
||||
self.grant = grant
|
||||
self.connection = sqlite3.connect(self.path, isolation_level=None)
|
||||
self.connection.execute("PRAGMA journal_mode=WAL")
|
||||
journal_mode = self.connection.execute("PRAGMA journal_mode=WAL").fetchone()
|
||||
if journal_mode is None or str(journal_mode[0]).lower() != "wal":
|
||||
raise ContractDenied("JOURNAL_MODE_INVALID")
|
||||
self.connection.execute("PRAGMA synchronous=FULL")
|
||||
synchronous = self.connection.execute("PRAGMA synchronous").fetchone()
|
||||
if synchronous is None or int(synchronous[0]) != 2:
|
||||
raise ContractDenied("JOURNAL_SYNCHRONOUS_INVALID")
|
||||
self.connection.execute("CREATE TABLE IF NOT EXISTS quota(operation TEXT PRIMARY KEY, state TEXT NOT NULL, effect_sha256 TEXT)")
|
||||
self.connection.execute("CREATE TABLE IF NOT EXISTS audit(seq INTEGER PRIMARY KEY AUTOINCREMENT, event BLOB NOT NULL, previous_hash TEXT NOT NULL, event_hash TEXT NOT NULL UNIQUE)")
|
||||
|
||||
@@ -301,10 +333,14 @@ def _verify_owner_binding(api: API, grant: TaskGrant) -> tuple[dict[str, Any], d
|
||||
latest = max(owners, key=lambda item: int(item.get("id") or -1))
|
||||
if latest.get("state") != "APPROVED" or latest.get("commit_id") != head or latest.get("official") is not True or latest.get("stale") is not False or latest.get("dismissed") is not False:
|
||||
raise ContractDenied("OWNER_EXACT_HEAD_APPROVAL_MISSING")
|
||||
submitted = _time(str(latest.get("submitted_at") or ""))
|
||||
raw_timestamp = str(latest.get("submitted_at") or "")
|
||||
submitted = _time(raw_timestamp)
|
||||
if submitted < grant.issued_at or submitted >= grant.expires_at:
|
||||
raise ContractDenied("OWNER_APPROVAL_EXPIRED")
|
||||
return pr, latest
|
||||
normalized_review = dict(latest)
|
||||
normalized_review["review_timestamp_raw"] = raw_timestamp
|
||||
normalized_review["review_timestamp_utc"] = _utc_timestamp(submitted)
|
||||
return pr, normalized_review
|
||||
|
||||
|
||||
def _signed_receipt(value: dict[str, Any], key: bytes) -> dict[str, Any]:
|
||||
@@ -325,7 +361,7 @@ class StatusBroker:
|
||||
mapped = {item.get("context"): item for item in evidence if isinstance(item, dict)}
|
||||
if set(mapped) != ALLOWED_CONTEXTS or any(set(item) != {"context", "evidence_sha256"} or not SHA256.fullmatch(str(item["evidence_sha256"])) or item["evidence_sha256"] != self.grant.raw["status_evidence_sha256"].get(context) for context, item in mapped.items()):
|
||||
raise ContractDenied("EVIDENCE_SET_INVALID")
|
||||
_verify_owner_binding(self.api, self.grant)
|
||||
_, review = _verify_owner_binding(self.api, self.grant)
|
||||
self.journal.reserve("statuses")
|
||||
published = []
|
||||
head = self.grant.raw["target_commit"]
|
||||
@@ -360,7 +396,7 @@ class StatusBroker:
|
||||
for context, item in latest.items()
|
||||
):
|
||||
raise ContractDenied("STATUS_READBACK_INVALID")
|
||||
receipt = {"status": "PASS", "operation": "publish_statuses", "grant_sha256": self.grant.digest, "target_commit": head, "published_count": 5, "published": published}
|
||||
receipt = {"status": "PASS", "operation": "publish_statuses", "grant_sha256": self.grant.digest, "target_commit": head, "published_count": 5, "published": published, "review_timestamp_raw": review["review_timestamp_raw"], "review_timestamp_utc": review["review_timestamp_utc"]}
|
||||
receipt["audit_signature"] = hmac.new(self.audit_key, canonical(receipt), hashlib.sha256).hexdigest()
|
||||
receipt["audit_chain_hash"] = self.journal.append(receipt)
|
||||
self.journal.commit("statuses", receipt)
|
||||
@@ -384,7 +420,7 @@ class MergeBroker:
|
||||
receipt["audit_chain_hash"] = self.journal.append(receipt)
|
||||
self.journal.commit("merge", receipt)
|
||||
return receipt
|
||||
pr, _ = _verify_owner_binding(self.api, self.grant)
|
||||
pr, review = _verify_owner_binding(self.api, self.grant)
|
||||
statuses = self.api.get(f"/repos/aios/aios/commits/{head}/statuses")
|
||||
latest = _latest_statuses(statuses)
|
||||
if set(latest) != ALLOWED_CONTEXTS or any(item.get("status", item.get("state")) != "success" or item.get("sha", head) != head or (item.get("creator") or {}).get("login") != self.grant.raw["status_identity"] or not str(item.get("description") or "").endswith(self.grant.raw["status_evidence_sha256"][context]) for context, item in latest.items()):
|
||||
@@ -395,7 +431,7 @@ class MergeBroker:
|
||||
observed_branch = self.api.get("/repos/aios/aios/branches/governance%2Fapprovals")
|
||||
if observed_pr.get("merged") is not True or observed_pr.get("merge_commit_sha") != head or (observed_branch.get("commit") or {}).get("id") != head:
|
||||
raise ContractDenied("FAST_FORWARD_RESULT_MISMATCH")
|
||||
receipt = {"status": "PASS", "operation": "fast_forward_merge", "grant_sha256": self.grant.digest, "pr_number": pr_number, "governance_oid": head, "merge_method": "fast-forward-only"}
|
||||
receipt = {"status": "PASS", "operation": "fast_forward_merge", "grant_sha256": self.grant.digest, "pr_number": pr_number, "governance_oid": head, "merge_method": "fast-forward-only", "review_timestamp_raw": review["review_timestamp_raw"], "review_timestamp_utc": review["review_timestamp_utc"]}
|
||||
receipt["audit_signature"] = hmac.new(self.audit_key, canonical(receipt), hashlib.sha256).hexdigest()
|
||||
receipt["audit_chain_hash"] = self.journal.append(receipt)
|
||||
self.journal.commit("merge", receipt)
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
"baseline_write_allowed", "production_write_allowed", "issued_at", "expires_at",
|
||||
"audit_key_ref", "grant_signer_identity", "grant_signature_namespace",
|
||||
"status_evidence_sha256", "supersedes_pr_number", "supersedes_head_oid",
|
||||
"renewal_required"
|
||||
"renewal_required", "timestamp_contract", "canonical_timezone",
|
||||
"raw_timestamp_preserved", "naive_datetime_allowed"
|
||||
],
|
||||
"properties": {
|
||||
"schema_id": {"const": "AIOS_TASK_STATUS_MERGE_GRANT"},
|
||||
@@ -40,6 +41,10 @@
|
||||
"supersedes_pr_number": {"const": 36},
|
||||
"supersedes_head_oid": {"const": "a8644fc172bd8ebbba17a16e6bf586c93fd1e793"},
|
||||
"renewal_required": {"const": true},
|
||||
"timestamp_contract": {"const": "RFC3339_OFFSET_AWARE_V2"},
|
||||
"canonical_timezone": {"const": "UTC"},
|
||||
"raw_timestamp_preserved": {"const": true},
|
||||
"naive_datetime_allowed": {"const": false},
|
||||
"status_identity": {"enum": ["codex", "gpt"]},
|
||||
"merge_identity": {"enum": ["codex", "gpt"]},
|
||||
"status_contexts": {
|
||||
|
||||
@@ -21,8 +21,11 @@ from aios_single_owner_governance.task_status_merge_broker import ( # noqa: E40
|
||||
QuotaJournal,
|
||||
StatusBroker,
|
||||
TaskGrant,
|
||||
_time,
|
||||
_utc_timestamp,
|
||||
canonical,
|
||||
load_signed_task_grant,
|
||||
read_runtime_secret,
|
||||
)
|
||||
from aios_single_owner_governance.task_credential_broker import TaskCredentialBroker # noqa: E402
|
||||
from aios_single_owner_governance import task_credential_expiry_sweeper as sweeper # noqa: E402
|
||||
@@ -51,6 +54,10 @@ def grant() -> dict:
|
||||
"supersedes_pr_number": 36,
|
||||
"supersedes_head_oid": OLD_HEAD,
|
||||
"renewal_required": True,
|
||||
"timestamp_contract": "RFC3339_OFFSET_AWARE_V2",
|
||||
"canonical_timezone": "UTC",
|
||||
"raw_timestamp_preserved": True,
|
||||
"naive_datetime_allowed": False,
|
||||
"status_identity": "codex",
|
||||
"merge_identity": "codex",
|
||||
"status_contexts": sorted(ALLOWED_CONTEXTS),
|
||||
@@ -75,7 +82,8 @@ def grant() -> dict:
|
||||
|
||||
class FakeAPI:
|
||||
def __init__(self, *, with_statuses: bool = False, fail_status_after: int | None = None,
|
||||
fail_after_merge: bool = False):
|
||||
fail_after_merge: bool = False, review_timestamp: str | None = None,
|
||||
reviewer: str = "owner", review_commit: str = HEAD):
|
||||
self.posts: list[tuple[str, dict]] = []
|
||||
self.merged = False
|
||||
self.status_rows = [{"id": index + 1, "context": value, "status": "success", "sha": HEAD,
|
||||
@@ -84,18 +92,21 @@ class FakeAPI:
|
||||
self.fail_status_after = fail_status_after
|
||||
self.fail_after_merge = fail_after_merge
|
||||
self.base = BASE
|
||||
self.review_timestamp = review_timestamp or datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
||||
self.reviewer = reviewer
|
||||
self.review_commit = review_commit
|
||||
|
||||
def get(self, path: str):
|
||||
if path.endswith("/reviews"):
|
||||
return [{
|
||||
"id": 76,
|
||||
"state": "APPROVED",
|
||||
"commit_id": HEAD,
|
||||
"commit_id": self.review_commit,
|
||||
"official": True,
|
||||
"stale": False,
|
||||
"dismissed": False,
|
||||
"submitted_at": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
|
||||
"user": {"id": 2, "login": "owner"},
|
||||
"submitted_at": self.review_timestamp,
|
||||
"user": {"id": 2 if self.reviewer == "owner" else 42, "login": self.reviewer},
|
||||
}]
|
||||
if path.endswith("/statuses"):
|
||||
return list(self.status_rows)
|
||||
@@ -166,6 +177,54 @@ class TaskStatusMergeBrokerTests(unittest.TestCase):
|
||||
with self.assertRaisesRegex(ContractDenied, "STATUS_QUOTA_EXHAUSTED"):
|
||||
StatusBroker(item, journal, api, b"audit-key").publish(evidence())
|
||||
|
||||
def test_rfc3339_offset_parser_normalizes_to_utc(self):
|
||||
cases = {
|
||||
"2026-08-01T02:03:02Z": "2026-08-01T02:03:02Z",
|
||||
"2026-08-01T10:03:02+08:00": "2026-08-01T02:03:02Z",
|
||||
"2026-07-31T18:03:02-08:00": "2026-08-01T02:03:02Z",
|
||||
"2026-08-01T10:03:02.123456+08:00": "2026-08-01T02:03:02.123456Z",
|
||||
}
|
||||
for raw, expected in cases.items():
|
||||
with self.subTest(raw=raw):
|
||||
self.assertEqual(_utc_timestamp(_time(raw)), expected)
|
||||
|
||||
def test_rfc3339_offset_parser_rejects_ambiguous_or_invalid_values(self):
|
||||
invalid = [
|
||||
"2026-08-01T10:03:02", "2026-08-01", "2026-08-01T10:03:02+0800",
|
||||
"2026-08-01T10:03:02+25:00", "2026-08-01T10:03:02z",
|
||||
" 2026-08-01T10:03:02+08:00", "2026-08-01T10:03:02+08:00 ",
|
||||
None, 1, [],
|
||||
]
|
||||
for raw in invalid:
|
||||
with self.subTest(raw=raw):
|
||||
with self.assertRaisesRegex(ContractDenied, "TIMESTAMP_INVALID"):
|
||||
_time(raw) # type: ignore[arg-type]
|
||||
|
||||
def test_offset_review_preserves_raw_and_canonical_utc_in_receipt(self):
|
||||
value = grant()
|
||||
value["issued_at"] = "2026-08-01T00:00:00Z"
|
||||
value["expires_at"] = "2026-08-01T12:00:00Z"
|
||||
item = TaskGrant.from_dict(value, now=datetime(2026, 8, 1, 3, tzinfo=timezone.utc))
|
||||
api = FakeAPI(review_timestamp="2026-08-01T10:03:02+08:00")
|
||||
receipt = StatusBroker(item, self.journal(item), api, b"audit-key").publish(evidence())
|
||||
self.assertEqual(receipt["review_timestamp_raw"], "2026-08-01T10:03:02+08:00")
|
||||
self.assertEqual(receipt["review_timestamp_utc"], "2026-08-01T02:03:02Z")
|
||||
|
||||
def test_review_expiry_boundary_is_fail_closed(self):
|
||||
value = grant()
|
||||
value["issued_at"] = "2026-08-01T00:00:00Z"
|
||||
value["expires_at"] = "2026-08-01T02:03:02Z"
|
||||
item = TaskGrant.from_dict(value, now=datetime(2026, 8, 1, 1, tzinfo=timezone.utc))
|
||||
for raw in ("2026-08-01T02:03:02Z", "2026-08-01T02:03:03Z"):
|
||||
with self.subTest(raw=raw), self.assertRaisesRegex(ContractDenied, "OWNER_APPROVAL_EXPIRED"):
|
||||
StatusBroker(item, self.journal(item), FakeAPI(review_timestamp=raw), b"audit-key").publish(evidence())
|
||||
|
||||
def test_wrong_reviewer_and_reviewed_head_remain_rejected(self):
|
||||
item = TaskGrant.from_dict(grant())
|
||||
for api in (FakeAPI(reviewer="codex"), FakeAPI(review_commit="e" * 40)):
|
||||
with self.assertRaisesRegex(ContractDenied, "OWNER_EXACT_HEAD_APPROVAL_MISSING"):
|
||||
StatusBroker(item, self.journal(item), api, b"audit-key").publish(evidence())
|
||||
|
||||
def test_merge_broker_uses_fast_forward_only_and_binds_exact_head(self):
|
||||
item = TaskGrant.from_dict(grant())
|
||||
api = FakeAPI(with_statuses=True)
|
||||
@@ -236,6 +295,30 @@ class TaskStatusMergeBrokerTests(unittest.TestCase):
|
||||
with self.assertRaisesRegex(ContractDenied, "IDENTITY_NOT_ALLOWED"):
|
||||
TaskGrant.from_dict(value)
|
||||
|
||||
def test_systemd_credential_projection_mode_and_name_are_supported_exactly(self):
|
||||
metadata = mock.Mock(st_mode=0o100440, st_uid=0, st_gid=0)
|
||||
credential = Path("/run/credentials/unit/gitea-token")
|
||||
with mock.patch.dict("os.environ", {"CREDENTIALS_DIRECTORY": str(credential.parent)}), \
|
||||
mock.patch("aios_single_owner_governance.task_status_merge_broker.os.open", return_value=7), \
|
||||
mock.patch("aios_single_owner_governance.task_status_merge_broker.os.fstat", return_value=metadata), \
|
||||
mock.patch("aios_single_owner_governance.task_status_merge_broker.os.read", return_value=b"x" * 40), \
|
||||
mock.patch("aios_single_owner_governance.task_status_merge_broker.os.close"):
|
||||
self.assertEqual(read_runtime_secret(credential), b"x" * 40)
|
||||
item = TaskGrant.from_dict(grant())
|
||||
broker = TaskCredentialBroker(item, self.root / "provision-audit", b"audit-key")
|
||||
connection = mock.MagicMock()
|
||||
connection.execute.return_value.fetchall.return_value = [(101, 42)]
|
||||
issued = mock.Mock(stdout=b"x" * 40)
|
||||
encrypted = mock.Mock(stdout=b"")
|
||||
with mock.patch("aios_single_owner_governance.task_credential_broker.os.geteuid", return_value=0, create=True), \
|
||||
mock.patch("aios_single_owner_governance.task_credential_broker._safe_absolute"), \
|
||||
mock.patch("aios_single_owner_governance.task_credential_broker._verify_local_governance_binding"), \
|
||||
mock.patch("aios_single_owner_governance.task_credential_broker.os.chmod"), \
|
||||
mock.patch("aios_single_owner_governance.task_credential_broker.subprocess.run", side_effect=[issued, encrypted]) as runner, \
|
||||
mock.patch("aios_single_owner_governance.task_credential_broker.sqlite3.connect", return_value=connection):
|
||||
broker.provision(identity="codex", token_name="pr37-dddddddddddd-status", encrypted_path=Path("/run/aios-task-credential-broker/status.cred"))
|
||||
self.assertIn("--name=gitea-token", runner.call_args_list[1].args[0])
|
||||
|
||||
def test_expiry_sweeper_revokes_projected_token_after_grant_expiry(self):
|
||||
item = TaskGrant.from_dict(grant())
|
||||
runtime_root = self.root / "runtime"
|
||||
|
||||
Reference in New Issue
Block a user