plugin-scanner 2.0.855__py3-none-any.whl → 2.0.856__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codex_plugin_scanner/guard/store.py +4 -1
- codex_plugin_scanner/guard/store_connection_schema.py +25 -1
- codex_plugin_scanner/guard/store_secret_policy_integrity.py +325 -24
- codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.855.dist-info → plugin_scanner-2.0.856.dist-info}/METADATA +1 -1
- {plugin_scanner-2.0.855.dist-info → plugin_scanner-2.0.856.dist-info}/RECORD +9 -9
- {plugin_scanner-2.0.855.dist-info → plugin_scanner-2.0.856.dist-info}/WHEEL +0 -0
- {plugin_scanner-2.0.855.dist-info → plugin_scanner-2.0.856.dist-info}/entry_points.txt +0 -0
- {plugin_scanner-2.0.855.dist-info → plugin_scanner-2.0.856.dist-info}/licenses/LICENSE +0 -0
|
@@ -19,7 +19,10 @@ from .store_oauth import StoreOAuthConnectMixin
|
|
|
19
19
|
from .store_policy import StorePolicyMixin
|
|
20
20
|
from .store_policy_integrity_runtime import StorePolicyIntegrityAdminMixin
|
|
21
21
|
from .store_receipts import StoreReceiptsRuntimeMixin
|
|
22
|
-
from .store_secret_policy_integrity import
|
|
22
|
+
from .store_secret_policy_integrity import (
|
|
23
|
+
StoreSecretPolicyIntegrityMixin,
|
|
24
|
+
_POLICY_INTEGRITY_LOOKUP_UNSET,
|
|
25
|
+
)
|
|
23
26
|
from .store_sessions import StoreSessionsMixin
|
|
24
27
|
|
|
25
28
|
|
|
@@ -6,6 +6,7 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
# ruff: noqa: F403,F405
|
|
8
8
|
from .store_base import *
|
|
9
|
+
from .store_secret_policy_integrity import _POLICY_INTEGRITY_LOOKUP_UNSET
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def _facade_store_attr(name: str, fallback: object) -> object:
|
|
@@ -42,6 +43,14 @@ def _sleep_compat(seconds: float) -> None:
|
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
class StoreConnectionSchemaMixin:
|
|
46
|
+
_startup_prefetched_policy_integrity_secret_material: object | tuple[bytes | None, str | None] = (
|
|
47
|
+
_POLICY_INTEGRITY_LOOKUP_UNSET
|
|
48
|
+
)
|
|
49
|
+
_startup_prefetched_policy_integrity_trusted_state: object | dict[str, object] | None = (
|
|
50
|
+
_POLICY_INTEGRITY_LOOKUP_UNSET
|
|
51
|
+
)
|
|
52
|
+
_startup_prefetched_policy_integrity_repair_failed = False
|
|
53
|
+
|
|
45
54
|
@contextmanager
|
|
46
55
|
def _connect(self) -> Iterator[sqlite3.Connection]:
|
|
47
56
|
connection = sqlite3.connect(self.path, timeout=SQLITE_CONNECT_TIMEOUT_SECONDS)
|
|
@@ -524,7 +533,22 @@ class StoreConnectionSchemaMixin:
|
|
|
524
533
|
self._record_schema_version(connection, version=2)
|
|
525
534
|
self._enable_wal_mode(connection)
|
|
526
535
|
self._repair_store_permissions()
|
|
527
|
-
|
|
536
|
+
# Prime policy-integrity secrets outside the SQLite transaction. Some
|
|
537
|
+
# credential-store lookups can block long enough to stall other Guard
|
|
538
|
+
# processes if initialization still holds the writer lock.
|
|
539
|
+
self._startup_prefetched_policy_integrity_secret_material = self._policy_integrity_secret_material(create=False)
|
|
540
|
+
self._startup_prefetched_policy_integrity_trusted_state = self._load_policy_integrity_control_state(
|
|
541
|
+
create=False
|
|
542
|
+
)
|
|
543
|
+
self._startup_prefetched_policy_integrity_repair_failed = False
|
|
544
|
+
self._prepare_startup_prefetched_policy_integrity_state()
|
|
545
|
+
try:
|
|
546
|
+
with self._connect() as connection:
|
|
547
|
+
self._refresh_policy_integrity_state(connection, now=_now(), create_key=False)
|
|
548
|
+
finally:
|
|
549
|
+
self._startup_prefetched_policy_integrity_secret_material = _POLICY_INTEGRITY_LOOKUP_UNSET
|
|
550
|
+
self._startup_prefetched_policy_integrity_trusted_state = _POLICY_INTEGRITY_LOOKUP_UNSET
|
|
551
|
+
self._startup_prefetched_policy_integrity_repair_failed = False
|
|
528
552
|
|
|
529
553
|
@staticmethod
|
|
530
554
|
def _enable_wal_mode(connection: sqlite3.Connection) -> None:
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
+
from .policy_integrity import POLICY_INTEGRITY_VERSION
|
|
8
|
+
|
|
7
9
|
# ruff: noqa: F403,F405
|
|
8
10
|
from .store_base import *
|
|
9
11
|
|
|
@@ -43,6 +45,9 @@ def _build_policy_integrity_secret_store_compat() -> SystemKeyringSecretStore |
|
|
|
43
45
|
return cast(SystemKeyringSecretStore, secret_store)
|
|
44
46
|
|
|
45
47
|
|
|
48
|
+
_POLICY_INTEGRITY_LOOKUP_UNSET = object()
|
|
49
|
+
|
|
50
|
+
|
|
46
51
|
class StoreSecretPolicyIntegrityMixin:
|
|
47
52
|
def __init__(self, guard_home: Path, *, guard_event_queue_limit: int = 1000) -> None:
|
|
48
53
|
self.guard_home = guard_home
|
|
@@ -53,6 +58,13 @@ class StoreSecretPolicyIntegrityMixin:
|
|
|
53
58
|
self._cached_oauth_secret_payload: tuple[str, str, str] | None = None
|
|
54
59
|
self._cached_policy_integrity_secret_material: tuple[str | None, float, tuple[bytes, str]] | None = None
|
|
55
60
|
self._cached_policy_integrity_control_state: tuple[str | None, float, dict[str, object]] | None = None
|
|
61
|
+
self._startup_prefetched_policy_integrity_secret_material: object | tuple[bytes | None, str | None] = (
|
|
62
|
+
_POLICY_INTEGRITY_LOOKUP_UNSET
|
|
63
|
+
)
|
|
64
|
+
self._startup_prefetched_policy_integrity_trusted_state: object | dict[str, object] | None = (
|
|
65
|
+
_POLICY_INTEGRITY_LOOKUP_UNSET
|
|
66
|
+
)
|
|
67
|
+
self._startup_prefetched_policy_integrity_repair_failed = False
|
|
56
68
|
self._policy_integrity_key_ref = self._build_scoped_secret_ref(_POLICY_INTEGRITY_KEY_REF)
|
|
57
69
|
self._policy_integrity_control_ref = self._build_scoped_secret_ref(_POLICY_INTEGRITY_CONTROL_REF)
|
|
58
70
|
self._oauth_local_credentials_ref = self._build_scoped_secret_ref(_OAUTH_LOCAL_CREDENTIALS_REF)
|
|
@@ -466,6 +478,7 @@ class StoreSecretPolicyIntegrityMixin:
|
|
|
466
478
|
where source not in {_REMOTE_POLICY_SOURCE_PLACEHOLDERS}
|
|
467
479
|
and (
|
|
468
480
|
integrity_version is null
|
|
481
|
+
or integrity_version != {POLICY_INTEGRITY_VERSION}
|
|
469
482
|
or payload_hash is null
|
|
470
483
|
or payload_mac is null
|
|
471
484
|
or integrity_key_id is null
|
|
@@ -591,21 +604,57 @@ class StoreSecretPolicyIntegrityMixin:
|
|
|
591
604
|
"version": _POLICY_INTEGRITY_CONTROL_VERSION,
|
|
592
605
|
}
|
|
593
606
|
else:
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
+
(
|
|
608
|
+
has_legacy_rows,
|
|
609
|
+
pending_candidates,
|
|
610
|
+
pending_valid,
|
|
611
|
+
current_valid,
|
|
612
|
+
) = self._classify_policy_integrity_pending_generation_rows(
|
|
613
|
+
rows,
|
|
614
|
+
key=key,
|
|
615
|
+
key_id=key_id,
|
|
616
|
+
current_generation=current_generation,
|
|
617
|
+
pending_generation=pending_generation,
|
|
618
|
+
)
|
|
619
|
+
if has_legacy_rows:
|
|
620
|
+
next_state = dict(trusted_state)
|
|
621
|
+
if pending_candidates > 0 and current_valid == len(rows):
|
|
622
|
+
next_state["pending_generation"] = None
|
|
623
|
+
elif pending_valid == len(rows):
|
|
624
|
+
next_state = {
|
|
625
|
+
"cutover_complete": True,
|
|
626
|
+
"generation": pending_generation,
|
|
627
|
+
"pending_generation": None,
|
|
628
|
+
"version": _POLICY_INTEGRITY_CONTROL_VERSION,
|
|
629
|
+
}
|
|
630
|
+
elif current_valid == len(rows):
|
|
631
|
+
next_state = dict(trusted_state)
|
|
632
|
+
next_state["pending_generation"] = None
|
|
633
|
+
else:
|
|
634
|
+
next_state = dict(trusted_state)
|
|
635
|
+
if not self._store_policy_integrity_control_state(next_state):
|
|
636
|
+
raise RuntimeError("Guard could not persist the policy integrity control state.")
|
|
637
|
+
return next_state
|
|
638
|
+
|
|
639
|
+
def _classify_policy_integrity_pending_generation_rows(
|
|
640
|
+
self,
|
|
641
|
+
rows: Sequence[sqlite3.Row],
|
|
642
|
+
*,
|
|
643
|
+
key: bytes,
|
|
644
|
+
key_id: str,
|
|
645
|
+
current_generation: int,
|
|
646
|
+
pending_generation: int,
|
|
647
|
+
) -> tuple[bool, int, int, int]:
|
|
648
|
+
has_legacy_rows = False
|
|
649
|
+
pending_candidates = 0
|
|
650
|
+
pending_valid = 0
|
|
651
|
+
current_valid = 0
|
|
652
|
+
for row in rows:
|
|
653
|
+
row_payload = _row_mapping(row)
|
|
654
|
+
if _mapping_int(row_payload, "integrity_version") != POLICY_INTEGRITY_VERSION:
|
|
655
|
+
has_legacy_rows = True
|
|
607
656
|
current_result = verify_local_policy_row(
|
|
608
|
-
|
|
657
|
+
row_payload,
|
|
609
658
|
key=key,
|
|
610
659
|
key_id=key_id,
|
|
611
660
|
degraded_mode=False,
|
|
@@ -613,20 +662,174 @@ class StoreSecretPolicyIntegrityMixin:
|
|
|
613
662
|
)
|
|
614
663
|
if current_result.status == "valid":
|
|
615
664
|
current_valid += 1
|
|
616
|
-
|
|
665
|
+
continue
|
|
666
|
+
pending_candidates += 1
|
|
667
|
+
pending_result = verify_local_policy_row(
|
|
668
|
+
row_payload,
|
|
669
|
+
key=key,
|
|
670
|
+
key_id=key_id,
|
|
671
|
+
degraded_mode=False,
|
|
672
|
+
trusted_generation=pending_generation,
|
|
673
|
+
)
|
|
674
|
+
if pending_result.status == "valid":
|
|
675
|
+
pending_valid += 1
|
|
676
|
+
continue
|
|
677
|
+
current_result = verify_local_policy_row(
|
|
678
|
+
row_payload,
|
|
679
|
+
key=key,
|
|
680
|
+
key_id=key_id,
|
|
681
|
+
degraded_mode=False,
|
|
682
|
+
trusted_generation=current_generation,
|
|
683
|
+
)
|
|
684
|
+
if current_result.status == "valid":
|
|
685
|
+
current_valid += 1
|
|
686
|
+
return has_legacy_rows, pending_candidates, pending_valid, current_valid
|
|
687
|
+
|
|
688
|
+
def _newer_authenticated_policy_integrity_generation(
|
|
689
|
+
self,
|
|
690
|
+
connection: sqlite3.Connection,
|
|
691
|
+
*,
|
|
692
|
+
key: bytes,
|
|
693
|
+
key_id: str,
|
|
694
|
+
trusted_generation: int | None,
|
|
695
|
+
) -> int | None:
|
|
696
|
+
current_valid = 0
|
|
697
|
+
newer_generations: set[int] = set()
|
|
698
|
+
rows = self._load_local_policy_rows(connection)
|
|
699
|
+
validated_rows = 0
|
|
700
|
+
for row in rows:
|
|
701
|
+
row_payload = _row_mapping(row)
|
|
702
|
+
if _mapping_int(row_payload, "integrity_version") != POLICY_INTEGRITY_VERSION:
|
|
703
|
+
return None
|
|
704
|
+
row_generation = _mapping_int(row_payload, "integrity_generation")
|
|
705
|
+
if row_generation is None:
|
|
706
|
+
return None
|
|
707
|
+
result = verify_local_policy_row(
|
|
708
|
+
row_payload,
|
|
709
|
+
key=key,
|
|
710
|
+
key_id=key_id,
|
|
711
|
+
degraded_mode=False,
|
|
712
|
+
trusted_generation=row_generation,
|
|
713
|
+
)
|
|
714
|
+
if result.status != "valid":
|
|
715
|
+
return None
|
|
716
|
+
validated_rows += 1
|
|
717
|
+
if trusted_generation is not None and row_generation == trusted_generation:
|
|
718
|
+
current_valid += 1
|
|
719
|
+
continue
|
|
720
|
+
if trusted_generation is None or row_generation > trusted_generation:
|
|
721
|
+
newer_generations.add(row_generation)
|
|
722
|
+
continue
|
|
723
|
+
return None
|
|
724
|
+
if current_valid > 0 or len(newer_generations) != 1 or validated_rows != len(rows):
|
|
725
|
+
return None
|
|
726
|
+
return next(iter(newer_generations))
|
|
727
|
+
|
|
728
|
+
def _resolved_policy_integrity_pending_generation(
|
|
729
|
+
self,
|
|
730
|
+
connection: sqlite3.Connection,
|
|
731
|
+
*,
|
|
732
|
+
key: bytes,
|
|
733
|
+
key_id: str,
|
|
734
|
+
trusted_state: dict[str, object],
|
|
735
|
+
) -> dict[str, object]:
|
|
736
|
+
current_generation = _mapping_int(trusted_state, "generation")
|
|
737
|
+
if current_generation is None:
|
|
738
|
+
raise RuntimeError("Guard policy integrity control state is invalid.")
|
|
739
|
+
pending_generation = trusted_state.get("pending_generation")
|
|
740
|
+
if not isinstance(pending_generation, int) or pending_generation <= current_generation:
|
|
741
|
+
return trusted_state
|
|
742
|
+
rows = self._load_local_policy_rows(connection)
|
|
743
|
+
next_state: dict[str, object]
|
|
744
|
+
if not rows:
|
|
745
|
+
next_state = {
|
|
746
|
+
"cutover_complete": True,
|
|
747
|
+
"generation": pending_generation,
|
|
748
|
+
"pending_generation": None,
|
|
749
|
+
"version": _POLICY_INTEGRITY_CONTROL_VERSION,
|
|
750
|
+
}
|
|
751
|
+
else:
|
|
752
|
+
(
|
|
753
|
+
has_legacy_rows,
|
|
754
|
+
pending_candidates,
|
|
755
|
+
pending_valid,
|
|
756
|
+
current_valid,
|
|
757
|
+
) = self._classify_policy_integrity_pending_generation_rows(
|
|
758
|
+
rows,
|
|
759
|
+
key=key,
|
|
760
|
+
key_id=key_id,
|
|
761
|
+
current_generation=current_generation,
|
|
762
|
+
pending_generation=pending_generation,
|
|
763
|
+
)
|
|
764
|
+
if has_legacy_rows:
|
|
765
|
+
next_state = dict(trusted_state)
|
|
766
|
+
if pending_candidates > 0 and current_valid == len(rows):
|
|
767
|
+
next_state["pending_generation"] = None
|
|
768
|
+
elif pending_valid == len(rows):
|
|
617
769
|
next_state = {
|
|
618
770
|
"cutover_complete": True,
|
|
619
771
|
"generation": pending_generation,
|
|
620
772
|
"pending_generation": None,
|
|
621
773
|
"version": _POLICY_INTEGRITY_CONTROL_VERSION,
|
|
622
774
|
}
|
|
623
|
-
|
|
775
|
+
elif current_valid == len(rows):
|
|
624
776
|
next_state = dict(trusted_state)
|
|
625
777
|
next_state["pending_generation"] = None
|
|
626
|
-
|
|
627
|
-
|
|
778
|
+
else:
|
|
779
|
+
next_state = dict(trusted_state)
|
|
628
780
|
return next_state
|
|
629
781
|
|
|
782
|
+
def _prepared_startup_policy_integrity_state(
|
|
783
|
+
self,
|
|
784
|
+
connection: sqlite3.Connection,
|
|
785
|
+
*,
|
|
786
|
+
key: bytes,
|
|
787
|
+
key_id: str,
|
|
788
|
+
trusted_state: dict[str, object],
|
|
789
|
+
) -> dict[str, object]:
|
|
790
|
+
has_legacy_rows = self._count_legacy_local_policy_rows(connection) > 0
|
|
791
|
+
prepared_state = dict(trusted_state)
|
|
792
|
+
pending_generation = prepared_state.get("pending_generation")
|
|
793
|
+
if not isinstance(pending_generation, int) and not has_legacy_rows:
|
|
794
|
+
newer_authenticated_generation = self._newer_authenticated_policy_integrity_generation(
|
|
795
|
+
connection,
|
|
796
|
+
key=key,
|
|
797
|
+
key_id=key_id,
|
|
798
|
+
trusted_generation=_mapping_int(prepared_state, "generation"),
|
|
799
|
+
)
|
|
800
|
+
if newer_authenticated_generation is not None:
|
|
801
|
+
prepared_state["generation"] = newer_authenticated_generation
|
|
802
|
+
prepared_state = self._resolved_policy_integrity_pending_generation(
|
|
803
|
+
connection,
|
|
804
|
+
key=key,
|
|
805
|
+
key_id=key_id,
|
|
806
|
+
trusted_state=prepared_state,
|
|
807
|
+
)
|
|
808
|
+
if not bool(prepared_state.get("cutover_complete")) and not has_legacy_rows:
|
|
809
|
+
prepared_state["cutover_complete"] = True
|
|
810
|
+
return prepared_state
|
|
811
|
+
|
|
812
|
+
def _prefetched_startup_state_still_matches_local_rows(
|
|
813
|
+
self,
|
|
814
|
+
connection: sqlite3.Connection,
|
|
815
|
+
*,
|
|
816
|
+
key: bytes,
|
|
817
|
+
key_id: str,
|
|
818
|
+
trusted_state: dict[str, object],
|
|
819
|
+
) -> bool:
|
|
820
|
+
trusted_generation = _mapping_int(trusted_state, "generation")
|
|
821
|
+
for row in self._load_local_policy_rows(connection):
|
|
822
|
+
result = verify_local_policy_row(
|
|
823
|
+
_row_mapping(row),
|
|
824
|
+
key=key,
|
|
825
|
+
key_id=key_id,
|
|
826
|
+
degraded_mode=False,
|
|
827
|
+
trusted_generation=trusted_generation,
|
|
828
|
+
)
|
|
829
|
+
if result.status != "valid":
|
|
830
|
+
return False
|
|
831
|
+
return True
|
|
832
|
+
|
|
630
833
|
def _refresh_policy_integrity_state(
|
|
631
834
|
self,
|
|
632
835
|
connection: sqlite3.Connection,
|
|
@@ -638,19 +841,48 @@ class StoreSecretPolicyIntegrityMixin:
|
|
|
638
841
|
) -> dict[str, object]:
|
|
639
842
|
existing = self._load_policy_integrity_state(connection) or {}
|
|
640
843
|
warnings = self._policy_integrity_path_warnings()
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
844
|
+
prefetched_trusted_state = getattr(
|
|
845
|
+
self,
|
|
846
|
+
"_startup_prefetched_policy_integrity_trusted_state",
|
|
847
|
+
_POLICY_INTEGRITY_LOOKUP_UNSET,
|
|
848
|
+
)
|
|
849
|
+
using_prefetched_trusted_state = prefetched_trusted_state is not _POLICY_INTEGRITY_LOOKUP_UNSET
|
|
850
|
+
if using_prefetched_trusted_state:
|
|
851
|
+
trusted_state = cast(dict[str, object] | None, prefetched_trusted_state)
|
|
852
|
+
else:
|
|
853
|
+
trusted_state = self._load_policy_integrity_control_state(create=create_key)
|
|
854
|
+
if using_prefetched_trusted_state and getattr(
|
|
855
|
+
self,
|
|
856
|
+
"_startup_prefetched_policy_integrity_repair_failed",
|
|
857
|
+
False,
|
|
858
|
+
):
|
|
859
|
+
trusted_state = None
|
|
860
|
+
warnings.append(POLICY_INTEGRITY_REASON_CONTROL_UNAVAILABLE)
|
|
861
|
+
prefetched_secret_material = getattr(
|
|
862
|
+
self,
|
|
863
|
+
"_startup_prefetched_policy_integrity_secret_material",
|
|
864
|
+
_POLICY_INTEGRITY_LOOKUP_UNSET,
|
|
646
865
|
)
|
|
866
|
+
using_prefetched_secret_material = prefetched_secret_material is not _POLICY_INTEGRITY_LOOKUP_UNSET
|
|
867
|
+
if secret_material is not None:
|
|
868
|
+
raw_key, key_id = secret_material
|
|
869
|
+
elif using_prefetched_secret_material:
|
|
870
|
+
raw_key, key_id = cast(tuple[bytes | None, str | None], prefetched_secret_material)
|
|
871
|
+
else:
|
|
872
|
+
raw_key, key_id = self._policy_integrity_secret_material(create=create_key)
|
|
647
873
|
if self._policy_integrity_secret_store is None:
|
|
648
874
|
warnings.append(POLICY_INTEGRITY_REASON_SYSTEM_KEYRING_UNAVAILABLE)
|
|
649
875
|
elif raw_key is None or key_id is None:
|
|
650
876
|
warnings.append(POLICY_INTEGRITY_REASON_KEY_UNAVAILABLE)
|
|
651
877
|
if trusted_state is None:
|
|
652
878
|
warnings.append(POLICY_INTEGRITY_REASON_CONTROL_UNAVAILABLE)
|
|
653
|
-
if
|
|
879
|
+
if (
|
|
880
|
+
not warnings
|
|
881
|
+
and trusted_state is not None
|
|
882
|
+
and raw_key is not None
|
|
883
|
+
and key_id is not None
|
|
884
|
+
and not using_prefetched_trusted_state
|
|
885
|
+
):
|
|
654
886
|
try:
|
|
655
887
|
trusted_state = self._reconcile_policy_integrity_pending_generation(
|
|
656
888
|
connection,
|
|
@@ -666,6 +898,7 @@ class StoreSecretPolicyIntegrityMixin:
|
|
|
666
898
|
and trusted_state is not None
|
|
667
899
|
and not bool(trusted_state.get("cutover_complete"))
|
|
668
900
|
and has_only_signed_rows
|
|
901
|
+
and not using_prefetched_trusted_state
|
|
669
902
|
):
|
|
670
903
|
next_trusted_state = dict(trusted_state)
|
|
671
904
|
next_trusted_state["cutover_complete"] = True
|
|
@@ -689,6 +922,74 @@ class StoreSecretPolicyIntegrityMixin:
|
|
|
689
922
|
self._store_policy_integrity_state(connection, payload, now=now)
|
|
690
923
|
return payload
|
|
691
924
|
|
|
925
|
+
def _prepare_startup_prefetched_policy_integrity_state(self) -> None:
|
|
926
|
+
prefetched_trusted_state = getattr(
|
|
927
|
+
self,
|
|
928
|
+
"_startup_prefetched_policy_integrity_trusted_state",
|
|
929
|
+
_POLICY_INTEGRITY_LOOKUP_UNSET,
|
|
930
|
+
)
|
|
931
|
+
prefetched_secret_material = getattr(
|
|
932
|
+
self,
|
|
933
|
+
"_startup_prefetched_policy_integrity_secret_material",
|
|
934
|
+
_POLICY_INTEGRITY_LOOKUP_UNSET,
|
|
935
|
+
)
|
|
936
|
+
if (
|
|
937
|
+
prefetched_trusted_state is _POLICY_INTEGRITY_LOOKUP_UNSET
|
|
938
|
+
or prefetched_secret_material is _POLICY_INTEGRITY_LOOKUP_UNSET
|
|
939
|
+
):
|
|
940
|
+
return
|
|
941
|
+
trusted_state = cast(dict[str, object] | None, prefetched_trusted_state)
|
|
942
|
+
if trusted_state is None:
|
|
943
|
+
return
|
|
944
|
+
raw_key, key_id = cast(tuple[bytes | None, str | None], prefetched_secret_material)
|
|
945
|
+
if raw_key is None or key_id is None:
|
|
946
|
+
return
|
|
947
|
+
|
|
948
|
+
def compute_prepared_state(base_state: dict[str, object]) -> dict[str, object]:
|
|
949
|
+
connection = sqlite3.connect(self.path, timeout=SQLITE_CONNECT_TIMEOUT_SECONDS)
|
|
950
|
+
connection.row_factory = sqlite3.Row
|
|
951
|
+
try:
|
|
952
|
+
connection.execute(f"pragma busy_timeout={SQLITE_BUSY_TIMEOUT_MS}")
|
|
953
|
+
return self._prepared_startup_policy_integrity_state(
|
|
954
|
+
connection,
|
|
955
|
+
key=raw_key,
|
|
956
|
+
key_id=key_id,
|
|
957
|
+
trusted_state=base_state,
|
|
958
|
+
)
|
|
959
|
+
finally:
|
|
960
|
+
connection.close()
|
|
961
|
+
|
|
962
|
+
prepared_state = compute_prepared_state(trusted_state)
|
|
963
|
+
current_trusted_state = self._load_policy_integrity_control_state(create=False)
|
|
964
|
+
if current_trusted_state is None:
|
|
965
|
+
connection = sqlite3.connect(self.path, timeout=SQLITE_CONNECT_TIMEOUT_SECONDS)
|
|
966
|
+
connection.row_factory = sqlite3.Row
|
|
967
|
+
try:
|
|
968
|
+
connection.execute(f"pragma busy_timeout={SQLITE_BUSY_TIMEOUT_MS}")
|
|
969
|
+
still_matches = self._prefetched_startup_state_still_matches_local_rows(
|
|
970
|
+
connection,
|
|
971
|
+
key=raw_key,
|
|
972
|
+
key_id=key_id,
|
|
973
|
+
trusted_state=trusted_state,
|
|
974
|
+
)
|
|
975
|
+
finally:
|
|
976
|
+
connection.close()
|
|
977
|
+
if prepared_state == trusted_state and still_matches:
|
|
978
|
+
self._startup_prefetched_policy_integrity_trusted_state = trusted_state
|
|
979
|
+
return
|
|
980
|
+
self._startup_prefetched_policy_integrity_repair_failed = True
|
|
981
|
+
return
|
|
982
|
+
if current_trusted_state != trusted_state:
|
|
983
|
+
trusted_state = dict(current_trusted_state)
|
|
984
|
+
prepared_state = compute_prepared_state(trusted_state)
|
|
985
|
+
if prepared_state == trusted_state:
|
|
986
|
+
self._startup_prefetched_policy_integrity_trusted_state = trusted_state
|
|
987
|
+
return
|
|
988
|
+
if self._store_policy_integrity_control_state(prepared_state):
|
|
989
|
+
self._startup_prefetched_policy_integrity_trusted_state = prepared_state
|
|
990
|
+
return
|
|
991
|
+
self._startup_prefetched_policy_integrity_repair_failed = True
|
|
992
|
+
|
|
692
993
|
def _policy_integrity_result_for_row(
|
|
693
994
|
self,
|
|
694
995
|
row: sqlite3.Row,
|
codex_plugin_scanner/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plugin-scanner
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.856
|
|
4
4
|
Summary: Protect local AI harnesses with HOL Guard and run scanner checks for Codex, Claude, Cursor, Gemini, OpenCode, and Pi.
|
|
5
5
|
Project-URL: Homepage, https://github.com/hashgraph-online/hol-guard
|
|
6
6
|
Project-URL: Repository, https://github.com/hashgraph-online/hol-guard
|
|
@@ -27,7 +27,7 @@ codex_plugin_scanner/trust_scoring.py,sha256=t_Lw13HZ766UCJzTtNNwllZK_k2ks_x-8St
|
|
|
27
27
|
codex_plugin_scanner/trust_skill_scoring.py,sha256=e-SrMBgAruzFn4RD0pGpP9rtPXxc2ji_CKpcnFPRh3M,12542
|
|
28
28
|
codex_plugin_scanner/trust_specs.py,sha256=aYGSB8M9RQp1uwhsRaWlVa1M9Vk51kUIxmBXkW8noZU,10729
|
|
29
29
|
codex_plugin_scanner/verification.py,sha256=J2nsnYlxNYecSvmSQhV_leImLslCOotIK2FSmEoIjg0,27896
|
|
30
|
-
codex_plugin_scanner/version.py,sha256=
|
|
30
|
+
codex_plugin_scanner/version.py,sha256=sk9IPKWaSML9BHMqUNXJJlynzAiNrnn5VGEhhvMmv4Y,72
|
|
31
31
|
codex_plugin_scanner/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
32
|
codex_plugin_scanner/checks/best_practices.py,sha256=uB3DXLggqxjfyBMH_1yvX4nyFhpIvaRHjTyyueoO-GQ,8473
|
|
33
33
|
codex_plugin_scanner/checks/claude.py,sha256=cMucH5pmKgQtBnDcycOzMMlKL-Q5HQemPqUmTLF0DzE,10189
|
|
@@ -108,13 +108,13 @@ codex_plugin_scanner/guard/shim_probe.py,sha256=4o_Cwi8B0VSJEcYQ4O15mSnh36l4ovSe
|
|
|
108
108
|
codex_plugin_scanner/guard/shims.py,sha256=GOhbFcD8sUdNfMnJ3zdx1OhG9NGGOkt_A12_C91kH7s,52729
|
|
109
109
|
codex_plugin_scanner/guard/sqlite_tuning.py,sha256=auE2AaawN-6_pPfzLg4Mj-ZhLyRGXI21CQoTVHR_ug8,243
|
|
110
110
|
codex_plugin_scanner/guard/stable_digest.py,sha256=O4Ts-kGtetRtHZPgMAaFZ8VRC2_x-pSbhZh4X-oZmm4,569
|
|
111
|
-
codex_plugin_scanner/guard/store.py,sha256=
|
|
111
|
+
codex_plugin_scanner/guard/store.py,sha256=SVXd8r_fCsofln_3ZQQMJ1xUnkrxir5oAfN4ZgYgX9c,1515
|
|
112
112
|
codex_plugin_scanner/guard/store_approval_facade.py,sha256=N10BrWESEbhgb_xtM9NYVFfY-AwHi1xSvPMWW26jg4E,17172
|
|
113
113
|
codex_plugin_scanner/guard/store_approvals.py,sha256=gGF8MeamalcNwgiwG51zBPyoZj-FYUo7hWFRStts7a4,44179
|
|
114
114
|
codex_plugin_scanner/guard/store_base.py,sha256=Qx-secIy8iaBdO1-ocRlpJk5WuidTyyn3BWL2IaEb0w,50267
|
|
115
115
|
codex_plugin_scanner/guard/store_cloud_events.py,sha256=zD_dFMMNs2aaczgR1TNCuK96nOLNBVzhCgNg2PC4KNA,15230
|
|
116
116
|
codex_plugin_scanner/guard/store_connect.py,sha256=rNuZ5F9WOSw9x-GVwFcpLLf-kvwhCvCeMuKzLOF-nhY,8102
|
|
117
|
-
codex_plugin_scanner/guard/store_connection_schema.py,sha256=
|
|
117
|
+
codex_plugin_scanner/guard/store_connection_schema.py,sha256=nS60SyagRSe6GyjCeC0ibzQqZf4weWhyy6MCyZ9JWE0,31503
|
|
118
118
|
codex_plugin_scanner/guard/store_event_receipts.py,sha256=RZghgg7rmMSNMzAu5QECNxfL3FxBkUn0qKYZcICXTZc,7340
|
|
119
119
|
codex_plugin_scanner/guard/store_evidence.py,sha256=bmvTq-Y9ctO61uCUygXdGJjPKEjN8zYsjuzMwMoeDrI,13755
|
|
120
120
|
codex_plugin_scanner/guard/store_evidence_facade.py,sha256=tDVUgagZJFOBVNhWXHeC_7FUtKzqZDtSGT4ZqMJebis,2209
|
|
@@ -127,7 +127,7 @@ codex_plugin_scanner/guard/store_policy_source_context.py,sha256=H-aIQR3w67o8HOn
|
|
|
127
127
|
codex_plugin_scanner/guard/store_receipt_rollups.py,sha256=ugJW5Ca-CaSorOSB5chyqCPXYKIILcnArK09SJXsspw,16814
|
|
128
128
|
codex_plugin_scanner/guard/store_receipts.py,sha256=Anh1DIc8M8PKa4NTprKqptV422y9S4dmd72W-XIRHfw,15625
|
|
129
129
|
codex_plugin_scanner/guard/store_resume.py,sha256=7x3Y4tSUMI86j67_463GO-Z7L4cNWQUQJgg5r0mW-Lw,5977
|
|
130
|
-
codex_plugin_scanner/guard/store_secret_policy_integrity.py,sha256=
|
|
130
|
+
codex_plugin_scanner/guard/store_secret_policy_integrity.py,sha256=XcjAcMMO9YeVgltU9ZsrUAu-GI-9097JEPY7-9rbkhg,45747
|
|
131
131
|
codex_plugin_scanner/guard/store_sessions.py,sha256=FiYh7f4dYZg-Wo_Alu5YoGqy1qclgNbyzDb3ENzZBog,21347
|
|
132
132
|
codex_plugin_scanner/guard/store_supply_chain.py,sha256=TpNyNfbUZjaci0uOVgmY7-lFIayfl67b4kaFUJesv5U,5901
|
|
133
133
|
codex_plugin_scanner/guard/store_threat_intel.py,sha256=rXmH1E_Wto9BOhAwt9mokfSy7ayWOD6PaEwvD4LnmDo,6449
|
|
@@ -335,8 +335,8 @@ codex_plugin_scanner/integrations/cisco_skill_scanner.py,sha256=gF6xJ3ptWpGNPZA1
|
|
|
335
335
|
codex_plugin_scanner/rules/__init__.py,sha256=bhAnNlVvEiAoksf_-Vd4f74JNz66pTINhUO2mMc8kvs,293
|
|
336
336
|
codex_plugin_scanner/rules/registry.py,sha256=7BaRerM26teDUM0S48d3sPHbqzmq2JhuelC0_ni3364,4580
|
|
337
337
|
codex_plugin_scanner/rules/specs.py,sha256=gpU0lijxqdSEZMMf28vciQxob-lvBEPcMQ4BG6N_r3I,650
|
|
338
|
-
plugin_scanner-2.0.
|
|
339
|
-
plugin_scanner-2.0.
|
|
340
|
-
plugin_scanner-2.0.
|
|
341
|
-
plugin_scanner-2.0.
|
|
342
|
-
plugin_scanner-2.0.
|
|
338
|
+
plugin_scanner-2.0.856.dist-info/METADATA,sha256=oez7sNDPOnonoZWEUIi3e4ALd-FZx18fOuY8mnPSc-0,40163
|
|
339
|
+
plugin_scanner-2.0.856.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
340
|
+
plugin_scanner-2.0.856.dist-info/entry_points.txt,sha256=SLGKi3GvvSsPwave-k2JQt2meG0pM7aaC6VlUy47Crg,122
|
|
341
|
+
plugin_scanner-2.0.856.dist-info/licenses/LICENSE,sha256=M6h9Xc242NSFZx5h6wBOKJpEQkFl_Wmz03yNrZd0_3E,5688
|
|
342
|
+
plugin_scanner-2.0.856.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|