openclaw-air-trust 0.2.0
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.
- package/LICENSE +15 -0
- package/README.md +138 -0
- package/dist/audit-ledger.d.ts +56 -0
- package/dist/audit-ledger.d.ts.map +1 -0
- package/dist/audit-ledger.js +230 -0
- package/dist/audit-ledger.js.map +1 -0
- package/dist/consent-gate.d.ts +47 -0
- package/dist/consent-gate.d.ts.map +1 -0
- package/dist/consent-gate.js +199 -0
- package/dist/consent-gate.js.map +1 -0
- package/dist/data-vault.d.ts +42 -0
- package/dist/data-vault.d.ts.map +1 -0
- package/dist/data-vault.js +190 -0
- package/dist/data-vault.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +284 -0
- package/dist/index.js.map +1 -0
- package/dist/injection-detector.d.ts +23 -0
- package/dist/injection-detector.d.ts.map +1 -0
- package/dist/injection-detector.js +160 -0
- package/dist/injection-detector.js.map +1 -0
- package/dist/plugin.d.ts +37 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +366 -0
- package/dist/plugin.js.map +1 -0
- package/dist/scanner-lite.d.ts +25 -0
- package/dist/scanner-lite.d.ts.map +1 -0
- package/dist/scanner-lite.js +71 -0
- package/dist/scanner-lite.js.map +1 -0
- package/dist/types.d.ts +172 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/openclaw.plugin.json +86 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License.
|
|
7
|
+
You may obtain a copy of the License at
|
|
8
|
+
|
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
|
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
See the License for the specific language governing permissions and
|
|
15
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# openclaw-air-trust
|
|
2
|
+
|
|
3
|
+
**The EU AI Act compliance plugin for OpenClaw** — tamper-evident audit trails, consent gating, data tokenization, and prompt injection detection for autonomous AI agents.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/openclaw-air-trust)
|
|
6
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
7
|
+
|
|
8
|
+
## Why This Exists
|
|
9
|
+
|
|
10
|
+
OpenClaw agents can read your email, execute shell commands, send messages, and manage files — autonomously. When something goes wrong, there's no tamper-evident record of what happened, no approval gate for destructive actions, and no protection against prompt injection attacks.
|
|
11
|
+
|
|
12
|
+
This plugin fixes that. EU AI Act enforcement begins **August 2026**. This is the compliance layer.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install openclaw-air-trust
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then add to your OpenClaw config:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"plugins": ["openclaw-air-trust"]
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## What It Does
|
|
29
|
+
|
|
30
|
+
| Capability | What It Does | EU AI Act Article |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| **Audit Ledger** | HMAC-SHA256 tamper-evident chain of every action | Article 12 (Record-Keeping) |
|
|
33
|
+
| **Consent Gate** | Blocks destructive tools until user approves | Article 14 (Human Oversight) |
|
|
34
|
+
| **Data Vault** | Tokenizes API keys, PII, credentials before they reach the LLM | Article 10 (Data Governance) |
|
|
35
|
+
| **Injection Detector** | Scores inbound messages for 15+ prompt injection patterns | Article 15 (Cybersecurity) |
|
|
36
|
+
| **Risk Classifier** | Classifies every tool by risk level (CRITICAL/HIGH/MEDIUM/LOW) | Article 9 (Risk Management) |
|
|
37
|
+
| **Compliance Scanner** | Checks agent code against all 6 EU AI Act articles | Articles 9-15 |
|
|
38
|
+
|
|
39
|
+
## Plugin Tools
|
|
40
|
+
|
|
41
|
+
Once installed, these tools are available to your OpenClaw agent:
|
|
42
|
+
|
|
43
|
+
| Tool | Description |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `air_audit_status` | Get audit chain length, validity, and time range |
|
|
46
|
+
| `air_verify_chain` | Verify tamper-evident chain integrity |
|
|
47
|
+
| `air_scan_injection` | Scan text for prompt injection patterns |
|
|
48
|
+
| `air_classify_risk` | Classify a tool by EU AI Act risk level |
|
|
49
|
+
| `air_export_audit` | Export the full audit chain as JSON |
|
|
50
|
+
| `air_compliance_check` | Run EU AI Act compliance check on code |
|
|
51
|
+
|
|
52
|
+
## How It Works
|
|
53
|
+
|
|
54
|
+
### Audit Ledger (Article 12)
|
|
55
|
+
|
|
56
|
+
Every tool call, LLM interaction, consent decision, and injection detection gets appended to a tamper-evident chain:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
Entry 1 → hash₁ ──┐
|
|
60
|
+
Entry 2 → hash₂ (prevHash = hash₁) ──┐
|
|
61
|
+
Entry 3 → hash₃ (prevHash = hash₂) ──┐
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Each entry is signed with HMAC-SHA256. Modifying any record breaks the entire chain downstream.
|
|
65
|
+
|
|
66
|
+
### Consent Gate (Article 14)
|
|
67
|
+
|
|
68
|
+
When the agent tries to call a destructive tool, the consent gate intercepts and sends an approval request. Risk classification is built-in: critical (code execution), high (file writes, deploys), medium (network/email), low (reads).
|
|
69
|
+
|
|
70
|
+
### Data Vault (Article 10)
|
|
71
|
+
|
|
72
|
+
Before tool arguments or context reaches the LLM, the vault scans for sensitive patterns and replaces them with opaque tokens. 14 built-in patterns: OpenAI/Anthropic/AWS/GitHub/Stripe keys, emails, phone numbers, SSNs, credit cards, connection strings, bearer tokens, private keys, and password assignments.
|
|
73
|
+
|
|
74
|
+
### Injection Detector (Article 15)
|
|
75
|
+
|
|
76
|
+
Scans inbound messages for 15+ prompt injection patterns: role override, identity hijacking, privilege escalation, safety bypass, jailbreak, data exfiltration, encoding evasion, and more. Three sensitivity levels (low/medium/high) control which patterns are active.
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"plugins": {
|
|
83
|
+
"openclaw-air-trust": {
|
|
84
|
+
"enabled": true,
|
|
85
|
+
"gatewayUrl": "https://your-air-gateway.example.com",
|
|
86
|
+
"gatewayKey": "your-api-key",
|
|
87
|
+
"consentGateEnabled": true,
|
|
88
|
+
"consentAlwaysRequire": "exec,spawn,shell,deploy",
|
|
89
|
+
"consentRiskThreshold": "high",
|
|
90
|
+
"consentTimeoutMs": 30000,
|
|
91
|
+
"injectionEnabled": true,
|
|
92
|
+
"injectionSensitivity": "medium",
|
|
93
|
+
"injectionBlockThreshold": 0.8,
|
|
94
|
+
"vaultEnabled": true
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Standalone Usage
|
|
101
|
+
|
|
102
|
+
You can also use the components directly without OpenClaw:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { createAirTrustPlugin } from 'openclaw-air-trust/standalone';
|
|
106
|
+
|
|
107
|
+
const trust = createAirTrustPlugin({
|
|
108
|
+
enabled: true,
|
|
109
|
+
consentGate: { enabled: true, alwaysRequire: ['exec', 'deploy'] },
|
|
110
|
+
injectionDetection: { enabled: true, sensitivity: 'medium', blockThreshold: 0.8 },
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Part of the AIR Blackbox Ecosystem
|
|
115
|
+
|
|
116
|
+
| Package | What It Does |
|
|
117
|
+
|---|---|
|
|
118
|
+
| [air-blackbox-mcp](https://github.com/airblackbox/air-blackbox-mcp) | MCP server for Claude Desktop — 10 compliance tools |
|
|
119
|
+
| [air-langchain-trust](https://pypi.org/project/air-langchain-trust/) | Python trust layer for LangChain agents |
|
|
120
|
+
| [air-crewai-trust](https://pypi.org/project/air-crewai-trust/) | Python trust layer for CrewAI agents |
|
|
121
|
+
| [air-autogen-trust](https://pypi.org/project/air-autogen-trust/) | Python trust layer for AutoGen agents |
|
|
122
|
+
| **openclaw-air-trust** | ← You are here |
|
|
123
|
+
|
|
124
|
+
Learn more at [airblackbox.ai](https://airblackbox.ai)
|
|
125
|
+
|
|
126
|
+
## Development
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
git clone https://github.com/airblackbox/openclaw-air-trust.git
|
|
130
|
+
cd openclaw-air-trust
|
|
131
|
+
npm install
|
|
132
|
+
npm run build
|
|
133
|
+
npm test
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
Apache 2.0
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openclaw-air-trust — Audit Ledger
|
|
3
|
+
*
|
|
4
|
+
* Tamper-evident action log using HMAC-SHA256 chaining.
|
|
5
|
+
* Each entry includes the hash of the previous entry, creating
|
|
6
|
+
* a blockchain-style chain. Modifying any entry breaks the chain.
|
|
7
|
+
*
|
|
8
|
+
* Supports local persistence and non-blocking forwarding to
|
|
9
|
+
* the AIR Blackbox gateway.
|
|
10
|
+
*/
|
|
11
|
+
import { AuditEntry, AuditLedgerConfig, ChainVerification, RiskLevel } from './types';
|
|
12
|
+
export declare class AuditLedger {
|
|
13
|
+
private entries;
|
|
14
|
+
private secret;
|
|
15
|
+
private lastHash;
|
|
16
|
+
private sequence;
|
|
17
|
+
private config;
|
|
18
|
+
private gatewayUrl?;
|
|
19
|
+
private gatewayKey?;
|
|
20
|
+
constructor(config: AuditLedgerConfig, gatewayUrl?: string, gatewayKey?: string);
|
|
21
|
+
/**
|
|
22
|
+
* Append an action to the audit chain.
|
|
23
|
+
* Returns the signed entry.
|
|
24
|
+
*/
|
|
25
|
+
append(params: {
|
|
26
|
+
action: string;
|
|
27
|
+
toolName?: string;
|
|
28
|
+
riskLevel: RiskLevel;
|
|
29
|
+
consentRequired: boolean;
|
|
30
|
+
consentGranted?: boolean;
|
|
31
|
+
dataTokenized: boolean;
|
|
32
|
+
injectionDetected: boolean;
|
|
33
|
+
metadata?: Record<string, unknown>;
|
|
34
|
+
}): AuditEntry;
|
|
35
|
+
/**
|
|
36
|
+
* Verify the integrity of the entire chain.
|
|
37
|
+
* Walks entries in order, checking prevHash linkage and HMAC signatures.
|
|
38
|
+
*/
|
|
39
|
+
verify(): ChainVerification;
|
|
40
|
+
/** Get the N most recent entries */
|
|
41
|
+
getRecent(n?: number): AuditEntry[];
|
|
42
|
+
/** Export all entries */
|
|
43
|
+
export(): AuditEntry[];
|
|
44
|
+
/** Chain stats */
|
|
45
|
+
stats(): {
|
|
46
|
+
totalEntries: number;
|
|
47
|
+
chainValid: boolean;
|
|
48
|
+
earliest?: string;
|
|
49
|
+
latest?: string;
|
|
50
|
+
};
|
|
51
|
+
private loadChain;
|
|
52
|
+
private saveChain;
|
|
53
|
+
private forwardEntry;
|
|
54
|
+
private ensureDir;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=audit-ledger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-ledger.d.ts","sourceRoot":"","sources":["../src/audit-ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,SAAS,EACV,MAAM,SAAS,CAAC;AAIjB,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAC,CAAS;gBAG1B,MAAM,EAAE,iBAAiB,EACzB,UAAU,CAAC,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM;IAoBrB;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE;QACb,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,SAAS,CAAC;QACrB,eAAe,EAAE,OAAO,CAAC;QACzB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,aAAa,EAAE,OAAO,CAAC;QACvB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,GAAG,UAAU;IA+Dd;;;OAGG;IACH,MAAM,IAAI,iBAAiB;IAmE3B,oCAAoC;IACpC,SAAS,CAAC,CAAC,GAAE,MAAW,GAAG,UAAU,EAAE;IAIvC,yBAAyB;IACzB,MAAM,IAAI,UAAU,EAAE;IAItB,kBAAkB;IAClB,KAAK,IAAI;QACP,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,OAAO,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IAYD,OAAO,CAAC,SAAS;IAiBjB,OAAO,CAAC,SAAS;YAWH,YAAY;IAa1B,OAAO,CAAC,SAAS;CAMlB"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* openclaw-air-trust — Audit Ledger
|
|
4
|
+
*
|
|
5
|
+
* Tamper-evident action log using HMAC-SHA256 chaining.
|
|
6
|
+
* Each entry includes the hash of the previous entry, creating
|
|
7
|
+
* a blockchain-style chain. Modifying any entry breaks the chain.
|
|
8
|
+
*
|
|
9
|
+
* Supports local persistence and non-blocking forwarding to
|
|
10
|
+
* the AIR Blackbox gateway.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.AuditLedger = void 0;
|
|
14
|
+
const crypto_1 = require("crypto");
|
|
15
|
+
const fs_1 = require("fs");
|
|
16
|
+
const path_1 = require("path");
|
|
17
|
+
const GENESIS_HASH = '0000000000000000000000000000000000000000000000000000000000000000';
|
|
18
|
+
class AuditLedger {
|
|
19
|
+
entries = [];
|
|
20
|
+
secret;
|
|
21
|
+
lastHash = GENESIS_HASH;
|
|
22
|
+
sequence = 0;
|
|
23
|
+
config;
|
|
24
|
+
gatewayUrl;
|
|
25
|
+
gatewayKey;
|
|
26
|
+
constructor(config, gatewayUrl, gatewayKey) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
this.gatewayUrl = gatewayUrl;
|
|
29
|
+
this.gatewayKey = gatewayKey;
|
|
30
|
+
// Load or generate HMAC key
|
|
31
|
+
const keyPath = config.localPath.replace(/\.json$/, '') + '.key';
|
|
32
|
+
if ((0, fs_1.existsSync)(keyPath)) {
|
|
33
|
+
this.secret = Buffer.from((0, fs_1.readFileSync)(keyPath, 'utf-8').trim(), 'hex');
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
this.secret = (0, crypto_1.randomBytes)(32);
|
|
37
|
+
this.ensureDir(keyPath);
|
|
38
|
+
(0, fs_1.writeFileSync)(keyPath, this.secret.toString('hex'), { mode: 0o600 });
|
|
39
|
+
}
|
|
40
|
+
// Load existing chain
|
|
41
|
+
this.loadChain();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Append an action to the audit chain.
|
|
45
|
+
* Returns the signed entry.
|
|
46
|
+
*/
|
|
47
|
+
append(params) {
|
|
48
|
+
this.sequence++;
|
|
49
|
+
const entry = {
|
|
50
|
+
id: (0, crypto_1.randomUUID)(),
|
|
51
|
+
sequence: this.sequence,
|
|
52
|
+
hash: '', // computed below
|
|
53
|
+
prevHash: this.lastHash,
|
|
54
|
+
signature: '', // computed below
|
|
55
|
+
timestamp: new Date().toISOString(),
|
|
56
|
+
action: params.action,
|
|
57
|
+
toolName: params.toolName,
|
|
58
|
+
riskLevel: params.riskLevel,
|
|
59
|
+
consentRequired: params.consentRequired,
|
|
60
|
+
consentGranted: params.consentGranted,
|
|
61
|
+
dataTokenized: params.dataTokenized,
|
|
62
|
+
injectionDetected: params.injectionDetected,
|
|
63
|
+
metadata: params.metadata ?? {},
|
|
64
|
+
};
|
|
65
|
+
// Compute content hash (everything except hash, signature, prevHash)
|
|
66
|
+
const contentForHash = JSON.stringify({
|
|
67
|
+
id: entry.id,
|
|
68
|
+
sequence: entry.sequence,
|
|
69
|
+
timestamp: entry.timestamp,
|
|
70
|
+
action: entry.action,
|
|
71
|
+
toolName: entry.toolName,
|
|
72
|
+
riskLevel: entry.riskLevel,
|
|
73
|
+
consentRequired: entry.consentRequired,
|
|
74
|
+
consentGranted: entry.consentGranted,
|
|
75
|
+
dataTokenized: entry.dataTokenized,
|
|
76
|
+
injectionDetected: entry.injectionDetected,
|
|
77
|
+
metadata: entry.metadata,
|
|
78
|
+
});
|
|
79
|
+
entry.hash = (0, crypto_1.createHash)('sha256').update(contentForHash).digest('hex');
|
|
80
|
+
// HMAC signature chains this entry to the previous one
|
|
81
|
+
const sigPayload = `${entry.sequence}|${entry.id}|${entry.hash}|${entry.prevHash}`;
|
|
82
|
+
entry.signature = (0, crypto_1.createHmac)('sha256', this.secret)
|
|
83
|
+
.update(sigPayload)
|
|
84
|
+
.digest('hex');
|
|
85
|
+
this.lastHash = entry.hash;
|
|
86
|
+
this.entries.push(entry);
|
|
87
|
+
// Trim if over max
|
|
88
|
+
if (this.config.maxEntries > 0 && this.entries.length > this.config.maxEntries) {
|
|
89
|
+
this.entries = this.entries.slice(-this.config.maxEntries);
|
|
90
|
+
}
|
|
91
|
+
// Persist locally
|
|
92
|
+
this.saveChain();
|
|
93
|
+
// Non-blocking forward to gateway
|
|
94
|
+
if (this.config.forwardToGateway && this.gatewayUrl) {
|
|
95
|
+
this.forwardEntry(entry).catch(() => {
|
|
96
|
+
// Silent fail — gateway forwarding is best-effort
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return entry;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Verify the integrity of the entire chain.
|
|
103
|
+
* Walks entries in order, checking prevHash linkage and HMAC signatures.
|
|
104
|
+
*/
|
|
105
|
+
verify() {
|
|
106
|
+
if (this.entries.length === 0) {
|
|
107
|
+
return { valid: true, totalEntries: 0 };
|
|
108
|
+
}
|
|
109
|
+
let expectedPrevHash = GENESIS_HASH;
|
|
110
|
+
for (const entry of this.entries) {
|
|
111
|
+
// Check prevHash linkage
|
|
112
|
+
if (entry.prevHash !== expectedPrevHash) {
|
|
113
|
+
return {
|
|
114
|
+
valid: false,
|
|
115
|
+
totalEntries: this.entries.length,
|
|
116
|
+
brokenAtSequence: entry.sequence,
|
|
117
|
+
brokenAtId: entry.id,
|
|
118
|
+
reason: `prevHash mismatch at sequence ${entry.sequence}`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
// Recompute content hash
|
|
122
|
+
const contentForHash = JSON.stringify({
|
|
123
|
+
id: entry.id,
|
|
124
|
+
sequence: entry.sequence,
|
|
125
|
+
timestamp: entry.timestamp,
|
|
126
|
+
action: entry.action,
|
|
127
|
+
toolName: entry.toolName,
|
|
128
|
+
riskLevel: entry.riskLevel,
|
|
129
|
+
consentRequired: entry.consentRequired,
|
|
130
|
+
consentGranted: entry.consentGranted,
|
|
131
|
+
dataTokenized: entry.dataTokenized,
|
|
132
|
+
injectionDetected: entry.injectionDetected,
|
|
133
|
+
metadata: entry.metadata,
|
|
134
|
+
});
|
|
135
|
+
const computedHash = (0, crypto_1.createHash)('sha256').update(contentForHash).digest('hex');
|
|
136
|
+
if (entry.hash !== computedHash) {
|
|
137
|
+
return {
|
|
138
|
+
valid: false,
|
|
139
|
+
totalEntries: this.entries.length,
|
|
140
|
+
brokenAtSequence: entry.sequence,
|
|
141
|
+
brokenAtId: entry.id,
|
|
142
|
+
reason: `Content hash mismatch at sequence ${entry.sequence}`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// Verify HMAC signature
|
|
146
|
+
const sigPayload = `${entry.sequence}|${entry.id}|${entry.hash}|${entry.prevHash}`;
|
|
147
|
+
const expectedSig = (0, crypto_1.createHmac)('sha256', this.secret)
|
|
148
|
+
.update(sigPayload)
|
|
149
|
+
.digest('hex');
|
|
150
|
+
if (entry.signature !== expectedSig) {
|
|
151
|
+
return {
|
|
152
|
+
valid: false,
|
|
153
|
+
totalEntries: this.entries.length,
|
|
154
|
+
brokenAtSequence: entry.sequence,
|
|
155
|
+
brokenAtId: entry.id,
|
|
156
|
+
reason: `Signature mismatch at sequence ${entry.sequence}`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
expectedPrevHash = entry.hash;
|
|
160
|
+
}
|
|
161
|
+
return { valid: true, totalEntries: this.entries.length };
|
|
162
|
+
}
|
|
163
|
+
/** Get the N most recent entries */
|
|
164
|
+
getRecent(n = 50) {
|
|
165
|
+
return this.entries.slice(-n);
|
|
166
|
+
}
|
|
167
|
+
/** Export all entries */
|
|
168
|
+
export() {
|
|
169
|
+
return [...this.entries];
|
|
170
|
+
}
|
|
171
|
+
/** Chain stats */
|
|
172
|
+
stats() {
|
|
173
|
+
const verification = this.verify();
|
|
174
|
+
return {
|
|
175
|
+
totalEntries: this.entries.length,
|
|
176
|
+
chainValid: verification.valid,
|
|
177
|
+
earliest: this.entries[0]?.timestamp,
|
|
178
|
+
latest: this.entries[this.entries.length - 1]?.timestamp,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// ─── Private Methods ────────────────────────────────────────
|
|
182
|
+
loadChain() {
|
|
183
|
+
if ((0, fs_1.existsSync)(this.config.localPath)) {
|
|
184
|
+
try {
|
|
185
|
+
const raw = (0, fs_1.readFileSync)(this.config.localPath, 'utf-8');
|
|
186
|
+
const data = JSON.parse(raw);
|
|
187
|
+
this.entries = data.entries ?? [];
|
|
188
|
+
this.sequence = data.sequence ?? 0;
|
|
189
|
+
this.lastHash = data.lastHash ?? GENESIS_HASH;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Corrupted file — start fresh
|
|
193
|
+
this.entries = [];
|
|
194
|
+
this.sequence = 0;
|
|
195
|
+
this.lastHash = GENESIS_HASH;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
saveChain() {
|
|
200
|
+
this.ensureDir(this.config.localPath);
|
|
201
|
+
const data = {
|
|
202
|
+
entries: this.entries,
|
|
203
|
+
sequence: this.sequence,
|
|
204
|
+
lastHash: this.lastHash,
|
|
205
|
+
savedAt: new Date().toISOString(),
|
|
206
|
+
};
|
|
207
|
+
(0, fs_1.writeFileSync)(this.config.localPath, JSON.stringify(data, null, 2));
|
|
208
|
+
}
|
|
209
|
+
async forwardEntry(entry) {
|
|
210
|
+
if (!this.gatewayUrl)
|
|
211
|
+
return;
|
|
212
|
+
const url = `${this.gatewayUrl}/v1/audit`;
|
|
213
|
+
await fetch(url, {
|
|
214
|
+
method: 'POST',
|
|
215
|
+
headers: {
|
|
216
|
+
'Content-Type': 'application/json',
|
|
217
|
+
...(this.gatewayKey ? { Authorization: `Bearer ${this.gatewayKey}` } : {}),
|
|
218
|
+
},
|
|
219
|
+
body: JSON.stringify(entry),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
ensureDir(filePath) {
|
|
223
|
+
const dir = (0, path_1.dirname)(filePath);
|
|
224
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
225
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
exports.AuditLedger = AuditLedger;
|
|
230
|
+
//# sourceMappingURL=audit-ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-ledger.js","sourceRoot":"","sources":["../src/audit-ledger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openclaw-air-trust — Consent Gate
|
|
3
|
+
*
|
|
4
|
+
* Intercepts destructive or sensitive tool calls and holds them
|
|
5
|
+
* pending user approval. Classifies tools by risk level and
|
|
6
|
+
* sends approval requests through OpenClaw's messaging channel.
|
|
7
|
+
*
|
|
8
|
+
* Flow:
|
|
9
|
+
* 1. before_tool_call fires
|
|
10
|
+
* 2. ConsentGate checks if tool requires consent
|
|
11
|
+
* 3. If yes: sends approval message, waits for response
|
|
12
|
+
* 4. If approved: tool executes normally
|
|
13
|
+
* 5. If rejected/timeout: tool call is blocked
|
|
14
|
+
* 6. All decisions are logged to the audit ledger
|
|
15
|
+
*/
|
|
16
|
+
import { ConsentGateConfig, ConsentRequest, RiskLevel, ToolCallEvent, ToolCallResult, PluginContext } from './types';
|
|
17
|
+
import { AuditLedger } from './audit-ledger';
|
|
18
|
+
export declare class ConsentGate {
|
|
19
|
+
private config;
|
|
20
|
+
private ledger;
|
|
21
|
+
private pendingRequests;
|
|
22
|
+
constructor(config: ConsentGateConfig, ledger: AuditLedger);
|
|
23
|
+
/**
|
|
24
|
+
* Classify risk level for a tool.
|
|
25
|
+
*/
|
|
26
|
+
classifyRisk(toolName: string): RiskLevel;
|
|
27
|
+
/**
|
|
28
|
+
* Check if a tool call requires consent.
|
|
29
|
+
*/
|
|
30
|
+
requiresConsent(toolName: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Intercept a tool call. If consent is needed, block until approved.
|
|
33
|
+
* Returns a ToolCallResult indicating whether the call should proceed.
|
|
34
|
+
*/
|
|
35
|
+
intercept(event: ToolCallEvent, ctx: PluginContext): Promise<ToolCallResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Handle a user response to a consent request.
|
|
38
|
+
* Call this when the user sends "approve <id>" or "reject <id>".
|
|
39
|
+
*/
|
|
40
|
+
handleResponse(consentId: string, approved: boolean): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Format a human-readable consent message.
|
|
43
|
+
*/
|
|
44
|
+
formatConsentMessage(request: ConsentRequest): string;
|
|
45
|
+
private waitForApproval;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=consent-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent-gate.d.ts","sourceRoot":"","sources":["../src/consent-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,SAAS,EAET,aAAa,EACb,cAAc,EACd,aAAa,EACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAqC7C,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,eAAe,CAGR;gBAEH,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,WAAW;IAK1D;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS;IAazC;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAY1C;;;OAGG;IACG,SAAS,CACb,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,aAAa,GACjB,OAAO,CAAC,cAAc,CAAC;IAwD1B;;;OAGG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO;IAS7D;;OAEG;IACH,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM;IAgCrD,OAAO,CAAC,eAAe;CAexB"}
|