verilink-aiverify-plugin 1.0.0__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.
- verilink_aiverify_plugin-1.0.0.dist-info/METADATA +71 -0
- verilink_aiverify_plugin-1.0.0.dist-info/RECORD +12 -0
- verilink_aiverify_plugin-1.0.0.dist-info/WHEEL +5 -0
- verilink_aiverify_plugin-1.0.0.dist-info/entry_points.txt +2 -0
- verilink_aiverify_plugin-1.0.0.dist-info/licenses/LICENSE +17 -0
- verilink_aiverify_plugin-1.0.0.dist-info/top_level.txt +1 -0
- verilink_plugin/__init__.py +18 -0
- verilink_plugin/client.py +102 -0
- verilink_plugin/exceptions.py +27 -0
- verilink_plugin/models.py +63 -0
- verilink_plugin/plugin.py +181 -0
- verilink_plugin/utils.py +31 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: verilink-aiverify-plugin
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: VeriLinkOS Active Governance Integration for AI Verify
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: httpx<0.28.0,>=0.23.0
|
|
10
|
+
Requires-Dist: pydantic<3.0.0,>=1.10.0
|
|
11
|
+
Requires-Dist: python-dotenv<2.1.0,>=0.19.0
|
|
12
|
+
Requires-Dist: tenacity<9.1.0,>=8.0.0
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
Dynamic: requires-python
|
|
15
|
+
|
|
16
|
+
# VeriLinkOS AI Verify Plugin
|
|
17
|
+
|
|
18
|
+
[](https://badge.fury.io/py/verilink-aiverify-plugin)
|
|
19
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
20
|
+
|
|
21
|
+
## Overview
|
|
22
|
+
|
|
23
|
+
VeriLinkOS AI Verify Plugin integrates active governance capabilities from VeriLinkOS into the AI Verify testing framework.
|
|
24
|
+
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
- ✅ Active Governance: Real-time policy enforcement during AI tests
|
|
28
|
+
- ✅ VAP Receipts: Cryptographic evidence for compliance audits
|
|
29
|
+
- ✅ AI-BOM Verification: Ensure model integrity and provenance
|
|
30
|
+
- ✅ Trust Tokens: JIT authentication for each test run
|
|
31
|
+
- ✅ Predictive Enforcement: ML-based risk prediction
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install verilink-aiverify-plugin
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Configuration
|
|
40
|
+
Set environment variables:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
export VERILINK_API_URL=https://api.verilink.os
|
|
44
|
+
export VERILINK_API_KEY=your_api_key
|
|
45
|
+
export VERILINK_AGENT_ID=your_agent_id
|
|
46
|
+
export VERILINK_ORG_ID=your_org_id
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
```python
|
|
51
|
+
import asyncio
|
|
52
|
+
from verilink_plugin import VeriLinkGovernancePlugin
|
|
53
|
+
|
|
54
|
+
async def test_model():
|
|
55
|
+
plugin = VeriLinkGovernancePlugin()
|
|
56
|
+
|
|
57
|
+
result = await plugin.run_test(
|
|
58
|
+
{"model_id": "model-001", "dataset": "test-data"},
|
|
59
|
+
lambda config: {"passed": True, "artifacts": {}}
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
print(f"Passed: {result.passed}")
|
|
63
|
+
print(f"Receipt: {result.receipt_id}")
|
|
64
|
+
|
|
65
|
+
await plugin.cleanup()
|
|
66
|
+
|
|
67
|
+
asyncio.run(test_model())
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
Apache 2.0
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
verilink_aiverify_plugin-1.0.0.dist-info/licenses/LICENSE,sha256=KSuq6JK0zk6CXmYDZDZ6lMUEUDkTMJR9nYL6ZeNZ4vs,623
|
|
2
|
+
verilink_plugin/__init__.py,sha256=_KCUidBhREjwW7uDAarqd_VKN0vPXnvKKWlV-oF8IQs,425
|
|
3
|
+
verilink_plugin/client.py,sha256=yH3bmfgKHx74flDw_ppo4sUQnsYOMjXzkOi3DOoFook,3814
|
|
4
|
+
verilink_plugin/exceptions.py,sha256=ooqhDs-mXrZMSPBHK7mkzQAtF7sJPPSxR0I4CPsXu_c,695
|
|
5
|
+
verilink_plugin/models.py,sha256=FAkAq9rllHLaKhLHHaZa6lEuI-8opM8UA6L4Ai5cv2U,2356
|
|
6
|
+
verilink_plugin/plugin.py,sha256=N3VkLot2kHNWTJvoTR_k4sKw5QYuNobR2OROeetXDmM,7467
|
|
7
|
+
verilink_plugin/utils.py,sha256=Sx_xkHy0yswoc5LG85ZBrcjtzhB7cDq6dtksjSlQYs4,1029
|
|
8
|
+
verilink_aiverify_plugin-1.0.0.dist-info/METADATA,sha256=PRsnDvLJQit1Lzsg9cZRVZmlBjaGJs5BFcbkiW96kjk,1958
|
|
9
|
+
verilink_aiverify_plugin-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
10
|
+
verilink_aiverify_plugin-1.0.0.dist-info/entry_points.txt,sha256=k8KmFSB6n70QiWSHuE0UitCeZOm0O4VJSp7XM3J2zXk,81
|
|
11
|
+
verilink_aiverify_plugin-1.0.0.dist-info/top_level.txt,sha256=jmMWUkgIaXjFsUH1J2rYnAaIqI3gRxNgRtnM23k1uFs,16
|
|
12
|
+
verilink_aiverify_plugin-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2024 Rajinder Jhol
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
verilink_plugin
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
VeriLinkOS AI Verify Plugin
|
|
3
|
+
"""
|
|
4
|
+
from .plugin import VeriLinkGovernancePlugin
|
|
5
|
+
from .client import VeriLinkClient
|
|
6
|
+
from .models import VeriLinkConfig, VeriLinkTestResult, VeriLinkEnforcementMode
|
|
7
|
+
from .exceptions import VeriLinkError
|
|
8
|
+
|
|
9
|
+
__version__ = "1.0.0"
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"VeriLinkGovernancePlugin",
|
|
13
|
+
"VeriLinkClient",
|
|
14
|
+
"VeriLinkConfig",
|
|
15
|
+
"VeriLinkTestResult",
|
|
16
|
+
"VeriLinkEnforcementMode",
|
|
17
|
+
"VeriLinkError"
|
|
18
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HTTP client for VeriLinkOS API
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
from typing import Dict, Any, Optional
|
|
8
|
+
import httpx
|
|
9
|
+
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
|
|
10
|
+
|
|
11
|
+
from .models import VeriLinkConfig
|
|
12
|
+
from .exceptions import (
|
|
13
|
+
VeriLinkConnectionError,
|
|
14
|
+
VeriLinkAuthError,
|
|
15
|
+
VeriLinkTimeoutError
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger("verilink_plugin")
|
|
19
|
+
|
|
20
|
+
class VeriLinkClient:
|
|
21
|
+
"""Core API client for VeriLinkOS"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, config: VeriLinkConfig):
|
|
24
|
+
self.config = config
|
|
25
|
+
self.headers = {
|
|
26
|
+
"X-VeriLink-API-Key": config.api_key,
|
|
27
|
+
"X-VeriLink-Agent-ID": config.agent_id,
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
"User-Agent": "VeriLink-AIVerify-Plugin/1.0.0"
|
|
30
|
+
}
|
|
31
|
+
self.client = httpx.AsyncClient(
|
|
32
|
+
base_url=config.api_url,
|
|
33
|
+
headers=self.headers,
|
|
34
|
+
timeout=config.timeout
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
async def verilink_health_check(self) -> bool:
|
|
38
|
+
"""Check if VeriLinkOS API is healthy"""
|
|
39
|
+
try:
|
|
40
|
+
response = await self.client.get("/health")
|
|
41
|
+
return response.status_code == 200
|
|
42
|
+
except Exception as e:
|
|
43
|
+
logger.error(f"VeriLink health check failed: {str(e)}")
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
@retry(
|
|
47
|
+
stop=stop_after_attempt(3),
|
|
48
|
+
wait=wait_exponential(multiplier=1, min=2, max=10),
|
|
49
|
+
retry=retry_if_exception_type((httpx.RequestError, VeriLinkTimeoutError))
|
|
50
|
+
)
|
|
51
|
+
async def _verilink_request(self, method: str, endpoint: str, data: Optional[Dict] = None) -> Dict:
|
|
52
|
+
"""Make a request to VeriLinkOS API with retry logic"""
|
|
53
|
+
try:
|
|
54
|
+
response = await self.client.request(method, endpoint, json=data)
|
|
55
|
+
|
|
56
|
+
if response.status_code == 401:
|
|
57
|
+
raise VeriLinkAuthError("Invalid VeriLink API credentials")
|
|
58
|
+
|
|
59
|
+
response.raise_for_status()
|
|
60
|
+
return response.json()
|
|
61
|
+
|
|
62
|
+
except httpx.TimeoutException:
|
|
63
|
+
raise VeriLinkTimeoutError(f"Request to {endpoint} timed out")
|
|
64
|
+
except httpx.HTTPStatusError as e:
|
|
65
|
+
raise VeriLinkConnectionError(f"API Error {e.response.status_code}: {e.response.text}")
|
|
66
|
+
except Exception as e:
|
|
67
|
+
raise VeriLinkConnectionError(f"Unexpected connection error: {str(e)}")
|
|
68
|
+
|
|
69
|
+
async def verilink_get_token(self) -> str:
|
|
70
|
+
"""Retrieve a short-lived JIT Trust Token"""
|
|
71
|
+
res = await self._verilink_request("POST", "/v1/auth/token", {
|
|
72
|
+
"org_id": self.config.organization_id,
|
|
73
|
+
"agent_id": self.config.agent_id
|
|
74
|
+
})
|
|
75
|
+
return res["trust_token"]
|
|
76
|
+
|
|
77
|
+
async def verilink_verify_aibom(self, aibom_data: Dict) -> Dict:
|
|
78
|
+
"""Validate Model Integrity via AI-BOM verification"""
|
|
79
|
+
return await self._verilink_request("POST", "/v1/governance/verify-aibom", aibom_data)
|
|
80
|
+
|
|
81
|
+
async def verilink_generate_receipt(self, test_evidence: Dict) -> Dict:
|
|
82
|
+
"""Generate a VAP Receipt"""
|
|
83
|
+
return await self._verilink_request("POST", "/v1/governance/receipt", {
|
|
84
|
+
"evidence": test_evidence,
|
|
85
|
+
"timestamp": time.time(),
|
|
86
|
+
"batch": True
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
async def verilink_verify_receipt(self, receipt_id: str) -> Dict:
|
|
90
|
+
"""Verify an existing receipt"""
|
|
91
|
+
return await self._verilink_request("GET", f"/v1/governance/receipt/{receipt_id}/verify")
|
|
92
|
+
|
|
93
|
+
async def verilink_predict_outcome(self, config_fingerprint: str) -> float:
|
|
94
|
+
"""Predict failure probability"""
|
|
95
|
+
res = await self._verilink_request("POST", "/v1/analytics/predict", {
|
|
96
|
+
"fingerprint": config_fingerprint
|
|
97
|
+
})
|
|
98
|
+
return res.get("failure_probability", 0.0)
|
|
99
|
+
|
|
100
|
+
async def close(self):
|
|
101
|
+
"""Close the HTTP client"""
|
|
102
|
+
await self.client.aclose()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom exceptions for VeriLinkOS plugin
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
class VeriLinkError(Exception):
|
|
6
|
+
"""Base exception for all VeriLink plugin errors."""
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
class VeriLinkConnectionError(VeriLinkError):
|
|
10
|
+
"""Raised when the plugin cannot connect to VeriLinkOS API."""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
class VeriLinkAuthError(VeriLinkError):
|
|
14
|
+
"""Raised on authentication failures."""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
class VeriLinkValidationError(VeriLinkError):
|
|
18
|
+
"""Raised when configuration or payload validation fails."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
class VeriLinkTimeoutError(VeriLinkError):
|
|
22
|
+
"""Raised when a VeriLinkOS operation times out."""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
class VeriLinkConfigError(VeriLinkError):
|
|
26
|
+
"""Raised when configuration is invalid."""
|
|
27
|
+
pass
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data models for VeriLinkOS plugin
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, Any, Optional
|
|
6
|
+
from pydantic import BaseModel, Field, validator
|
|
7
|
+
from enum import Enum
|
|
8
|
+
import uuid
|
|
9
|
+
|
|
10
|
+
class VeriLinkEnforcementMode(str, Enum):
|
|
11
|
+
"""Enforcement modes for governance"""
|
|
12
|
+
BLOCK = "block"
|
|
13
|
+
WARN = "warn"
|
|
14
|
+
LOG = "log"
|
|
15
|
+
OBSERVE = "observe"
|
|
16
|
+
|
|
17
|
+
class VeriLinkConfig(BaseModel):
|
|
18
|
+
"""Configuration for VeriLinkOS plugin"""
|
|
19
|
+
api_url: str = Field(..., description="VeriLinkOS API URL")
|
|
20
|
+
api_key: str = Field(..., description="VeriLinkOS API key")
|
|
21
|
+
agent_id: str = Field(..., description="Agent identifier")
|
|
22
|
+
organization_id: str = Field(..., description="Organization identifier")
|
|
23
|
+
enforce_mode: VeriLinkEnforcementMode = Field(
|
|
24
|
+
default=VeriLinkEnforcementMode.BLOCK,
|
|
25
|
+
description="Enforcement mode"
|
|
26
|
+
)
|
|
27
|
+
timeout: int = Field(default=30, description="Request timeout in seconds")
|
|
28
|
+
max_retries: int = Field(default=3, description="Maximum retry attempts")
|
|
29
|
+
use_cache: bool = Field(default=True, description="Enable caching")
|
|
30
|
+
cache_ttl: int = Field(default=300, description="Cache TTL in seconds")
|
|
31
|
+
predictive_enabled: bool = Field(default=True, description="Enable predictive enforcement")
|
|
32
|
+
jurisdiction: str = Field(default="SG", description="Legal jurisdiction")
|
|
33
|
+
|
|
34
|
+
@validator("api_url")
|
|
35
|
+
def validate_url(cls, v):
|
|
36
|
+
if not v.startswith(("http://", "https://")):
|
|
37
|
+
raise ValueError("VERILINK_API_URL must start with http:// or https://")
|
|
38
|
+
return v
|
|
39
|
+
|
|
40
|
+
class VeriLinkTestResult(BaseModel):
|
|
41
|
+
"""Result of a test with VeriLinkOS governance"""
|
|
42
|
+
passed: bool
|
|
43
|
+
error: Optional[str] = None
|
|
44
|
+
details: Dict[str, Any] = Field(default_factory=dict)
|
|
45
|
+
artifacts: Dict[str, Any] = Field(default_factory=dict)
|
|
46
|
+
receipt_id: Optional[str] = None
|
|
47
|
+
verification_url: Optional[str] = None
|
|
48
|
+
merkle_root: Optional[str] = None
|
|
49
|
+
prediction_score: Optional[float] = None
|
|
50
|
+
governance_status: str = "evaluated"
|
|
51
|
+
|
|
52
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
53
|
+
return self.dict(exclude_none=True)
|
|
54
|
+
|
|
55
|
+
class VeriLinkReceipt(BaseModel):
|
|
56
|
+
"""VAP Receipt model"""
|
|
57
|
+
receipt_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
58
|
+
test_id: str
|
|
59
|
+
timestamp: float
|
|
60
|
+
signature: str
|
|
61
|
+
evidence_hash: str
|
|
62
|
+
merkle_root: Optional[str] = None
|
|
63
|
+
verification_url: Optional[str] = None
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main VeriLinkOS Governance Plugin for AI Verify
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import asyncio
|
|
7
|
+
import hashlib
|
|
8
|
+
import json
|
|
9
|
+
from typing import Dict, Any, Optional, Callable
|
|
10
|
+
|
|
11
|
+
from .models import VeriLinkConfig, VeriLinkTestResult, VeriLinkEnforcementMode
|
|
12
|
+
from .client import VeriLinkClient
|
|
13
|
+
from .exceptions import VeriLinkAuthError
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger("verilink_plugin")
|
|
16
|
+
|
|
17
|
+
class VeriLinkGovernancePlugin:
|
|
18
|
+
"""Main integration plugin for AI Verify"""
|
|
19
|
+
|
|
20
|
+
PLUGIN_ID = "verilink_os_governance"
|
|
21
|
+
PLUGIN_VERSION = "1.0.0"
|
|
22
|
+
|
|
23
|
+
def __init__(self, config_dict: Optional[Dict] = None):
|
|
24
|
+
self.config = VeriLinkConfig(**(config_dict or {}))
|
|
25
|
+
self.client = VeriLinkClient(self.config)
|
|
26
|
+
self._aibom_cache: Dict[str, Any] = {}
|
|
27
|
+
self._initialized = False
|
|
28
|
+
|
|
29
|
+
async def verilink_setup(self):
|
|
30
|
+
"""Initialize the plugin and check connectivity"""
|
|
31
|
+
try:
|
|
32
|
+
if await self.client.verilink_health_check():
|
|
33
|
+
self._initialized = True
|
|
34
|
+
logger.info("VeriLinkOS connection established")
|
|
35
|
+
else:
|
|
36
|
+
logger.warning("VeriLinkOS API unreachable. Operating in fail-safe mode.")
|
|
37
|
+
self._initialized = False
|
|
38
|
+
except Exception as e:
|
|
39
|
+
logger.error(f"VeriLink setup failed: {str(e)}")
|
|
40
|
+
self._initialized = False
|
|
41
|
+
|
|
42
|
+
async def run_test(self, test_config: Dict[str, Any], test_func: Callable) -> VeriLinkTestResult:
|
|
43
|
+
"""Run an AI test with VeriLinkOS governance"""
|
|
44
|
+
try:
|
|
45
|
+
if not self._initialized:
|
|
46
|
+
await self.verilink_setup()
|
|
47
|
+
|
|
48
|
+
# 1. Predictive enforcement
|
|
49
|
+
if self.config.predictive_enabled:
|
|
50
|
+
prediction = await self._verilink_predict_risk(test_config)
|
|
51
|
+
if prediction > 0.8 and self.config.enforce_mode == VeriLinkEnforcementMode.BLOCK:
|
|
52
|
+
return VeriLinkTestResult(
|
|
53
|
+
passed=False,
|
|
54
|
+
error="Predictive block: high risk detected",
|
|
55
|
+
prediction_score=prediction,
|
|
56
|
+
governance_status="blocked"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# 2. Get trust token
|
|
60
|
+
try:
|
|
61
|
+
await self.client.verilink_get_token()
|
|
62
|
+
logger.info("VeriLink trust token acquired")
|
|
63
|
+
except Exception as e:
|
|
64
|
+
if self.config.enforce_mode == VeriLinkEnforcementMode.BLOCK:
|
|
65
|
+
return VeriLinkTestResult(
|
|
66
|
+
passed=False,
|
|
67
|
+
error=f"Authentication failed: {str(e)}",
|
|
68
|
+
governance_status="blocked"
|
|
69
|
+
)
|
|
70
|
+
return self._fail_open_result(str(e))
|
|
71
|
+
|
|
72
|
+
# 3. Verify AI-BOM
|
|
73
|
+
try:
|
|
74
|
+
aibom_status = await self._verilink_check_aibom(test_config)
|
|
75
|
+
if not aibom_status.get("valid") and self.config.enforce_mode == VeriLinkEnforcementMode.BLOCK:
|
|
76
|
+
return VeriLinkTestResult(
|
|
77
|
+
passed=False,
|
|
78
|
+
error="AI-BOM Verification Failed: Model not registered or tampered",
|
|
79
|
+
governance_status="blocked"
|
|
80
|
+
)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
if self.config.enforce_mode == VeriLinkEnforcementMode.BLOCK:
|
|
83
|
+
return VeriLinkTestResult(
|
|
84
|
+
passed=False,
|
|
85
|
+
error=f"AI-BOM verification error: {str(e)}",
|
|
86
|
+
governance_status="blocked"
|
|
87
|
+
)
|
|
88
|
+
logger.warning(f"AI-BOM verification degraded: {e}")
|
|
89
|
+
|
|
90
|
+
# 4. Run the actual test
|
|
91
|
+
test_start_time = asyncio.get_event_loop().time()
|
|
92
|
+
test_raw_result = await test_func(test_config)
|
|
93
|
+
test_duration = asyncio.get_event_loop().time() - test_start_time
|
|
94
|
+
|
|
95
|
+
# 5. Generate VAP receipt
|
|
96
|
+
evidence = {
|
|
97
|
+
"test_config": test_config,
|
|
98
|
+
"raw_result": test_raw_result,
|
|
99
|
+
"duration": test_duration,
|
|
100
|
+
"timestamp": asyncio.get_event_loop().time()
|
|
101
|
+
}
|
|
102
|
+
receipt_data = await self.client.verilink_generate_receipt(evidence)
|
|
103
|
+
|
|
104
|
+
# 6. Verify the receipt
|
|
105
|
+
verification = await self.client.verilink_verify_receipt(receipt_data["receipt_id"])
|
|
106
|
+
is_verified = verification.get("verified", False)
|
|
107
|
+
|
|
108
|
+
return VeriLinkTestResult(
|
|
109
|
+
passed=test_raw_result.get("passed", False),
|
|
110
|
+
receipt_id=receipt_data["receipt_id"],
|
|
111
|
+
verification_url=f"{self.config.api_url}/verify/{receipt_data['receipt_id']}",
|
|
112
|
+
merkle_root=receipt_data.get("merkle_root"),
|
|
113
|
+
details={
|
|
114
|
+
"receipt_verified": is_verified,
|
|
115
|
+
"duration": test_duration,
|
|
116
|
+
"governance_mode": self.config.enforce_mode.value
|
|
117
|
+
},
|
|
118
|
+
artifacts=test_raw_result.get("artifacts", {}),
|
|
119
|
+
governance_status="approved" if is_verified else "pending"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
except VeriLinkAuthError as e:
|
|
123
|
+
if self.config.enforce_mode == VeriLinkEnforcementMode.BLOCK:
|
|
124
|
+
raise
|
|
125
|
+
return self._fail_open_result(str(e))
|
|
126
|
+
|
|
127
|
+
except Exception as e:
|
|
128
|
+
logger.error(f"VeriLink plugin error: {e}", exc_info=True)
|
|
129
|
+
if self.config.enforce_mode == VeriLinkEnforcementMode.BLOCK:
|
|
130
|
+
return VeriLinkTestResult(
|
|
131
|
+
passed=False,
|
|
132
|
+
error=str(e),
|
|
133
|
+
governance_status="blocked"
|
|
134
|
+
)
|
|
135
|
+
return self._fail_open_result(str(e))
|
|
136
|
+
|
|
137
|
+
async def _verilink_check_aibom(self, test_config: Dict) -> Dict:
|
|
138
|
+
"""Check AI-BOM with caching"""
|
|
139
|
+
model_id = test_config.get("model_id", "default")
|
|
140
|
+
|
|
141
|
+
if self.config.use_cache and model_id in self._aibom_cache:
|
|
142
|
+
logger.debug(f"AI-BOM cache hit for {model_id}")
|
|
143
|
+
return self._aibom_cache[model_id]
|
|
144
|
+
|
|
145
|
+
res = await self.client.verilink_verify_aibom({"model_id": model_id})
|
|
146
|
+
|
|
147
|
+
if self.config.use_cache:
|
|
148
|
+
self._aibom_cache[model_id] = res
|
|
149
|
+
|
|
150
|
+
return res
|
|
151
|
+
|
|
152
|
+
async def _verilink_predict_risk(self, test_config: Dict) -> float:
|
|
153
|
+
"""Predict failure probability"""
|
|
154
|
+
fingerprint = hashlib.sha256(
|
|
155
|
+
json.dumps(test_config, sort_keys=True).encode()
|
|
156
|
+
).hexdigest()
|
|
157
|
+
|
|
158
|
+
return await self.client.verilink_predict_outcome(fingerprint)
|
|
159
|
+
|
|
160
|
+
def _fail_open_result(self, error_msg: str) -> VeriLinkTestResult:
|
|
161
|
+
"""Return fail-open result (pass with warning)"""
|
|
162
|
+
logger.warning(f"VeriLink fail-open: {error_msg}")
|
|
163
|
+
return VeriLinkTestResult(
|
|
164
|
+
passed=True,
|
|
165
|
+
error=error_msg,
|
|
166
|
+
governance_status="degraded",
|
|
167
|
+
details={"mode": "fail_open"}
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def get_metadata(self) -> Dict[str, str]:
|
|
171
|
+
"""Return plugin metadata for AI Verify"""
|
|
172
|
+
return {
|
|
173
|
+
"name": "VeriLinkOS Governance Plugin",
|
|
174
|
+
"version": self.PLUGIN_VERSION,
|
|
175
|
+
"entry_point": "verilink_os",
|
|
176
|
+
"description": "Active governance integration for AI Verify"
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async def cleanup(self):
|
|
180
|
+
"""Clean up resources"""
|
|
181
|
+
await self.client.close()
|
verilink_plugin/utils.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for VeriLinkOS plugin
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
8
|
+
from typing import Dict, Any
|
|
9
|
+
|
|
10
|
+
def verilink_setup_logger(name: str = "verilink_plugin") -> logging.Logger:
|
|
11
|
+
"""Setup and return a logger for the plugin"""
|
|
12
|
+
logger = logging.getLogger(name)
|
|
13
|
+
if not logger.handlers:
|
|
14
|
+
handler = logging.StreamHandler()
|
|
15
|
+
formatter = logging.Formatter(
|
|
16
|
+
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
17
|
+
)
|
|
18
|
+
handler.setFormatter(formatter)
|
|
19
|
+
logger.addHandler(handler)
|
|
20
|
+
return logger
|
|
21
|
+
|
|
22
|
+
def verilink_calculate_evidence_hash(data: Dict[str, Any]) -> str:
|
|
23
|
+
"""Computes a deterministic SHA256 hash of the evidence data."""
|
|
24
|
+
encoded = json.dumps(data, sort_keys=True).encode()
|
|
25
|
+
return hashlib.sha256(encoded).hexdigest()
|
|
26
|
+
|
|
27
|
+
def verilink_validate_config(url: str, key: str) -> bool:
|
|
28
|
+
"""Basic validation for VeriLink API connectivity parameters."""
|
|
29
|
+
if not url.startswith(("http://", "https://")):
|
|
30
|
+
return False
|
|
31
|
+
return len(key) > 8
|