Skill: tiger-xzp/senpub (version 1.0.21)

--- acceptLicenseTerms: true description: Install and configure the security-related plugins required by OpenClaw, including the `ai-assistant-security-openclaw` plugins. Use this skill when you want to complete installation and basic configuration of these plugins for an OpenClaw environment in one go. license: MIT name: ClawSentry --- # ClawSentry ## Use Cases - Use this skill when you need to install and prepare the `ai-assistant-security-openclaw` plugins for the OpenClaw environment on the current machine. - Trigger this skill when the user mentions requirements such as "install clawSentry / ai-assistant-security-openclaw plugin / 帮我安装clawSentry技能 / 安装clawSentry技能". ## Steps ### 1. Verify Node.js is installed: Ensure Node.js (version 14 or higher) is installed on the system. You can check with: ```bash node --version ``` ### 2. Execute the combined script to install plugin and generate login link: ```bash node {baseDir}/scripts/bundle.cjs ``` After the script completes: - The script will display the login URL directly. You can also extract it from the output by looking for the line that starts with `[LOGIN_URL]:` - You can also read the login URL from `{baseDir}/.state/login_state.json` file (look for the `loginUrl` field). If the file doesn't exist, try reading it again after a few seconds. If you can't find the files, try looking in `.openclaw/skills/ClawSentry/scripts/` directory. - Send the login URL to the user along with this message: "After completing login, if you want to check if the installation was successful, please wait one minute and then ask me such as 'view all plugin list / 查看我的本地插件 '" ## Data & Privacy - The script generates a per-machine device identifier locally, then sends only its SHA-256 hash as `X-Ai-Device-Fingerprint` for device-binding of the login session. - The script does not collect or transmit MAC addresses, hostname, or other hardware identifiers. - The script persists login state under `{baseDir}/.state/`, including `loginToken`, the hashed `deviceFingerprint`, expiry metadata, and login progress flags. - The polling log written to `{baseDir}/.state/poll_login.log` records operational output and API responses; once authorization succeeds, those responses may include credential material such as `ApiKey` and `AppId`. - After authorization, the script writes the received `ApiKey` and `AppId` into the local OpenClaw plugin configuration so the installed plugin can call the remote service. - That configuration update is performed by invoking the local `openclaw` CLI with a JSON payload, so credential values may be exposed transiently in local process arguments or shell/audit tooling on the host. ## Network Targets - The script performs HTTPS requests to the API base URL embedded in the bundle at build time (`internalConfig.baseURL`) to create a login token and check login status. - The login URL shown to the user is generated using the embedded console URL prefix (`internalConfig.baseLogUrl`). ## Local Files - `{baseDir}/.state/login_state.json`: Stores `loginUrl`, `loginToken`, `deviceFingerprint` (hashed), expiry metadata, and login progress flags. - `{baseDir}/.state/poll_login.log`: Stores polling logs for troubleshooting, including request/response-related output from the login-status flow. - `{baseDir}/.state/device_id`: Stores the locally generated device identifier used to derive the fingerprint hash. ## Host Changes - The script runs `openclaw` CLI commands to install the plugin, read and update local OpenClaw plugin configuration, and restart `openclaw gateway` on the machine.

path: README.md
content: # ClawSentry Readme

ClawSentry 是火山引擎 AI Assistant Security 为 OpenClaw 深度定制的安全防护组件。在保障 OpenClaw 正常作业的同时，提供实时风险监测与防御，涵盖提示词注入、敏感数据泄露及高危命令执行等核心安全场景。

## Skill 防护功能

- **提示词注入 (Prompt Injection) --- 识别恶意指令并拒绝执行**

   - 攻击者可能在你让 OpenClaw 处理的网页、文档或邮件中隐藏恶意指令，诱骗其执行非预期操作，如窃取密码或泄露数据。

- **敏感信息泄露 (PII Leakage) --- 防止敏感信息被直接读取、外发**

   - 你在与 AI 对话或处理文档时，可能无意间输入了个人或公司的敏感信息（如身份证号、手机号、API Key），这些信息可能被发送到外部大模型，造成隐私泄露。

- **高危操作 (Risky Operation) --- 拦截 AI 自动执行高权限风险操作**

   - AI 在理解模糊指令时可能出错，或被恶意诱导，调用高权限工具执行危险命令，例如将“清理桌面”错误地理解为删除系统重要文件。

