@eurekadevsecops/radar 1.8.2 → 1.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,78 @@
1
+ {"name":"Alvin Kam","email":"64457658+alvinkam33@users.noreply.github.com"}
2
+ {"name":"Alvin","email":"alvin.kam.33@gmail.com"}
3
+ {"name":"alvinkam","email":"alvin.kam.33@gmail.com"}
4
+ {"name":"Andrew Cheah","email":"62257557+atcheah@users.noreply.github.com"}
5
+ {"name":"Artem Kh","email":"artem@fintelconnect.com"}
6
+ {"name":"Artem","email":"artem@FC-LAPTOP69.local"}
7
+ {"name":"Artem","email":"artem@FC-LAPTOP69.localdomain"}
8
+ {"name":"Ben Lu","email":"blu@nautilusdigital.com"}
9
+ {"name":"carvalhojg","email":"48341861+carvalhojg@users.noreply.github.com"}
10
+ {"name":"Cheryl Ho","email":"91933101+cho196@users.noreply.github.com"}
11
+ {"name":"cho196","email":"ho.cheryl@outlook.com"}
12
+ {"name":"Cristian Najera","email":"45493332+CristianNajeraL@users.noreply.github.com"}
13
+ {"name":"cristianenajera@gmail.com","email":"*O+0clqH5dR@]mC]{1w!|6:P]&/.XFo}Mp0HtAlAQ&~Q:@{a(^iVn'H;jggA)!y!Gcff5\lc"}
14
+ {"name":"Darren Luck","email":"dluck@nautilusdigital.com"}
15
+ {"name":"Darren542","email":"Darrenluck@icloud.com"}
16
+ {"name":"David Moseley","email":"david@fintelconnect.com"}
17
+ {"name":"David Moseley","email":"davidmoseley@gmail.com"}
18
+ {"name":"Divyansh Tangri","email":"divyansh.tangri@fintelconnect.com"}
19
+ {"name":"Divyansh","email":"divyansh.tangri@fintelconnect.com"}
20
+ {"name":"EC2 Default User","email":"ec2-user@ip-10-1-1-185.us-east-2.compute.internal"}
21
+ {"name":"Eddie Moon","email":"97134997+eddiemoon99@users.noreply.github.com"}
22
+ {"name":"George Fairbairn","email":"57505013+georgefairbairn@users.noreply.github.com"}
23
+ {"name":"giovangonzalez","email":"giovangonzalez@gmail.com"}
24
+ {"name":"GitHub","email":"noreply@github.com"}
25
+ {"name":"Hashim Syed","email":"112725957+HashimSyedUBC@users.noreply.github.com"}
26
+ {"name":"Hashim Syed","email":"hsyed@nautilusdigital.com"}
27
+ {"name":"HashimSyedUBC","email":"112725957+HashimSyedUBC@users.noreply.github.com"}
28
+ {"name":"ihu-cc","email":"132296630+ihu-cc@users.noreply.github.com"}
29
+ {"name":"ihu-cc","email":"ihu@nautilusdigital.com"}
30
+ {"name":"Ivan Hu","email":"132296630+ihu-cc@users.noreply.github.com"}
31
+ {"name":"James Dahl","email":"33878022+jamesgdahl@users.noreply.github.com"}
32
+ {"name":"jgabrielsantos","email":"48341861+carvalhojg@users.noreply.github.com"}
33
+ {"name":"Joao Gabriel Carvalho","email":"joaoocarvalhol.santos@gmail.com"}
34
+ {"name":"João Gabriel Santos","email":"48341861+jgabrielsantos@users.noreply.github.com"}
35
+ {"name":"Johnathan Tam","email":"97713947+tamjohn@users.noreply.github.com"}
36
+ {"name":"Kelsey Chua","email":"77289991+kelc4@users.noreply.github.com"}
37
+ {"name":"Kelsey Chua","email":"kchua@nautilusdigital.com"}
38
+ {"name":"kelseychua","email":"112977843+kelseychua@users.noreply.github.com"}
39
+ {"name":"Leonart Gutz","email":"leonartgutz@hotmail.com"}
40
+ {"name":"LeonartGutz","email":"leonartgutz@hotmail.com"}
41
+ {"name":"lgutz","email":"lgutz@nautilusdigital.com"}
42
+ {"name":"Lisa Zhu","email":"lzhu@nautilusdigital.com"}
43
+ {"name":"lisaz-cc","email":"132297040+lisaz-cc@users.noreply.github.com"}
44
+ {"name":"Matheus Franceschini","email":"m.franceschini17@gmail.com"}
45
+ {"name":"Matheus Franceschini","email":"matheus@fintelconnect.com"}
46
+ {"name":"matheusfintel","email":"matheus@fintelconnect.com"}
47
+ {"name":"Michael Zhang","email":"michael@fintelconnect.com"}
48
+ {"name":"Milton Gonzalez","email":"giovangonzalez@gmail.com"}
49
+ {"name":"Milton Gonzalez","email":"mgonzalez@nautilusdigital.com"}
50
+ {"name":"Pavel","email":"psmolov@nautilusdigital.com"}
51
+ {"name":"Raphael Cassio de Souza","email":"rapha.cassio@gmail.com"}
52
+ {"name":"Raphael Cássio de Souza","email":"rapha.cassio@gmail.com"}
53
+ {"name":"rohitsh1","email":"rohit_sh1@yahoo.ca"}
54
+ {"name":"rohitsh1","email":"rohit@fintelconnect.com"}
55
+ {"name":"SamanthaOkada","email":"93158687+SamanthaOkada@users.noreply.github.com"}
56
+ {"name":"Sher Shah Arsalaie","email":"69232811+arsala1995@users.noreply.github.com"}
57
+ {"name":"Sher Shah Arsalaie","email":"sherarsalaie@convergncesmini.lan"}
58
+ {"name":"Sultan Singh Atwal","email":"58763741+atwalsultan@users.noreply.github.com"}
59
+ {"name":"Sultan","email":"sultan.singh.atwal@gmail.com"}
60
+ {"name":"Tamara Smith","email":"50457535+tamaradesmith@users.noreply.github.com"}
61
+ {"name":"Tamara Smith","email":"tdesmith81@gmail.com"}
62
+ {"name":"tamaradesmith","email":"tsmith@nautilusdigital.com"}
63
+ {"name":"tamjohn","email":"tamjohnathan@gmail.com"}
64
+ {"name":"Tausif Ezaj Khan","email":"49699301+tausvels@users.noreply.github.com"}
65
+ {"name":"Tausif Khan","email":"tkhan@nautilusdigital.com"}
66
+ {"name":"Tony Xu","email":"xxy1994212@gmail.com"}
67
+ {"name":"Vikram","email":"72895347+vikrams171@users.noreply.github.com"}
68
+ {"name":"vikramfc","email":"vikramjit@fintelconnect.com"}
69
+ {"name":"Vikramjit Singh","email":"vikramjit@fintelconnect.com"}
70
+ {"name":"Vivek R","email":"vivek@fintelconnect.com"}
71
+ {"name":"Vivek Raskar","email":"vivek@fintelconnect.com"}
72
+ {"name":"Wataru Maeda","email":"14352132+wataru-maeda@users.noreply.github.com"}
73
+ {"name":"Wataru Maeda","email":"14352132+wtr07@users.noreply.github.com"}
74
+ {"name":"Wataru Maeda","email":"WataruMaeda@users.noreply.github.com"}
75
+ {"name":"WataruMaeda","email":"w.maeda.jp@gmail.com"}
76
+ {"name":"Xiaoyong Xu","email":"xiaoyongxu@Mac-mini.hitronhub.home"}
77
+ {"name":"Xiaoyong Xu","email":"xiaoyongxu@XiaoyongdeMini.hitronhub.home"}
78
+ {"name":"XiaoyongXu","email":"xxy1994212@gmail.com"}
@@ -0,0 +1,78 @@
1
+ Alvin Kam:64457658+alvinkam33@users.noreply.github.com
2
+ Alvin:alvin.kam.33@gmail.com
3
+ alvinkam:alvin.kam.33@gmail.com
4
+ Andrew Cheah:62257557+atcheah@users.noreply.github.com
5
+ Artem Kh:artem@fintelconnect.com
6
+ Artem:artem@FC-LAPTOP69.local
7
+ Artem:artem@FC-LAPTOP69.localdomain
8
+ Ben Lu:blu@nautilusdigital.com
9
+ carvalhojg:48341861+carvalhojg@users.noreply.github.com
10
+ Cheryl Ho:91933101+cho196@users.noreply.github.com
11
+ cho196:ho.cheryl@outlook.com
12
+ Cristian Najera:45493332+CristianNajeraL@users.noreply.github.com
13
+ cristianenajera@gmail.com:*O+0clqH5dR@]mC]{1w!|6:P]&/.XFo}Mp0HtAlAQ&~Q:@{a(^iVn'H;jggA)!y!Gcff5\lc
14
+ Darren Luck:dluck@nautilusdigital.com
15
+ Darren542:Darrenluck@icloud.com
16
+ David Moseley:david@fintelconnect.com
17
+ David Moseley:davidmoseley@gmail.com
18
+ Divyansh Tangri:divyansh.tangri@fintelconnect.com
19
+ Divyansh:divyansh.tangri@fintelconnect.com
20
+ EC2 Default User:ec2-user@ip-10-1-1-185.us-east-2.compute.internal
21
+ Eddie Moon:97134997+eddiemoon99@users.noreply.github.com
22
+ George Fairbairn:57505013+georgefairbairn@users.noreply.github.com
23
+ giovangonzalez:giovangonzalez@gmail.com
24
+ GitHub:noreply@github.com
25
+ Hashim Syed:112725957+HashimSyedUBC@users.noreply.github.com
26
+ Hashim Syed:hsyed@nautilusdigital.com
27
+ HashimSyedUBC:112725957+HashimSyedUBC@users.noreply.github.com
28
+ ihu-cc:132296630+ihu-cc@users.noreply.github.com
29
+ ihu-cc:ihu@nautilusdigital.com
30
+ Ivan Hu:132296630+ihu-cc@users.noreply.github.com
31
+ James Dahl:33878022+jamesgdahl@users.noreply.github.com
32
+ jgabrielsantos:48341861+carvalhojg@users.noreply.github.com
33
+ Joao Gabriel Carvalho:joaoocarvalhol.santos@gmail.com
34
+ João Gabriel Santos:48341861+jgabrielsantos@users.noreply.github.com
35
+ Johnathan Tam:97713947+tamjohn@users.noreply.github.com
36
+ Kelsey Chua:77289991+kelc4@users.noreply.github.com
37
+ Kelsey Chua:kchua@nautilusdigital.com
38
+ kelseychua:112977843+kelseychua@users.noreply.github.com
39
+ Leonart Gutz:leonartgutz@hotmail.com
40
+ LeonartGutz:leonartgutz@hotmail.com
41
+ lgutz:lgutz@nautilusdigital.com
42
+ Lisa Zhu:lzhu@nautilusdigital.com
43
+ lisaz-cc:132297040+lisaz-cc@users.noreply.github.com
44
+ Matheus Franceschini:m.franceschini17@gmail.com
45
+ Matheus Franceschini:matheus@fintelconnect.com
46
+ matheusfintel:matheus@fintelconnect.com
47
+ Michael Zhang:michael@fintelconnect.com
48
+ Milton Gonzalez:giovangonzalez@gmail.com
49
+ Milton Gonzalez:mgonzalez@nautilusdigital.com
50
+ Pavel:psmolov@nautilusdigital.com
51
+ Raphael Cassio de Souza:rapha.cassio@gmail.com
52
+ Raphael Cássio de Souza:rapha.cassio@gmail.com
53
+ rohitsh1:rohit_sh1@yahoo.ca
54
+ rohitsh1:rohit@fintelconnect.com
55
+ SamanthaOkada:93158687+SamanthaOkada@users.noreply.github.com
56
+ Sher Shah Arsalaie:69232811+arsala1995@users.noreply.github.com
57
+ Sher Shah Arsalaie:sherarsalaie@convergncesmini.lan
58
+ Sultan Singh Atwal:58763741+atwalsultan@users.noreply.github.com
59
+ Sultan:sultan.singh.atwal@gmail.com
60
+ Tamara Smith:50457535+tamaradesmith@users.noreply.github.com
61
+ Tamara Smith:tdesmith81@gmail.com
62
+ tamaradesmith:tsmith@nautilusdigital.com
63
+ tamjohn:tamjohnathan@gmail.com
64
+ Tausif Ezaj Khan:49699301+tausvels@users.noreply.github.com
65
+ Tausif Khan:tkhan@nautilusdigital.com
66
+ Tony Xu:xxy1994212@gmail.com
67
+ Vikram:72895347+vikrams171@users.noreply.github.com
68
+ vikramfc:vikramjit@fintelconnect.com
69
+ Vikramjit Singh:vikramjit@fintelconnect.com
70
+ Vivek R:vivek@fintelconnect.com
71
+ Vivek Raskar:vivek@fintelconnect.com
72
+ Wataru Maeda:14352132+wataru-maeda@users.noreply.github.com
73
+ Wataru Maeda:14352132+wtr07@users.noreply.github.com
74
+ Wataru Maeda:WataruMaeda@users.noreply.github.com
75
+ WataruMaeda:w.maeda.jp@gmail.com
76
+ Xiaoyong Xu:xiaoyongxu@Mac-mini.hitronhub.home
77
+ Xiaoyong Xu:xiaoyongxu@XiaoyongdeMini.hitronhub.home
78
+ XiaoyongXu:xxy1994212@gmail.com
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eurekadevsecops/radar",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "description": "Radar is an open-source orchestrator of security scanners.",
5
5
  "homepage": "https://www.eurekadevsecops.com/radar",
6
6
  "keywords": [
@@ -27,7 +27,7 @@
27
27
  "url": "https://github.com/EurekaDevSecOps/radarctl.git"
28
28
  },
29
29
  "dependencies": {
30
- "@persistr/clif": "^1.11.1",
30
+ "@persistr/clif": "^1.11.2",
31
31
  "@persistr/clif-plugin-settings": "^2.3.1",
32
32
  "hosted-git-info": "^9.0.0",
33
33
  "humanize-duration": "^3.33.0",
@@ -17,6 +17,7 @@ module.exports = {
17
17
  },
18
18
  options: [
19
19
  { name: 'CATEGORIES', short: 'c', long: 'categories', type: 'string', description: 'list of scanner categories' },
20
+ { name: 'DEBUG', short: 'd', long: 'debug', type: 'boolean', description: 'log detailed debug info to stdout' },
20
21
  { name: 'ESCALATE', short: 'e', long: 'escalate', type: 'string', description: 'severities to treat as high/error' },
21
22
  { name: 'FORMAT', short: 'f', long: 'format', type: 'string', description: 'severity format' },
22
23
  { name: 'OUTPUT', short: 'o', long: 'output', type: 'string', description: 'output SARIF file' },
@@ -75,6 +76,8 @@ module.exports = {
75
76
  examples: [
76
77
  '$ radar scan ' + '(scan current working directory)'.grey,
77
78
  '$ radar scan . ' + '(scan current working directory)'.grey,
79
+ '$ radar scan -d' + '(turn debug mode on)'.grey,
80
+ '$ radar scan --debug' + '(turn debug mode on)'.grey,
78
81
  '$ radar scan /my/repo/dir ' + '(scan target directory)'.grey,
79
82
  '$ radar scan --output=scan.sarif ' + '(save findings in a file)'.grey,
80
83
  '$ radar scan -o scan.sarif /my/repo/dir ' + '(short versions of options)'.grey,
@@ -87,9 +90,12 @@ module.exports = {
87
90
  '$ radar scan -e moderate,low ' + '(treat lower severities as high)'.grey,
88
91
  '$ radar scan -f sarif -e warning,note ' + '(treat lower severities as errors)'.grey
89
92
  ],
90
- run: async (toolbox, args) => {
93
+ run: async (toolbox, args, globals) => {
91
94
  const { log, scanners: availableScanners, categories: availableCategories, telemetry, git } = toolbox
92
95
 
96
+ // Enable debug mode, if needed.
97
+ if (args.DEBUG) globals.debug = true
98
+
93
99
  // Set defaults for args and options.
94
100
  args.TARGET ??= process.cwd()
95
101
  args.FORMAT ??= 'security'
@@ -150,14 +156,24 @@ module.exports = {
150
156
  }
151
157
  catch (error) {
152
158
  log(`WARNING: Telemetry will be skipped for this scan run: ${error.message}\n`)
159
+ if (args.DEBUG) {
160
+ log(error)
161
+ if (error?.cause?.code === 'ECONNREFUSED') {
162
+ log(error.cause.errors)
163
+ log()
164
+ }
165
+ }
153
166
  }
154
167
  }
155
168
 
156
169
  // Send telemetry: git metadata.
157
170
  const metadata = git.metadata(target)
171
+ if (metadata.type === 'error') throw new Error(`${metadata.error.code}: ${metadata.error.details}`)
158
172
  if (telemetry.enabled && scanID) {
159
- await telemetry.send(`scans/:scanID/metadata`, { scanID }, { metadata })
160
- await telemetry.sendSensitive(`scans/:scanID/metadata`, { scanID }, { metadata })
173
+ let res = await telemetry.send(`scans/:scanID/metadata`, { scanID }, { metadata })
174
+ if (!res.ok) log(`WARNING: Scan metadata (stage 1) telemetry upload failed: [${res.status}] ${res.statusText}: ${await res.text()}`)
175
+ res = await telemetry.sendSensitive(`scans/:scanID/metadata`, { scanID }, { metadata })
176
+ if (!res.ok) log(`WARNING: Scan metadata (stage 2) telemetry upload failed: [${res.status}] ${res.statusText}: ${await res.text()}`)
161
177
  }
162
178
 
163
179
  // Run scanners.
@@ -170,7 +186,10 @@ module.exports = {
170
186
  catch (error) {
171
187
  log(`\n${error}`)
172
188
  if (!args.QUIET) log('Scan NOT completed!')
173
- if (telemetry.enabled) await telemetry.send(`scans/:scanID/failed`, { scanID })
189
+ if (telemetry.enabled) {
190
+ const res = await telemetry.send(`scans/:scanID/failed`, { scanID })
191
+ if (!res.ok) log(`WARNING: Scan status (not completed) telemetry upload failed: [${res.status}] ${res.statusText}: ${await res.text()}`)
192
+ }
174
193
  fs.rmSync(tmpdir, { recursive: true, force: true }) // Clean up.
175
194
  return 0x10 // exit code
176
195
  }
@@ -184,7 +203,8 @@ module.exports = {
184
203
 
185
204
  // Send telemetry: scan results.
186
205
  if (telemetry.enabled && scanID) {
187
- await telemetry.sendSensitive(`scans/:scanID/results`, { scanID }, { findings: results.sarif, log: results.log })
206
+ const res = await telemetry.sendSensitive(`scans/:scanID/results`, { scanID }, { findings: results.sarif, log: results.log })
207
+ if (!res.ok) log(`WARNING: Scan results telemetry upload failed: [${res.status}] ${res.statusText}: ${await res.text()}`)
188
208
  }
189
209
 
190
210
  // Analyze scan results: group findings by severity level.
@@ -199,7 +219,8 @@ module.exports = {
199
219
 
200
220
  // Send telemetry: scan summary.
201
221
  if (telemetry.enabled && scanID) {
202
- await telemetry.send(`scans/:scanID/completed`, { scanID }, summary)
222
+ const res = await telemetry.send(`scans/:scanID/completed`, { scanID }, summary)
223
+ if (!res.ok) log(`WARNING: Scan status (completed) telemetry upload failed: [${res.status}] ${res.statusText}: ${await res.text()}`)
203
224
  }
204
225
 
205
226
  // Display summarized findings.
@@ -25,13 +25,31 @@ function metadata(folder) {
25
25
  // Get the tags for the current commit.
26
26
  let tags = execSync('git tag --points-at HEAD', { cwd: folder }).toString().trim()
27
27
  tags = '["' + tags.split('\n').join('","') + '"]'
28
- tags = JSON.parse(tags).filter(tag => tag)
28
+ try {
29
+ tags = JSON.parse(tags).filter(tag => tag)
30
+ }
31
+ catch (error) {
32
+ throw new Error(`failed parsing repo tags: ${error.message}`)
33
+ }
29
34
 
30
35
  // Get the list of unique repo contributors (authors and committers).
31
- const template = '"{\\\"name\\\":\\\"%cn\\\",\\\"email\\\":\\\"%ce\\\"}%n{\\\"name\\\":\\\"%an\\\",\\\"email\\\":\\\"%ae\\\"}"'
36
+ const template = '%cn:%ce%n%an:%ae%n'
32
37
  let contributors = execSync(`git log --pretty=${template} | sort -u`, { cwd: folder }).toString().trim()
33
- contributors = '[' + contributors.split('\n').join(',') + ']'
34
- contributors = JSON.parse(contributors)
38
+ try {
39
+ contributors = contributors
40
+ .split('\n')
41
+ .map(c => {
42
+ const ne = (c + '').split(':')
43
+ return {
44
+ name: ne?.at(0) ?? '',
45
+ email: ne?.at(1) ?? ''
46
+ }
47
+ })
48
+ .filter(c => isValidEmail(c.email))
49
+ }
50
+ catch (error) {
51
+ throw new Error(`failed parsing repo contributors: ${error.message}`)
52
+ }
35
53
 
36
54
  const script = `MAX_LENGTH=4;
37
55
  git rev-list --abbrev=4 --abbrev-commit --all | \
@@ -49,7 +67,7 @@ git rev-list --abbrev=4 --abbrev-commit --all | \
49
67
  */
50
68
 
51
69
  // Return the repo metadata.
52
- return {
70
+ const metadata = {
53
71
  type: 'git',
54
72
  repo: {
55
73
  url: {
@@ -73,6 +91,21 @@ git rev-list --abbrev=4 --abbrev-commit --all | \
73
91
  tags
74
92
  }
75
93
  }
94
+
95
+ // Validate repo metadata.
96
+ if (!metadata.repo.url.origin) throw new Error('remote.origin.url not present')
97
+ if (!metadata.repo.url.https) throw new Error('remote.origin.url (https) not present')
98
+ if (!metadata.repo.source.type) throw new Error('unable to determine repository type')
99
+ if (!metadata.repo.source.domain) throw new Error('unable to determine repository domain')
100
+ if (!metadata.repo.owner) throw new Error('unknown repo owner')
101
+ if (!metadata.repo.name) throw new Error('unknown repo name')
102
+ if (!metadata.repo.abbrevs) throw new Error('unable to determine number of significant digits for commit IDs')
103
+ if (!metadata.repo.contributors) throw new Error('no repository contributors present')
104
+ if (!metadata.commit.id) throw new Error('commit ID not present')
105
+ if (!metadata.commit.time) throw new Error('commit time not present')
106
+ if (!metadata.commit.branch) throw new Error('branch not present')
107
+
108
+ return metadata
76
109
  } catch (error) {
77
110
  return {
78
111
  type: 'error',
@@ -100,6 +133,33 @@ function root(folder) {
100
133
  }
101
134
  }
102
135
 
136
+ /**
137
+ * Thanks to:
138
+ * http://fightingforalostcause.net/misc/2006/compare-email-regex.php
139
+ * http://thedailywtf.com/Articles/Validating_Email_Addresses.aspx
140
+ * http://stackoverflow.com/questions/201323/what-is-the-best-regular-expression-for-validating-email-addresses/201378#201378
141
+ * https://en.wikipedia.org/wiki/Email_address The format of an email address is local-part@domain, where the
142
+ * local part may be up to 64 octets long and the domain may have a maximum of 255 octets.[4]
143
+ */
144
+ function isValidEmail(email) {
145
+ if (!email) return false
146
+
147
+ const parts = email.split('@')
148
+ if (parts.length !== 2) return false
149
+
150
+ const account = parts[0]
151
+ if (account.length > 64) return false
152
+
153
+ const address = parts[1]
154
+ if (address.length > 255) return false
155
+
156
+ const domainParts = address.split('.')
157
+ if (domainParts.some((part) => part.length > 63 )) return false
158
+
159
+ const emailRE = /^[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
160
+ return emailRE.test(email)
161
+ }
162
+
103
163
  module.exports = {
104
164
  metadata,
105
165
  root