@riddledc/openclaw-riddledc 0.3.2 → 0.3.4

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/CHECKSUMS.txt ADDED
@@ -0,0 +1,4 @@
1
+ 3185b3314096550b3b62561d979cedbf819a24a4fea6d001ace2a95c9c6e7f18 dist/index.cjs
2
+ 94ce04f0e2d84bf64dd68f0500dfdd2f951287a3deccec87f197261961927f6f dist/index.d.cts
3
+ 94ce04f0e2d84bf64dd68f0500dfdd2f951287a3deccec87f197261961927f6f dist/index.d.ts
4
+ 599dba9020d9825d7809b8f0ae6bceb6ccdfefc9a9be247299d13f1b73d84398 dist/index.js
package/README.md CHANGED
@@ -1,34 +1,78 @@
1
1
  # @riddledc/openclaw-riddledc
2
2
 
3
- OpenClaw integration package for RiddleDC. No secrets. No assumption about MCP.
3
+ OpenClaw plugin for [Riddle](https://riddledc.com) - hosted browser automation API. Take screenshots, run Playwright scripts, and automate web interactions from your OpenClaw agent.
4
4
 
5
5
  ## Install
6
6
 
7
- ```
7
+ ```bash
8
+ # 1. Install the plugin
8
9
  openclaw plugins install @riddledc/openclaw-riddledc
9
- openclaw plugins enable openclaw-riddledc
10
+
11
+ # 2. Add to allowlist and enable
12
+ openclaw config set plugins.allow --json '["discord","telegram","memory-core","openclaw-riddledc"]'
13
+ openclaw config set tools.alsoAllow --json '["openclaw-riddledc"]'
14
+
15
+ # 3. Set your API key
16
+ openclaw config set plugins.entries.openclaw-riddledc.config.apiKey "YOUR_RIDDLE_API_KEY"
17
+
18
+ # 4. Restart gateway
19
+ openclaw gateway restart
20
+ # Or if using systemd: systemctl restart openclaw-gateway
10
21
  ```
11
22
 
12
- ## OpenClaw plugin metadata
23
+ Get your API key at [riddledc.com](https://riddledc.com).
13
24
 
14
- This package ships `openclaw.plugin.json` for OpenClaw registration.
25
+ ## Tools
15
26
 
16
- The plugin id is `openclaw-riddledc`.
27
+ | Tool | Description |
28
+ |------|-------------|
29
+ | `riddle_screenshot` | Take a screenshot of a single URL |
30
+ | `riddle_screenshots` | Take screenshots of multiple URLs in one job |
31
+ | `riddle_steps` | Run a workflow using steps (goto/click/fill/etc.) |
32
+ | `riddle_script` | Run full Playwright code |
33
+ | `riddle_run` | Low-level pass-through to the Riddle API |
17
34
 
18
- ## Configuration
35
+ All tools return screenshots + console logs by default. Pass `include: ["har"]` to also capture network traffic.
19
36
 
20
- The plugin accepts:
21
- - `apiKey` (or `RIDDLE_API_KEY` env var)
22
- - `baseUrl` (defaults to `https://api.riddledc.com`)
37
+ ## How It Works
23
38
 
24
- ## Tools
39
+ Screenshots are automatically saved to `~/.openclaw/workspace/riddle/screenshots/` and the tool returns a file reference instead of inline base64. This keeps agent context small and prevents token overflow.
25
40
 
26
- - `riddle_run`
27
- - `riddle_screenshot`
28
- - `riddle_screenshots`
29
- - `riddle_steps`
30
- - `riddle_script`
41
+ Example response:
42
+ ```json
43
+ {
44
+ "ok": true,
45
+ "job_id": "job_abc123",
46
+ "screenshot": { "saved": "riddle/screenshots/job_abc123.png", "sizeBytes": 45000 },
47
+ "console": []
48
+ }
49
+ ```
50
+
51
+ ## Configuration
52
+
53
+ | Option | Description |
54
+ |--------|-------------|
55
+ | `apiKey` | Your Riddle API key (or set `RIDDLE_API_KEY` env var) |
56
+ | `baseUrl` | API endpoint (defaults to `https://api.riddledc.com`) |
31
57
 
32
58
  ## Security
33
59
 
34
- Do not hardcode keys. Provide credentials via env vars or your secret manager.
60
+ - Never hardcode API keys in config files
61
+ - Use environment variables or a secret manager
62
+ - The plugin only communicates with `api.riddledc.com` over HTTPS
63
+ - Hardcoded domain allowlist prevents credential exfiltration
64
+ - See [SECURITY.md](./SECURITY.md) for full threat model and data flow
65
+
66
+ ## Reproducible Builds
67
+
68
+ To verify a build matches the published package:
69
+
70
+ 1. Clone the repo at the tagged version
71
+ 2. Run: `pnpm install && pnpm build`
72
+ 3. Compare checksums: `shasum -a 256 dist/*`
73
+
74
+ Expected checksums are in `CHECKSUMS.txt`.
75
+
76
+ ## License
77
+
78
+ MIT
package/SECURITY.md ADDED
@@ -0,0 +1,76 @@
1
+ # Security Model
2
+
3
+ ## Data Flow
4
+
5
+ ```
6
+ ┌─────────────────┐ HTTPS only ┌──────────────────┐
7
+ │ OpenClaw Agent │ ──────────────────► │ api.riddledc.com │
8
+ │ │ POST /v1/run │ │
9
+ │ - RIDDLE_API_KEY │ - Runs browser │
10
+ │ - User prompts │ ◄────────────────── │ - Returns data │
11
+ └─────────────────┘ JSON response └──────────────────┘
12
+
13
+
14
+ ┌─────────────────┐
15
+ │ Local Workspace │
16
+ │ ~/.openclaw/ │
17
+ │ workspace/riddle│ Screenshots saved
18
+ │ /screenshots/ │ as files (not inline)
19
+ └─────────────────┘
20
+ ```
21
+
22
+ ## What This Plugin CAN Do
23
+
24
+ - Send requests to api.riddledc.com (hardcoded, cannot be changed)
25
+ - Read your RIDDLE_API_KEY from config or environment
26
+ - Write screenshot/HAR files to your workspace directory
27
+ - Execute Playwright scripts on Riddle's remote browser
28
+
29
+ ## What This Plugin CANNOT Do
30
+
31
+ - Send your API key to any other domain (blocked by `assertAllowedBaseUrl`)
32
+ - Access your filesystem outside the workspace directory
33
+ - Make network requests to arbitrary URLs from your machine
34
+ - Run code locally (all execution happens on Riddle's servers)
35
+
36
+ ## Security Controls
37
+
38
+ ### 1. Hardcoded Domain Allowlist
39
+
40
+ ```typescript
41
+ function assertAllowedBaseUrl(baseUrl: string) {
42
+ const url = new URL(baseUrl);
43
+ if (url.protocol !== "https:")
44
+ throw new Error(`Riddle baseUrl must be https`);
45
+ if (url.hostname !== "api.riddledc.com")
46
+ throw new Error(`Refusing to use non-official Riddle host`);
47
+ }
48
+ ```
49
+
50
+ This runs on EVERY request. Even if config is manipulated, keys never leave riddledc.com.
51
+
52
+ ### 2. No Inline Base64
53
+
54
+ Screenshots are saved to disk, not returned inline. This prevents:
55
+
56
+ - Context overflow attacks
57
+ - Memory exhaustion
58
+ - Accidental key leakage in logs
59
+
60
+ ### 3. Minimal Permissions
61
+
62
+ Only requires one secret: `RIDDLE_API_KEY`. No OAuth, no cookies, no session state.
63
+
64
+ ## Threat Model
65
+
66
+ | Threat | Mitigation |
67
+ |--------|------------|
68
+ | API key exfiltration to attacker server | Hardcoded domain check blocks all non-riddledc.com requests |
69
+ | Malicious config injection | Domain check runs at request time, not config time |
70
+ | Supply chain attack (npm) | Use npm provenance to verify package origin |
71
+ | Build tampering | Checksums + reproducible builds |
72
+ | Local file access | Plugin only writes to designated workspace subdirectory |
73
+
74
+ ## Reporting Security Issues
75
+
76
+ Email: security@riddledc.com
package/dist/index.cjs CHANGED
@@ -93,15 +93,32 @@ async function applySafetySpec(result, opts) {
93
93
  if (result.screenshot != null) {
94
94
  let base64Data = null;
95
95
  if (typeof result.screenshot === "string") {
96
- base64Data = result.screenshot;
96
+ base64Data = result.screenshot.replace(/^data:image\/\w+;base64,/, "");
97
97
  } else if (typeof result.screenshot === "object" && result.screenshot.data) {
98
- base64Data = result.screenshot.data;
98
+ base64Data = result.screenshot.data.replace(/^data:image\/\w+;base64,/, "");
99
99
  }
100
100
  if (base64Data) {
101
101
  const ref = await writeArtifactBinary(opts.workspace, "screenshots", `${jobId}.png`, base64Data);
102
102
  result.screenshot = { saved: ref.path, sizeBytes: ref.sizeBytes };
103
103
  }
104
104
  }
105
+ if (Array.isArray(result.screenshots)) {
106
+ const savedRefs = [];
107
+ for (let i = 0; i < result.screenshots.length; i++) {
108
+ const ss = result.screenshots[i];
109
+ let base64Data = null;
110
+ if (typeof ss === "string") {
111
+ base64Data = ss;
112
+ } else if (typeof ss === "object" && ss.data) {
113
+ base64Data = ss.data.replace(/^data:image\/\w+;base64,/, "");
114
+ }
115
+ if (base64Data) {
116
+ const ref = await writeArtifactBinary(opts.workspace, "screenshots", `${jobId}-${i}.png`, base64Data);
117
+ savedRefs.push({ saved: ref.path, sizeBytes: ref.sizeBytes });
118
+ }
119
+ }
120
+ result.screenshots = savedRefs;
121
+ }
105
122
  if (result.rawPngBase64 != null) {
106
123
  const ref = await writeArtifactBinary(opts.workspace, "screenshots", `${jobId}.png`, result.rawPngBase64);
107
124
  result.screenshot = { saved: ref.path, sizeBytes: ref.sizeBytes };
package/dist/index.js CHANGED
@@ -69,15 +69,32 @@ async function applySafetySpec(result, opts) {
69
69
  if (result.screenshot != null) {
70
70
  let base64Data = null;
71
71
  if (typeof result.screenshot === "string") {
72
- base64Data = result.screenshot;
72
+ base64Data = result.screenshot.replace(/^data:image\/\w+;base64,/, "");
73
73
  } else if (typeof result.screenshot === "object" && result.screenshot.data) {
74
- base64Data = result.screenshot.data;
74
+ base64Data = result.screenshot.data.replace(/^data:image\/\w+;base64,/, "");
75
75
  }
76
76
  if (base64Data) {
77
77
  const ref = await writeArtifactBinary(opts.workspace, "screenshots", `${jobId}.png`, base64Data);
78
78
  result.screenshot = { saved: ref.path, sizeBytes: ref.sizeBytes };
79
79
  }
80
80
  }
81
+ if (Array.isArray(result.screenshots)) {
82
+ const savedRefs = [];
83
+ for (let i = 0; i < result.screenshots.length; i++) {
84
+ const ss = result.screenshots[i];
85
+ let base64Data = null;
86
+ if (typeof ss === "string") {
87
+ base64Data = ss;
88
+ } else if (typeof ss === "object" && ss.data) {
89
+ base64Data = ss.data.replace(/^data:image\/\w+;base64,/, "");
90
+ }
91
+ if (base64Data) {
92
+ const ref = await writeArtifactBinary(opts.workspace, "screenshots", `${jobId}-${i}.png`, base64Data);
93
+ savedRefs.push({ saved: ref.path, sizeBytes: ref.sizeBytes });
94
+ }
95
+ }
96
+ result.screenshots = savedRefs;
97
+ }
81
98
  if (result.rawPngBase64 != null) {
82
99
  const ref = await writeArtifactBinary(opts.workspace, "screenshots", `${jobId}.png`, result.rawPngBase64);
83
100
  result.screenshot = { saved: ref.path, sizeBytes: ref.sizeBytes };
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-riddledc",
3
3
  "name": "Riddle",
4
4
  "description": "Riddle (riddledc.com) hosted browser API tools for OpenClaw agents.",
5
- "version": "0.3.2",
5
+ "version": "0.3.4",
6
6
  "notes": "0.3.1: Screenshots now saved to workspace files instead of inline base64 to prevent context bloat.",
7
7
  "configSchema": {
8
8
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/openclaw-riddledc",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "OpenClaw integration package for RiddleDC (no secrets).",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",
@@ -22,7 +22,9 @@
22
22
  },
23
23
  "files": [
24
24
  "dist",
25
- "openclaw.plugin.json"
25
+ "openclaw.plugin.json",
26
+ "CHECKSUMS.txt",
27
+ "SECURITY.md"
26
28
  ],
27
29
  "sideEffects": false,
28
30
  "engines": {
@@ -42,10 +44,11 @@
42
44
  "typescript": "^5.4.5"
43
45
  },
44
46
  "scripts": {
45
- "build": "tsup src/index.ts --format cjs,esm --dts --out-dir dist",
47
+ "build": "npm run sync:openclaw-plugin-version && tsup src/index.ts --format cjs,esm --dts --out-dir dist",
46
48
  "clean": "rm -rf dist",
47
49
  "lint": "echo 'lint: (not configured)'",
48
50
  "test": "echo 'test: (not configured)'",
49
- "sync:openclaw-plugin-version": "node scripts/sync-openclaw-plugin-version.mjs"
51
+ "sync:openclaw-plugin-version": "node scripts/sync-openclaw-plugin-version.mjs",
52
+ "checksums": "node scripts/generate-checksums.mjs"
50
53
  }
51
54
  }