dependabot-npm_and_yarn 0.205.1 → 0.208.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7b367f078e9c5ac0a29c0a7d7bc2f541e60c2e39c1813cf04d50d3d5fb60f1a
4
- data.tar.gz: 91063ae048150cb001487cfc4415b2aab20a7887e975e86c3632af9065ac5a65
3
+ metadata.gz: 5990fccf1bcbb74dcdb4344897dd59a8a8c7a733c6d9b591d80df770fa5f7c12
4
+ data.tar.gz: 7f7bf93df6bf5340484968dffd1558d1edd899718986f19d5d8b016fd45cb4f9
5
5
  SHA512:
6
- metadata.gz: 37ef889be3aa66ad6d196543d289d38ee7ec8df99d27ae91cbdd49368d0374120f0364e7ca64a3330ae34e3a3bdf5b3edb60c839a872c962f49c2fd8721ad716
7
- data.tar.gz: f49fcdf6cd6e2b266a02a13b2558dd8c542fd02c8354f72fecea6c5183d6797f5e28f99d208e0f32f57ec269e72d3de085c254f1edba481d6a0145bc11fde68a
6
+ metadata.gz: '09974c6a5f64107c6611e011c7555a964560f5efaa09e22b99340b0c230ff01461f1f977db70620b67ed7362acc0fdbd0a5aeef4a6748d4af345cee89f7eff95'
7
+ data.tar.gz: b810fe67227d6e74132b20d8722265ec434a238b22700f32d351d992c3fd77d43e060575f2e0309e69d651671eb70a962b34b8c905a16ecb3413b982e7646878
@@ -28,7 +28,7 @@
28
28
 
29
29
  const Arborist = require('@npmcli/arborist')
30
30
  const nock = require('nock')
31
- const { inspect, promisify } = require('util');
31
+ const { promisify } = require('util');
32
32
  const exec = promisify(require('child_process').exec)
33
33
 
34
34
  async function findVulnerableDependencies(directory, advisories) {
@@ -59,16 +59,25 @@ async function findVulnerableDependencies(directory, advisories) {
59
59
 
60
60
  try {
61
61
  const name = advisories[0].dependency_name
62
- const auditReport = await arb.audit()
63
- const vuln = auditReport.get(name)
64
- const version = [...vuln.nodes][0].version
65
- const fixAvailable = vuln.fixAvailable
66
62
  const response = {
67
63
  dependency_name: name,
68
- current_version: version,
69
- fix_available: Boolean(fixAvailable),
70
64
  fix_updates: [],
65
+ top_level_ancestors: [],
66
+ }
67
+ const auditReport = await arb.audit()
68
+ if (!auditReport.has(name)) {
69
+ if (auditReport.tree.children.has(name)) {
70
+ response.current_version = auditReport.tree.children.get(name).version
71
+ }
72
+ response.fix_available = false
73
+ return response
71
74
  }
75
+ const vuln = auditReport.get(name)
76
+ const version = [...vuln.nodes][0].version
77
+ const fixAvailable = vuln.fixAvailable
78
+
79
+ response.current_version = version
80
+ response.fix_available = Boolean(fixAvailable)
72
81
 
73
82
  if (!Boolean(fixAvailable)) {
74
83
  return response
@@ -76,24 +85,41 @@ async function findVulnerableDependencies(directory, advisories) {
76
85
 
77
86
  const chains = buildDependencyChains(auditReport, name)
78
87
 
79
- // In order to consider the vuln dependency in question fixable,
88
+ // In order for the vuln dependency in question to be considered fixable,
80
89
  // all dependency chains originating from it must be fixable.
81
90
  if (chains.some((chain) => !Boolean(chain.fixAvailable))) {
82
91
  response.fix_available = false
83
92
  return response
84
93
  }
85
94
 
86
- for (const chain of chains) {
87
- const topMost = chain.items[0]
88
- if (topMost.name === name) {
89
- continue
90
- }
95
+ const groupedFixUpdateChains = groupBy(chains, (chain) => chain.nodes[0].pkgid)
96
+ let topLevelAncestors = new Set()
97
+
98
+ for (const group of groupedFixUpdateChains.values()) {
99
+ const fixUpdateNode = group[0].nodes[0]
100
+ const groupTopLevelAncestors = group.reduce((anc, chain) => {
101
+ const topLevelNode = chain.nodes[chain.nodes.length - 1]
102
+ return anc.add(topLevelNode.name)
103
+ }, new Set())
104
+
105
+ // Add group's top-level ancestors to the set of all top-level ancestors of
106
+ // the vuln dependency in question.
107
+ topLevelAncestors = new Set([...topLevelAncestors, ...groupTopLevelAncestors])
108
+
109
+ // If a chain consists of only one node, chain.nodes[0].name == chain.nodes[chain.nodes.length-1].name.
110
+ // In such cases, don't include the fix update node as an ancestor of itself.
111
+ const fixUpdateNodeTopLevelAncestors =
112
+ [...groupTopLevelAncestors].filter((nodeName) => nodeName !== fixUpdateNode.name).sort()
113
+
91
114
  response.fix_updates.push({
92
- dependency_name: topMost.name,
93
- current_version: topMost.version,
115
+ dependency_name: fixUpdateNode.name,
116
+ current_version: fixUpdateNode.version,
117
+ top_level_ancestors: fixUpdateNodeTopLevelAncestors,
94
118
  })
95
119
  }
96
120
 
121
+ response.top_level_ancestors = [...topLevelAncestors].sort()
122
+
97
123
  const fixTree = await arb.audit({
98
124
  fix: true,
99
125
  })
@@ -135,75 +161,60 @@ function convertAdvisoriesToRegistryBulkFormat(advisories) {
135
161
  }, {})
136
162
  }
137
163
 
138
- /* Traverses all effects originating from the named dependency in the
139
- * audit report and returns an array of dependency chains rooted in the named
140
- * dependency,
164
+ /* Returns an array of dependency chains rooted in the named dependency,
141
165
  * [
142
166
  * {
143
167
  * fixAvailable: true | false | object,
144
- * items: [
145
- * { name: 'foo', version: '1.0.0' },
168
+ * nodes: [
169
+ * ArboristNode {
170
+ * name: 'foo',
171
+ * version: '1.0.0',
172
+ * ...
173
+ * },
146
174
  * ...
147
175
  * ]
148
176
  * },
149
177
  * ...
150
178
  * ]
151
179
  *
152
- * The first item in the chain is always the top-most dependency affected by
153
- * the vulnerable dependency in question. The `fixAvailable` field
154
- * applies to the first item in the chain (if that item is fixable, then
155
- * every item after it must be fixable, too).
180
+ * The first node in each chain is the innermost dependency affected by the vuln;
181
+ * the `fixAvailable` field applies to this dependency. The last node in each
182
+ * chain is always a top-level dependency.
156
183
  */
157
184
  function buildDependencyChains(auditReport, name) {
158
- const helper = (name, chain, visited) => {
159
- // The vuln for this dependency.
160
- const vuln = auditReport.get(name)
161
-
162
- // The current version of this dependency.
163
- const version = [...vuln.nodes][0].version
164
-
165
- // The item that will represent this dependency in this chain.
166
- const item = { name, version }
167
-
168
- // Array of effects, excluding cycles.
169
- const effects = [...vuln.effects]
170
-
171
- if (visited.has(name)) {
172
- // We've already visited this dependency in this chain, so we've detected a cycle.
173
- // We currently throw when this happens. Ultimately we want to gracefully handle
174
- // cycles and still return the recommended fix updates.
175
- const source = chain.items[chain.items.length-1]
176
- const message = `Cycle detected while traversing effects from ` +
177
- `${source.name}@${source.version}: ` +
178
- inspect([name, ...visited], {
179
- breakLength: Infinity,
180
- depth: 1,
181
- maxStringLength: 255,
182
- })
183
- throw new Error(message)
185
+ const helper = (node, chain, visited) => {
186
+ if (!node) {
187
+ return []
184
188
  }
185
-
186
- if (!effects.length) {
187
- // If the current vuln has no effects, we've reached the end of this chain.
188
- return [{ fixAvailable: vuln.fixAvailable, items: [item, ...chain.items] }]
189
+ if (visited.has(node.name)) {
190
+ // We've already seen this node; end path.
191
+ return []
189
192
  }
190
-
191
- return effects.reduce((chains, effect) => {
192
- return chains.concat(
193
- helper(effect.name, { items: [item, ...chain.items] }, new Set([name, ...visited])))
193
+ if (auditReport.has(node.name)) {
194
+ const vuln = auditReport.get(node.name)
195
+ return [{ fixAvailable: vuln.fixAvailable, nodes: [node, ...chain.nodes] }]
196
+ }
197
+ if (!node.edgesOut.size) {
198
+ // This is a leaf node that is unaffected by the vuln; end path.
199
+ return []
200
+ }
201
+ return [...node.edgesOut.values()].reduce((chains, { to }) => {
202
+ // Only prepend current node to chain/visited if it's not the project root.
203
+ const newChain = node.isProjectRoot ? chain : { nodes: [node, ...chain.nodes] }
204
+ const newVisited = node.isProjectRoot ? visited : new Set([node.name, ...visited])
205
+ return chains.concat(helper(to, newChain, newVisited))
194
206
  }, [])
195
207
  }
208
+ return helper(auditReport.tree, { nodes: [] }, new Set())
209
+ }
196
210
 
197
- const chains = helper(name, { items: [] }, new Set())
198
- const seen = new Set()
199
- return chains.filter(chain => {
200
- const head = chain.items[0]
201
- if (seen.has(head.name)) {
202
- return false
203
- }
204
- seen.add(head.name)
205
- return true
206
- })
211
+ function groupBy(elems, fn) {
212
+ const groups = new Map()
213
+ for (const [index, elem] of [...elems].entries()) {
214
+ const key = fn(elem, index, elems)
215
+ groups.set(key, (groups.get(key) || []).concat([elem]))
216
+ }
217
+ return groups
207
218
  }
208
219
 
209
220
  async function loadNpmConfig() {
@@ -6,7 +6,7 @@
6
6
  "": {
7
7
  "name": "@dependabot/helper",
8
8
  "dependencies": {
9
- "@dependabot/yarn-lib": "^1.21.1",
9
+ "@dependabot/yarn-lib": "^1.22.19",
10
10
  "@npmcli/arborist": "^5.3.1",
11
11
  "detect-indent": "^6.1.0",
12
12
  "nock": "^13.2.8",
@@ -580,9 +580,9 @@
580
580
  "dev": true
581
581
  },
582
582
  "node_modules/@dependabot/yarn-lib": {
583
- "version": "1.21.1",
584
- "resolved": "https://registry.npmjs.org/@dependabot/yarn-lib/-/yarn-lib-1.21.1.tgz",
585
- "integrity": "sha512-wroyXO/e0h077IkPxLGJRIdMYzQWGIiLiYrhx2y6pwDfh6I492Saof5G80si21ltU3fHpCpGlB9cenr9b7Ak3Q==",
583
+ "version": "1.22.19",
584
+ "resolved": "https://registry.npmjs.org/@dependabot/yarn-lib/-/yarn-lib-1.22.19.tgz",
585
+ "integrity": "sha512-+ayu/53xZOUpRvQrzlhkHPogTNnmu+UIxd2S+IfQDLfBhyhrLqPB8F3kjIkGZRyNXPed2MyxEljyEVBSljPmxw==",
586
586
  "dependencies": {
587
587
  "@zkochan/cmd-shim": "^3.1.0",
588
588
  "babel-runtime": "^6.26.0",
@@ -14648,9 +14648,9 @@
14648
14648
  "dev": true
14649
14649
  },
14650
14650
  "@dependabot/yarn-lib": {
14651
- "version": "1.21.1",
14652
- "resolved": "https://registry.npmjs.org/@dependabot/yarn-lib/-/yarn-lib-1.21.1.tgz",
14653
- "integrity": "sha512-wroyXO/e0h077IkPxLGJRIdMYzQWGIiLiYrhx2y6pwDfh6I492Saof5G80si21ltU3fHpCpGlB9cenr9b7Ak3Q==",
14651
+ "version": "1.22.19",
14652
+ "resolved": "https://registry.npmjs.org/@dependabot/yarn-lib/-/yarn-lib-1.22.19.tgz",
14653
+ "integrity": "sha512-+ayu/53xZOUpRvQrzlhkHPogTNnmu+UIxd2S+IfQDLfBhyhrLqPB8F3kjIkGZRyNXPed2MyxEljyEVBSljPmxw==",
14654
14654
  "requires": {
14655
14655
  "@zkochan/cmd-shim": "^3.1.0",
14656
14656
  "babel-runtime": "^6.26.0",
data/helpers/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "test": "jest"
10
10
  },
11
11
  "dependencies": {
12
- "@dependabot/yarn-lib": "^1.21.1",
12
+ "@dependabot/yarn-lib": "^1.22.19",
13
13
  "@npmcli/arborist": "^5.3.1",
14
14
  "detect-indent": "^6.1.0",
15
15
  "nock": "^13.2.8",
@@ -36,11 +36,16 @@ module Dependabot
36
36
  # * :dependency_name [String] the name of the blocking dependency
37
37
  # * :current_version [String] the current version of the blocking dependency
38
38
  # * :target_version [String] the target version of the blocking dependency
39
+ # * :top_level_ancestors [Array<String>] the names of top-level dependencies with a transitive
40
+ # dependency on the blocking dependency
41
+ # * :top_level_ancestors [Array<String>] the names of all top-level dependencies with a transitive
42
+ # dependency on the dependency
39
43
  def audit(dependency:, security_advisories:)
40
44
  fix_unavailable = {
41
45
  "dependency_name" => dependency.name,
42
46
  "fix_available" => false,
43
- "fix_updates" => []
47
+ "fix_updates" => [],
48
+ "top_level_ancestors" => []
44
49
  }
45
50
 
46
51
  SharedHelpers.in_a_temporary_directory do
@@ -68,7 +73,7 @@ module Dependabot
68
73
  function: "npm:vulnerabilityAuditor",
69
74
  args: [Dir.pwd, vuln_versions]
70
75
  )
71
- return fix_unavailable unless valid_audit_result?(audit_result, security_advisories)
76
+ return fix_unavailable unless viable_audit_result?(audit_result, security_advisories)
72
77
 
73
78
  audit_result
74
79
  end
@@ -81,15 +86,21 @@ module Dependabot
81
86
 
82
87
  attr_reader :dependency_files, :credentials
83
88
 
84
- def valid_audit_result?(audit_result, security_advisories)
85
- # we only need to check results that indicate a fix is available
86
- return true unless audit_result["fix_available"]
89
+ def viable_audit_result?(audit_result, security_advisories)
90
+ validation_result = validate_audit_result(audit_result, security_advisories)
91
+ return true if validation_result == :viable
87
92
 
88
- return false if vulnerable_dependency_removed?(audit_result)
89
- return false if dependency_still_vulnerable?(audit_result, security_advisories)
90
- return false if downgrades_dependencies?(audit_result)
93
+ Dependabot.logger.info("VulnerabilityAuditor: audit result not viable: #{validation_result}")
94
+ false
95
+ end
96
+
97
+ def validate_audit_result(audit_result, security_advisories)
98
+ return :fix_unavailable unless audit_result["fix_available"]
99
+ return :vulnerable_dependency_removed if vulnerable_dependency_removed?(audit_result)
100
+ return :dependency_still_vulnerable if dependency_still_vulnerable?(audit_result, security_advisories)
101
+ return :downgrades_dependencies if downgrades_dependencies?(audit_result)
91
102
 
92
- true
103
+ :viable
93
104
  end
94
105
 
95
106
  def vulnerable_dependency_removed?(audit_result)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-npm_and_yarn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.205.1
4
+ version: 0.208.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-08 00:00:00.000000000 Z
11
+ date: 2022-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dependabot-common
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.205.1
19
+ version: 0.208.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.205.1
26
+ version: 0.208.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: debase
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -128,14 +128,14 @@ dependencies:
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 1.31.2
131
+ version: 1.33.0
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 1.31.2
138
+ version: 1.33.0
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: ruby-debug-ide
141
141
  requirement: !ruby/object:Gem::Requirement