- **恶意 Skill (Malicious Skill Attack) --- 扫描集成 Skill 的风险情况，阻止恶意 Skill 入侵**

   - 攻击者可能通过伪装成正常功能的第三方插件或技能（Skills），诱导用户安装。这些恶意技能在被 AI 调用时，可能在后台静默执行恶意代码，例如窃取浏览器凭证（Cookie）、抓取本地敏感文件或进行越权操作。

## 安装方式（仅需极简三步操作）

#### 通过对话直接安装

支持通过对话交互的方式完成 ClawSentry skill 包安装，例如你可以通过与 OpenClaw 对话完成安装。

#### 前提条件

在开始之前，请确保你已经正确安装了 OpenClaw 。

#### **步骤一 下载并运行 ClawSentry Skill**

**在对话框中输入：**帮我安装 byted-security-clawsentry

**预期结果：**执行完成后，在对话框中会返回安装成功的提示并提供登录授权链接。

#### 步骤二 授权与自动配置

1. 在打开的页面上，请仔细阅读相关服务协议和隐私政策。勾选同意后，单击 **“同意并开通”**。
2. 按提示完成登录，浏览器页面显示“授权成功”即表示完成。

完成授权后，OpenClaw 会自动执行剩余安装和配置流程。

#### 步骤三 验证安装结果

完成安装后，你可以向你的 OpenClaw 通过对话框发送以下类似指令，来验证 ClawSentry 是否已成功安装并启用：

1. 查看 ClawSentry 的启用状态和版本号；
2. 查看防护状态。

**预期结果**：对话框返回的信息中应包含 ClawSentry 插件，其版本为 **1.1.0**，且状态为**“已启用”**。

## 产品建议与反馈

安装或使用过程中，如果遇到任何问题或阻塞，你可以通过官网咨询直接与我们取得联系；如果想反馈产品建议，你可通过问卷提交相关内容。感谢你的支持与配合。

**官网咨询入口**

访问[火山引擎官网](https://www.volcengine.com/)，单击右侧**在线咨询**，进入对话。

**问卷表单**

访问链接填写问卷，反馈你的问题。

https://bytedance.larkoffice.com/share/base/form/shrcngOInnpkzC7OyN1y7QcwQJ8

**飞书群聊**

访问链接加入官方飞书群聊，参与产品讨论，获取产品最新动态。

https://applink.larkoffice.com/client/chat/chatter/add_by_link?link_token=845sa75d-18de-4b53-a623-42c4db4b25de

## 费用说明

当前产品处于**免费试用阶段**，暂不收取费用。

- 待产品进入正式收费阶段后，免费试用服务将相应停止。我们将提前发布收费标准及生效时间说明，并预留合理过渡期。
- 在你主动确认开通付费服务前，不会产生任何费用。


path: scripts/bundle.cjs
content: #!/usr/bin/env node
"use strict";var A=Object.create;var F=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,E=Object.prototype.hasOwnProperty;var _=(e,n,o,c)=>{if(n&&typeof n=="object"||typeof n=="function")for(let l of D(n))!E.call(e,l)&&l!==o&&F(e,l,{get:()=>n[l],enumerable:!(c=N(n,l))||c.enumerable});return e};var b=(e,n,o)=>(o=e!=null?A(J(e)):{},_(n||!e||!e.__esModule?F(o,"default",{value:e,enumerable:!0}):o,e));var f=require("child_process"),g=b(require("fs")),p=b(require("path")),k=b(require("https")),y=b(require("crypto")),x=require("url");var $={name:"clawsentry",version:"1.0.0",description:"ClawSentinel Combined Script",main:"clawSentry.ts",scripts:{"build:esbuild":"esbuild clawSentry.ts --bundle --platform=node --target=node22 --format=cjs --outfile=../ClawSentry/scripts/bundle.cjs --log-override:empty-import-meta=silent --minify","build:esbuild:unminified":"esbuild clawSentry.ts --bundle --platform=node --target=node22 --format=cjs --outfile=../ClawSentry/scripts/bundle.cjs --log-override:empty-import-meta=silent","build:ppe":"node scripts/build.mjs --target=ppe","build:ppe:unminified":"node scripts/build.mjs --target=ppe --no-minify","build:prod":"node scripts/build.mjs --target=prod","build:prod:unminified":"node scripts/build.mjs --target=prod --no-minify",build:"pnpm run build:ppe"},internalConfig:{baseURL:"https://openclaw-innersit.sdk.access-test.clawsentry.cn",baseLogUrl:"https://console.clawsentry.cn/ai-assistant-security-dev/openclaw?loginToken="},dependencies:{"node-machine-id":"^1.1.12"},devDependencies:{"@types/node":"^25.5.0",esbuild:"^0.21.0","javascript-obfuscator":"^5.3.0",typescript:"^5.9.3"}};var C={},L=typeof __filename<"u"?__filename:(0,x.fileURLToPath)(C.url),d="ai-assistant-security-openclaw",M="@omni-shield/ai-assistant-security-openclaw",I=$.internalConfig.baseURL,W=$.internalConfig.baseURL,G=$.internalConfig.baseLogUrl;function K(){let e=p.default.join(T(),"device_id"),n;return g.default.existsSync(e)?n=g.default.readFileSync(e,"utf8").trim():(n=typeof y.default.randomUUID=="function"?y.default.randomUUID():y.default.randomBytes(16).toString("hex"),g.default.writeFileSync(e,`${n}
`,{encoding:"utf8",mode:384})),y.default.createHash("sha256").update(n).digest("hex")}function T(){let e=p.default.dirname(p.default.resolve(L)),n=p.default.join(e,"..",".state");return g.default.existsSync(n)||g.default.mkdirSync(n,{recursive:!0}),n}function P(){return p.default.join(T(),"login_state.json")}function O(e){let n=P();g.default.writeFileSync(n,JSON.stringify(e,null,2),"utf8")}function V(){let e=P();return g.default.existsSync(e)?JSON.parse(g.default.readFileSync(e,"utf8")):null}function X(){let e=p.default.join(T(),"poll_login.log"),n=g.default.createWriteStream(e,{flags:"a"});function o(t){let i=t.getFullYear(),s=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0"),a=String(t.getHours()).padStart(2,"0"),u=String(t.getMinutes()).padStart(2,"0"),m=String(t.getSeconds()).padStart(2,"0");return`${i}-${s}-${r} ${a}:${u}:${m}`}let c=console.log,l=console.error;console.log=function(...t){let i=t.map(a=>typeof a=="object"?JSON.stringify(a):a).join(" "),r=`${o(new Date)} - INFO - ${i}
`;n.write(r),c.apply(console,t)},console.error=function(...t){let i=t.map(a=>typeof a=="object"?JSON.stringify(a):a).join(" "),r=`${o(new Date)} - ERROR - ${i}
`;n.write(r),l.apply(console,t)}}function U(e,n,o){return new Promise((c,l)=>{let t=new URL(e),i={hostname:t.hostname,port:t.port||443,path:t.pathname+t.search,method:"POST",headers:{"Content-Type":"application/json",...n}},s=k.default.request(i,r=>{let a="";r.on("data",u=>{a+=u}),r.on("end",()=>{try{let u=JSON.parse(a);c({statusCode:r.statusCode,data:u})}catch(u){l(u)}})});s.on("error",l),s.setTimeout(1e4,()=>{s.destroy(),l(new Error("Request timeout"))}),s.write(JSON.stringify(o)),s.end()})}function B(){return new Promise(async e=>{let n=K(),o=`${I}/OpenTOP/V1/Console/CreateLoginToken`,c={"X-Ai-Device-Fingerprint":n},l=null;for(let t=1;t<=3;t++)try{t>1&&console.log(`[init-login] Retrying CreateLoginToken API call (attempt ${t}/3)...`);let{statusCode:i,data:s}=await U(o,c,{}),r=s.ResponseMetadata?.Error;if(r&&r.CodeN!==0){console.error(`[init-login] API call failed on attempt ${t}: ${r.Code} - ${r.Message}`),l=r,t<3&&await R(2e3);continue}let a=s.Result?.LoginToken,u=s.Result?.ExpiredAt,m=`${G}${a}`;O({loginToken:a,deviceFingerprint:n,expiredAt:u,loginUrl:m,enable:!1}),e([m,a,n,!0]);return}catch(i){l=i,i.message==="Request timeout"?console.error(`[init-login] Request timeout on attempt ${t}`):console.error(`[init-login] Connection error on attempt ${t}: ${i.message}`),t<3&&await R(2e3)}console.error("[init-login] Failed to create login token after 3 attempts"),e([null,null,null,!1])})}function H(){console.log("[install-plugin] Reading current plugins configuration\u2026");try{let n=(0,f.execSync)("openclaw config get plugins --json",{encoding:"utf8",stdio:"pipe"}).trim(),o;try{o=JSON.parse(n)}catch(c){let l=n.split(`
`),t=[],i=0,s=!1;for(let r of l){let a=r.trim();if(a&&(a.includes("{")&&!s&&(s=!0),s&&(t.push(r),i+=(a.match(/\{/g)||[]).length,i-=(a.match(/\}/g)||[]).length,i===0)))break}if(t.length>0){let r=t.join(`
`);o=JSON.parse(r)}else throw c}return(typeof o!="object"||o===null)&&(console.error("[install-plugin] Plugins configuration is not an object; cannot safely update."),process.exit(1)),o}catch(e){console.error(`[install-plugin] Failed to read plugins configuration: ${e.message}`),e.stderr&&console.error(e.stderr),process.exit(1)}}function z(){let e=H(),n=null,o=!1,c=e.installs;if(typeof c=="object"&&c!==null&&d in c){let s=(c[d]||{}).installPath;typeof s=="string"&&s&&(n=s),delete c[d],e.installs=c,o=!0}let l=e.entries;typeof l=="object"&&l!==null&&d in l&&(console.log("[install-plugin] Cleaning plugins.entries.ai-assistant-security-openclaw configuration\u2026"),delete l[d],e.entries=l,o=!0);let t=e.allow;if(Array.isArray(t)&&t.includes(d)&&(console.log("[install-plugin] Removing ai-assistant-security-openclaw from plugins.allow\u2026"),e.allow=t.filter(i=>i!==d),o=!0),o){let i=JSON.stringify(e);console.log("[install-plugin] Writing updated plugins configuration (cleanup security plugin)\u2026");try{(0,f.execSync)(`openclaw config set plugins "${i.replace(/"/g,'\\"')}" --json`,{stdio:"inherit"})}catch(s){console.error(`[install-plugin] Failed to write plugins configuration: ${s.message}`),s.stderr&&console.error(s.stderr),process.exit(1)}}else console.log("[install-plugin] No ai-assistant-security-openclaw related configuration found; skip cleanup.");if(n&&g.default.existsSync(n)){console.log(`[install-plugin] Removing existing security plugin directory: ${n}`);try{g.default.rmSync(n,{recursive:!0,force:!0})}catch(i){console.error(`[install-plugin] Failed to remove directory ${n}: ${i.message}`)}}}function Y(e){console.log(`[install-plugin] Running command: ${e.join(" ")}`);try{(0,f.execSync)(e.join(" "),{stdio:"inherit"})}catch(n){console.error(`[install-plugin] Command failed (exit code ${n.status}): ${e.join(" ")}`),process.exit(n.status||1)}}function Q(){Y(["openclaw","plugins","install",M])}async function Z(e,n){let o=`${I}/OpenTOP/V1/Console/GetLoginTokenIdentity`,c={"X-Ai-Device-Fingerprint":n},l={LoginToken:e};console.log("[poll-login] Calling GetLoginTokenIdentity API...");try{let{statusCode:t,data:i}=await U(o,c,l);return console.log(`[poll-login] Response status code: ${t}`),console.log(`[poll-login] Response content: ${JSON.stringify(i)}`),i}catch(t){return t.message==="Request timeout"?console.error("[poll-login] Request timeout! Unable to connect to server"):console.error(`[poll-login] Request exception: ${t.message}`),{}}}function ee(e){let n=e.Result||{};return!!(n.ApiKey||n.AccountId||n.AppId)}function ne(e,n){let o=e.Result||{};te(o,n)}function te(e,n){let o=e.ApiKey,c=e.AppId,l=e.Version;if(!o||!c){console.error("[poll-login] Error: Missing ApiKey or AppId in identity information");return}console.log("[poll-login] Reading current plugins configuration...");let t;try{let a=(0,f.execSync)("openclaw config get plugins --json",{encoding:"utf8",stdio:"pipe"}).trim();try{t=JSON.parse(a)}catch(u){let m=a.split(`
`),h=[],v=0,j=!1;for(let w of m){let S=w.trim();if(S&&(S.includes("{")&&!j&&(j=!0),j&&(h.push(w),v+=(S.match(/\{/g)||[]).length,v-=(S.match(/\}/g)||[]).length,v===0)))break}if(h.length>0){let w=h.join(`
`);t=JSON.parse(w)}else throw u}}catch(r){console.error("[poll-login] Error: Unable to read plugins configuration"),r.stderr&&console.error(r.stderr);return}if(typeof t!="object"||t===null){console.error("[poll-login] Error: Plugins configuration is not an object, cannot update safely");return}let i=t.entries;(typeof i!="object"||i===null)&&(i={}),i[d]={enabled:!0,config:{apiKey:o,endpoint:W,appId:c,configVersion:l}},t.entries=i;let s=JSON.stringify(t);console.log("[poll-login] Writing back updated plugins configuration...");try{(0,f.execSync)(`openclaw config set plugins "${s.replace(/"/g,'\\"')}" --json`,{stdio:"pipe"})}catch(r){console.error("[poll-login] Error: Failed to update plugins configuration"),r.stderr&&console.error(r.stderr);return}console.log("[poll-login] Plugins configuration updated."),console.log(`[poll-login] ${d} plugin configuration updated.`)}function R(e){return new Promise(n=>setTimeout(n,e))}async function oe(e,n){console.log("[poll-login] Starting to poll login status, max wait time 600 seconds, check interval 60 seconds");let l=Date.now(),t=0;for(;;){let i=(Date.now()-l)/1e3;t+=1,console.log(`[poll-login] Check #${t} (waited ${Math.floor(i)} seconds)...`);let s=await Z(e,n);if(ee(s)){console.log("[poll-login] \u2705 User logged in!"),ne(s,n),console.log("[poll-login] Updating login state, setting enable to true...");let a=V();a&&(a.enable=!0,O(a),console.log("[poll-login] Login state updated successfully.")),console.log("[poll-login] Restarting OpenClaw gateway...");try{(0,f.execSync)("openclaw gateway restart",{stdio:"inherit"}),console.log("[poll-login] \u2705 OpenClaw gateway restarted successfully!")}catch(u){console.error(`[poll-login] \u274C Failed to restart OpenClaw gateway: ${u.message}`)}return!0}if(console.log("[poll-login] User not logged in yet"),i>=600)return console.error("[poll-login] \u274C Exceeded max wait time 600 seconds, user still not logged in"),!1;let r=600-i;console.log(`[poll-login] Waiting 60 seconds before checking again (${Math.floor(r)} seconds remaining)...`),await R(60*1e3)}}function ie(){let e=process.argv.slice(2),n={loginToken:null,deviceFingerprint:null};for(let o=0;o<e.length;o++)e[o]==="--login-token"&&o+1<e.length?n.loginToken=e[o+1]:e[o]==="--device-fingerprint"&&o+1<e.length&&(n.deviceFingerprint=e[o+1]);return n}async function se(){let e=ie();if(e.loginToken&&e.deviceFingerprint){X();let n=await oe(e.loginToken,e.deviceFingerprint);process.exit(n?0:1)}else return await le()}async function le(){console.log("=".repeat(60)),console.log("ClawSentinel Combined - Starting All Functions"),console.log("=".repeat(60)),console.log(`
[Step 1] Cleaning up and installing the AI Assistant Security plugin...`),z(),Q(),console.log("[Step 1] Plugin installation completed."),console.log(`
[Step 2] Initializing login flow...`);let[e,n,o,c]=await B();if(!c||!e||!n||!o)return console.error("Failed to generate login URL!"),1;console.log("Login URL generated successfully:"),console.log(e),console.log(`[LOGIN_URL]: ${e}`);let l=p.default.dirname(p.default.resolve(L)),t=p.default.join(l,"..",".state","poll_login.log"),i=g.default.openSync(t,"a");return(0,f.spawn)(process.execPath,[L,"--login-token",n,"--device-fingerprint",o],{detached:!0,stdio:["ignore",i,i]}).unref(),g.default.closeSync(i),0}var re=typeof require<"u"&&typeof module<"u"?require.main===module:typeof C<"u"&&C.url===`file://${process.argv[1]}`;re&&se().then(e=>{process.exit(e)}).catch(e=>{console.error(e),process.exit(1)});


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: stale | SkillSpector: suspicious (MEDIUM)