correctover 2.0.2__tar.gz → 2.2.0__tar.gz
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.
- correctover-2.2.0/PKG-INFO +12 -0
- correctover-2.2.0/pyproject.toml +21 -0
- correctover-2.2.0/src/correctover/__init__.py +22 -0
- correctover-2.2.0/src/correctover/guardrail.py +211 -0
- correctover-2.2.0/src/correctover/validator.py +37 -0
- correctover-2.2.0/src/correctover.egg-info/PKG-INFO +12 -0
- correctover-2.2.0/src/correctover.egg-info/SOURCES.txt +9 -0
- correctover-2.2.0/src/correctover.egg-info/requires.txt +1 -0
- correctover-2.0.2/LICENSE +0 -8
- correctover-2.0.2/PKG-INFO +0 -94
- correctover-2.0.2/README.md +0 -55
- correctover-2.0.2/correctover/__init__.py +0 -12
- correctover-2.0.2/correctover/validator.py +0 -98
- correctover-2.0.2/correctover.egg-info/PKG-INFO +0 -94
- correctover-2.0.2/correctover.egg-info/SOURCES.txt +0 -10
- correctover-2.0.2/correctover.egg-info/requires.txt +0 -1
- correctover-2.0.2/setup.py +0 -39
- {correctover-2.0.2 → correctover-2.2.0}/setup.cfg +0 -0
- {correctover-2.0.2 → correctover-2.2.0/src}/correctover.egg-info/dependency_links.txt +0 -0
- {correctover-2.0.2 → correctover-2.2.0/src}/correctover.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: correctover
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: Correctover - AI Agent Runtime Assurance. CCS standard + GuardrailProvider + MCP Security Scanner.
|
|
5
|
+
Author-email: Guigui Wang <wangguigui@correctover.com>
|
|
6
|
+
License: Proprietary Commercial License
|
|
7
|
+
Project-URL: Homepage, https://correctover.com
|
|
8
|
+
Project-URL: CCS Standard, https://correctover.com/ccs
|
|
9
|
+
Project-URL: GitHub, https://github.com/Correctover
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: pydantic>=1.10
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "correctover"
|
|
7
|
+
version = "2.2.0"
|
|
8
|
+
authors = [{name="Guigui Wang", email="wangguigui@correctover.com"}]
|
|
9
|
+
description = "Correctover - AI Agent Runtime Assurance. CCS standard + GuardrailProvider + MCP Security Scanner."
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = {text = "Proprietary Commercial License"}
|
|
12
|
+
requires-python = ">=3.8"
|
|
13
|
+
dependencies = ["pydantic>=1.10"]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
Homepage = "https://correctover.com"
|
|
17
|
+
"CCS Standard" = "https://correctover.com/ccs"
|
|
18
|
+
GitHub = "https://github.com/Correctover"
|
|
19
|
+
|
|
20
|
+
[tool.setuptools.packages.find]
|
|
21
|
+
where = ["src"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Correctover v2.2.0 - AI Agent Runtime Assurance"""
|
|
2
|
+
__version__ = "2.2.0"
|
|
3
|
+
__author__ = "Guigui Wang"
|
|
4
|
+
__email__ = "wangguigui@correctover.com"
|
|
5
|
+
from .validator import CCSValidator, ValidationResult
|
|
6
|
+
from .guardrail import (
|
|
7
|
+
GuardrailDecisionV1, ActionEnvelopeV1, ToolCallContext,
|
|
8
|
+
GuardrailProvider, AllowAllGuardrailProvider, DenyAllGuardrailProvider,
|
|
9
|
+
ToolListGuardrailProvider, CKGGuardrailProvider, EnvProtectionProvider,
|
|
10
|
+
CompositeGuardrailProvider, AuditTrail, GuardrailContext,
|
|
11
|
+
make_guardrail_hook, detect_missing_guardrail, compute_decision_id,
|
|
12
|
+
canonical_json, MCPSecurityValidator,
|
|
13
|
+
)
|
|
14
|
+
__all__ = [
|
|
15
|
+
"CCSValidator","ValidationResult",
|
|
16
|
+
"GuardrailDecisionV1","ActionEnvelopeV1","ToolCallContext",
|
|
17
|
+
"GuardrailProvider","AllowAllGuardrailProvider","DenyAllGuardrailProvider",
|
|
18
|
+
"ToolListGuardrailProvider","CKGGuardrailProvider","EnvProtectionProvider",
|
|
19
|
+
"CompositeGuardrailProvider","AuditTrail","GuardrailContext",
|
|
20
|
+
"make_guardrail_hook","detect_missing_guardrail","compute_decision_id",
|
|
21
|
+
"canonical_json","MCPSecurityValidator",
|
|
22
|
+
]
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""
|
|
2
|
+
correctover.guardrail - Framework-agnostic runtime security guardrail system.
|
|
3
|
+
CCS reference implementation.
|
|
4
|
+
"""
|
|
5
|
+
import hashlib, json, time
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import Any, Callable, Dict, List, Optional, Set
|
|
9
|
+
|
|
10
|
+
def canonical_json(obj):
|
|
11
|
+
return json.dumps(obj, sort_keys=True, separators=(",",":"), default=str)
|
|
12
|
+
|
|
13
|
+
def compute_decision_id(claims, expires_at=None):
|
|
14
|
+
preimage = claims.copy()
|
|
15
|
+
if expires_at is not None:
|
|
16
|
+
preimage["_expires_at"] = expires_at
|
|
17
|
+
return hashlib.sha256(canonical_json(preimage).encode("utf-8")).hexdigest()
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class GuardrailDecisionV1:
|
|
21
|
+
decision_id: str
|
|
22
|
+
authorized: bool
|
|
23
|
+
claims: Dict[str, Any]
|
|
24
|
+
expires_at: Optional[float] = None
|
|
25
|
+
def is_expired(self):
|
|
26
|
+
if self.expires_at is None: return False
|
|
27
|
+
return time.time() > self.expires_at
|
|
28
|
+
def verify_integrity(self):
|
|
29
|
+
return self.decision_id == compute_decision_id(self.claims, self.expires_at)
|
|
30
|
+
def to_dict(self):
|
|
31
|
+
return {"decision_id":self.decision_id,"authorized":self.authorized,"claims":self.claims,"expires_at":self.expires_at}
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class ActionEnvelopeV1:
|
|
35
|
+
decision_id: str
|
|
36
|
+
tool_result_digest: str
|
|
37
|
+
executed_at: float
|
|
38
|
+
duration_ms: float
|
|
39
|
+
@staticmethod
|
|
40
|
+
def digest_result(result):
|
|
41
|
+
raw = canonical_json(result) if result is not None else ""
|
|
42
|
+
return hashlib.sha256(raw.encode("utf-8")).hexdigest()
|
|
43
|
+
def to_dict(self):
|
|
44
|
+
return {"decision_id":self.decision_id,"tool_result_digest":self.tool_result_digest,"executed_at":self.executed_at,"duration_ms":self.duration_ms}
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class ToolCallContext:
|
|
48
|
+
tool_name: str
|
|
49
|
+
tool_args: Dict[str, Any] = field(default_factory=dict)
|
|
50
|
+
agent_id: str = "unknown"
|
|
51
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
52
|
+
|
|
53
|
+
class GuardrailProvider(ABC):
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def authorize(self, context: ToolCallContext) -> GuardrailDecisionV1: ...
|
|
56
|
+
|
|
57
|
+
class AllowAllGuardrailProvider(GuardrailProvider):
|
|
58
|
+
def authorize(self, context):
|
|
59
|
+
claims = {"provider":"AllowAllGuardrailProvider","tool_name":context.tool_name,"agent_id":context.agent_id}
|
|
60
|
+
return GuardrailDecisionV1(decision_id=compute_decision_id(claims), authorized=True, claims=claims)
|
|
61
|
+
|
|
62
|
+
class DenyAllGuardrailProvider(GuardrailProvider):
|
|
63
|
+
def authorize(self, context):
|
|
64
|
+
claims = {"provider":"DenyAllGuardrailProvider","tool_name":context.tool_name,"reason":"deny-all safety lock active"}
|
|
65
|
+
return GuardrailDecisionV1(decision_id=compute_decision_id(claims), authorized=False, claims=claims)
|
|
66
|
+
|
|
67
|
+
class ToolListGuardrailProvider(GuardrailProvider):
|
|
68
|
+
def __init__(self, allowed_tools=None, denied_tools=None):
|
|
69
|
+
self.allowed_tools = allowed_tools
|
|
70
|
+
self.denied_tools = denied_tools or set()
|
|
71
|
+
def authorize(self, context):
|
|
72
|
+
claims = {"provider":"ToolListGuardrailProvider","tool_name":context.tool_name,"agent_id":context.agent_id}
|
|
73
|
+
if self.allowed_tools is not None:
|
|
74
|
+
authorized = context.tool_name in self.allowed_tools
|
|
75
|
+
claims["policy"] = "allowlist"
|
|
76
|
+
claims["allowed_tools"] = sorted(self.allowed_tools)
|
|
77
|
+
elif context.tool_name in self.denied_tools:
|
|
78
|
+
authorized = False
|
|
79
|
+
claims["policy"] = "denylist"
|
|
80
|
+
claims["denied_tools"] = sorted(self.denied_tools)
|
|
81
|
+
else:
|
|
82
|
+
authorized = True
|
|
83
|
+
claims["policy"] = "default-allow"
|
|
84
|
+
return GuardrailDecisionV1(decision_id=compute_decision_id(claims), authorized=authorized, claims=claims)
|
|
85
|
+
|
|
86
|
+
class CKGGuardrailProvider(GuardrailProvider):
|
|
87
|
+
def __init__(self):
|
|
88
|
+
self._constraints = []
|
|
89
|
+
def add_constraint(self, predicate, **kwargs):
|
|
90
|
+
self._constraints.append({"predicate":predicate, **kwargs})
|
|
91
|
+
return self
|
|
92
|
+
def authorize(self, context):
|
|
93
|
+
claims = {"provider":"CKGGuardrailProvider","tool_name":context.tool_name,"agent_id":context.agent_id}
|
|
94
|
+
satisfied, failed_predicate = True, None
|
|
95
|
+
for c in self._constraints:
|
|
96
|
+
p = c["predicate"]
|
|
97
|
+
if p=="tool_name_in" and context.tool_name not in c["tools"]: satisfied,failed_predicate=False,p
|
|
98
|
+
elif p=="tool_name_not_in" and context.tool_name in c["tools"]: satisfied,failed_predicate=False,p
|
|
99
|
+
elif p=="agent_id_in" and context.agent_id not in c["agents"]: satisfied,failed_predicate=False,p
|
|
100
|
+
elif p=="param_matches" and context.tool_args.get(c["name"])!=c["value"]: satisfied,failed_predicate=False,p
|
|
101
|
+
elif p=="has_param" and c["name"] not in context.tool_args: satisfied,failed_predicate=False,p
|
|
102
|
+
elif p=="no_param" and c["name"] in context.tool_args: satisfied,failed_predicate=False,p
|
|
103
|
+
if not satisfied: claims["failed_predicate"]=failed_predicate
|
|
104
|
+
claims["constraints_count"]=len(self._constraints)
|
|
105
|
+
return GuardrailDecisionV1(decision_id=compute_decision_id(claims), authorized=satisfied, claims=claims)
|
|
106
|
+
|
|
107
|
+
class EnvProtectionProvider(GuardrailProvider):
|
|
108
|
+
DANGEROUS_PATTERNS = {
|
|
109
|
+
"env_vars":["API_KEY","API_SECRET","SECRET_KEY","PRIVATE_KEY","ACCESS_TOKEN","AUTH_TOKEN","DB_PASSWORD","DATABASE_URL","AWS_SECRET","AWS_ACCESS_KEY","GITHUB_TOKEN","OPENAI_API_KEY","ANTHROPIC_API_KEY"],
|
|
110
|
+
"env_commands":["os.environ","process.env","getenv","env ","printenv","echo $","${"],
|
|
111
|
+
"file_paths":[".env",".env.local",".env.production","credentials.json","service-account.json"],
|
|
112
|
+
}
|
|
113
|
+
def __init__(self, extra_patterns=None):
|
|
114
|
+
self.extra_patterns = extra_patterns or []
|
|
115
|
+
def authorize(self, context):
|
|
116
|
+
claims = {"provider":"EnvProtectionProvider","tool_name":context.tool_name,"agent_id":context.agent_id}
|
|
117
|
+
args_str = canonical_json(context.tool_args).lower()
|
|
118
|
+
matched = [f"{cat}:{p}" for cat,pats in self.DANGEROUS_PATTERNS.items() for p in pats if p.lower() in args_str]
|
|
119
|
+
matched += [f"custom:{p}" for p in self.extra_patterns if p.lower() in args_str]
|
|
120
|
+
if matched:
|
|
121
|
+
claims["matched_patterns"]=matched
|
|
122
|
+
claims["reason"]="Environment variable access attempt detected"
|
|
123
|
+
return GuardrailDecisionV1(decision_id=compute_decision_id(claims), authorized=False, claims=claims)
|
|
124
|
+
claims["reason"]="No environment variable access detected"
|
|
125
|
+
return GuardrailDecisionV1(decision_id=compute_decision_id(claims), authorized=True, claims=claims)
|
|
126
|
+
|
|
127
|
+
class CompositeGuardrailProvider(GuardrailProvider):
|
|
128
|
+
def __init__(self, providers, mode="AND"):
|
|
129
|
+
if mode not in ("AND","OR"): raise ValueError(f"mode must be AND or OR, got {mode}")
|
|
130
|
+
self.providers = providers
|
|
131
|
+
self.mode = mode
|
|
132
|
+
def authorize(self, context):
|
|
133
|
+
results = [p.authorize(context) for p in self.providers]
|
|
134
|
+
authorized = all(d.authorized for d in results) if self.mode=="AND" else any(d.authorized for d in results)
|
|
135
|
+
claims = {"provider":"CompositeGuardrailProvider","mode":self.mode,"tool_name":context.tool_name,"agent_id":context.agent_id,"sub_decisions":[d.decision_id for d in results],"sub_providers":[d.claims.get("provider","unknown") for d in results]}
|
|
136
|
+
return GuardrailDecisionV1(decision_id=compute_decision_id(claims), authorized=authorized, claims=claims)
|
|
137
|
+
|
|
138
|
+
class AuditTrail:
|
|
139
|
+
def __init__(self):
|
|
140
|
+
self._decisions = {}
|
|
141
|
+
self._envelopes = {}
|
|
142
|
+
def record_decision(self, decision): self._decisions[decision.decision_id] = decision
|
|
143
|
+
def record_envelope(self, envelope): self._envelopes[envelope.decision_id] = envelope
|
|
144
|
+
def get_decision(self, did): return self._decisions.get(did)
|
|
145
|
+
def get_envelope(self, did): return self._envelopes.get(did)
|
|
146
|
+
def get_all_decisions(self): return list(self._decisions.values())
|
|
147
|
+
def get_all_envelopes(self): return list(self._envelopes.values())
|
|
148
|
+
def clear(self): self._decisions.clear(); self._envelopes.clear()
|
|
149
|
+
@property
|
|
150
|
+
def decision_count(self): return len(self._decisions)
|
|
151
|
+
@property
|
|
152
|
+
def envelope_count(self): return len(self._envelopes)
|
|
153
|
+
def verify_all(self): return all(d.verify_integrity() for d in self._decisions.values())
|
|
154
|
+
def export(self): return {"decisions":[d.to_dict() for d in self._decisions.values()],"envelopes":[e.to_dict() for e in self._envelopes.values()],"verified":self.verify_all()}
|
|
155
|
+
|
|
156
|
+
class GuardrailContext:
|
|
157
|
+
def __init__(self, provider, trail=None, on_deny=None):
|
|
158
|
+
self.provider = provider; self.trail = trail or AuditTrail(); self.on_deny = on_deny
|
|
159
|
+
def authorize(self, context):
|
|
160
|
+
decision = self.provider.authorize(context)
|
|
161
|
+
self.trail.record_decision(decision)
|
|
162
|
+
if not decision.authorized and self.on_deny: self.on_deny(decision)
|
|
163
|
+
return decision
|
|
164
|
+
def after_tool_call(self, decision, result, start_time):
|
|
165
|
+
duration_ms = (time.time()-start_time)*1000
|
|
166
|
+
envelope = ActionEnvelopeV1(decision_id=decision.decision_id, tool_result_digest=ActionEnvelopeV1.digest_result(result), executed_at=time.time(), duration_ms=duration_ms)
|
|
167
|
+
self.trail.record_envelope(envelope)
|
|
168
|
+
return envelope
|
|
169
|
+
|
|
170
|
+
def make_guardrail_hook(provider, trail=None, on_deny=None):
|
|
171
|
+
ctx = GuardrailContext(provider=provider, trail=trail, on_deny=on_deny)
|
|
172
|
+
def _hook(tool_name, tool_args=None, agent_id="unknown", **kwargs):
|
|
173
|
+
context = ToolCallContext(tool_name=tool_name, tool_args=tool_args or {}, agent_id=agent_id, metadata=kwargs)
|
|
174
|
+
decision = ctx.authorize(context)
|
|
175
|
+
if not decision.authorized: return False
|
|
176
|
+
return None
|
|
177
|
+
_hook._guardrail_context = ctx
|
|
178
|
+
return _hook
|
|
179
|
+
|
|
180
|
+
def detect_missing_guardrail(agents):
|
|
181
|
+
findings = []
|
|
182
|
+
for agent in agents:
|
|
183
|
+
agent_name = getattr(agent,"role",getattr(agent,"name",getattr(agent,"id","unknown")))
|
|
184
|
+
has_hooks = getattr(agent,"_before_tool_call_hooks",None) or getattr(agent,"before_tool_call",None) or getattr(agent,"_guardrails",None) or getattr(agent,"guardrail_provider",None)
|
|
185
|
+
if not has_hooks:
|
|
186
|
+
findings.append({"severity":"CRITICAL","pattern":"AS-GUARDRAIL-MISS-001","agent":agent_name,"message":f"Agent '{agent_name}' has no registered GuardrailProvider","remediation":"Register a GuardrailProvider via make_guardrail_hook()","ccs_ref":"https://correctover.com/ccs"})
|
|
187
|
+
return findings
|
|
188
|
+
|
|
189
|
+
class MCPSecurityValidator:
|
|
190
|
+
DANGEROUS_COMMANDS = ["rm -rf","curl | sh","wget | bash","eval(","exec(","os.system","subprocess.call"]
|
|
191
|
+
SENSITIVE_ENV_PATTERNS = ["API_KEY","SECRET","TOKEN","PASSWORD","PRIVATE_KEY","CREDENTIALS","AUTH"]
|
|
192
|
+
@staticmethod
|
|
193
|
+
def validate_tool_definition(tool_def):
|
|
194
|
+
issues = []
|
|
195
|
+
impl = str(tool_def.get("implementation",""))
|
|
196
|
+
for cmd in MCPSecurityValidator.DANGEROUS_COMMANDS:
|
|
197
|
+
if cmd in impl: issues.append({"severity":"CRITICAL","type":"command_injection","pattern":cmd,"cve_ref":"CVE-2026-42271"})
|
|
198
|
+
env_section = str(tool_def.get("env",{}))
|
|
199
|
+
for pattern in MCPSecurityValidator.SENSITIVE_ENV_PATTERNS:
|
|
200
|
+
if pattern.upper() in env_section.upper(): issues.append({"severity":"HIGH","type":"env_exposure","pattern":pattern,"cve_ref":"CVE-2026-12957"})
|
|
201
|
+
return {"safe":len(issues)==0,"issues":issues,"tool_name":tool_def.get("name","unknown")}
|
|
202
|
+
@staticmethod
|
|
203
|
+
def validate_mcp_config(config):
|
|
204
|
+
issues = []
|
|
205
|
+
transport = config.get("transport","stdio")
|
|
206
|
+
if transport == "stdio":
|
|
207
|
+
env = config.get("env",{})
|
|
208
|
+
if env: issues.append({"severity":"HIGH","type":"stdio_env_exposure","message":"stdio transport with env variables","cve_ref":"CVE-2026-12957","remediation":"Use env_isolation or switch to sse/http transport"})
|
|
209
|
+
for tool in config.get("tools",[]):
|
|
210
|
+
issues.extend(MCPSecurityValidator.validate_tool_definition(tool)["issues"])
|
|
211
|
+
return {"safe":len(issues)==0,"issues":issues,"config_name":config.get("name","unknown")}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import Dict, List, Any, Optional
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
import hashlib, hmac, json
|
|
5
|
+
|
|
6
|
+
class ValidationResult(BaseModel):
|
|
7
|
+
is_valid: bool
|
|
8
|
+
errors: List[str] = Field(default_factory=list)
|
|
9
|
+
warnings: List[str] = Field(default_factory=list)
|
|
10
|
+
validated_at: str = Field(default_factory=lambda: datetime.utcnow().isoformat())
|
|
11
|
+
trace_id: Optional[str] = None
|
|
12
|
+
|
|
13
|
+
class CCSValidator:
|
|
14
|
+
def __init__(self, required_fields=None, supported_fields=None, forbidden_fields=None, enable_integrity=False, integrity_key=None):
|
|
15
|
+
self.required_fields = set(required_fields or [])
|
|
16
|
+
self.supported_fields = set(supported_fields or [])
|
|
17
|
+
self.forbidden_fields = set(forbidden_fields or [])
|
|
18
|
+
self.enable_integrity = enable_integrity
|
|
19
|
+
self.integrity_key = integrity_key or "default-key"
|
|
20
|
+
def validate(self, output, trace_id=None):
|
|
21
|
+
errors, warnings = [], []
|
|
22
|
+
for f in self.required_fields:
|
|
23
|
+
if f not in output: errors.append(f"Missing required field: {f}")
|
|
24
|
+
for f in self.forbidden_fields:
|
|
25
|
+
if f in output: errors.append(f"Forbidden field present: {f}")
|
|
26
|
+
unexpected = set(output.keys()) - self.required_fields - self.supported_fields - self.forbidden_fields
|
|
27
|
+
for f in unexpected: warnings.append(f"Unexpected field: {f}")
|
|
28
|
+
if self.enable_integrity:
|
|
29
|
+
stored_hash = output.get("integrity_hash")
|
|
30
|
+
computed = self._compute_integrity(output)
|
|
31
|
+
if stored_hash is None: warnings.append("No integrity hash in output")
|
|
32
|
+
elif stored_hash != computed: errors.append("Integrity hash mismatch")
|
|
33
|
+
return ValidationResult(is_valid=len(errors)==0, errors=errors, warnings=warnings, trace_id=trace_id)
|
|
34
|
+
def _compute_integrity(self, output):
|
|
35
|
+
data = {k:v for k,v in output.items() if k != "integrity_hash"}
|
|
36
|
+
canonical = json.dumps(data, sort_keys=True, separators=(",",":"))
|
|
37
|
+
return hmac.new(self.integrity_key.encode(), canonical.encode(), hashlib.sha256).hexdigest()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: correctover
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: Correctover - AI Agent Runtime Assurance. CCS standard + GuardrailProvider + MCP Security Scanner.
|
|
5
|
+
Author-email: Guigui Wang <wangguigui@correctover.com>
|
|
6
|
+
License: Proprietary Commercial License
|
|
7
|
+
Project-URL: Homepage, https://correctover.com
|
|
8
|
+
Project-URL: CCS Standard, https://correctover.com/ccs
|
|
9
|
+
Project-URL: GitHub, https://github.com/Correctover
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: pydantic>=1.10
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
src/correctover/__init__.py
|
|
3
|
+
src/correctover/guardrail.py
|
|
4
|
+
src/correctover/validator.py
|
|
5
|
+
src/correctover.egg-info/PKG-INFO
|
|
6
|
+
src/correctover.egg-info/SOURCES.txt
|
|
7
|
+
src/correctover.egg-info/dependency_links.txt
|
|
8
|
+
src/correctover.egg-info/requires.txt
|
|
9
|
+
src/correctover.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pydantic>=1.10
|
correctover-2.0.2/LICENSE
DELETED
correctover-2.0.2/PKG-INFO
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: correctover
|
|
3
|
-
Version: 2.0.2
|
|
4
|
-
Summary: CCS v1.0 - Conformance Protocol for Agentic Runtime Systems
|
|
5
|
-
Home-page: https://correctover.github.io
|
|
6
|
-
Author: Guigui Wang
|
|
7
|
-
Author-email: wangguigui@correctover.com
|
|
8
|
-
Project-URL: Bug Tracker, https://github.com/Correctover/standards/issues
|
|
9
|
-
Project-URL: Documentation, https://correctover.github.io
|
|
10
|
-
Project-URL: Source, https://github.com/Correctover/standards
|
|
11
|
-
Project-URL: DOI, https://doi.org/10.5281/zenodo.21234580
|
|
12
|
-
Keywords: llm,validation,conformance,compliance,hipaa,gdpr,soc2,agent,mcp
|
|
13
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
-
Classifier: License :: Other/Proprietary License
|
|
17
|
-
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
-
Classifier: Operating System :: OS Independent
|
|
23
|
-
Requires-Python: >=3.8
|
|
24
|
-
Description-Content-Type: text/markdown
|
|
25
|
-
License-File: LICENSE
|
|
26
|
-
Requires-Dist: pydantic>=2.0.0
|
|
27
|
-
Dynamic: author
|
|
28
|
-
Dynamic: author-email
|
|
29
|
-
Dynamic: classifier
|
|
30
|
-
Dynamic: description
|
|
31
|
-
Dynamic: description-content-type
|
|
32
|
-
Dynamic: home-page
|
|
33
|
-
Dynamic: keywords
|
|
34
|
-
Dynamic: license-file
|
|
35
|
-
Dynamic: project-url
|
|
36
|
-
Dynamic: requires-dist
|
|
37
|
-
Dynamic: requires-python
|
|
38
|
-
Dynamic: summary
|
|
39
|
-
|
|
40
|
-
# Correctover - CCS v1.0
|
|
41
|
-
|
|
42
|
-
**Conformance Protocol for Agentic Runtime Systems**
|
|
43
|
-
|
|
44
|
-
DOI: [10.5281/zenodo.21234580](https://doi.org/10.5281/zenodo.21234580)
|
|
45
|
-
|
|
46
|
-
## Overview
|
|
47
|
-
|
|
48
|
-
Correctover implements the CCS v1.0 standard for validating LLM outputs in agentic systems. It provides provider-agnostic, framework-agnostic conformance verification.
|
|
49
|
-
|
|
50
|
-
## Quick Start
|
|
51
|
-
|
|
52
|
-
```python
|
|
53
|
-
from correctover import CCSValidator
|
|
54
|
-
|
|
55
|
-
validator = CCSValidator(
|
|
56
|
-
required_fields=["decision", "confidence", "trace_id"],
|
|
57
|
-
supported_fields=["reasoning", "metadata"],
|
|
58
|
-
forbidden_fields=["pii", "credentials"]
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
result = validator.validate({
|
|
62
|
-
"decision": "approve",
|
|
63
|
-
"confidence": 0.95,
|
|
64
|
-
"trace_id": "abc-123"
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
print(f"Valid: {result.is_valid}")
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Components
|
|
71
|
-
|
|
72
|
-
- **CCS-Core**: Schema validation (Required/Supported/Forbidden)
|
|
73
|
-
- **CCS-Compliance**: HIPAA, GDPR, SOC2 mapping
|
|
74
|
-
- **CCS-Cost-Flow-Analysis**: Token budget enforcement
|
|
75
|
-
- **CCS-Integrity**: HMAC-based output binding
|
|
76
|
-
- **CCS-Audit**: Comprehensive audit logging
|
|
77
|
-
|
|
78
|
-
## Integration
|
|
79
|
-
|
|
80
|
-
Framework adapters available:
|
|
81
|
-
- LangChain
|
|
82
|
-
- CrewAI
|
|
83
|
-
- AutoGen
|
|
84
|
-
- LlamaIndex
|
|
85
|
-
|
|
86
|
-
## License
|
|
87
|
-
|
|
88
|
-
Proprietary Commercial License — see LICENSE file
|
|
89
|
-
|
|
90
|
-
## Resources
|
|
91
|
-
|
|
92
|
-
- Website: https://correctover.github.io
|
|
93
|
-
- Specification: https://github.com/Correctover/standards
|
|
94
|
-
- DOI: https://doi.org/10.5281/zenodo.21234580
|
correctover-2.0.2/README.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Correctover - CCS v1.0
|
|
2
|
-
|
|
3
|
-
**Conformance Protocol for Agentic Runtime Systems**
|
|
4
|
-
|
|
5
|
-
DOI: [10.5281/zenodo.21234580](https://doi.org/10.5281/zenodo.21234580)
|
|
6
|
-
|
|
7
|
-
## Overview
|
|
8
|
-
|
|
9
|
-
Correctover implements the CCS v1.0 standard for validating LLM outputs in agentic systems. It provides provider-agnostic, framework-agnostic conformance verification.
|
|
10
|
-
|
|
11
|
-
## Quick Start
|
|
12
|
-
|
|
13
|
-
```python
|
|
14
|
-
from correctover import CCSValidator
|
|
15
|
-
|
|
16
|
-
validator = CCSValidator(
|
|
17
|
-
required_fields=["decision", "confidence", "trace_id"],
|
|
18
|
-
supported_fields=["reasoning", "metadata"],
|
|
19
|
-
forbidden_fields=["pii", "credentials"]
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
result = validator.validate({
|
|
23
|
-
"decision": "approve",
|
|
24
|
-
"confidence": 0.95,
|
|
25
|
-
"trace_id": "abc-123"
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
print(f"Valid: {result.is_valid}")
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Components
|
|
32
|
-
|
|
33
|
-
- **CCS-Core**: Schema validation (Required/Supported/Forbidden)
|
|
34
|
-
- **CCS-Compliance**: HIPAA, GDPR, SOC2 mapping
|
|
35
|
-
- **CCS-Cost-Flow-Analysis**: Token budget enforcement
|
|
36
|
-
- **CCS-Integrity**: HMAC-based output binding
|
|
37
|
-
- **CCS-Audit**: Comprehensive audit logging
|
|
38
|
-
|
|
39
|
-
## Integration
|
|
40
|
-
|
|
41
|
-
Framework adapters available:
|
|
42
|
-
- LangChain
|
|
43
|
-
- CrewAI
|
|
44
|
-
- AutoGen
|
|
45
|
-
- LlamaIndex
|
|
46
|
-
|
|
47
|
-
## License
|
|
48
|
-
|
|
49
|
-
Proprietary Commercial License — see LICENSE file
|
|
50
|
-
|
|
51
|
-
## Resources
|
|
52
|
-
|
|
53
|
-
- Website: https://correctover.github.io
|
|
54
|
-
- Specification: https://github.com/Correctover/standards
|
|
55
|
-
- DOI: https://doi.org/10.5281/zenodo.21234580
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Correctover - CCS v1.0
|
|
3
|
-
Conformance Protocol for Agentic Runtime Systems
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
__version__ = "2.0.2"
|
|
7
|
-
__author__ = "Guigui Wang"
|
|
8
|
-
__email__ = "wangguigui@correctover.com"
|
|
9
|
-
|
|
10
|
-
from .validator import CCSValidator, ValidationResult
|
|
11
|
-
|
|
12
|
-
__all__ = ["CCSValidator", "ValidationResult"]
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
CCS v1.0 Validator
|
|
3
|
-
Validates LLM outputs against Required/Supported/Forbidden schemas
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from typing import Dict, List, Any, Optional
|
|
7
|
-
from pydantic import BaseModel, Field
|
|
8
|
-
from datetime import datetime
|
|
9
|
-
import hashlib
|
|
10
|
-
import hmac
|
|
11
|
-
import json
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class ValidationResult(BaseModel):
|
|
15
|
-
"""Result of CCS validation"""
|
|
16
|
-
is_valid: bool
|
|
17
|
-
errors: List[str] = Field(default_factory=list)
|
|
18
|
-
warnings: List[str] = Field(default_factory=list)
|
|
19
|
-
validated_at: str = Field(default_factory=lambda: datetime.utcnow().isoformat())
|
|
20
|
-
trace_id: Optional[str] = None
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class CCSValidator:
|
|
24
|
-
"""
|
|
25
|
-
CCS v1.0 Validator
|
|
26
|
-
|
|
27
|
-
Validates LLM outputs against Required/Supported/Forbidden schemas.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
def __init__(
|
|
31
|
-
self,
|
|
32
|
-
required_fields: List[str] = None,
|
|
33
|
-
supported_fields: List[str] = None,
|
|
34
|
-
forbidden_fields: List[str] = None,
|
|
35
|
-
enable_integrity: bool = False,
|
|
36
|
-
integrity_key: str = None
|
|
37
|
-
):
|
|
38
|
-
self.required_fields = set(required_fields or [])
|
|
39
|
-
self.supported_fields = set(supported_fields or [])
|
|
40
|
-
self.forbidden_fields = set(forbidden_fields or [])
|
|
41
|
-
self.enable_integrity = enable_integrity
|
|
42
|
-
self.integrity_key = integrity_key or "default-key"
|
|
43
|
-
|
|
44
|
-
def validate(self, output: Dict[str, Any], trace_id: str = None) -> ValidationResult:
|
|
45
|
-
"""Validate LLM output against CCS schema"""
|
|
46
|
-
errors = []
|
|
47
|
-
warnings = []
|
|
48
|
-
|
|
49
|
-
for field in self.required_fields:
|
|
50
|
-
if field not in output:
|
|
51
|
-
errors.append(f"Missing required field: {field}")
|
|
52
|
-
|
|
53
|
-
for field in self.forbidden_fields:
|
|
54
|
-
if field in output:
|
|
55
|
-
errors.append(f"Forbidden field present: {field}")
|
|
56
|
-
|
|
57
|
-
output_fields = set(output.keys())
|
|
58
|
-
all_allowed = self.required_fields | self.supported_fields
|
|
59
|
-
unexpected = output_fields - all_allowed - self.forbidden_fields
|
|
60
|
-
for field in unexpected:
|
|
61
|
-
warnings.append(f"Unexpected field: {field}")
|
|
62
|
-
|
|
63
|
-
if self.enable_integrity:
|
|
64
|
-
stored_hash = output.get("integrity_hash")
|
|
65
|
-
computed_hash = self._compute_integrity(output)
|
|
66
|
-
|
|
67
|
-
if stored_hash is None:
|
|
68
|
-
warnings.append("No integrity hash in output")
|
|
69
|
-
elif stored_hash != computed_hash:
|
|
70
|
-
errors.append("Integrity hash mismatch")
|
|
71
|
-
|
|
72
|
-
return ValidationResult(
|
|
73
|
-
is_valid=len(errors) == 0,
|
|
74
|
-
errors=errors,
|
|
75
|
-
warnings=warnings,
|
|
76
|
-
trace_id=trace_id
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
def _compute_integrity(self, output: Dict[str, Any]) -> str:
|
|
80
|
-
"""Compute HMAC-based integrity hash using JSON serialization"""
|
|
81
|
-
# Exclude integrity_hash from computation
|
|
82
|
-
filtered = {k: v for k, v in output.items() if k != "integrity_hash"}
|
|
83
|
-
# Use JSON serialization (sorted keys, no extra spaces)
|
|
84
|
-
data_str = json.dumps(filtered, sort_keys=True, separators=(',', ':'))
|
|
85
|
-
|
|
86
|
-
signature = hmac.new(
|
|
87
|
-
self.integrity_key.encode(),
|
|
88
|
-
data_str.encode(),
|
|
89
|
-
hashlib.sha256
|
|
90
|
-
).hexdigest()
|
|
91
|
-
|
|
92
|
-
return signature
|
|
93
|
-
|
|
94
|
-
def add_integrity_hash(self, output: Dict[str, Any]) -> Dict[str, Any]:
|
|
95
|
-
"""Add integrity hash to output"""
|
|
96
|
-
output_copy = output.copy()
|
|
97
|
-
output_copy["integrity_hash"] = self._compute_integrity(output_copy)
|
|
98
|
-
return output_copy
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: correctover
|
|
3
|
-
Version: 2.0.2
|
|
4
|
-
Summary: CCS v1.0 - Conformance Protocol for Agentic Runtime Systems
|
|
5
|
-
Home-page: https://correctover.github.io
|
|
6
|
-
Author: Guigui Wang
|
|
7
|
-
Author-email: wangguigui@correctover.com
|
|
8
|
-
Project-URL: Bug Tracker, https://github.com/Correctover/standards/issues
|
|
9
|
-
Project-URL: Documentation, https://correctover.github.io
|
|
10
|
-
Project-URL: Source, https://github.com/Correctover/standards
|
|
11
|
-
Project-URL: DOI, https://doi.org/10.5281/zenodo.21234580
|
|
12
|
-
Keywords: llm,validation,conformance,compliance,hipaa,gdpr,soc2,agent,mcp
|
|
13
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
-
Classifier: License :: Other/Proprietary License
|
|
17
|
-
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
-
Classifier: Operating System :: OS Independent
|
|
23
|
-
Requires-Python: >=3.8
|
|
24
|
-
Description-Content-Type: text/markdown
|
|
25
|
-
License-File: LICENSE
|
|
26
|
-
Requires-Dist: pydantic>=2.0.0
|
|
27
|
-
Dynamic: author
|
|
28
|
-
Dynamic: author-email
|
|
29
|
-
Dynamic: classifier
|
|
30
|
-
Dynamic: description
|
|
31
|
-
Dynamic: description-content-type
|
|
32
|
-
Dynamic: home-page
|
|
33
|
-
Dynamic: keywords
|
|
34
|
-
Dynamic: license-file
|
|
35
|
-
Dynamic: project-url
|
|
36
|
-
Dynamic: requires-dist
|
|
37
|
-
Dynamic: requires-python
|
|
38
|
-
Dynamic: summary
|
|
39
|
-
|
|
40
|
-
# Correctover - CCS v1.0
|
|
41
|
-
|
|
42
|
-
**Conformance Protocol for Agentic Runtime Systems**
|
|
43
|
-
|
|
44
|
-
DOI: [10.5281/zenodo.21234580](https://doi.org/10.5281/zenodo.21234580)
|
|
45
|
-
|
|
46
|
-
## Overview
|
|
47
|
-
|
|
48
|
-
Correctover implements the CCS v1.0 standard for validating LLM outputs in agentic systems. It provides provider-agnostic, framework-agnostic conformance verification.
|
|
49
|
-
|
|
50
|
-
## Quick Start
|
|
51
|
-
|
|
52
|
-
```python
|
|
53
|
-
from correctover import CCSValidator
|
|
54
|
-
|
|
55
|
-
validator = CCSValidator(
|
|
56
|
-
required_fields=["decision", "confidence", "trace_id"],
|
|
57
|
-
supported_fields=["reasoning", "metadata"],
|
|
58
|
-
forbidden_fields=["pii", "credentials"]
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
result = validator.validate({
|
|
62
|
-
"decision": "approve",
|
|
63
|
-
"confidence": 0.95,
|
|
64
|
-
"trace_id": "abc-123"
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
print(f"Valid: {result.is_valid}")
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Components
|
|
71
|
-
|
|
72
|
-
- **CCS-Core**: Schema validation (Required/Supported/Forbidden)
|
|
73
|
-
- **CCS-Compliance**: HIPAA, GDPR, SOC2 mapping
|
|
74
|
-
- **CCS-Cost-Flow-Analysis**: Token budget enforcement
|
|
75
|
-
- **CCS-Integrity**: HMAC-based output binding
|
|
76
|
-
- **CCS-Audit**: Comprehensive audit logging
|
|
77
|
-
|
|
78
|
-
## Integration
|
|
79
|
-
|
|
80
|
-
Framework adapters available:
|
|
81
|
-
- LangChain
|
|
82
|
-
- CrewAI
|
|
83
|
-
- AutoGen
|
|
84
|
-
- LlamaIndex
|
|
85
|
-
|
|
86
|
-
## License
|
|
87
|
-
|
|
88
|
-
Proprietary Commercial License — see LICENSE file
|
|
89
|
-
|
|
90
|
-
## Resources
|
|
91
|
-
|
|
92
|
-
- Website: https://correctover.github.io
|
|
93
|
-
- Specification: https://github.com/Correctover/standards
|
|
94
|
-
- DOI: https://doi.org/10.5281/zenodo.21234580
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
setup.py
|
|
4
|
-
correctover/__init__.py
|
|
5
|
-
correctover/validator.py
|
|
6
|
-
correctover.egg-info/PKG-INFO
|
|
7
|
-
correctover.egg-info/SOURCES.txt
|
|
8
|
-
correctover.egg-info/dependency_links.txt
|
|
9
|
-
correctover.egg-info/requires.txt
|
|
10
|
-
correctover.egg-info/top_level.txt
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pydantic>=2.0.0
|
correctover-2.0.2/setup.py
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
from setuptools import setup, find_packages
|
|
2
|
-
|
|
3
|
-
with open("README.md", "r", encoding="utf-8") as fh:
|
|
4
|
-
long_description = fh.read()
|
|
5
|
-
|
|
6
|
-
setup(
|
|
7
|
-
name="correctover",
|
|
8
|
-
version="2.0.2",
|
|
9
|
-
author="Guigui Wang",
|
|
10
|
-
author_email="wangguigui@correctover.com",
|
|
11
|
-
description="CCS v1.0 - Conformance Protocol for Agentic Runtime Systems",
|
|
12
|
-
long_description=long_description,
|
|
13
|
-
long_description_content_type="text/markdown",
|
|
14
|
-
url="https://correctover.github.io",
|
|
15
|
-
project_urls={
|
|
16
|
-
"Bug Tracker": "https://github.com/Correctover/standards/issues",
|
|
17
|
-
"Documentation": "https://correctover.github.io",
|
|
18
|
-
"Source": "https://github.com/Correctover/standards",
|
|
19
|
-
"DOI": "https://doi.org/10.5281/zenodo.21234580",
|
|
20
|
-
},
|
|
21
|
-
packages=find_packages(),
|
|
22
|
-
classifiers=[
|
|
23
|
-
"Development Status :: 5 - Production/Stable",
|
|
24
|
-
"Intended Audience :: Developers",
|
|
25
|
-
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
26
|
-
"License :: Other/Proprietary License",
|
|
27
|
-
"Programming Language :: Python :: 3",
|
|
28
|
-
"Programming Language :: Python :: 3.8",
|
|
29
|
-
"Programming Language :: Python :: 3.9",
|
|
30
|
-
"Programming Language :: Python :: 3.10",
|
|
31
|
-
"Programming Language :: Python :: 3.11",
|
|
32
|
-
"Operating System :: OS Independent",
|
|
33
|
-
],
|
|
34
|
-
python_requires=">=3.8",
|
|
35
|
-
install_requires=[
|
|
36
|
-
"pydantic>=2.0.0",
|
|
37
|
-
],
|
|
38
|
-
keywords="llm, validation, conformance, compliance, hipaa, gdpr, soc2, agent, mcp",
|
|
39
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|