@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 +4 -0
- package/README.md +61 -17
- package/SECURITY.md +76 -0
- package/dist/index.cjs +19 -2
- package/dist/index.js +19 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +7 -4
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
|
|
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
|
-
|
|
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
|
-
|
|
23
|
+
Get your API key at [riddledc.com](https://riddledc.com).
|
|
13
24
|
|
|
14
|
-
|
|
25
|
+
## Tools
|
|
15
26
|
|
|
16
|
-
|
|
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
|
-
|
|
35
|
+
All tools return screenshots + console logs by default. Pass `include: ["har"]` to also capture network traffic.
|
|
19
36
|
|
|
20
|
-
|
|
21
|
-
- `apiKey` (or `RIDDLE_API_KEY` env var)
|
|
22
|
-
- `baseUrl` (defaults to `https://api.riddledc.com`)
|
|
37
|
+
## How It Works
|
|
23
38
|
|
|
24
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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 };
|
package/openclaw.plugin.json
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
}
|