Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
- name: Verify Linux WASM worker resource boundary
run: |
cargo test -p codeguard-runtime --features wasm-precheck --test process_contract
cargo test -p codeguard-runtime --features wasm-precheck --test wasm_grammar_load --test dart_upstream_corpus
cargo test -p codeguard-cli --features wasm-precheck --test syntax_worker_candidate --test typescript_syntax_fallback_candidate --test java_syntax_fallback_candidate

- name: Run test suite
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ cargo build --locked -p codeguard-cli

Version output includes `cli_version`, `target`, and protocol information. Discovery and init preview return observations and unknowns. Dry-run creates neither `AGENTS.md` nor a project data directory. Add Cargo `--offline` only when dependencies are cached.

The workspace requires `std`. The opt-in `wasm-precheck` build feature uses a bounded Rust worker with twenty-two pinned grammar candidates: ArkTS, C, C++, C#, Go, Java, JavaScript, Lua, Luau, Objective-C, Python, Rust, Solidity, TypeScript, TSX, Zig, Nix, Terraform, R, Ruby, PHP, and Kotlin. Java and TypeScript/TSX have narrow single-file fallback paths; `lint python` can provide bounded suspected syntax positions when Ruff is unavailable or no project Ruff configuration is declared. Native Ruff results take priority. It is absent from the published npm `0.1.2` package. No `no_std`, completed project-wide WASM fallback, zero-unsafe, or fastest-runtime claim is made. Runtime OS calls require safety review; a comprehensive security audit is not claimed.
The workspace requires `std`. The opt-in `wasm-precheck` build feature uses a bounded Rust worker with twenty-three pinned grammar candidates: ArkTS, C, C++, C#, Go, Java, JavaScript, Lua, Luau, Objective-C, Python, Rust, Solidity, TypeScript, TSX, Zig, Nix, Terraform, R, Ruby, PHP, Kotlin, and Dart. Java and TypeScript/TSX have narrow single-file fallback paths; `lint python` can provide bounded suspected syntax positions when Ruff is unavailable or no project Ruff configuration is declared. Native Ruff results take priority. It is absent from the published npm `0.1.2` package. No `no_std`, completed project-wide WASM fallback, zero-unsafe, or fastest-runtime claim is made. Runtime OS calls require safety review; a comprehensive security audit is not claimed.

