Skip to content

Commit e86bfc1

Browse files
committed
补齐Gradle漏洞任务原上下文复检与防误关闭验收
1 parent 972d67d commit e86bfc1

28 files changed

Lines changed: 2477 additions & 110 deletions

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1089,4 +1089,4 @@ Real native tests cover all seven rules, repeated identity, presence, noqa suppr
10891089

10901090
Read-only four-core acceptance plan: `codeguard capabilities [language] --acceptance-plan --format=json`. All 57 languages / 228 obligations remain unqualified; filtering preserves full obligations. See [acceptance plan](docs/Codeguard-Production-Acceptance-Plan.md).
10911091

1092-
Java Gradle vulnerability checks now expose explicit original tasks through `codeguard cve java`, preserving JSON configuration and optional module-cache inputs. Observations remain unverified; initialized workspaces now sync sanitized stable preparation tasks; next/task show retain original input/task flags, while task verify, unified check and full native acceptance remain pending. See [Gradle OWASP](docs/Codeguard-Gradle-Vulnerability-Checks.md).
1092+
Java Gradle vulnerability checks now expose explicit original tasks through `codeguard cve java`, preserving JSON configuration and optional module-cache inputs. Observations remain unverified; initialized workspaces now sync sanitized stable preparation tasks; next/task show retain original input/task flags, while task verify now records frozen-context local observations; unified check and full native acceptance remain pending. See [Gradle OWASP](docs/Codeguard-Gradle-Vulnerability-Checks.md).

‎README.zh-CN.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1066,4 +1066,4 @@ flowchart LR
10661066

10671067
四核心验收计划只读入口:`codeguard capabilities [language] --acceptance-plan --format=json`. 57 语言/228 义务保留未授予资格,筛选不缩减总义务。参见[acceptance plan](docs/Codeguard-Production-Acceptance-Plan.zh_CN.md).
10681068

1069-
Java Gradle 漏洞检查新增显式原任务入口 `codeguard cve java`;JSON 原配置与可选模块缓存保留,结果仍未受信;已初始化工作区自动同步脱敏稳定准备任务,next/task show列出原输入/任务复扫参数;task verify、统一check及完整原生验收待完成。详见[Gradle OWASP](docs/Codeguard-Gradle-Vulnerability-Checks.zh_CN.md).
1069+
Java Gradle 漏洞检查新增显式原任务入口 `codeguard cve java`;JSON 原配置与可选模块缓存保留,结果仍未受信;已初始化工作区自动同步脱敏稳定准备任务,next/task show列出原输入/任务复扫参数;task verify已接入冻结上下文局部复检;统一check及完整原生验收待完成。详见[Gradle OWASP](docs/Codeguard-Gradle-Vulnerability-Checks.zh_CN.md).

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

Lines changed: 477 additions & 0 deletions
Large diffs are not rendered by default.

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

Lines changed: 29 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,22 @@ use std::{
1010
};
1111
static NEXT: AtomicU64 = AtomicU64::new(0);
1212

13-
/// 保存已绑定当前输入的脱敏观察并自动同步;未初始化返回false,错误不冒充同步成功。
14-
/// 参数为项目根、原选定输入及任务、可选缓存选择和本轮原生报告;返回是否完成同步。
15-
pub fn persist(
13+
/// 构造已绑定当前输入的脱敏观察;未初始化返回None,不在此函数写工作区。
14+
/// 参数为项目根、原选定输入及任务、可选缓存选择和本轮原生报告;返回待同步观察。
15+
pub(crate) fn prepare(
1616
root: &Path,
1717
paths: &BTreeSet<PathBuf>,
1818
tasks: &[String],
1919
cache: bool,
2020
native: &Value,
21-
) -> Result<bool, &'static str> {
21+
) -> Result<Option<Value>, &'static str> {
2222
if codeguard_runtime::sigint_cancellation_requested() {
2323
return Err("request_cancelled");
2424
}
2525
let baseline = crate::workspace_refresh::read_workspace_baseline(root)
2626
.map_err(|_| "gradle_cve_workspace_invalid")?;
2727
let Some(workspace) = baseline.and_then(|b| b.workspace_id().map(str::to_owned)) else {
28-
return Ok(false);
28+
return Ok(None);
2929
};
3030
let snapshot = SourceSnapshot::capture(
3131
root,
@@ -70,12 +70,7 @@ pub fn persist(
7070
if codeguard_runtime::sigint_cancellation_requested() {
7171
return Err("request_cancelled");
7272
}
73-
crate::work_sync::save_local_report(root, &report)?;
74-
let summary = crate::work_sync::sync_local_workspace(root)?;
75-
if summary.failed_reports != 0 {
76-
return Err("gradle_cve_sync_incomplete");
77-
}
78-
Ok(true)
73+
Ok(Some(report))
7974
}
8075

8176
/// 检查脱敏观察的封闭协议及当前输入;历史查询可保留原始输入,导入必须核对当前字节。
@@ -294,61 +289,6 @@ pub(crate) fn fingerprint(v: &Value) -> Result<String, &'static str> {
294289
.map_err(|_| "gradle_cve_encoding_invalid")?,
295290
))
296291
}
297-
/// 从原报告摘要核对后构造具体原任务复检参数;占位工具需要重新核验,不能自动运行。
298-
pub(crate) fn recheck_argv(root: &Path, fact: &Value) -> Result<Value, &'static str> {
299-
let run = fact["first_run_id"]
300-
.as_str()
301-
.filter(|s| {
302-
s.starts_with("cve-gradle-")
303-
&& s.len() <= 256
304-
&& s.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'-')
305-
})
306-
.ok_or("gradle_cve_run_invalid")?;
307-
let bytes = codeguard_runtime::read_bounded_regular_file(
308-
&root.join(format!(".codeguard/reports/{run}.json")),
309-
128 * 1024,
310-
)
311-
.map_err(|_| "gradle_cve_origin_unavailable")?;
312-
if fact["first_report_sha256"] != hash(&bytes) {
313-
return Err("gradle_cve_origin_changed");
314-
}
315-
let report =
316-
codeguard_adapters::parse_unique_json(&bytes).map_err(|_| "gradle_cve_origin_invalid")?;
317-
validate(root, &report, false)?;
318-
if report["workspace_id"] != fact["workspace_id"]
319-
|| report["run_id"] != run
320-
|| fingerprint(&report)? != fact["fingerprint"]
321-
{
322-
return Err("gradle_cve_origin_invalid");
323-
}
324-
let mut args = vec![
325-
json!("codeguard"),
326-
json!("cve"),
327-
json!("java"),
328-
json!("."),
329-
json!("--gradle-bundle"),
330-
json!("<原Gradle绝对路径>"),
331-
json!("--java-home"),
332-
json!("<原JDK21绝对路径>"),
333-
];
334-
for row in report["inputs"]
335-
.as_array()
336-
.ok_or("gradle_cve_inputs_invalid")?
337-
{
338-
args.extend([json!("--gradle-project-file"), row["path"].clone()]);
339-
}
340-
for t in report["task_paths"]
341-
.as_array()
342-
.ok_or("gradle_cve_tasks_invalid")?
343-
{
344-
args.extend([json!("--gradle-owasp-task"), t.clone()]);
345-
}
346-
if report["module_cache_selected"] == true {
347-
args.extend([json!("--gradle-module-cache"), json!("<原缓存绝对路径>")]);
348-
}
349-
args.push(json!("--format=json"));
350-
Ok(json!(args))
351-
}
352292
fn exact(v: &Value, keys: &[&str]) -> bool {
353293
v.as_object()
354294
.is_some_and(|o| o.len() == keys.len() && keys.iter().all(|k| o.contains_key(*k)))
@@ -381,3 +321,26 @@ fn safe_task(s: &str) -> bool {
381321
fn hash(bytes: &[u8]) -> String {
382322
format!("{:x}", Sha256::digest(bytes))
383323
}
324+
325+
/// 保存脱敏观察并自动同步已初始化工作区;参数和返回含义同prepare,未初始化返回false。
326+
pub fn persist(
327+
root: &Path,
328+
paths: &BTreeSet<PathBuf>,
329+
tasks: &[String],
330+
cache: bool,
331+
native: &Value,
332+
) -> Result<bool, &'static str> {
333+
let Some(report) = prepare(root, paths, tasks, cache, native)? else {
334+
return Ok(false);
335+
};
336+
crate::work_sync::save_local_report(root, &report)?;
337+
if crate::work_sync::sync_local_workspace(root)?.failed_reports != 0 {
338+
return Err("gradle_cve_sync_incomplete");
339+
}
340+
Ok(true)
341+
}
342+
343+
/// 选定输入只接受根内普通相对路径;复检和首次导入共享同一边界。
344+
pub(crate) fn safe_input_path(s: &str) -> bool {
345+
safe_path(s)
346+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub fn run(args: &[String]) -> ExitCode {
8484
let next_action = if native["reason"] == "gradle_owasp_report_budget_exceeded" {
8585
"累计报告或反馈超出预算;保留完整任务清单,将原任务分批执行并汇总所有分批结果。不得删除检查义务、降低规则或把未读取报告当作无漏洞;超大单任务需要独立的大报告处理方案,当前交付仍未评估"
8686
} else if native["native_status"] == "reports_observed_unverified" {
87-
"按原任务报告调查活动与原生抑制的漏洞,核对真实依赖归属和漏洞库时效;使用相同工具、输入、原任务复检。空报告不能证明无漏洞,已初始化工作区可用next/task show查询准备任务;task verify与可信关闭仍未接线"
87+
"按原任务报告调查活动与原生抑制的漏洞,核对真实依赖归属和漏洞库时效;使用相同工具、输入、原任务复检。空报告不能证明无漏洞,已初始化工作区可用next/task show查询准备任务;task verify可记录冻结原上下文局部复检,可信关闭仍未验收"
8888
} else {
8989
"读取原生reason恢复已有Gradle/JDK、原OWASP任务、原JSON配置和选定输入;缺插件或漏洞库属于环境阻塞,不修改无关源码。恢复后使用相同原任务复检"
9090
};

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,3 +529,6 @@ mod gradle_owasp_report_budget;
529529

530530
#[cfg(unix)]
531531
pub mod gradle_cve_workbench;
532+
533+
#[cfg(unix)]
534+
pub mod gradle_cve_task_recheck;

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

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,28 @@ fn candidate(root: &Path, id: &str, fact: &Value) -> Result<Candidate, &'static
589589
} else if checker_id == "java.gradle.dependency_check" {
590590
#[cfg(unix)]
591591
{
592-
crate::gradle_cve_workbench::recheck_argv(root, fact)?
592+
{
593+
let original = crate::gradle_cve_task_recheck::original(
594+
root,
595+
&json!({"checker_id":checker_id,"kind":kind,"scope":fact["scope"],"task_id":id,"evidence_ref":{"first_run_id":first_run,"first_report_sha256":report_sha}}),
596+
)?;
597+
let mut args = vec![
598+
json!("codeguard"),
599+
json!("task"),
600+
json!("verify"),
601+
json!(id),
602+
json!("."),
603+
json!("--gradle-bundle"),
604+
json!("<原Gradle绝对路径>"),
605+
json!("--java-home"),
606+
json!("<原JDK21绝对路径>"),
607+
];
608+
if original["module_cache_selected"] == true {
609+
args.extend([json!("--gradle-module-cache"), json!("<原缓存绝对路径>")]);
610+
}
611+
args.push(json!("--format=json"));
612+
json!(args)
613+
}
593614
}
594615
#[cfg(not(unix))]
595616
{
@@ -803,7 +824,7 @@ fn candidate(root: &Path, id: &str, fact: &Value) -> Result<Candidate, &'static
803824
(
804825
5,
805826
"actionable",
806-
"按原任务/选定输入恢复Gradle OWASP;超限保留全部任务分批执行,核验漏洞库和依赖归属;未确认advisory不作为源码漏洞,task verify尚未接线,原命令复扫不可信关闭",
827+
"按原task verify冻结范围恢复Gradle OWASP;配置/工具变化先审查并重扫,超限保留全部义务;核验漏洞库和依赖归属,局部观察及空报告无可信关闭权威",
807828
)
808829
} else if checker_id == "java.maven.dependency_check" {
809830
(
@@ -958,10 +979,15 @@ fn candidate(root: &Path, id: &str, fact: &Value) -> Result<Candidate, &'static
958979
)
959980
};
960981
if checker_id == "java.gradle.dependency_check" {
961-
brief["schema_version"] = json!("0.26.0");
962-
brief["task_verify_status"] = json!("not_integrated");
982+
brief["schema_version"] = json!("0.27.0");
983+
brief["task_verify_status"] = json!("local_observation_only");
984+
#[cfg(unix)]
985+
{
986+
let original = crate::gradle_cve_task_recheck::original(root, &brief)?;
987+
brief["original_context"] = json!({"inputs":original["inputs"],"task_paths":original["task_paths"],"module_cache_selected":original["module_cache_selected"]});
988+
}
963989
brief["closure_condition"] = json!(
964-
"原工具完整复检、漏洞库与依赖归属及可信策略均核验;task verify尚未接线,当前观察无关闭权威"
990+
"原工具完整复检、漏洞库与依赖归属及可信策略均核验;当前task verify只记录原上下文局部观察,无关闭权威"
965991
);
966992
}
967993
if checker_id == "java.gradle.javadoc" {
@@ -1173,6 +1199,9 @@ fn candidate(root: &Path, id: &str, fact: &Value) -> Result<Candidate, &'static
11731199
};
11741200
if let Some(observation) = verification_observation.as_ref() {
11751201
let outcome = observation.outcome.as_str();
1202+
if checker_id == "java.gradle.dependency_check" {
1203+
brief["verification_observation"] = json!(outcome);
1204+
}
11761205
brief["verification_run_id"] = json!(observation.run_id);
11771206
if let Some(reason) = observation.checkstyle_reason.as_ref() {
11781207
brief["verification_reason"] = json!(reason);
@@ -1956,6 +1985,15 @@ fn latest_verification_observation(
19561985
latest_run = sequence;
19571986
continue;
19581987
}
1988+
if brief["checker_id"] == "java.gradle.dependency_check"
1989+
&& event["report_sha256"] == format!("{:x}", Sha256::digest(&report_bytes))
1990+
&& report["task_input_stable"] == true
1991+
&& !crate::gradle_cve_task_recheck::inputs_current(root, &report)
1992+
{
1993+
latest_verify = None;
1994+
latest_run = sequence;
1995+
continue;
1996+
}
19591997
let python_scoped_report = brief["checker_id"] == "python.ruff"
19601998
&& matches!(report["schema_version"].as_str(), Some("0.18.0" | "0.19.0"));
19611999
let go_report = brief["checker_id"] == "go.vet";
@@ -1964,7 +2002,11 @@ fn latest_verification_observation(
19642002
let checkstyle_report = brief["checker_id"] == "java.checkstyle";
19652003
let preparation_report = brief["checker_id"] == "java.checkstyle.preparation";
19662004
let cve_report = brief["checker_id"] == "java.maven.dependency_check";
1967-
let report_shape_valid = if brief["checker_id"] == "python.ruff.doctor" {
2005+
let report_shape_valid = if brief["checker_id"] == "java.gradle.dependency_check" {
2006+
crate::gradle_cve_task_recheck::validate_binding(root, brief, &report).is_ok()
2007+
&& event["observation"]
2008+
== crate::gradle_cve_task_recheck::classify(brief, &report)
2009+
} else if brief["checker_id"] == "python.ruff.doctor" {
19682010
crate::work_sync::valid_doctor_report(&report)
19692011
&& event["observation"] == classify_doctor(brief, &report)
19702012
} else if matches!(
@@ -2218,6 +2260,7 @@ fn latest_verification_observation(
22182260
checkstyle_reason: if checkstyle_report
22192261
|| preparation_report
22202262
|| brief["checker_id"] == "java.gradle.javadoc"
2263+
|| brief["checker_id"] == "java.gradle.dependency_check"
22212264
{
22222265
report["reason"].as_str().map(str::to_owned)
22232266
} else {
@@ -2273,6 +2316,12 @@ fn latest_verification_observation(
22732316
}
22742317

22752318
fn run_sequence(run_id: &str) -> Option<u128> {
2319+
if run_id.starts_with("cve-gradle-task-") {
2320+
return run_id.rsplit('-').next()?.parse().ok();
2321+
}
2322+
if run_id.starts_with("cve-gradle-") {
2323+
return run_id.rsplit('-').nth(1)?.parse().ok();
2324+
}
22762325
if let Some(value) = run_id.strip_prefix("shellcheck-") {
22772326
return value.split('-').next()?.parse().ok();
22782327
}
@@ -2312,7 +2361,7 @@ fn run_sequence(run_id: &str) -> Option<u128> {
23122361

23132362
fn view(disposition: &str, reason: &str, brief: Value, actions: Value) -> Value {
23142363
json!({
2315-
"schema_version":if brief["schema_version"] == "0.26.0" {json!("0.26.0")}else if brief["schema_version"] == "0.25.0" {json!("0.25.0")}else if brief["schema_version"] == "0.24.0" {json!("0.24.0")}else if brief["schema_version"] == "0.23.0" {json!("0.23.0")}else if brief["schema_version"] == "0.22.0" {json!("0.22.0")}else if brief["schema_version"] == "0.21.0" {json!("0.21.0")}else if brief["schema_version"] == "0.20.0" {json!("0.20.0")}else if brief["checker_id"] == "shell.shellcheck" {json!("0.17.0")} else if brief["checker_id"] == "go.vet" {json!("0.13.0")} else if brief["checker_id"] == "syntax.native_confirmation" {brief["schema_version"].clone()} else {json!("0.1.0")}, "report_type":"repair_brief_preview",
2364+
"schema_version":if brief["schema_version"] == "0.27.0" {json!("0.27.0")}else if brief["schema_version"] == "0.26.0" {json!("0.26.0")}else if brief["schema_version"] == "0.25.0" {json!("0.25.0")}else if brief["schema_version"] == "0.24.0" {json!("0.24.0")}else if brief["schema_version"] == "0.23.0" {json!("0.23.0")}else if brief["schema_version"] == "0.22.0" {json!("0.22.0")}else if brief["schema_version"] == "0.21.0" {json!("0.21.0")}else if brief["schema_version"] == "0.20.0" {json!("0.20.0")}else if brief["checker_id"] == "shell.shellcheck" {json!("0.17.0")} else if brief["checker_id"] == "go.vet" {json!("0.13.0")} else if brief["checker_id"] == "syntax.native_confirmation" {brief["schema_version"].clone()} else {json!("0.1.0")}, "report_type":"repair_brief_preview",
23162365
"operation":"next", "command_status":"complete", "exit_code":0,
23172366
"disposition":disposition, "reason":reason,
23182367
"repair_brief":brief, "next_actions":actions,

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ fn verified_rechecks(
438438
.nth(1)
439439
.and_then(|s| s.parse::<u128>().ok())
440440
} else if run_id.starts_with("syntax-native-")
441+
|| run_id.starts_with("cve-gradle-task-")
441442
|| run_id.starts_with("checkstyle-")
442443
|| run_id.starts_with("javadoc-")
443444
|| run_id.starts_with("eslint-")
@@ -542,6 +543,13 @@ fn verified_rechecks(
542543
{
543544
continue;
544545
}
546+
if brief["checker_id"] == "java.gradle.dependency_check"
547+
&& report["task_input_stable"] == true
548+
&& event["report_sha256"] == digest(&report_bytes)
549+
&& !crate::gradle_cve_task_recheck::inputs_current(root, &report)
550+
{
551+
continue;
552+
}
545553
if brief["checker_id"] == "java.gradle.javadoc"
546554
&& report["task_input_stable"] == true
547555
&& event["report_sha256"] == digest(&report_bytes)
@@ -563,7 +571,10 @@ fn verified_rechecks(
563571
{
564572
continue;
565573
}
566-
let report_matches = if brief["checker_id"] == "syntax.native_confirmation" {
574+
let report_matches = if brief["checker_id"] == "java.gradle.dependency_check" {
575+
crate::gradle_cve_task_recheck::validate_binding(root, brief, &report).is_ok()
576+
&& event["observation"] == crate::gradle_cve_task_recheck::classify(brief, &report)
577+
} else if brief["checker_id"] == "syntax.native_confirmation" {
567578
crate::syntax_task_recheck::valid_shape(root, &report)
568579
&& event["observation"] == crate::syntax_task_recheck::classify(&report)
569580
} else if brief["checker_id"] == "python.ruff.doctor" {

0 commit comments

Comments
 (0)