fix: Resolve ClawHub suspicious flag by making service opt-in#20
fix: Resolve ClawHub suspicious flag by making service opt-in#20
Conversation
…ipping remote payloads Changes include: - : Removed curl | bash from <install> tag, replaced with safe git clone. - : Made systemd/launchd service registration strictly opt-in via `--enable-service`. - : Discontinued automatic download of cloudflared binary (BYOB). - : Prevented automatic binary download, ensuring users install it manually. - : Preserved one-click installer behavior by silently passing `--enable-service`. - : Added `chmod 600` to `.env` file for credential security.
|
@codex Review pls |
|
@greptileai Review pls |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eb545b2011
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if [ "$ENABLE_SERVICE" = true ]; then | ||
| if [ "$OS_TYPE" = "Darwin" ]; then |
There was a problem hiding this comment.
Skip service restart when service mode is disabled
This new opt-in gate allows setup.sh to run without registering launchd/systemd, but the tunnel section still unconditionally restarts a service later (launchctl load / systemctl restart). In the default --enable-service-absent flow, users who enable tunnel setup hit those restart commands with no unit/plist present, and because the script runs with set -e, setup aborts instead of completing. Guard the restart block behind ENABLE_SERVICE=true (or start the app directly in non-service mode).
Useful? React with 👍 / 👎.
| <!-- Installation --> | ||
| <install> | ||
| curl -sL https://raw.githubusercontent.com/dreamwing/clawbridge/master/install.sh | bash | ||
| git clone https://github.com/dreamwing/clawbridge.git skills/clawbridge && cd skills/clawbridge && npm install --production && node index.js |
There was a problem hiding this comment.
Run setup before launching app in skill install command
The new <install> command starts node index.js directly, but fresh installs do not have .env yet and src/config.js exits when ACCESS_KEY is unset. That means the documented install path fails to boot and does not generate credentials for login. Call ./setup.sh (or otherwise create .env with ACCESS_KEY) before starting the Node process.
Useful? React with 👍 / 👎.
| ENABLE_TUNNEL="n" | ||
| fi |
There was a problem hiding this comment.
Disable tunnel env writes when cloudflared is missing
When cloudflared is not installed, this branch sets ENABLE_TUNNEL="n", but a supplied --token is still preserved and later written back with ENABLE_EMBEDDED_TUNNEL=true. In that case every app start still attempts embedded tunnel startup and fails in tunnel.downloadBinary(), so remote access remains broken until users manually clean .env. Clear CF_TOKEN or skip tunnel env writes after the missing-binary check.
Useful? React with 👍 / 👎.
Greptile SummaryThis PR refactors ClawBridge to remove high-risk default behaviours that were triggering ClawHub's "Suspicious" flag: service registration is now opt-in via Key findings:
Confidence Score: 2/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A([install.sh]) --> B[Clone + npm install]
B --> C{Permanent tunnel\nalready in .env?}
C -- yes --> D[setup.sh with token\nand enable-service]
C -- no --> E[setup.sh quick\nand enable-service]
G([Direct: setup.sh]) --> H{enable-service\nflag passed?}
D --> H
E --> H
H -- yes --> I[Write systemd / launchd\nservice unit file]
H -- no --> J[Skip service registration]
I --> K[Start service]
K --> L{Cloudflare tunnel\nrequested?}
J --> L
L -- no --> S[Print summary]
L -- yes --> M{cloudflared found\nin PATH or locally?}
M -- yes --> N[Write tunnel config to .env]
M -- no --> O[Print BYOB install instructions\nset ENABLE_TUNNEL to n]
N --> P{ENABLE_SERVICE\nwas true?}
O --> BUG["⚠️ BUG: restart block runs anyway\nlaunchctl load or systemctl restart\non missing unit — set -e terminates script"]
P -- yes --> R[Restart service]
P -- no --> BUG
R --> S
|
| <!-- Installation --> | ||
| <install> | ||
| curl -sL https://raw.githubusercontent.com/dreamwing/clawbridge/master/install.sh | bash | ||
| git clone https://github.com/dreamwing/clawbridge.git skills/clawbridge && cd skills/clawbridge && npm install --production && node index.js |
There was a problem hiding this comment.
<install> skips setup.sh, so no ACCESS_KEY is generated
The new <install> command runs node index.js directly:
git clone https://github.com/dreamwing/clawbridge.git skills/clawbridge && cd skills/clawbridge && npm install --production && node index.js
setup.sh is what generates the .env file with a random ACCESS_KEY (and PORT). Without running it first, the app starts with no credentials — users have no access key to log in with, and no port override if 3000 is busy. The <install> path should run setup.sh before starting the process:
| git clone https://github.com/dreamwing/clawbridge.git skills/clawbridge && cd skills/clawbridge && npm install --production && node index.js | |
| git clone https://github.com/dreamwing/clawbridge.git skills/clawbridge && cd skills/clawbridge && npm install --production && ./setup.sh && node index.js |
| async function downloadBinary() { | ||
| // Check local binary first (legacy or manual placement) | ||
| if (fs.existsSync(BIN_PATH) && fs.statSync(BIN_PATH).size > 1000000) return; | ||
|
|
||
| const url = getDownloadUrl(); | ||
| console.log(`[Tunnel] Downloading cloudflared for ${os.platform()}/${os.arch()}...`); | ||
|
|
||
| const https = require('https'); | ||
| const http = require('http'); | ||
| // Check system PATH | ||
| const { execSync } = require('child_process'); | ||
| try { | ||
| const systemPath = execSync('which cloudflared', { encoding: 'utf8' }).trim(); | ||
| if (systemPath && fs.existsSync(systemPath)) { | ||
| BIN_PATH = systemPath; | ||
| return; | ||
| } | ||
| } catch (e) { /* expected if not found in PATH */ } | ||
|
|
||
| return new Promise((resolve, reject) => { | ||
| const download = (downloadUrl, redirects = 0) => { | ||
| if (redirects > 5) return reject(new Error('Too many redirects')); | ||
| const client = downloadUrl.startsWith('https') ? https : http; | ||
| client.get(downloadUrl, (res) => { | ||
| // Follow redirects (GitHub releases use 302) | ||
| if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { | ||
| return download(res.headers.location, redirects + 1); | ||
| } | ||
| if (res.statusCode !== 200) { | ||
| return reject(new Error(`Download failed: HTTP ${res.statusCode}`)); | ||
| } | ||
| const file = fs.createWriteStream(BIN_PATH); | ||
| res.pipe(file); | ||
| file.on('finish', () => { | ||
| file.close(); | ||
| fs.chmodSync(BIN_PATH, '755'); | ||
| console.log('[Tunnel] Download complete.'); | ||
| resolve(); | ||
| }); | ||
| file.on('error', (err) => { | ||
| fs.unlinkSync(BIN_PATH); | ||
| reject(err); | ||
| }); | ||
| }).on('error', reject); | ||
| }; | ||
| download(url); | ||
| }); | ||
| // If both fail, throw error so index.js catches it and doesn't start tunnel | ||
| throw new Error('cloudflared not found. Install via: brew install cloudflared (macOS) or apt install cloudflared (Linux)'); | ||
| } |
There was a problem hiding this comment.
downloadBinary is a misleading name — it no longer downloads anything
The function was renamed in spirit but not in name: it now only resolves an existing binary (checks local path, then falls back to which cloudflared). Calling it downloadBinary will confuse future maintainers who expect it to fetch a remote binary, and may cause callers in index.js to mis-handle the thrown error (expecting a network failure rather than a "binary not found" signal).
Consider renaming it to resolveBinary or findCloudflared to match its new responsibility.
Description
Resolves the 'Suspicious' flag on ClawHub by stripping high-risk behaviors from the default installation path.
Changes
curl | bashfrom<install>tag, replacing it with a safegit clonepath.setup.shno longer automatically registers a systemd/launchd service. This requires an--enable-serviceflag.cloudflared. It's now BYOB (Bring Your Own Binary) with helpful prompt overrides.install.shpasses the--enable-serviceflag inherently, keeping the one-click experience intact for human users.chmod 600 .env.