The current source-built CLI's `codeguard grammar status --format=json` reports the coverage gap without loading grammars or running lint: the pinned CodeGraph source has 30 vendored WASM files plus two distinct grammars resolved through its dependency; CodeGuard has twenty-two unqualified asset candidates (ArkTS, C, C++, C#, Go, Java, JavaScript, Lua, Luau, Objective-C, Python, Rust, Solidity, TypeScript, TSX, Zig, Nix, Terraform, R, Ruby, PHP, Kotlin) and zero released syntax capabilities. C, C++, C#, Go, JavaScript, Lua, Luau and Rust are byte-pinned CodeGraph assets that load in the Rust worker but have no public lint route or release qualification. ArkTS, Nix and Terraform are also pinned Rust-loadable candidates with narrow valid/invalid fixtures and no public lint route or release qualification; see the [limited acceptance record](tests/acceptance/arkts-nix-terraform-grammar-candidates.md). The two dependency grammars retain their source bytes and use a byte-pinned legacy `dylink` metadata conversion for Rust loading; they have no public lint route or release qualification. Zig uses a byte-pinned import adaptation so the corrected CodeGraph grammar can load in Rust; it has no public `lint zig` route or release qualification yet. [Zig's limited acceptance record](tests/acceptance/zig-grammar-candidate.md) distinguishes this from released syntax support. Python has a source-build-only, unqualified CLI fallback path; its report remains incomplete and cannot approve delivery. R, Ruby, PHP and Kotlin have pinned CodeGraph bytes, licenses, measured Rust ABI and narrow worker samples; PHP covers mixed HTML/PHP, and all four remain unqualified. Dart remains a gap because its source WASM external scanner import cannot be instantiated by the current Rust worker; see the [limited acceptance record](tests/acceptance/r-ruby-php-kotlin-grammar-candidates.md). The separate [coverage inventory](grammars/codegraph-coverage.json) is not an approved asset manifest. COBOL's 16.4 MB source asset also exceeds the current Rust loader's 8 MiB limit.
The current source-built CLI's `codeguard grammar status --format=json` reports the coverage gap without loading grammars or running lint: the pinned CodeGraph source has 30 vendored WASM files plus two distinct grammars resolved through its dependency; CodeGuard has twenty-three unqualified asset candidates (ArkTS, C, C++, C#, Go, Java, JavaScript, Lua, Luau, Objective-C, Python, Rust, Solidity, TypeScript, TSX, Zig, Nix, Terraform, R, Ruby, PHP, Kotlin, Dart) and zero released syntax capabilities. C, C++, C#, Go, JavaScript, Lua, Luau and Rust are byte-pinned CodeGraph assets that load in the Rust worker but have no public lint route or release qualification. ArkTS, Nix and Terraform are also pinned Rust-loadable candidates with narrow valid/invalid fixtures and no public lint route or release qualification; see the [limited acceptance record](tests/acceptance/arkts-nix-terraform-grammar-candidates.md). The two dependency grammars retain their source bytes and use a byte-pinned legacy `dylink` metadata conversion for Rust loading; they have no public lint route or release qualification. Zig uses a byte-pinned import adaptation so the corrected CodeGraph grammar can load in Rust; it has no public `lint zig` route or release qualification yet. [Zig's limited acceptance record](tests/acceptance/zig-grammar-candidate.md) distinguishes this from released syntax support. Python has a source-build-only, unqualified CLI fallback path; its report remains incomplete and cannot approve delivery. R, Ruby, PHP and Kotlin have pinned CodeGraph bytes, licenses, measured Rust ABI and narrow worker samples; PHP covers mixed HTML/PHP, and all four remain unqualified. The original CodeGraph Dart WASM cannot be instantiated by Rust, but CodeGuard now pins a reproducible Zig rebuild with the real scanner and a byte-pinned WASI import adaptation. Its narrow Rust and worker fixtures pass, while public lint and release remain unqualified; see the [Dart rebuild record](tests/acceptance/dart-grammar-rebuild-candidate.md). The separate [coverage inventory](grammars/codegraph-coverage.json) is not an approved asset manifest. COBOL's 16.4 MB source asset also exceeds the current Rust loader's 8 MiB limit.

### npm installation and one-off use

Expand Down
4 changes: 2 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ cargo build --locked -p codeguard-cli

版本输出包含 `cli_version`、`target` 和协议信息。发现与 init 预览返回观察及未知项。Dry-run 不创建 `AGENTS.md` 或项目数据目录。仅在依赖已缓存时为 Cargo 添加 `--offline`。

工作区要求 `std`。可选 `wasm-precheck` 构建特性已用固定的 Java、Python、TypeScript、TSX grammar 候选资产与有界 Rust worker。Java 和 TypeScript/TSX 有局部单文件兜底;`lint python` 在 Ruff 不可用或项目未声明 Ruff 配置时可报告有界疑似语法位置,原生 Ruff 结果优先。公开 npm `0.1.2` 不含该路径。不声称 `no_std`、已完成项目级 WASM 兜底、零 unsafe 或性能最快。运行层系统调用需要安全审查,当前不宣称已完成整体安全审计。
工作区要求 `std`。可选 `wasm-precheck` 构建特性已有二十三份固定 grammar 候选资产与有界 Rust worker。Java 和 TypeScript/TSX 有局部单文件兜底;`lint python` 在 Ruff 不可用或项目未声明 Ruff 配置时可报告有界疑似语法位置,原生 Ruff 结果优先。公开 npm `0.1.2` 不含该路径。不声称 `no_std`、已完成项目级 WASM 兜底、零 unsafe 或性能最快。运行层系统调用需要安全审查,当前不宣称已完成整体安全审计。

当前源码构建的 CLI 可用 `codeguard grammar status --format=json` 只读展示来源覆盖缺口,不加载 grammar 或运行 lint:固定 CodeGraph 来源含 30 份随仓 WASM,另有两种独立 grammar 从其依赖取得;CodeGuard 当前有 C、C++、C#、Go、Java、JavaScript、Lua、Luau、Objective-C、Python、Rust、Solidity、TypeScript、TSX、Zig 十五份未验收资产候选,已发行语法能力为零。C、C++、C#、Go、JavaScript、Lua、Luau、Rust 已作为固定字节的 Rust worker 候选接入,但没有公开 lint 路由或发行验收。Objective-C 与 Solidity 的依赖包原始字节、许可证和固定散列的 `dylink` 元数据转换已入库,可作为 Rust 隔离 worker 的候选观察,但没有公开 lint 路由或发行验收;Zig 也仍是未验收候选。Python 已有仅限源码可选构建的 CLI 候选兜底,结果保持不完整,不能批准交付。[覆盖库存](grammars/codegraph-coverage.json)不是已批准资产清单。COBOL 来源资产约 16.4 MB,还超过当前 Rust 加载器的 8 MiB 限额。
当前源码构建的 CLI 可用 `codeguard grammar status --format=json` 只读展示来源覆盖缺口,不加载 grammar 或运行 lint:固定 CodeGraph 来源含 30 份随仓 WASM,另有两种独立 grammar 从其依赖取得;CodeGuard 当前有二十三份未验收资产候选、零项已发行语法能力。C、C++、C#、Go、JavaScript、Lua、Luau、Rust、ArkTS、Nix、Terraform、R、Ruby、PHP、Kotlin 等已固定字节并由 Rust worker 做窄范围验证,但没有公开 lint 路由或发行验收。Objective-C 与 Solidity 的依赖包原始字节、许可证和固定散列的 `dylink` 元数据转换已入库;Zig 的新版语法也有固定导入适配。CodeGraph 原 Dart WASM 无法由 Rust 直接加载;CodeGuard 现以固定源码和真实外部 scanner 经 Zig 重建,并固定适配前后字节,150 例上游语料错误分类与预期一致,但仍未做 Dart 原生工具对照或发布验收,见[Dart 重建局部验收](tests/acceptance/dart-grammar-rebuild-candidate.md)。Python 已有仅限源码可选构建的 CLI 候选兜底,结果保持不完整,不能批准交付。[覆盖库存](grammars/codegraph-coverage.json)不是已批准资产清单。COBOL 来源资产约 16.4 MB,还超过当前 Rust 加载器的 8 MiB 限额。

### npm 安装与一次性调用

Expand Down
16 changes: 16 additions & 0 deletions crates/codeguard-adapters/examples/adapt_dart_wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//! 固定 Dart 重建资产的离线发行字节生成器。

use std::path::Path;

use codeguard_adapters::adapt_dart_wasm;

fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = std::env::args().collect::<Vec<_>>();
let [_, source, output] = args.as_slice() else {
return Err("用法:adapt_dart_wasm <source.wasm> <parser.wasm>".into());
};
let source = std::fs::read(Path::new(source))?;
let adapted = adapt_dart_wasm(&source)?;
std::fs::write(Path::new(output), adapted)?;
Ok(())
}
81 changes: 81 additions & 0 deletions crates/codeguard-adapters/src/dart_wasm_compat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//! 固定 Dart 重建 WASM 的 Rust WasmStore 导入适配。

use sha2::{Digest, Sha256};

const SOURCE_SHA256: &str = "bbb37cc6aebca30188fab912bb2ae7201604d2b001a64ea6ef2c397674f81f5c";
const ADAPTED_SHA256: &str = "7dad281b3b24924d619cb7059a42b409e7690ebeb8ebbc82882e68167656e012";
const OLD_IMPORT: &[u8] = b"\x10__main_argc_argv";
const NEW_IMPORT: &[u8] = b"\x08args_get";

