Skip to content

Commit 5579b69

Browse files
committed
feat: 在项目发现中输出原生工具准备候选
1 parent fefdde1 commit 5579b69

25 files changed

Lines changed: 6487 additions & 31 deletions

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ Supply the original checker's required tool/configuration options to `task verif
153153

154154
| Family | Value | Current boundary |
155155
| :--- | :--- | :--- |
156-
| `--version`, `capabilities`, `detect` | Binary, inventory, project observations | Queries do not perform checks |
156+
| `--version`, `capabilities`, `detect` | Binary, inventory, project observations | Current source `detect` 0.4.0 exposes local ESLint/Maven Wrapper candidates; no tool is run or approved |
157157
| `init --dry-run / --apply` | Preview/create/refresh workbench | No implicit install, build, Hook takeover, or architecture certification |
158158
| `config validate / explain`, `rules list` | Inspect configuration and rule origins | Candidates do not establish policy authority |
159159
| `tools list / verify`, `doctor` | Inspect tools and limited environment probes | Explicit Ruff doctor probe; `tools install --apply` is blocked |

‎README.zh-CN.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ codeguard task verify "$TASK_ID" . --format json
153153

154154
| 命令族 | 价值 | 当前边界 |
155155
| :--- | :--- | :--- |
156-
| `--version`、`capabilities`、`detect` | 查询二进制、清单、项目观察 | 查询不执行检查 |
156+
| `--version`、`capabilities`、`detect` | 查询二进制、清单、项目观察 | 当前源码的 `detect` 0.4.0 输出本地 ESLint/Maven Wrapper 候选,不执行或批准工具 |
157157
| `init --dry-run / --apply` | 预览、创建、刷新工作台 | 不隐式安装、构建、接管 Hook 或认证架构 |
158158
| `config validate / explain`、`rules list` | 查看配置和规则来源 | 候选不构成策略批准 |
159159
| `tools list / verify`、`doctor` | 查看工具和有限环境探测 | 显式 Ruff doctor 探测;`tools install --apply` 受阻 |

‎crates/codeguard-cli/src/check_command.rs‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1452,7 +1452,7 @@ pub fn run(args: &[String]) -> ExitCode {
14521452
})
14531453
.count();
14541454
let report = json!({
1455-
"schema_version":"0.30.0", "report_type":"check_feedback",
1455+
"schema_version":"0.31.0", "report_type":"check_feedback",
14561456
"operation":"check", "selection":parsed.selection.as_str(), "command_status":if request_cancelled { "cancelled" } else { "incomplete" },
14571457
"exit_code":if request_cancelled { 130 } else { 3 }, "delivery_decision":if parsed.selection == Selection::All { "incomplete" } else { "not_evaluated" }, "authority":"local_unverified",
14581458
"reason":if request_cancelled { "request_cancelled" } else if parsed.selection == Selection::All { "full_project_obligations_and_trusted_policy_unavailable" } else { "java_selection_obligations_and_trusted_policy_unavailable" },
@@ -1512,6 +1512,23 @@ pub fn run(args: &[String]) -> ExitCode {
15121512
}
15131513
}
15141514
}
1515+
if let Some(tools) = report["discovery"]["native_tool_candidates"].as_array() {
1516+
for tool in tools {
1517+
if parsed.selection == Selection::Java && tool["checker_id"] != "java.maven" {
1518+
continue;
1519+
}
1520+
println!(
1521+
"原生工具候选 {} [{:?}]:{};版本 {};下一步:{}(未执行)",
1522+
tool["checker_id"].as_str().unwrap_or("未知"),
1523+
tool["build_root"].as_str().unwrap_or("未知"),
1524+
tool["state"].as_str().unwrap_or("unknown"),
1525+
tool["observed_version"].as_str().unwrap_or("未知"),
1526+
tool["next_action"]
1527+
.as_str()
1528+
.unwrap_or("核对原生工具及项目配置")
1529+
);
1530+
}
1531+
}
15151532
if let Some(files) = report["native_results"]["python_lint"]["files"].as_array() {
15161533
for file in files {
15171534
let path = file["path"].as_str().unwrap_or("未知目标");

‎crates/codeguard-cli/src/discovery.rs‎

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! 只读项目发现;候选不等于已验证的检查能力。
22
33
use crate::eslint_discovery::{ESLINT_CONFIG_NAMES, inspect_node_eslint};
4+
use crate::native_tool_candidate::NativeToolCandidate;
5+
use crate::native_tool_discovery::inspect_native_tools;
46
use crate::npm_audit_discovery::inspect_node_npm_audit;
57
use crate::python_dependency_discovery::inspect_python_cve_inputs;
68
use codeguard_adapters::{
@@ -54,6 +56,9 @@ pub struct DiscoveryReport {
5456
pub checker_config_sha256: BTreeMap<String, String>,
5557
pub lockfiles: BTreeSet<String>,
5658
pub checker_configurations: Vec<CheckerConfiguration>,
59+
/// 项目本地原生工具的只读候选;不能视为本轮工具检查成功。
60+
pub native_tool_candidates: Vec<NativeToolCandidate>,
61+
pub(crate) tool_hint_paths: BTreeSet<String>,
5762
ruff_config_files: BTreeSet<String>,
5863
eslint_config_files: BTreeSet<String>,
5964
/// (源码根、语言、用途);仅按目录约定推断。
@@ -65,6 +70,8 @@ pub struct DiscoveryReport {
6570
pub dot_prefix_roots_excluded: usize,
6671
/// 按配置发现例外实际观察到的点前缀普通文件数。
6772
pub configuration_exception_files_observed: usize,
73+
/// 普通源码扫描跳过的依赖目录根数;固定路径工具探测仍可只读观察。
74+
pub dependency_roots_excluded: usize,
6875
}
6976

7077
impl DiscoveryReport {
@@ -90,15 +97,16 @@ impl DiscoveryReport {
9097
})
9198
.collect();
9299
json!({
93-
"schema_version":"0.3.0",
100+
"schema_version":"0.4.0",
94101
"report_type":"discovery",
95102
"root":self.root,
96103
"observation_complete":self.observation_complete,
97104
"observed_entries":self.observed_entries,
98105
"scope_summary":{
99-
"ordinary_scan_policy":"dot_prefix_default_v1",
106+
"ordinary_scan_policy":"project_sources_default_v2",
100107
"dot_prefix_roots_excluded":self.dot_prefix_roots_excluded,
101108
"configuration_exception_files_observed":self.configuration_exception_files_observed,
109+
"dependency_roots_excluded":self.dependency_roots_excluded,
102110
"git_safety_status":"not_evaluated"
103111
},
104112
"languages":languages,
@@ -107,6 +115,7 @@ impl DiscoveryReport {
107115
"manifest_sha256":self.manifest_sha256,
108116
"lockfiles":self.lockfiles,
109117
"checker_configurations":self.checker_configurations,
118+
"native_tool_candidates":self.native_tool_candidates,
110119
"source_set_candidates":source_set_candidates,
111120
"blocked_paths":self.blocked_paths,
112121
"unknown_conditions":self.unknown_conditions,
@@ -147,6 +156,8 @@ pub fn discover<P: ObservationPort>(
147156
checker_config_sha256: BTreeMap::new(),
148157
lockfiles: BTreeSet::new(),
149158
checker_configurations: Vec::new(),
159+
native_tool_candidates: Vec::new(),
160+
tool_hint_paths: BTreeSet::new(),
150161
ruff_config_files: BTreeSet::new(),
151162
eslint_config_files: BTreeSet::new(),
152163
source_set_candidates: BTreeSet::new(),
@@ -155,6 +166,7 @@ pub fn discover<P: ObservationPort>(
155166
observed_entries: 0,
156167
dot_prefix_roots_excluded: 0,
157168
configuration_exception_files_observed: 0,
169+
dependency_roots_excluded: 0,
158170
};
159171
if !matches!(observation.classify(root), Ok(ObservedPathKind::Directory)) {
160172
report.observation_complete = false;
@@ -180,6 +192,24 @@ pub fn discover<P: ObservationPort>(
180192
&& (exact_config_files.contains(name)
181193
|| ESLINT_CONFIG_NAMES.contains(&name)
182194
|| matches!(name, ".ruff.toml" | ".pre-commit-config.yaml"));
195+
if matches!(name, "node_modules" | ".mvn") {
196+
match observation.classify(&child) {
197+
Ok(kind) => {
198+
if let Some(relative) = relative_path(root, &child) {
199+
report.tool_hint_paths.insert(relative);
200+
}
201+
if name == "node_modules" && kind == ObservedPathKind::Directory {
202+
report.dependency_roots_excluded += 1;
203+
} else if kind != ObservedPathKind::Directory {
204+
block(&mut report, root, &child);
205+
}
206+
}
207+
Err(_) => block(&mut report, root, &child),
208+
}
209+
if name == "node_modules" {
210+
continue;
211+
}
212+
}
183213
if name.starts_with('.') && !configuration_exception {
184214
report.dot_prefix_roots_excluded += 1;
185215
continue;
@@ -203,6 +233,9 @@ pub fn discover<P: ObservationPort>(
203233
}
204234
Ok(ObservedPathKind::Directory) => pending.push(child),
205235
Ok(ObservedPathKind::File) => {
236+
if name == "mvnw" {
237+
report.tool_hint_paths.insert(relative.clone());
238+
}
206239
if configuration_exception {
207240
report.configuration_exception_files_observed += 1;
208241
}
@@ -216,6 +249,9 @@ pub fn discover<P: ObservationPort>(
216249
)
217250
}
218251
Ok(ObservedPathKind::Symlink | ObservedPathKind::Other) | Err(_) => {
252+
if name == "mvnw" {
253+
report.tool_hint_paths.insert(relative.clone());
254+
}
219255
block(&mut report, root, &child);
220256
}
221257
}
@@ -258,6 +294,7 @@ pub fn discover<P: ObservationPort>(
258294
inspect_python_cve_inputs(root, observation, &mut report);
259295
let eslint_candidates = report.eslint_config_files.clone();
260296
inspect_node_eslint(root, observation, &mut report, &eslint_candidates);
297+
inspect_native_tools(root, observation, &mut report);
261298
inspect_node_npm_audit(root, observation, &mut report);
262299
if report
263300
.languages

‎crates/codeguard-cli/src/lib.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ mod maven_dependency_probe;
118118
mod maven_javadoc_probe;
119119
#[cfg(unix)]
120120
pub mod maven_probe;
121+
mod native_tool_candidate;
122+
mod native_tool_discovery;
121123
pub mod next_command;
122124
#[cfg(unix)]
123125
mod owasp_maven_probe;

‎crates/codeguard-cli/src/main.rs‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,10 @@ fn detect(args: &[String]) -> ExitCode {
207207
report.observation_complete, report.observed_entries
208208
);
209209
println!(
210-
"普通扫描点前缀排除根:{};配置例外文件:{};入库安全:未评估",
211-
report.dot_prefix_roots_excluded, report.configuration_exception_files_observed
210+
"普通扫描点前缀排除根:{};依赖目录排除根:{};配置例外文件:{};入库安全:未评估",
211+
report.dot_prefix_roots_excluded,
212+
report.dependency_roots_excluded,
213+
report.configuration_exception_files_observed
212214
);
213215
for (language, evidence) in &report.languages {
214216
println!(
@@ -233,6 +235,16 @@ fn detect(args: &[String]) -> ExitCode {
233235
checker.next_action
234236
);
235237
}
238+
for tool in &report.native_tool_candidates {
239+
println!(
240+
"原生工具候选 {} [{:?}]:{};版本:{};下一步:{}(未执行)",
241+
tool.checker_id,
242+
tool.build_root,
243+
tool.state,
244+
tool.observed_version.as_deref().unwrap_or("未知"),
245+
tool.next_action
246+
);
247+
}
236248
}
237249
if report.observation_complete {
238250
ExitCode::SUCCESS
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use serde::Serialize;
2+
3+
/// 只读发现的项目原生工具候选,不证明可启动、受批准或检查通过。
4+
#[derive(Debug, Serialize)]
5+
pub struct NativeToolCandidate {
6+
/// 候选所属的项目构建根,相对工作区。
7+
pub build_root: String,
8+
/// 对应检查器的稳定身份。
9+
pub checker_id: String,
10+
/// 候选或阻塞的精确分类。
11+
pub state: String,
12+
/// 从本地包或 Wrapper 配置观察的版本。
13+
pub observed_version: Option<String>,
14+
/// 项目依赖声明是否可见;不输出可能包含秘密的原始声明。
15+
pub declaration_observed: bool,
16+
/// 下一步原生核验或环境修复动作。
17+
pub next_action: String,
18+
}

0 commit comments

Comments
 (0)