@grindfree/guard 0.0.0-stage → 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 +21 -0
- package/README.md +130 -2
- package/dist/client.d.ts +18 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +65 -0
- package/dist/client.js.map +1 -0
- package/dist/decisions.d.ts +35 -0
- package/dist/decisions.d.ts.map +1 -0
- package/dist/decisions.js +139 -0
- package/dist/decisions.js.map +1 -0
- package/dist/errors.d.ts +31 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +49 -0
- package/dist/errors.js.map +1 -0
- package/dist/guard.d.ts +161 -0
- package/dist/guard.d.ts.map +1 -0
- package/dist/guard.js +187 -0
- package/dist/guard.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/services.d.ts +96 -0
- package/dist/services.d.ts.map +1 -0
- package/dist/services.js +63 -0
- package/dist/services.js.map +1 -0
- package/dist/wrap-mcp.d.ts +24 -0
- package/dist/wrap-mcp.d.ts.map +1 -0
- package/dist/wrap-mcp.js +53 -0
- package/dist/wrap-mcp.js.map +1 -0
- package/package.json +58 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GrindFree / Rob
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,131 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @grindfree/guard
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Drop-in guards that screen **[GrindFree](https://grindfree.net)** x402 agent-security APIs **before** an agent acts, then enforce `allow | review | deny`. Payments use the **caller’s** Base USDC wallet via the official [`@x402/fetch`](https://www.npmjs.com/package/@x402/fetch) / Python `x402` clients. This repo never ships or accepts GrindFree keys.
|
|
4
|
+
|
|
5
|
+
| Service | Endpoint | Price | What it checks |
|
|
6
|
+
|---|---|---|---|
|
|
7
|
+
| **PromptShield** | `POST https://promptshield.grindfree.net/v1/screen` · `/v1/clean` | $0.002 | Prompt-injection screen + HTML→LLM clean text |
|
|
8
|
+
| **CrawlSafe** | `POST https://crawlsafe.grindfree.net/v1/check` | $0.002 | robots.txt / X-Robots-Tag crawl gate (not malware scanning) |
|
|
9
|
+
| **ToolGuard** | `POST https://toolguard.grindfree.net/v1/evaluate` | $0.002 | Pre-tool-call `allow \| review \| deny` vs owner policy |
|
|
10
|
+
| **CommandGuard** | `POST https://commandguard.grindfree.net/v1/command` · `/v1/sql` | $0.001 | Shell + SQL check: `allow \| warn \| block` |
|
|
11
|
+
| **MemoryGuard** | `POST https://memoryguard.grindfree.net/v1/screen` | $0.002 | Content before it is written to agent memory |
|
|
12
|
+
| **WireShield** | `POST https://wireshield.grindfree.net/v1/scan` | $0.002 | Outbound leak check on agent output |
|
|
13
|
+
| **DocShield** | `POST https://docshield.grindfree.net/v1/scan` | $0.004 | Hidden instructions in PDF / Office / HTML / Markdown |
|
|
14
|
+
| **SafeRead** | `POST https://saferead.grindfree.net/v1/read` | $0.003 | Fetch a URL as clean Markdown with hidden text + prompt injections removed (`allow \| warn \| block`) |
|
|
15
|
+
|
|
16
|
+
Network: Base (`eip155:8453`). The remote API is advisory — **your adapter must enforce the decision**. This package does that: `deny` / `block` / high risk throws `GuardDenied` and does not run the action; `review` / `warn` / medium risk throws `GuardReviewRequired` unless you set `onReview: "allow"`. Transport failures **fail closed** (`onError: "deny"` by default).
|
|
17
|
+
|
|
18
|
+
Also available: the MCP server [`@grindfree/agent-security-mcp`](https://github.com/ronin93/grindfree-agent-security) (`npx -y @grindfree/agent-security-mcp`).
|
|
19
|
+
|
|
20
|
+
Contact: [rob@grindfree.net](mailto:rob@grindfree.net) · [@grindfreelabs](https://x.com/grindfreelabs)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm i @grindfree/guard
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install 'grindfree-guard[x402]'
|
|
32
|
+
# until PyPI is published: pip install -e 'python/[x402]'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Set the **caller’s** funded Base USDC key (never commit it):
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
export X402_PRIVATE_KEY=0xYOUR_BASE_USDC_PRIVATE_KEY
|
|
39
|
+
# PRIVATE_KEY and EVM_PRIVATE_KEY are also accepted
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quickstart (TypeScript)
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { createGuard, GuardDenied, GuardReviewRequired } from "@grindfree/guard";
|
|
46
|
+
|
|
47
|
+
const guard = createGuard({
|
|
48
|
+
policy: { allow_tools: ["http.fetch"], allow_hosts: ["api.example.com"], require_https: true },
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const { value } = await guard.guardToolCall(
|
|
52
|
+
{ tool: "http.fetch", destination: { host: "api.example.com", scheme: "https", port: 443 }, arg_classes: ["url"] },
|
|
53
|
+
() => fetch("https://api.example.com/items").then((r) => r.json()),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Shell: CommandGuard runs first. A wipe like `rm -rf $HOME` throws GuardDenied and is not executed.
|
|
57
|
+
await guard.guardCommand({ command: "ls -la" }, () => runShell("ls -la"));
|
|
58
|
+
|
|
59
|
+
// Web pages: SafeRead fetches the URL for you and returns clean Markdown with injected
|
|
60
|
+
// instructions already removed. safeRead never throws on "block"; requireSafeRead does.
|
|
61
|
+
const page = await guard.safeRead("https://example.com/blog/post");
|
|
62
|
+
console.log(page.title, page.decision.verdict, page.text);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Python: `text, decision = guard.safe_read("https://example.com/blog/post")` (or `require_safe_read` to enforce).
|
|
66
|
+
|
|
67
|
+
`createGuard({ fetch })` (JS) or `create_guard(post=...)` (Python) injects a mock for tests — unit tests in this repo never make a funded call.
|
|
68
|
+
|
|
69
|
+
### Verdicts
|
|
70
|
+
|
|
71
|
+
| Service result | Normalized `verdict` | Default handling |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| ToolGuard `allow`; CommandGuard `allow`; PromptShield `low`; CrawlSafe `crawl_allowed: true` | `allow` | run the action |
|
|
74
|
+
| ToolGuard `review`; CommandGuard `warn`; PromptShield `medium`; Memory/Wire/Doc `review` | `review` | throw `GuardReviewRequired` (or set `onReview: "allow"` / `"deny"` / a callback) |
|
|
75
|
+
| ToolGuard `deny`; CommandGuard `block`; PromptShield `high`; CrawlSafe not allowed; Memory/Wire/Doc `block` | `deny` | throw `GuardDenied`, do not run the action |
|
|
76
|
+
| Timeout / HTTP error | (fail closed) | throw `GuardDenied` unless `onError: "throw"` or `"allow"` |
|
|
77
|
+
|
|
78
|
+
## Framework examples
|
|
79
|
+
|
|
80
|
+
Copy-paste starters (wallet still comes from `X402_PRIVATE_KEY`):
|
|
81
|
+
|
|
82
|
+
- [`examples/basic.ts`](examples/basic.ts) — `guardToolCall` + `guardCommand`
|
|
83
|
+
- [`examples/langchain-js/guard-tools.ts`](examples/langchain-js/guard-tools.ts) — wrap LangChain JS tools
|
|
84
|
+
- [`examples/langchain-py/guard_tools.py`](examples/langchain-py/guard_tools.py) — LangChain Python wrapper + callback
|
|
85
|
+
- [`examples/openai-agents/guardrails.ts`](examples/openai-agents/guardrails.ts) — OpenAI Agents SDK input/output guardrails
|
|
86
|
+
- [`examples/mcp-client/wrap-mcp.ts`](examples/mcp-client/wrap-mcp.ts) — wrap `client.callTool` (CommandGuard for shell/SQL tools, ToolGuard otherwise)
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { wrapMcpClient } from "@grindfree/guard";
|
|
90
|
+
const client = wrapMcpClient(mcpClient, { policy: { allow_tools: ["mcp.search"] } });
|
|
91
|
+
// Shell/SQL-named tools go through CommandGuard; everything else through ToolGuard.
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Python twin (`guard_tool_call`, `guard_command`, `require_safe_prompt`, …) lives in [`python/`](python/).
|
|
95
|
+
|
|
96
|
+
## API (JS)
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const guard = createGuard({
|
|
100
|
+
privateKey, // optional; otherwise env
|
|
101
|
+
fetch, // optional mock / pre-wrapped paid fetch
|
|
102
|
+
policy, // default owner policy
|
|
103
|
+
onReview: "throw", // or "allow" | "deny" | async (d) => "allow"
|
|
104
|
+
onError: "deny", // or "throw" | "allow"
|
|
105
|
+
timeoutMs: 15_000,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
await guard.guardToolCall(call, fn);
|
|
109
|
+
guard.wrapTool(name, fn, classify?);
|
|
110
|
+
await guard.guardCommand({ command }, fn);
|
|
111
|
+
await guard.guardSql({ sql }, fn);
|
|
112
|
+
await guard.requireSafePrompt({ content, format });
|
|
113
|
+
await guard.requireCrawlAllowed({ url });
|
|
114
|
+
await guard.requireSafeMemory({ content, source });
|
|
115
|
+
await guard.requireSafeOutput({ content, format });
|
|
116
|
+
await guard.requireSafeDocument({ content, format });
|
|
117
|
+
// plus evaluateTool / checkCommand / screenPrompt / … for inspect-only
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Honest limits
|
|
121
|
+
|
|
122
|
+
- Heuristic / policy layers, not a full security program.
|
|
123
|
+
- CrawlSafe is not malware, phishing, or legal advice.
|
|
124
|
+
- Deterministic rule packs can miss novel phrasing. Decisions are advisory at the API; **this library enforces them in-process** and cannot stop a caller who bypasses it.
|
|
125
|
+
- DocShield parses structure and text; it does not OCR images. Legacy `.doc` / `.xls` / `.ppt` return 415 and are not charged.
|
|
126
|
+
- Missing ToolGuard policy → `review`.
|
|
127
|
+
- Unit tests mock payment. Do not point tests at the live paid endpoints without a separate spend approval.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT — see [LICENSE](./LICENSE).
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type PaidFetch = typeof fetch;
|
|
2
|
+
export type ClientOptions = {
|
|
3
|
+
/** Caller wallet. Falls back to X402_PRIVATE_KEY, PRIVATE_KEY, or EVM_PRIVATE_KEY. */
|
|
4
|
+
privateKey?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Injected fetch. Use this in tests (mocked, never pays) or to supply a
|
|
7
|
+
* pre-wrapped x402 fetch. When set, no private key is read or required.
|
|
8
|
+
*/
|
|
9
|
+
fetch?: PaidFetch;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
};
|
|
12
|
+
export declare function readPrivateKey(explicit?: string): string | undefined;
|
|
13
|
+
export declare function createPaidFetch(opts?: ClientOptions): PaidFetch;
|
|
14
|
+
export declare function postJson(paidFetch: PaidFetch, url: string, body: unknown, opts?: {
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
service: string;
|
|
17
|
+
}): Promise<Record<string, unknown>>;
|
|
18
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC;AAErC,MAAM,MAAM,aAAa,GAAG;IAC1B,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEpE;AAED,wBAAgB,eAAe,CAAC,IAAI,GAAE,aAAkB,GAAG,SAAS,CAenE;AAED,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,SAAS,EACpB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,OAAO,EACb,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAA2B,GACrE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAoClC"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
|
|
2
|
+
import { ExactEvmScheme } from "@x402/evm";
|
|
3
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
4
|
+
import { GuardConfigError, GuardError } from "./errors.js";
|
|
5
|
+
import { NETWORK } from "./services.js";
|
|
6
|
+
const KEY_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
8
|
+
export function readPrivateKey(explicit) {
|
|
9
|
+
return explicit || process.env.X402_PRIVATE_KEY || process.env.PRIVATE_KEY || process.env.EVM_PRIVATE_KEY;
|
|
10
|
+
}
|
|
11
|
+
export function createPaidFetch(opts = {}) {
|
|
12
|
+
if (opts.fetch)
|
|
13
|
+
return opts.fetch;
|
|
14
|
+
const pk = readPrivateKey(opts.privateKey);
|
|
15
|
+
if (!pk) {
|
|
16
|
+
throw new GuardConfigError("Missing X402_PRIVATE_KEY (or PRIVATE_KEY / EVM_PRIVATE_KEY). Set the caller's Base USDC wallet key. GrindFree never supplies keys. For tests, pass { fetch } with a mock.");
|
|
17
|
+
}
|
|
18
|
+
if (!KEY_RE.test(pk)) {
|
|
19
|
+
throw new GuardConfigError("X402_PRIVATE_KEY / PRIVATE_KEY / EVM_PRIVATE_KEY must be 0x + 64 hex characters (caller wallet only).");
|
|
20
|
+
}
|
|
21
|
+
const account = privateKeyToAccount(pk);
|
|
22
|
+
return wrapFetchWithPaymentFromConfig(fetch, {
|
|
23
|
+
schemes: [{ network: NETWORK, client: new ExactEvmScheme(account) }],
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export async function postJson(paidFetch, url, body, opts = { service: "unknown" }) {
|
|
27
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
30
|
+
let res;
|
|
31
|
+
try {
|
|
32
|
+
res = await paidFetch(url, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
35
|
+
body: JSON.stringify(body ?? {}),
|
|
36
|
+
signal: controller.signal,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
const aborted = err instanceof Error && err.name === "AbortError";
|
|
41
|
+
throw new GuardError(aborted ? `Request timed out after ${timeoutMs}ms` : err instanceof Error ? err.message : String(err), {
|
|
42
|
+
service: opts.service,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
clearTimeout(timer);
|
|
47
|
+
}
|
|
48
|
+
const text = await res.text();
|
|
49
|
+
let json;
|
|
50
|
+
try {
|
|
51
|
+
json = text ? JSON.parse(text) : {};
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
json = { raw: text.slice(0, 2000) };
|
|
55
|
+
}
|
|
56
|
+
if (!res.ok) {
|
|
57
|
+
throw new GuardError(`${opts.service} HTTP ${res.status}`, {
|
|
58
|
+
service: opts.service,
|
|
59
|
+
status: res.status,
|
|
60
|
+
body: json,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return json;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ServiceName } from "./services.js";
|
|
2
|
+
/** Unified verdict used by every GrindFree check. */
|
|
3
|
+
export type Verdict = "allow" | "review" | "deny";
|
|
4
|
+
export type ReviewMode = "throw" | "allow" | "deny";
|
|
5
|
+
export type ErrorMode = "deny" | "throw" | "allow";
|
|
6
|
+
export interface NormalizedDecision {
|
|
7
|
+
/** allow = proceed; review = pause for a human (CommandGuard "warn", medium risk); deny = do not proceed (block / high risk). */
|
|
8
|
+
verdict: Verdict;
|
|
9
|
+
service: ServiceName;
|
|
10
|
+
endpoint: string;
|
|
11
|
+
/** Raw service decision when present (allow/review/deny/warn/block). */
|
|
12
|
+
rawDecision?: string;
|
|
13
|
+
/** PromptShield risk when present. */
|
|
14
|
+
risk?: "low" | "medium" | "high";
|
|
15
|
+
reasons: string[];
|
|
16
|
+
findings: unknown[];
|
|
17
|
+
advice?: string;
|
|
18
|
+
sanitized?: string;
|
|
19
|
+
safeText?: string;
|
|
20
|
+
crawlAllowed?: boolean;
|
|
21
|
+
raw: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
export type EnforceOptions = {
|
|
24
|
+
onReview?: ReviewMode | ((d: NormalizedDecision) => ReviewMode | Promise<ReviewMode>);
|
|
25
|
+
onError?: ErrorMode;
|
|
26
|
+
};
|
|
27
|
+
export declare function normalizeDecision(service: ServiceName, endpoint: string, raw: Record<string, unknown>): NormalizedDecision;
|
|
28
|
+
/**
|
|
29
|
+
* Apply allow/review/deny policy. Deny and (by default) review throw.
|
|
30
|
+
* Timeouts and transport errors should be turned into GuardError first,
|
|
31
|
+
* then passed through enforceError.
|
|
32
|
+
*/
|
|
33
|
+
export declare function enforce(decision: NormalizedDecision, opts?: EnforceOptions): Promise<NormalizedDecision>;
|
|
34
|
+
export declare function enforceError(err: unknown, service: string, opts?: EnforceOptions): never;
|
|
35
|
+
//# sourceMappingURL=decisions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decisions.d.ts","sourceRoot":"","sources":["../src/decisions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,qDAAqD;AACrD,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAElD,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AACpD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAEnD,MAAM,WAAW,kBAAkB;IACjC,iIAAiI;IACjI,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACjC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC,EAAE,kBAAkB,KAAK,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IACtF,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,kBAAkB,CA6CpB;AA4CD;;;;GAIG;AACH,wBAAsB,OAAO,CAC3B,QAAQ,EAAE,kBAAkB,EAC5B,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAQ7B;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,KAAK,CAyB5F"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { GuardDenied, GuardError, GuardReviewRequired } from "./errors.js";
|
|
2
|
+
export function normalizeDecision(service, endpoint, raw) {
|
|
3
|
+
const findings = Array.isArray(raw.findings)
|
|
4
|
+
? raw.findings
|
|
5
|
+
: service === "saferead"
|
|
6
|
+
? [...(Array.isArray(raw.removed) ? raw.removed : []), ...(Array.isArray(raw.flagged) ? raw.flagged : [])]
|
|
7
|
+
: [];
|
|
8
|
+
const reasons = collectReasons(raw, findings);
|
|
9
|
+
const sanitized = asString(raw.sanitized);
|
|
10
|
+
const safeText = asString(raw.safe_text) ?? asString(raw.safeText) ?? (service === "saferead" ? asString(raw.content) : undefined);
|
|
11
|
+
const advice = asString(raw.advice);
|
|
12
|
+
const rawDecision = typeof raw.decision === "string"
|
|
13
|
+
? raw.decision.toLowerCase()
|
|
14
|
+
: service === "saferead" && typeof raw.verdict === "string"
|
|
15
|
+
? raw.verdict.toLowerCase()
|
|
16
|
+
: undefined;
|
|
17
|
+
const risk = asRisk(raw.risk);
|
|
18
|
+
let verdict;
|
|
19
|
+
if (service === "promptshield" && endpoint === "screen") {
|
|
20
|
+
verdict = risk === "high" ? "deny" : risk === "medium" ? "review" : "allow";
|
|
21
|
+
}
|
|
22
|
+
else if (service === "crawlsafe") {
|
|
23
|
+
const allowed = raw.crawl_allowed === true || raw.crawlAllowed === true;
|
|
24
|
+
verdict = allowed ? "allow" : "deny";
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
verdict = mapRawDecision(rawDecision);
|
|
28
|
+
}
|
|
29
|
+
const crawlAllowed = service === "crawlsafe" ? raw.crawl_allowed === true || raw.crawlAllowed === true : undefined;
|
|
30
|
+
return {
|
|
31
|
+
verdict,
|
|
32
|
+
service,
|
|
33
|
+
endpoint,
|
|
34
|
+
rawDecision,
|
|
35
|
+
risk,
|
|
36
|
+
reasons,
|
|
37
|
+
findings,
|
|
38
|
+
advice,
|
|
39
|
+
sanitized,
|
|
40
|
+
safeText,
|
|
41
|
+
crawlAllowed,
|
|
42
|
+
raw,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function mapRawDecision(raw) {
|
|
46
|
+
if (!raw)
|
|
47
|
+
return "review";
|
|
48
|
+
if (raw === "allow")
|
|
49
|
+
return "allow";
|
|
50
|
+
if (raw === "review" || raw === "warn")
|
|
51
|
+
return "review";
|
|
52
|
+
if (raw === "deny" || raw === "block")
|
|
53
|
+
return "deny";
|
|
54
|
+
return "review";
|
|
55
|
+
}
|
|
56
|
+
function collectReasons(raw, findings) {
|
|
57
|
+
const out = [];
|
|
58
|
+
if (Array.isArray(raw.reasons)) {
|
|
59
|
+
for (const r of raw.reasons) {
|
|
60
|
+
if (typeof r === "string")
|
|
61
|
+
out.push(r);
|
|
62
|
+
else if (r && typeof r === "object" && "reason" in r && typeof r.reason === "string") {
|
|
63
|
+
out.push(r.reason);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const f of findings) {
|
|
68
|
+
if (f && typeof f === "object") {
|
|
69
|
+
const rec = f;
|
|
70
|
+
const rule = typeof rec.rule === "string" ? rec.rule : undefined;
|
|
71
|
+
const reason = typeof rec.reason === "string" ? rec.reason : undefined;
|
|
72
|
+
if (rule && reason)
|
|
73
|
+
out.push(`${rule}: ${reason}`);
|
|
74
|
+
else if (rule)
|
|
75
|
+
out.push(rule);
|
|
76
|
+
else if (reason)
|
|
77
|
+
out.push(reason);
|
|
78
|
+
}
|
|
79
|
+
else if (typeof f === "string") {
|
|
80
|
+
out.push(f);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
function asString(v) {
|
|
86
|
+
return typeof v === "string" ? v : undefined;
|
|
87
|
+
}
|
|
88
|
+
function asRisk(v) {
|
|
89
|
+
if (v === "low" || v === "medium" || v === "high")
|
|
90
|
+
return v;
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Apply allow/review/deny policy. Deny and (by default) review throw.
|
|
95
|
+
* Timeouts and transport errors should be turned into GuardError first,
|
|
96
|
+
* then passed through enforceError.
|
|
97
|
+
*/
|
|
98
|
+
export async function enforce(decision, opts = {}) {
|
|
99
|
+
if (decision.verdict === "allow")
|
|
100
|
+
return decision;
|
|
101
|
+
if (decision.verdict === "deny")
|
|
102
|
+
throw new GuardDenied(decision);
|
|
103
|
+
const mode = await resolveReview(opts.onReview ?? "throw", decision);
|
|
104
|
+
if (mode === "allow")
|
|
105
|
+
return decision;
|
|
106
|
+
if (mode === "deny")
|
|
107
|
+
throw new GuardDenied(decision, `GrindFree ${decision.service} review treated as deny`);
|
|
108
|
+
throw new GuardReviewRequired(decision);
|
|
109
|
+
}
|
|
110
|
+
export function enforceError(err, service, opts = {}) {
|
|
111
|
+
const mode = opts.onError ?? "deny";
|
|
112
|
+
if (err instanceof GuardDenied || err instanceof GuardReviewRequired)
|
|
113
|
+
throw err;
|
|
114
|
+
const wrapped = err instanceof GuardError
|
|
115
|
+
? err
|
|
116
|
+
: new GuardError(err instanceof Error ? err.message : String(err), { service });
|
|
117
|
+
if (mode === "throw")
|
|
118
|
+
throw wrapped;
|
|
119
|
+
if (mode === "allow") {
|
|
120
|
+
// Caller asked to proceed on transport failure. Still throw so they can
|
|
121
|
+
// distinguish this path if they catch GuardError; use mode "allow" only
|
|
122
|
+
// via tryGuard helpers that swallow this.
|
|
123
|
+
throw wrapped;
|
|
124
|
+
}
|
|
125
|
+
throw new GuardDenied({
|
|
126
|
+
verdict: "deny",
|
|
127
|
+
service: service,
|
|
128
|
+
endpoint: "error",
|
|
129
|
+
reasons: [wrapped.message],
|
|
130
|
+
findings: [],
|
|
131
|
+
raw: { error: wrapped.message, status: wrapped.status },
|
|
132
|
+
}, `GrindFree ${service} failed closed: ${wrapped.message}`);
|
|
133
|
+
}
|
|
134
|
+
async function resolveReview(onReview, decision) {
|
|
135
|
+
if (typeof onReview === "function")
|
|
136
|
+
return onReview(decision);
|
|
137
|
+
return onReview;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=decisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decisions.js","sourceRoot":"","sources":["../src/decisions.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { NormalizedDecision } from "./decisions.js";
|
|
2
|
+
export declare class GuardConfigError extends Error {
|
|
3
|
+
readonly code = "GUARD_CONFIG";
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class GuardError extends Error {
|
|
7
|
+
readonly code = "GUARD_ERROR";
|
|
8
|
+
readonly status?: number;
|
|
9
|
+
readonly service: string;
|
|
10
|
+
readonly body?: unknown;
|
|
11
|
+
constructor(message: string, opts: {
|
|
12
|
+
service: string;
|
|
13
|
+
status?: number;
|
|
14
|
+
body?: unknown;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export declare class GuardDenied extends Error {
|
|
18
|
+
readonly code = "GUARD_DENIED";
|
|
19
|
+
readonly verdict: "deny";
|
|
20
|
+
readonly service: string;
|
|
21
|
+
readonly decision: NormalizedDecision;
|
|
22
|
+
constructor(decision: NormalizedDecision, message?: string);
|
|
23
|
+
}
|
|
24
|
+
export declare class GuardReviewRequired extends Error {
|
|
25
|
+
readonly code = "GUARD_REVIEW";
|
|
26
|
+
readonly verdict: "review";
|
|
27
|
+
readonly service: string;
|
|
28
|
+
readonly decision: NormalizedDecision;
|
|
29
|
+
constructor(decision: NormalizedDecision, message?: string);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,kBAAkB;gBACnB,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;gBACZ,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE;CAOxF;AAED,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,OAAO,EAAG,MAAM,CAAU;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;gBAC1B,QAAQ,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,MAAM;CAM3D;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,OAAO,EAAG,QAAQ,CAAU;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;gBAC1B,QAAQ,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,MAAM;CAM3D"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export class GuardConfigError extends Error {
|
|
2
|
+
code = "GUARD_CONFIG";
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "GuardConfigError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class GuardError extends Error {
|
|
9
|
+
code = "GUARD_ERROR";
|
|
10
|
+
status;
|
|
11
|
+
service;
|
|
12
|
+
body;
|
|
13
|
+
constructor(message, opts) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "GuardError";
|
|
16
|
+
this.service = opts.service;
|
|
17
|
+
this.status = opts.status;
|
|
18
|
+
this.body = opts.body;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class GuardDenied extends Error {
|
|
22
|
+
code = "GUARD_DENIED";
|
|
23
|
+
verdict = "deny";
|
|
24
|
+
service;
|
|
25
|
+
decision;
|
|
26
|
+
constructor(decision, message) {
|
|
27
|
+
super(message ?? defaultMessage(decision, "denied"));
|
|
28
|
+
this.name = "GuardDenied";
|
|
29
|
+
this.service = decision.service;
|
|
30
|
+
this.decision = decision;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export class GuardReviewRequired extends Error {
|
|
34
|
+
code = "GUARD_REVIEW";
|
|
35
|
+
verdict = "review";
|
|
36
|
+
service;
|
|
37
|
+
decision;
|
|
38
|
+
constructor(decision, message) {
|
|
39
|
+
super(message ?? defaultMessage(decision, "needs review"));
|
|
40
|
+
this.name = "GuardReviewRequired";
|
|
41
|
+
this.service = decision.service;
|
|
42
|
+
this.decision = decision;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function defaultMessage(d, kind) {
|
|
46
|
+
const reason = d.reasons[0] ?? d.advice ?? d.rawDecision ?? d.risk ?? kind;
|
|
47
|
+
return `GrindFree ${d.service} ${kind}: ${reason}`;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,GAAG,cAAc,CAAC;IAC/B,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC1B,IAAI,GAAG,aAAa,CAAC;IACrB,MAAM,CAAU;IAChB,OAAO,CAAS;IAChB,IAAI,CAAW;IACxB,YAAY,OAAe,EAAE,IAA0D;QACrF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;CACF;AAED,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,IAAI,GAAG,cAAc,CAAC;IACtB,OAAO,GAAG,MAAe,CAAC;IAC1B,OAAO,CAAS;IAChB,QAAQ,CAAqB;IACtC,YAAY,QAA4B,EAAE,OAAgB;QACxD,KAAK,CAAC,OAAO,IAAI,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IACnC,IAAI,GAAG,cAAc,CAAC;IACtB,OAAO,GAAG,QAAiB,CAAC;IAC5B,OAAO,CAAS;IAChB,QAAQ,CAAqB;IACtC,YAAY,QAA4B,EAAE,OAAgB;QACxD,KAAK,CAAC,OAAO,IAAI,cAAc,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED,SAAS,cAAc,CAAC,CAAqB,EAAE,IAAY;IACzD,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC;IAC3E,OAAO,aAAa,CAAC,CAAC,OAAO,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;AACrD,CAAC"}
|