/// 将固定 Dart 重建 WASM 的同签名 WASI 参数导入改为运行时可提供的名称。
/// 参数为经固定源码构建的 WASM;返回适配字节,来源或结果散列不符时拒绝。
/// 外部 scanner 已和 parser 一起编译;此函数不修改 scanner 或语法表。
pub fn adapt_dart_wasm(source: &[u8]) -> Result<Vec<u8>, String> {
if source.len() != 989331 || format!("{:x}", Sha256::digest(source)) != SOURCE_SHA256 {
return Err("Dart grammar 重建来源字节不符".into());
}
let mut offset = 8;
while offset < source.len() {
let section_start = offset;
let section_id = *source.get(offset).ok_or("WASM section 缺失")?;
offset += 1;
let (section_len, payload_start) = read_leb(source, offset)?;
let payload_end = payload_start
.checked_add(section_len)
.filter(|end| *end <= source.len())
.ok_or("WASM section 越界")?;
if section_id == 2 {
let payload = &source[payload_start..payload_end];
let matches = payload
.windows(OLD_IMPORT.len())
.enumerate()
.filter_map(|(index, bytes)| (bytes == OLD_IMPORT).then_some(index))
.collect::<Vec<_>>();
let [position] = matches.as_slice() else {
return Err("Dart grammar 导入布局不符".into());
};
let mut adapted_payload = Vec::with_capacity(payload.len() - 8);
adapted_payload.extend_from_slice(&payload[..*position]);
adapted_payload.extend_from_slice(NEW_IMPORT);
adapted_payload.extend_from_slice(&payload[position + OLD_IMPORT.len()..]);
let mut adapted = Vec::with_capacity(source.len() - 8);
adapted.extend_from_slice(&source[..section_start + 1]);
write_leb(adapted_payload.len(), &mut adapted);
adapted.extend_from_slice(&adapted_payload);
adapted.extend_from_slice(&source[payload_end..]);
if format!("{:x}", Sha256::digest(&adapted)) != ADAPTED_SHA256 {
return Err("Dart grammar 适配结果不符".into());
}
return Ok(adapted);
}
offset = payload_end;
}
Err("Dart grammar 无导入区段".into())
}

fn read_leb(source: &[u8], mut offset: usize) -> Result<(usize, usize), String> {
let mut result = 0usize;
for shift in (0..35).step_by(7) {
let byte = *source.get(offset).ok_or("WASM section 长度缺失")?;
offset += 1;
result |= usize::from(byte & 0x7f) << shift;
if byte & 0x80 == 0 {
return Ok((result, offset));
}
}
Err("WASM section 长度无效".into())
}

fn write_leb(mut value: usize, output: &mut Vec<u8>) {
loop {
let mut byte = (value & 0x7f) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
output.push(byte);
if value == 0 {
return;
}
}
}
37 changes: 34 additions & 3 deletions crates/codeguard-adapters/src/grammar_asset_manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const R_COMMIT: &str = "e9944e9801595ad484f49be492daf0c4c81547ef";
const RUBY_COMMIT: &str = "71bd32fb7607035768799732addba884a37a6210";
const PHP_COMMIT: &str = "5b5627faaa290d89eb3d01b9bf47c3bb9e797dea";
const KOTLIN_COMMIT: &str = "e1a2d5ad1f61f5740677183cd4125bb071cd2f30";
const DART_COMMIT: &str = "d4d8f3e337d8be23be27ffc35a0aef972343cd54";

/// 代码来源、许可和每份候选 grammar 的固定身份。
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
Expand Down Expand Up @@ -201,6 +202,10 @@ fn verify_bundled_asset(asset: &GrammarAsset) -> Result<&'static [u8], String> {
include_bytes!("../../../grammars/kotlin/parser.wasm").as_slice(),
include_bytes!("../../../grammars/kotlin/LICENSE").as_slice(),
),
"dart" => (
include_bytes!("../../../grammars/dart/parser.wasm").as_slice(),
include_bytes!("../../../grammars/dart/LICENSE").as_slice(),
),
"java" => (
include_bytes!("../../../grammars/java/parser.wasm").as_slice(),
include_bytes!("../../../grammars/java/LICENSE").as_slice(),
Expand Down Expand Up @@ -245,6 +250,14 @@ fn verify_bundled_asset(asset: &GrammarAsset) -> Result<&'static [u8], String> {
{
return Err("Zig grammar 来源适配关系不符".into());
}
if asset.language == "dart" {
let source = include_bytes!("../../../grammars/dart/source.wasm");
if asset.source_sha256.as_deref() != Some(digest(source).as_str())
|| crate::adapt_dart_wasm(source)? != wasm
{
return Err("Dart grammar 重建来源适配关系不符".into());
}
}
if matches!(asset.language.as_str(), "objc" | "solidity") {
let source = if asset.language == "objc" {
include_bytes!("../../../grammars/objc/source.wasm").as_slice()
Expand Down Expand Up @@ -274,15 +287,15 @@ pub fn parse_grammar_asset_manifest(raw: &[u8]) -> Result<GrammarAssetManifest,
|| manifest.codegraph_license != "LICENSE.codegraph"
|| manifest.codegraph_license_sha256 != CODEGRAPH_LICENSE_SHA256
|| manifest.build_tool
!= "tree-sitter-cli 0.25.10 build --wasm; Zig: tree-sitter-cli 0.27.0 generate --abi 15 + Zig 0.16.0 wasm32-wasi; ArkTS/Terraform: pinned npm prebuilt WASM"
!= "tree-sitter-cli 0.25.10 build --wasm; Zig: tree-sitter-cli 0.27.0 generate --abi 15 + Zig 0.16.0 wasm32-wasi; Dart: pinned parser.c/scanner.c + Zig 0.16.0 wasm32-wasi; ArkTS/Terraform: pinned npm prebuilt WASM"
|| manifest.dependency_package_integrity.as_deref()
!= Some(
"tree-sitter-wasms@0.1.13 sha512-wT+cR6DwaIz80/vho3AvSF0N4txuNx/5bcRKoXouOfClpxh/qqrF4URNLQXbbt8MaAxeksZcZd1j8gcGjc+QxQ==",
)
|| manifest.dependency_package_license.as_deref() != Some("LICENSE.tree-sitter-wasms")
|| manifest.dependency_package_license_sha256.as_deref()
!= Some("6b0382b16279f26ff69014300541967a356a666eb0b91b422f6862f6b7dad17e")
|| manifest.assets.len() != 22
|| manifest.assets.len() != 23
{
return Err("grammar 清单版本、来源或资产数量不符".into());
}
Expand All @@ -297,6 +310,7 @@ pub fn parse_grammar_asset_manifest(raw: &[u8]) -> Result<GrammarAssetManifest,
"arkts",
"cpp",
"csharp",
"dart",
"go",
"java",
"javascript",
Expand Down Expand Up @@ -362,6 +376,16 @@ pub fn parse_grammar_asset_manifest(raw: &[u8]) -> Result<GrammarAssetManifest,
"csharp/LICENSE",
"778fb7d63b8c1844da315648c02f325c4713a6f4a5d19644fd413421422776d3",
),
"dart" => (
"https://github.com/UserNobody14/tree-sitter-dart",
DART_COMMIT,
"master@d4d8f3e+zig-0.16.0-rebuild",
"dart/parser.wasm",
"7dad281b3b24924d619cb7059a42b409e7690ebeb8ebbc82882e68167656e012",
989323,
"dart/LICENSE",
"d270cb3a4985d75033bd77d875ccebff1d66e32788a3f727891e28d76132dd46",
),
"go" => (
"https://github.com/tree-sitter/tree-sitter-go",
GO_COMMIT,
Expand Down Expand Up @@ -576,6 +600,13 @@ pub fn parse_grammar_asset_manifest(raw: &[u8]) -> Result<GrammarAssetManifest,
"160745e470f234cae903a9ba445d19e758d0b02e1197401fc765976c6254d2b6",
)
}
"dart" => {
asset.source_wasm.as_deref() != Some("dart/source.wasm")
|| asset.source_sha256.as_deref()
!= Some(
"bbb37cc6aebca30188fab912bb2ae7201604d2b001a64ea6ef2c397674f81f5c",
)
}
_ => asset.source_wasm.is_some() || asset.source_sha256.is_some(),
})
|| asset.grammar_repository != repo
Expand All @@ -589,7 +620,7 @@ pub fn parse_grammar_asset_manifest(raw: &[u8]) -> Result<GrammarAssetManifest,
|| asset.abi_version
!= if matches!(
asset.language.as_str(),
"zig" | "c" | "csharp" | "javascript" | "lua" | "rust" | "nix" | "php"
"zig" | "c" | "csharp" | "javascript" | "lua" | "rust" | "nix" | "php" | "dart"
) {
15
} else {
Expand Down
Loading
Loading