mirror of
https://github.com/denoland/deno.git
synced 2025-12-23 08:48:24 +00:00
fix(audit): fix deserde for transitive npm audit actions (#31671)
Fixes https://github.com/denoland/deno/issues/31613
This commit is contained in:
parent
dd47e25a30
commit
d9a8d18a0b
10 changed files with 124 additions and 23 deletions
|
|
@ -450,8 +450,8 @@ mod npm {
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct AuditActionResolve {
|
pub struct AuditActionResolve {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
|
pub path: Option<String>,
|
||||||
// TODO(bartlomieju): currently not used, commented out so it's not flagged by clippy
|
// TODO(bartlomieju): currently not used, commented out so it's not flagged by clippy
|
||||||
// pub path: String,
|
|
||||||
// pub dev: bool,
|
// pub dev: bool,
|
||||||
// pub optional: bool,
|
// pub optional: bool,
|
||||||
// pub bundled: bool,
|
// pub bundled: bool,
|
||||||
|
|
@ -463,7 +463,7 @@ mod npm {
|
||||||
pub is_major: bool,
|
pub is_major: bool,
|
||||||
pub action: String,
|
pub action: String,
|
||||||
pub resolves: Vec<AuditActionResolve>,
|
pub resolves: Vec<AuditActionResolve>,
|
||||||
pub module: String,
|
pub module: Option<String>,
|
||||||
pub target: Option<String>,
|
pub target: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -491,30 +491,41 @@ mod npm {
|
||||||
|
|
||||||
impl AuditAdvisory {
|
impl AuditAdvisory {
|
||||||
fn find_actions(&self, actions: &[AuditAction]) -> Vec<String> {
|
fn find_actions(&self, actions: &[AuditAction]) -> Vec<String> {
|
||||||
let mut acts = vec![];
|
let mut acts = Vec::new();
|
||||||
|
|
||||||
for action in actions {
|
for action in actions {
|
||||||
if action
|
if !action.resolves.iter().any(|r| r.id == self.id) {
|
||||||
.resolves
|
continue;
|
||||||
.iter()
|
|
||||||
.any(|action_resolve| action_resolve.id == self.id)
|
|
||||||
{
|
|
||||||
acts.push(format!(
|
|
||||||
"{} {}{}{}",
|
|
||||||
action.action,
|
|
||||||
action.module,
|
|
||||||
if let Some(target) = &action.target {
|
|
||||||
format!("@{}", target)
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
},
|
|
||||||
if action.is_major {
|
|
||||||
" (major upgrade)"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let module = action
|
||||||
|
.module
|
||||||
|
.as_deref()
|
||||||
|
.map(str::to_owned)
|
||||||
|
.or_else(|| {
|
||||||
|
// Fallback to infer from dependency path
|
||||||
|
action.resolves.first().and_then(|r| {
|
||||||
|
r.path
|
||||||
|
.as_deref()
|
||||||
|
.and_then(|p| p.split('>').next_back())
|
||||||
|
.map(|s| s.trim().to_string())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| "<unknown>".to_string());
|
||||||
|
|
||||||
|
let target = action
|
||||||
|
.target
|
||||||
|
.as_deref()
|
||||||
|
.map(|t| format!("@{}", t))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let major = if action.is_major {
|
||||||
|
" (major upgrade)"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
acts.push(format!("{} {}{}{}", action.action, module, target, major));
|
||||||
}
|
}
|
||||||
|
|
||||||
acts
|
acts
|
||||||
|
|
|
||||||
3
tests/registry/npm/@denotest/with-vuln3/1.0.0/index.js
Normal file
3
tests/registry/npm/@denotest/with-vuln3/1.0.0/index.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function sayHello() {
|
||||||
|
return '@denotest/with-vuln3 says hello!';
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/with-vuln3",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
3
tests/registry/npm/@denotest/with-vuln3/1.1.0/index.js
Normal file
3
tests/registry/npm/@denotest/with-vuln3/1.1.0/index.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function sayHello() {
|
||||||
|
return '@denotest/with-vuln3 says hello!';
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/with-vuln3",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
14
tests/specs/audit/module_fallback/__test__.jsonc
Normal file
14
tests/specs/audit/module_fallback/__test__.jsonc
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"args": "install",
|
||||||
|
"output": "install.out"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": "audit",
|
||||||
|
"output": "audit.out",
|
||||||
|
"exitCode": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
11
tests/specs/audit/module_fallback/audit.out
Normal file
11
tests/specs/audit/module_fallback/audit.out
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
╭ @denotest/with-vuln3 has security vulnerability
|
||||||
|
│ Severity: high
|
||||||
|
│ Package: @edenotest/with-vuln3
|
||||||
|
│ Vulnerable: <1.1.0
|
||||||
|
│ Patched: >=1.1.0
|
||||||
|
│ Path: @denotest/with-vuln3
|
||||||
|
│ Info: https://example.com/vuln/303030
|
||||||
|
╰ Actions: install @denotest/with-vuln3@1.1.0
|
||||||
|
|
||||||
|
Found 1 vulnerabilities
|
||||||
|
Severity: 0 low, 0 moderate, 1 high, 0 critical
|
||||||
5
tests/specs/audit/module_fallback/deno.json
Normal file
5
tests/specs/audit/module_fallback/deno.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"@denotest/with-vuln3": "npm:@denotest/with-vuln3@1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
tests/specs/audit/module_fallback/install.out
Normal file
6
tests/specs/audit/module_fallback/install.out
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
Download http://localhost:4260/@denotest%2fwith-vuln3
|
||||||
|
Download http://localhost:4260/@denotest/with-vuln3/1.0.0.tgz
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
+ npm:@denotest/with-vuln3 1.0.0
|
||||||
|
|
||||||
|
|
@ -474,6 +474,11 @@ fn process_npm_security_audits_body(
|
||||||
advisories.insert(202020, get_advisory_for_with_vuln2());
|
advisories.insert(202020, get_advisory_for_with_vuln2());
|
||||||
vuln_critical += 1;
|
vuln_critical += 1;
|
||||||
}
|
}
|
||||||
|
if requires_map_keys.contains(&"@denotest/with-vuln3".to_string()) {
|
||||||
|
actions.push(get_action_for_with_vuln3());
|
||||||
|
advisories.insert(303030, get_advisory_for_with_vuln3());
|
||||||
|
vuln_high += 1;
|
||||||
|
}
|
||||||
|
|
||||||
Some(json!({
|
Some(json!({
|
||||||
"actions": actions,
|
"actions": actions,
|
||||||
|
|
@ -573,3 +578,36 @@ fn get_advisory_for_with_vuln2() -> serde_json::Value {
|
||||||
"url": "https://example.com/vuln/202020"
|
"url": "https://example.com/vuln/202020"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_action_for_with_vuln3() -> serde_json::Value {
|
||||||
|
json!({
|
||||||
|
"isMajor": false,
|
||||||
|
"action": "install",
|
||||||
|
"resolves": [{
|
||||||
|
"id": 303030,
|
||||||
|
"path": "@denotest/with-vuln3",
|
||||||
|
"dev": false,
|
||||||
|
"optional": false,
|
||||||
|
"bundled": false,
|
||||||
|
}],
|
||||||
|
// Note: "module" field is intentionally omitted to test fallback logic
|
||||||
|
"target": "1.1.0"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_advisory_for_with_vuln3() -> serde_json::Value {
|
||||||
|
json!({
|
||||||
|
"findings": [
|
||||||
|
{"version": "1.0.0", "paths": ["@denotest/with-vuln3"]}
|
||||||
|
],
|
||||||
|
"id": 303030,
|
||||||
|
"overview": "Lorem ipsum dolor sit amet",
|
||||||
|
"title": "@denotest/with-vuln3 has security vulnerability",
|
||||||
|
"severity": "high",
|
||||||
|
"module_name": "@edenotest/with-vuln3",
|
||||||
|
"vulnerable_versions": "<1.1.0",
|
||||||
|
"recommendations": "Upgrade to version 1.1.0 or later",
|
||||||
|
"patched_versions": ">=1.1.0",
|
||||||
|
"url": "https://example.com/vuln/303030"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue