🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

shellward

Package Overview
Dependencies
Maintainers
1
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

shellward - npm Package Compare versions

Comparing version
0.7.13
to
0.7.14
+38
dist/init.d.ts
/** 标准 MCP 接入条目:零安装,npx 拉取已发布的 shellward-mcp */
export declare const SHELLWARD_MCP_ENTRY: {
command: string;
args: string[];
};
export interface InitTarget {
name: string;
path: string;
/** 配置里放 MCP 服务器的字段名(绝大多数是 mcpServers) */
key: string;
/** 工具未安装时是否允许新建配置文件 */
createIfMissing?: boolean;
}
/** 已知 AI 工具的 MCP 配置位置(跨平台) */
export declare function knownTargets(home?: string): InitTarget[];
export type MergeResult = {
status: 'added' | 'updated';
config: any;
} | {
status: 'unchanged';
config: any;
};
/**
* 纯合并:把 shellward 条目并入配置对象。已存在且相同→unchanged;不同→updated;没有→added。
* 不破坏其它 MCP 服务器条目。
*/
export declare function mergeShellward(config: any, key: string): MergeResult;
export interface InitOutcome {
name: string;
path: string;
result: 'added' | 'updated' | 'unchanged' | 'skipped' | 'error';
detail?: string;
}
/** 执行接入:探测→读取→合并→备份→写回。dryRun 仅预览不写。 */
export declare function runInit(opts?: {
dryRun?: boolean;
home?: string;
}): InitOutcome[];
// src/init.ts — `shellward init`:一条命令把 ShellWard 接入已安装的 AI 工具(MCP 运行时防护)
//
// 把"扫描 → 运行时防护"的部署摩擦降到一条命令:自动探测 Claude Desktop / Cursor /
// Claude Code / Windsurf 的 MCP 配置,安全地加入 shellward 条目(备份、合并、不覆盖)。
// 这是「安装按钮」的正确形态——只对已知配置文件操作、改前备份、可 --dry-run 预览。
import { readFileSync, writeFileSync, existsSync, copyFileSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import { homedir } from 'os';
/** 标准 MCP 接入条目:零安装,npx 拉取已发布的 shellward-mcp */
export const SHELLWARD_MCP_ENTRY = {
command: 'npx',
args: ['-y', '-p', 'shellward', 'shellward-mcp'],
};
/** 已知 AI 工具的 MCP 配置位置(跨平台) */
export function knownTargets(home = homedir()) {
const appData = process.env.APPDATA || join(home, 'AppData', 'Roaming');
const claudeDesktop = process.platform === 'darwin' ? join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')
: process.platform === 'win32' ? join(appData, 'Claude', 'claude_desktop_config.json')
: join(home, '.config', 'Claude', 'claude_desktop_config.json');
return [
{ name: 'Claude Desktop', path: claudeDesktop, key: 'mcpServers', createIfMissing: true },
{ name: 'Cursor', path: join(home, '.cursor', 'mcp.json'), key: 'mcpServers', createIfMissing: true },
{ name: 'Claude Code', path: join(home, '.claude.json'), key: 'mcpServers' },
{ name: 'Windsurf', path: join(home, '.codeium', 'windsurf', 'mcp_config.json'), key: 'mcpServers' },
];
}
/**
* 纯合并:把 shellward 条目并入配置对象。已存在且相同→unchanged;不同→updated;没有→added。
* 不破坏其它 MCP 服务器条目。
*/
export function mergeShellward(config, key) {
const cfg = config && typeof config === 'object' ? config : {};
const servers = cfg[key] && typeof cfg[key] === 'object' ? cfg[key] : {};
const existing = servers.shellward;
const same = existing && JSON.stringify(existing) === JSON.stringify(SHELLWARD_MCP_ENTRY);
if (same)
return { status: 'unchanged', config: cfg };
const status = existing ? 'updated' : 'added';
cfg[key] = { ...servers, shellward: { ...SHELLWARD_MCP_ENTRY } };
return { status, config: cfg };
}
/** 执行接入:探测→读取→合并→备份→写回。dryRun 仅预览不写。 */
export function runInit(opts = {}) {
const targets = knownTargets(opts.home);
const out = [];
for (const t of targets) {
const exists = existsSync(t.path);
if (!exists && !t.createIfMissing) {
out.push({ name: t.name, path: t.path, result: 'skipped', detail: '未安装/无配置' });
continue;
}
try {
let config = {};
if (exists) {
const raw = readFileSync(t.path, 'utf-8').trim();
config = raw ? JSON.parse(raw) : {};
}
const merged = mergeShellward(config, t.key);
if (merged.status === 'unchanged') {
out.push({ name: t.name, path: t.path, result: 'unchanged', detail: '已接入' });
continue;
}
if (!opts.dryRun) {
if (exists)
copyFileSync(t.path, t.path + '.shellward.bak'); // 改前备份
else
mkdirSync(dirname(t.path), { recursive: true });
writeFileSync(t.path, JSON.stringify(merged.config, null, 2) + '\n');
}
out.push({ name: t.name, path: t.path, result: merged.status, detail: opts.dryRun ? '预览(未写入)' : (exists ? '已加入(原文件已备份 .bak)' : '已新建配置') });
}
catch (e) {
out.push({ name: t.name, path: t.path, result: 'error', detail: e?.message || String(e) });
}
}
return out;
}
// src/init.ts — `shellward init`:一条命令把 ShellWard 接入已安装的 AI 工具(MCP 运行时防护)
//
// 把"扫描 → 运行时防护"的部署摩擦降到一条命令:自动探测 Claude Desktop / Cursor /
// Claude Code / Windsurf 的 MCP 配置,安全地加入 shellward 条目(备份、合并、不覆盖)。
// 这是「安装按钮」的正确形态——只对已知配置文件操作、改前备份、可 --dry-run 预览。
import { readFileSync, writeFileSync, existsSync, copyFileSync, mkdirSync } from 'fs'
import { join, dirname } from 'path'
import { homedir } from 'os'
/** 标准 MCP 接入条目:零安装,npx 拉取已发布的 shellward-mcp */
export const SHELLWARD_MCP_ENTRY = {
command: 'npx',
args: ['-y', '-p', 'shellward', 'shellward-mcp'],
}
export interface InitTarget {
name: string
path: string
/** 配置里放 MCP 服务器的字段名(绝大多数是 mcpServers) */
key: string
/** 工具未安装时是否允许新建配置文件 */
createIfMissing?: boolean
}
/** 已知 AI 工具的 MCP 配置位置(跨平台) */
export function knownTargets(home = homedir()): InitTarget[] {
const appData = process.env.APPDATA || join(home, 'AppData', 'Roaming')
const claudeDesktop =
process.platform === 'darwin' ? join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')
: process.platform === 'win32' ? join(appData, 'Claude', 'claude_desktop_config.json')
: join(home, '.config', 'Claude', 'claude_desktop_config.json')
return [
{ name: 'Claude Desktop', path: claudeDesktop, key: 'mcpServers', createIfMissing: true },
{ name: 'Cursor', path: join(home, '.cursor', 'mcp.json'), key: 'mcpServers', createIfMissing: true },
{ name: 'Claude Code', path: join(home, '.claude.json'), key: 'mcpServers' },
{ name: 'Windsurf', path: join(home, '.codeium', 'windsurf', 'mcp_config.json'), key: 'mcpServers' },
]
}
export type MergeResult =
| { status: 'added' | 'updated'; config: any }
| { status: 'unchanged'; config: any }
/**
* 纯合并:把 shellward 条目并入配置对象。已存在且相同→unchanged;不同→updated;没有→added。
* 不破坏其它 MCP 服务器条目。
*/
export function mergeShellward(config: any, key: string): MergeResult {
const cfg = config && typeof config === 'object' ? config : {}
const servers = cfg[key] && typeof cfg[key] === 'object' ? cfg[key] : {}
const existing = servers.shellward
const same = existing && JSON.stringify(existing) === JSON.stringify(SHELLWARD_MCP_ENTRY)
if (same) return { status: 'unchanged', config: cfg }
const status = existing ? 'updated' : 'added'
cfg[key] = { ...servers, shellward: { ...SHELLWARD_MCP_ENTRY } }
return { status, config: cfg }
}
export interface InitOutcome {
name: string
path: string
result: 'added' | 'updated' | 'unchanged' | 'skipped' | 'error'
detail?: string
}
/** 执行接入:探测→读取→合并→备份→写回。dryRun 仅预览不写。 */
export function runInit(opts: { dryRun?: boolean; home?: string } = {}): InitOutcome[] {
const targets = knownTargets(opts.home)
const out: InitOutcome[] = []
for (const t of targets) {
const exists = existsSync(t.path)
if (!exists && !t.createIfMissing) {
out.push({ name: t.name, path: t.path, result: 'skipped', detail: '未安装/无配置' })
continue
}
try {
let config: any = {}
if (exists) {
const raw = readFileSync(t.path, 'utf-8').trim()
config = raw ? JSON.parse(raw) : {}
}
const merged = mergeShellward(config, t.key)
if (merged.status === 'unchanged') {
out.push({ name: t.name, path: t.path, result: 'unchanged', detail: '已接入' })
continue
}
if (!opts.dryRun) {
if (exists) copyFileSync(t.path, t.path + '.shellward.bak') // 改前备份
else mkdirSync(dirname(t.path), { recursive: true })
writeFileSync(t.path, JSON.stringify(merged.config, null, 2) + '\n')
}
out.push({ name: t.name, path: t.path, result: merged.status, detail: opts.dryRun ? '预览(未写入)' : (exists ? '已加入(原文件已备份 .bak)' : '已新建配置') })
} catch (e: any) {
out.push({ name: t.name, path: t.path, result: 'error', detail: e?.message || String(e) })
}
}
return out
}
+34
-0

@@ -20,2 +20,3 @@ #!/usr/bin/env node

import { renderHtmlReport } from './compliance/html-report.js';
import { runInit } from './init.js';
import { resolveLocale } from './types.js';

@@ -35,2 +36,6 @@ const argv = process.argv.slice(2);

}
if (cmd === 'init') {
runInitCommand(argv.includes('--dry-run'));
return;
}
if (cmd === 'web') {

@@ -156,2 +161,29 @@ const { startWebServer } = await import('./web/scan-server.js');

}
/** `shellward init`:把 ShellWard 接入已安装 AI 工具的 MCP 配置(运行时防护) */
function runInitCommand(dryRun) {
const out = runInit({ dryRun });
console.log('\n🔌 ShellWard 接入 AI 工具运行时防护' + (dryRun ? '(预览,不写入)' : '') + '\n');
const ICON = { added: '✅', updated: '✅', unchanged: '✔️', skipped: '·', error: '⚠️' };
let touched = 0;
for (const o of out) {
const label = { added: '已接入', updated: '已更新', unchanged: '已接入(无变化)', skipped: '跳过', error: '失败' }[o.result];
if (o.result === 'added' || o.result === 'updated')
touched++;
console.log(` ${ICON[o.result] || '·'} ${o.name} — ${label}${o.detail ? ':' + o.detail : ''}`);
if (o.result !== 'skipped')
console.log(` ${o.path}`);
}
console.log('');
if (dryRun) {
console.log('这是预览。去掉 --dry-run 实际接入。');
}
else if (touched > 0) {
console.log('✅ 已接入。请重启对应的 AI 工具,ShellWard 即作为运行时防护生效(拦注入/外泄/危险命令)。');
console.log(' 验证:在工具里问"调用 shellward 的 security_status"。原配置已备份为 *.shellward.bak。');
}
else {
console.log('未发现可接入的已安装 AI 工具配置。也可手动加 MCP:');
console.log(' {"mcpServers":{"shellward":{"command":"npx","args":["-y","-p","shellward","shellward-mcp"]}}}');
}
}
/** 跨平台在默认浏览器打开 URL 或本地文件(失败静默,不影响主流程) */

@@ -193,2 +225,3 @@ function openBrowser(target) {

shellward web --local Local web GUI: scan a local path (private, no upload)
shellward init Install ShellWard into your AI tools (MCP runtime guard)
shellward mcp Start MCP server (stdio)

@@ -213,2 +246,3 @@ shellward --help

shellward web --local 本地 web GUI:填本地路径扫描(私有、不上传,客户端体验)
shellward init 一键接入你的 AI 工具(MCP 运行时防护,--dry-run 预览)
shellward mcp 启动 MCP 服务器(stdio)

@@ -215,0 +249,0 @@ shellward --help

+4
-3
{
"name": "shellward",
"version": "0.7.13",
"version": "0.7.14",
"mcpName": "io.github.jnMetaCode/shellward",

@@ -60,3 +60,3 @@ "description": "AI agent security & MCP security middleware — prompt injection detection, AI firewall, runtime guardrails & data-loss prevention for LLM tool calls. 8-layer defense against data exfiltration & dangerous commands. Zero dependencies. SDK + OpenClaw plugin. Supports LangChain, AutoGPT, Claude Code, Cursor, OpenAI Agents, Hermes Agent.",

"mcp": "npx tsx src/mcp-server.ts",
"test": "npx tsx test-sdk.ts && npx tsx test-integration.ts && npx tsx test-edge-cases.ts && npx tsx test-rugpull.ts && npx tsx test-redos.ts && npx tsx test-mcp-client.ts && npx tsx test-mcp.ts && npx tsx test-compliance.ts && npx tsx test-web.ts",
"test": "npx tsx test-sdk.ts && npx tsx test-integration.ts && npx tsx test-edge-cases.ts && npx tsx test-rugpull.ts && npx tsx test-redos.ts && npx tsx test-mcp-client.ts && npx tsx test-mcp.ts && npx tsx test-compliance.ts && npx tsx test-web.ts && npx tsx test-init.ts",
"test:redos": "npx tsx test-redos.ts",

@@ -70,3 +70,4 @@ "test:compliance": "npx tsx test-compliance.ts",

"bench:scan": "npx tsx bench/scan-bench.ts",
"prepublishOnly": "npm run build"
"prepublishOnly": "npm run build",
"test:init": "npx tsx test-init.ts"
},

@@ -73,0 +74,0 @@ "openclaw": {

@@ -11,3 +11,3 @@ <p align="center">

[![license](https://img.shields.io/badge/license-Apache--2.0-blue)](./LICENSE)
[![tests](https://img.shields.io/badge/tests-303%20passing-brightgreen)](#performance)
[![tests](https://img.shields.io/badge/tests-315%20passing-brightgreen)](#performance)
[![deps](https://img.shields.io/badge/dependencies-0-brightgreen)](#performance)

@@ -14,0 +14,0 @@

@@ -21,2 +21,3 @@ #!/usr/bin/env node

import { renderHtmlReport } from './compliance/html-report.js'
import { runInit } from './init.js'
import { resolveLocale } from './types.js'

@@ -40,2 +41,7 @@

if (cmd === 'init') {
runInitCommand(argv.includes('--dry-run'))
return
}
if (cmd === 'web') {

@@ -168,2 +174,26 @@ const { startWebServer } = await import('./web/scan-server.js')

/** `shellward init`:把 ShellWard 接入已安装 AI 工具的 MCP 配置(运行时防护) */
function runInitCommand(dryRun: boolean): void {
const out = runInit({ dryRun })
console.log('\n🔌 ShellWard 接入 AI 工具运行时防护' + (dryRun ? '(预览,不写入)' : '') + '\n')
const ICON: Record<string, string> = { added: '✅', updated: '✅', unchanged: '✔️', skipped: '·', error: '⚠️' }
let touched = 0
for (const o of out) {
const label = { added: '已接入', updated: '已更新', unchanged: '已接入(无变化)', skipped: '跳过', error: '失败' }[o.result]
if (o.result === 'added' || o.result === 'updated') touched++
console.log(` ${ICON[o.result] || '·'} ${o.name} — ${label}${o.detail ? ':' + o.detail : ''}`)
if (o.result !== 'skipped') console.log(` ${o.path}`)
}
console.log('')
if (dryRun) {
console.log('这是预览。去掉 --dry-run 实际接入。')
} else if (touched > 0) {
console.log('✅ 已接入。请重启对应的 AI 工具,ShellWard 即作为运行时防护生效(拦注入/外泄/危险命令)。')
console.log(' 验证:在工具里问"调用 shellward 的 security_status"。原配置已备份为 *.shellward.bak。')
} else {
console.log('未发现可接入的已安装 AI 工具配置。也可手动加 MCP:')
console.log(' {"mcpServers":{"shellward":{"command":"npx","args":["-y","-p","shellward","shellward-mcp"]}}}')
}
}
/** 跨平台在默认浏览器打开 URL 或本地文件(失败静默,不影响主流程) */

@@ -205,2 +235,3 @@ function openBrowser(target: string): void {

shellward web --local Local web GUI: scan a local path (private, no upload)
shellward init Install ShellWard into your AI tools (MCP runtime guard)
shellward mcp Start MCP server (stdio)

@@ -224,2 +255,3 @@ shellward --help

shellward web --local 本地 web GUI:填本地路径扫描(私有、不上传,客户端体验)
shellward init 一键接入你的 AI 工具(MCP 运行时防护,--dry-run 预览)
shellward mcp 启动 MCP 服务器(stdio)

@@ -226,0 +258,0 @@ shellward --help