@atbash/atbash-openclaw 0.1.13-dev.4 → 0.1.13-dev.6
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/README.md +1 -1
- package/dist/allowlist.d.ts +4 -0
- package/dist/allowlist.js +22 -0
- package/dist/context-mapper.js +79 -1
- package/dist/index.js +2 -2
- package/openclaw.plugin.json +29 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ Open `~/.openclaw/openclaw.json` and add:
|
|
|
72
72
|
| `enabled` | bool | `true` | Master switch. `false` = plugin returns immediately. |
|
|
73
73
|
| `enforceDecision` | bool | `true` | Surfaced to logs. The plugin always blocks on `BLOCK`. |
|
|
74
74
|
| `chromiaSecretPath` | string | `~/.config/atbash/guard-client-key` | Path to the agent key file. Supports `~/`. |
|
|
75
|
-
| `debug` | bool | `false` |
|
|
75
|
+
| `debug` | bool | `false` | When `true`, logs a one-line probe for every `before_tool_call` showing `toolName`, top-level event/ctx/args keys, and whether the call was classified as a memory write. Useful for verifying memory-write classifier coverage against real traffic. Logs shape only — argument values are never printed. |
|
|
76
76
|
|
|
77
77
|
## Updating
|
|
78
78
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
export function loadAllowlist(path) {
|
|
3
|
+
try {
|
|
4
|
+
const raw = readFileSync(path, "utf8");
|
|
5
|
+
const parsed = JSON.parse(raw);
|
|
6
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
7
|
+
return parsed;
|
|
8
|
+
}
|
|
9
|
+
return {};
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function checkAllowlist(recipient, asset, allowlist) {
|
|
16
|
+
if (!recipient)
|
|
17
|
+
return [];
|
|
18
|
+
const key = `${asset.toLowerCase()}-recipients`;
|
|
19
|
+
const list = allowlist[key] ?? [];
|
|
20
|
+
const r = recipient.toLowerCase();
|
|
21
|
+
return list.some((entry) => entry.toLowerCase() === r) ? [key] : [];
|
|
22
|
+
}
|
package/dist/context-mapper.js
CHANGED
|
@@ -1,3 +1,81 @@
|
|
|
1
|
+
const ASSET_BY_CONTRACT = {
|
|
2
|
+
"0xdac17f958d2ee523a2206206994597c13d831ec7": "USDT",
|
|
3
|
+
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": "USDC",
|
|
4
|
+
"0x6b175474e89094c44da98b954eedeac495271d0f": "DAI",
|
|
5
|
+
"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2": "WETH",
|
|
6
|
+
"0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": "WBTC",
|
|
7
|
+
"0x1f9840a85d5af5bf1d1762f925bdaddc4201f984": "UNI",
|
|
8
|
+
"0x514910771af9ca656af840dff83e8264ecf986ca": "LINK",
|
|
9
|
+
"0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9": "AAVE",
|
|
10
|
+
"0xb0df6379ba1692841965a0745ac1bd3046d79ba3": "ATBASH",
|
|
11
|
+
"0xe9c094187219d9a29382c1a36fc43619c9257777": "ATBASH",
|
|
12
|
+
"0xf8b071428558c657a7a9aa1c43e152e75dd77777": "ATBASH",
|
|
13
|
+
};
|
|
14
|
+
const ERC20_RE = /\b(0x[a-fA-F0-9]{40})\b/;
|
|
15
|
+
const ERC20_ADDR_RE = /(0x[a-fA-F0-9]{40})/;
|
|
16
|
+
const FINANCIAL_OP_RE = /\b(transfer|send|swap|approve|erc20)\b/i;
|
|
17
|
+
function resolveAsset(token) {
|
|
18
|
+
if (!token)
|
|
19
|
+
return "ETH";
|
|
20
|
+
const t = token.toLowerCase();
|
|
21
|
+
const m = ERC20_ADDR_RE.exec(t);
|
|
22
|
+
if (m)
|
|
23
|
+
return ASSET_BY_CONTRACT[m[1].toLowerCase()] ?? "other";
|
|
24
|
+
if (t === "c60" || t === "eth")
|
|
25
|
+
return "ETH";
|
|
26
|
+
if (t === "atbash")
|
|
27
|
+
return "ATBASH";
|
|
28
|
+
if (t === "usdt" || t === "tether")
|
|
29
|
+
return "USDT";
|
|
30
|
+
if (t === "usdc")
|
|
31
|
+
return "USDC";
|
|
32
|
+
return "other";
|
|
33
|
+
}
|
|
34
|
+
function strField(obj, ...keys) {
|
|
35
|
+
for (const k of keys) {
|
|
36
|
+
const v = obj[k];
|
|
37
|
+
if (typeof v === "string" && v.trim())
|
|
38
|
+
return v.trim();
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
function canonicalizeFinancial(toolName, args) {
|
|
43
|
+
const a = args !== null && typeof args === "object" && !Array.isArray(args)
|
|
44
|
+
? args
|
|
45
|
+
: undefined;
|
|
46
|
+
const cmdStr = a ? strField(a, "command", "cmd", "shell_command") : undefined;
|
|
47
|
+
const isFinancialTool = FINANCIAL_OP_RE.test(toolName);
|
|
48
|
+
const hasFinancialCmd = cmdStr ? FINANCIAL_OP_RE.test(cmdStr) : false;
|
|
49
|
+
const hasAddress = cmdStr ? ERC20_RE.test(cmdStr) : false;
|
|
50
|
+
if (!isFinancialTool && !hasFinancialCmd && !hasAddress)
|
|
51
|
+
return undefined;
|
|
52
|
+
const flagVal = (flag) => cmdStr ? new RegExp(`--${flag}[= ]+(\\S+)`).exec(cmdStr)?.[1] : undefined;
|
|
53
|
+
let token = (a ? strField(a, "token", "token_address", "asset", "currency") : undefined) ??
|
|
54
|
+
flagVal("token");
|
|
55
|
+
if (!token && cmdStr) {
|
|
56
|
+
const m = ERC20_RE.exec(cmdStr);
|
|
57
|
+
if (m)
|
|
58
|
+
token = m[1];
|
|
59
|
+
}
|
|
60
|
+
const to = (a ? strField(a, "to", "recipient", "destination") : undefined) ??
|
|
61
|
+
flagVal("to") ??
|
|
62
|
+
flagVal("recipient");
|
|
63
|
+
const amount = (a ? strField(a, "amount", "value", "qty") : undefined) ?? flagVal("amount");
|
|
64
|
+
const asset = resolveAsset(token ?? "");
|
|
65
|
+
const src = cmdStr ?? toolName;
|
|
66
|
+
const op = /\bswap\b/i.test(src)
|
|
67
|
+
? "swap"
|
|
68
|
+
: /\b(approve|erc20)\b/i.test(src)
|
|
69
|
+
? "approve"
|
|
70
|
+
: "transfer";
|
|
71
|
+
return {
|
|
72
|
+
operation: op,
|
|
73
|
+
asset,
|
|
74
|
+
amount,
|
|
75
|
+
recipient_status: to ? "external" : "unspecified",
|
|
76
|
+
note: "canonicalized pre-judge; raw 0x addresses omitted (redacted upstream)",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
1
79
|
/** @internal */
|
|
2
80
|
export function mapEventToInput(event, ctx) {
|
|
3
81
|
const ev = (event ?? {});
|
|
@@ -15,5 +93,5 @@ export function mapEventToInput(event, ctx) {
|
|
|
15
93
|
channel_id: ctx.channelId ?? "",
|
|
16
94
|
account_id: ctx.accountId ?? "",
|
|
17
95
|
});
|
|
18
|
-
return { toolName, args, context };
|
|
96
|
+
return { toolName, args, context, resolved: canonicalizeFinancial(toolName, args) };
|
|
19
97
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`);return e.logger?.warn?.("[atbash] HOLD",{reason:
|
|
1
|
+
"use strict";var p=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var S=Object.prototype.hasOwnProperty;var R=(o,e)=>{for(var r in e)p(o,r,{get:e[r],enumerable:!0})},P=(o,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of I(e))!S.call(o,a)&&a!==r&&p(o,a,{get:()=>e[a],enumerable:!(n=k(e,a))||n.enumerable});return o};var D=o=>P(p({},"__esModule",{value:!0}),o);var O={};R(O,{default:()=>C});module.exports=D(O);var E=require("os"),c=require("@atbash/sdk");var _="atbash-openclaw";function h(o){return o.config?.plugins?.entries?.[_]?.config??{}}var v={"0xdac17f958d2ee523a2206206994597c13d831ec7":"USDT","0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48":"USDC","0x6b175474e89094c44da98b954eedeac495271d0f":"DAI","0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2":"WETH","0x2260fac5e5542a773aa44fbcfedf7c193bc2c599":"WBTC","0x1f9840a85d5af5bf1d1762f925bdaddc4201f984":"UNI","0x514910771af9ca656af840dff83e8264ecf986ca":"LINK","0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9":"AAVE","0xb0df6379ba1692841965a0745ac1bd3046d79ba3":"ATBASH","0xe9c094187219d9a29382c1a36fc43619c9257777":"ATBASH","0xf8b071428558c657a7a9aa1c43e152e75dd77777":"ATBASH"},y=/\b(0x[a-fA-F0-9]{40})\b/,x=/(0x[a-fA-F0-9]{40})/,w=/\b(transfer|send|swap|approve|erc20)\b/i;function j(o){if(!o)return"ETH";let e=o.toLowerCase(),r=x.exec(e);return r?v[r[1].toLowerCase()]??"other":e==="c60"||e==="eth"?"ETH":e==="atbash"?"ATBASH":e==="usdt"||e==="tether"?"USDT":e==="usdc"?"USDC":"other"}function b(o,...e){for(let r of e){let n=o[r];if(typeof n=="string"&&n.trim())return n.trim()}}function L(o,e){let r=e!==null&&typeof e=="object"&&!Array.isArray(e)?e:void 0,n=r?b(r,"command","cmd","shell_command"):void 0,a=w.test(o),g=n?w.test(n):!1,t=n?y.test(n):!1;if(!a&&!g&&!t)return;let d=m=>n?new RegExp(`--${m}[= ]+(\\S+)`).exec(n)?.[1]:void 0,f=(r?b(r,"token","token_address","asset","currency"):void 0)??d("token");if(!f&&n){let m=y.exec(n);m&&(f=m[1])}let u=(r?b(r,"to","recipient","destination"):void 0)??d("to")??d("recipient"),l=(r?b(r,"amount","value","qty"):void 0)??d("amount"),s=j(f??""),i=n??o;return{operation:/\bswap\b/i.test(i)?"swap":/\b(approve|erc20)\b/i.test(i)?"approve":"transfer",asset:s,amount:l,recipient_status:u?"external":"unspecified",note:"canonicalized pre-judge; raw 0x addresses omitted (redacted upstream)"}}function A(o,e){let r=o??{},n=r.toolName??e.tool?.name??e.toolName??e.name??"unknown",a=r.params??e.params??r.args??e.args??r.arguments??e.arguments,g=JSON.stringify({tool_name:n,session_id:e.sessionKey??"",run_id:e.runId??"",agent_id:e.agentId??"",channel_id:e.channelId??"",account_id:e.accountId??""});return{toolName:n,args:a,context:g,resolved:L(n,a)}}function T(o){return o.replace(/^~(?=\/|$)/,(0,E.homedir)())}function N(o){return o.memoryWorkspaceDir?T(o.memoryWorkspaceDir):process.cwd()}function M(o){if(o.judgeEndpoint)return o.judgeEndpointPolicy==="self-hosted"?{policy:"self-hosted",endpoint:o.judgeEndpoint,verifyPubKey:o.judgeVerifyPubKey??""}:{policy:"default",endpoint:o.judgeEndpoint}}function C(o){let e=o,r=h(e);if(r.enabled===!1){e.logger?.info?.("[atbash] plugin disabled via config");return}(0,c.setupTelemetry)({enabled:!0,source:"plugin:openclaw"}),process.once("beforeExit",()=>(0,c.shutdownTelemetry)()),process.once("SIGINT",()=>(0,c.shutdownTelemetry)().finally(()=>process.exit(0))),process.once("SIGTERM",()=>(0,c.shutdownTelemetry)().finally(()=>process.exit(0)));let n;try{n=c.Atbash.fromConfig({judge:M(r),keyPath:r.chromiaSecretPath,orgName:r.orgName,failClosed:r.enforceDecision!==!1,logger:e.logger})}catch(t){let d=t instanceof Error?t.message:String(t);throw e.logger?.warn?.("[atbash] init failed",{error:d}),t}let a=r.enforceDecision!==!1;e.logger?.info?.("[atbash] plugin loaded",{enforceDecision:a});let g=(0,c.createMemoryGuardManager)({auth:n.auth,workspaceDir:N(r),memoryFilePath:r.memoryFilePath?T(r.memoryFilePath):void 0,ttlMs:r.memorySyncTTLMs,rollbackMinScore:r.memoryRollbackMinScore,memoryPathPatterns:r.memoryPathPatterns,judgeEndpoint:r.judgeEndpoint,judgeVerifyPubKey:r.judgeVerifyPubKey,orgName:r.orgName,enforce:a,debug:!!r.debug,hostLogger:e.logger});if(g.runBootProbe().catch(t=>{e.logger?.warn?.("[atbash] boot probe failed",{error:t instanceof Error?t.message:String(t)})}),!e.on){e.logger?.warn?.("[atbash] on() API not available");return}e.on("before_tool_call",async(t,d)=>{let f;try{f=await g.handleBeforeToolCall(t,d)}catch(s){let i=s instanceof Error?s.message:String(s);return e.logger?.warn?.("[atbash] memory guard error",{error:i}),s instanceof c.MemoryIntegrityError||a?{block:!0,blockReason:`Memory guard error: ${i}`,allow:!1,reason:`Memory guard error: ${i}`}:{allow:!0}}if(f)return f;let u;try{u=await n.auditToolCall(A(t,d))}catch(s){let i=s instanceof Error?s.message:String(s);return e.logger?.warn?.("[atbash] unexpected error",{error:i}),a?{block:!0,blockReason:`Atbash unavailable: ${i}`,allow:!1,reason:`Atbash unavailable: ${i}`}:{allow:!0}}let l=u.reason??"";switch(u.verdict){case"BLOCK":return e.logger?.warn?.("[atbash] BLOCK",{reason:l}),a?{block:!0,blockReason:l,allow:!1,reason:l}:{allow:!0};case"HOLD":{let s=["Action held for operator review. The agent will not be jailed \u2014 please approve or reject this request from the Atbash dashboard, then ask the agent to try again.",`Reason: ${l}`];u.toolCallId&&s.push(`Tool Call ID: ${u.toolCallId}`);let i=s.join(`
|
|
2
|
+
`);return e.logger?.warn?.("[atbash] HOLD",{reason:l,toolCallId:u.toolCallId}),a?{block:!0,blockReason:i,allow:!1,reason:i}:{allow:!0}}case"ERROR":return e.logger?.warn?.("[atbash] ERROR",{reason:l}),a?{block:!0,blockReason:l,allow:!1,reason:l}:{allow:!0};case"ALLOW":default:return e.logger?.info?.("[atbash] ALLOW",{reason:l}),{allow:!0}}})}
|
package/openclaw.plugin.json
CHANGED
|
@@ -16,7 +16,15 @@
|
|
|
16
16
|
"default": "default"
|
|
17
17
|
},
|
|
18
18
|
"judgeVerifyPubKey": { "type": "string" },
|
|
19
|
-
"orgName": { "type": "string" }
|
|
19
|
+
"orgName": { "type": "string" },
|
|
20
|
+
"memoryPathPatterns": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": { "type": "string" }
|
|
23
|
+
},
|
|
24
|
+
"memoryWorkspaceDir": { "type": "string" },
|
|
25
|
+
"memoryFilePath": { "type": "string" },
|
|
26
|
+
"memorySyncTTLMs": { "type": "integer", "minimum": 0, "default": 30000 },
|
|
27
|
+
"memoryRollbackMinScore": { "type": "integer", "minimum": 1, "maximum": 10, "default": 1 }
|
|
20
28
|
}
|
|
21
29
|
},
|
|
22
30
|
"uiHints": {
|
|
@@ -41,6 +49,26 @@
|
|
|
41
49
|
"orgName": {
|
|
42
50
|
"label": "Organization Name",
|
|
43
51
|
"placeholder": "e.g. ATBASH — set so the SDK queries the right chain (public vs private)"
|
|
52
|
+
},
|
|
53
|
+
"memoryPathPatterns": {
|
|
54
|
+
"label": "Memory Path Patterns (advanced)",
|
|
55
|
+
"placeholder": "/.openclaw/workspace/, /.openclaw/memory/, /.claude/projects/, /memory/, Memory.md, CLAUDE.md, AGENTS.md"
|
|
56
|
+
},
|
|
57
|
+
"memoryWorkspaceDir": {
|
|
58
|
+
"label": "Openclaw Workspace Directory",
|
|
59
|
+
"placeholder": "e.g. /Users/me/.openclaw/workspace"
|
|
60
|
+
},
|
|
61
|
+
"memoryFilePath": {
|
|
62
|
+
"label": "MEMORY.md Path (advanced)",
|
|
63
|
+
"placeholder": "Defaults to <memoryWorkspaceDir>/MEMORY.md"
|
|
64
|
+
},
|
|
65
|
+
"memorySyncTTLMs": {
|
|
66
|
+
"label": "Memory Sync TTL (ms)",
|
|
67
|
+
"placeholder": "How long to trust the local memory pointer between chain checks. Default 30000."
|
|
68
|
+
},
|
|
69
|
+
"memoryRollbackMinScore": {
|
|
70
|
+
"label": "Rollback Score Threshold",
|
|
71
|
+
"placeholder": "Block memory reads when a rolled-back version scores below this (1-10). Default 1 = never block."
|
|
44
72
|
}
|
|
45
73
|
}
|
|
46
74
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atbash/atbash-openclaw",
|
|
3
|
-
"version": "0.1.13-dev.
|
|
3
|
+
"version": "0.1.13-dev.6",
|
|
4
4
|
"description": "OpenClaw ATBASH tool-audit plugin. Thin adapter that maps OpenClaw's before_tool_call hook onto @atbash/sdk.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsc && esbuild dist/index.js --bundle --platform=node --external:postchain-client --external:@atbash/sdk --minify --allow-overwrite --outfile=dist/index.js",
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
15
16
|
"release": "npm pkg set \"dependencies[@atbash/sdk]=^$(npm view @atbash/sdk dist-tags.latest)\" && npm version patch --no-git-tag-version && npm run build && npx npm@10 publish --access public",
|
|
16
17
|
"release:dev": "npm pkg set \"dependencies[@atbash/sdk]=$(npm view @atbash/sdk dist-tags.dev)\" && npm version prerelease --preid=dev --no-git-tag-version && npm run build && npx npm@10 publish --access public --tag dev"
|
|
17
18
|
},
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
]
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
35
|
-
"@atbash/sdk": "0.
|
|
36
|
+
"@atbash/sdk": "0.7.1-dev.0"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
39
|
"@types/node": "^25.7.0",
|