presidium 2.0.1 → 2.1.1

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.
package/Archive.js CHANGED
@@ -1,6 +1,7 @@
1
+ require('rubico/global')
1
2
  const tar = require('tar-stream')
2
3
  const fs = require('fs/promises')
3
- const pathWalk = require('./internal/pathWalk')
4
+ const walk = require('./internal/walk')
4
5
  const isArray = require('./internal/isArray')
5
6
  const parsePath = require('./internal/parsePath')
6
7
  const pipe = require('rubico/pipe')
@@ -50,7 +51,15 @@ class Archive {
50
51
  const pack = tar.pack()
51
52
 
52
53
  pipe(path, [
53
- curry.arity(2, pathWalk, __, { ignore }),
54
+ walk,
55
+ filter(path => {
56
+ for (const part of ignore) {
57
+ if (path.includes(part)) {
58
+ return false
59
+ }
60
+ }
61
+ return true
62
+ }),
54
63
 
55
64
  reduce(async (pack, filepath) => {
56
65
  pack.entry({
package/HTTP.js CHANGED
@@ -69,6 +69,7 @@ class HTTP {
69
69
 
70
70
  request(options) {
71
71
  const { body, ...requestOptions } = options
72
+
72
73
  return new Promise((resolve, reject) => {
73
74
  const client = requestOptions.protocol == 'https:' ? https : http
74
75
  const request = client.request(requestOptions, response => {
package/Password.js CHANGED
@@ -1,4 +1,4 @@
1
- const bcrypt = require('bcrypt')
1
+ const crypto = require('crypto')
2
2
 
3
3
  /**
4
4
  * @name Password
@@ -31,9 +31,15 @@ const Password = {}
31
31
  * ```
32
32
  */
33
33
  Password.hash = async function hash(plaintext) {
34
- const salt = await bcrypt.genSalt(10)
35
- const hashed = await bcrypt.hash(plaintext, salt)
36
- return hashed
34
+ return new Promise((resolve, reject) => {
35
+ const salt = crypto.randomBytes(10).toString('hex')
36
+ crypto.scrypt(plaintext, salt, 64, (error, derivedKey) => {
37
+ if (error) {
38
+ reject(error)
39
+ }
40
+ resolve(`${salt}:${derivedKey.toString('hex')}`)
41
+ })
42
+ })
37
43
  }
38
44
 
39
45
  /**
@@ -68,11 +74,22 @@ Password.hash = async function hash(plaintext) {
68
74
  */
69
75
 
70
76
  Password.verify = async function verify(plaintext, hashed) {
71
- const isValid = await bcrypt.compare(plaintext, hashed)
72
- if (!isValid) {
73
- throw new Error('Invalid password')
77
+ const [salt, derivedKey] = hashed.split(':')
78
+ const hashedPlaintext = await Password.hash(plaintext)
79
+
80
+ const hashed1 = await new Promise((resolve, reject) => {
81
+ crypto.scrypt(plaintext, salt, 64, (error, derivedKey1) => {
82
+ if (error) {
83
+ reject(error)
84
+ }
85
+ resolve(`${salt}:${derivedKey1.toString('hex')}`)
86
+ })
87
+ })
88
+
89
+ if (hashed == hashed1) {
90
+ return undefined
74
91
  }
75
- return undefined
92
+ throw new Error('Invalid password')
76
93
  }
77
94
 
78
95
  module.exports = Password
package/S3Bucket.js CHANGED
@@ -9,6 +9,8 @@ const AwsError = require('./internal/AwsError')
9
9
  const parseURL = require('./internal/parseURL')
10
10
  const createS3DeleteObjectError = require('./internal/createS3DeleteObjectError')
11
11
  const XML = require('./XML')
12
+ const HTMLEntities = require('html-entities')
13
+ const encodeURIComponentRFC3986 = require('./internal/encodeURIComponentRFC3986')
12
14
 
13
15
  /**
14
16
  * @name S3Bucket
@@ -327,10 +329,10 @@ class S3Bucket {
327
329
 
328
330
  if (response.ok) {
329
331
  const text = await Readable.Text(response)
330
- const data = XML.parse(text)
332
+ const xmlData = XML.parse(HTMLEntities.decode(text))
331
333
  return {
332
- LocationConstraint: typeof data.LocationConstraint == 'string'
333
- ? data.LocationConstraint
334
+ LocationConstraint: typeof xmlData.LocationConstraint == 'string'
335
+ ? xmlData.LocationConstraint
334
336
  : null
335
337
  }
336
338
  }
@@ -979,7 +981,8 @@ class S3Bucket {
979
981
  options.ObjectLockLegalHoldStatus
980
982
  }
981
983
 
982
- const response = await this._awsRequest1('PUT', `/${key}`, headers, body)
984
+ const encodedKey = encodeURIComponentRFC3986(key).replace(/%2F/g, '/')
985
+ const response = await this._awsRequest1('PUT', `/${encodedKey}`, headers, body)
983
986
 
984
987
  if (response.ok) {
985
988
  const data = {}
@@ -1306,11 +1309,12 @@ class S3Bucket {
1306
1309
  searchParams.set('versionId', options.VersionId)
1307
1310
  }
1308
1311
 
1312
+ const encodedKey = encodeURIComponentRFC3986(key).replace(/%2F/g, '/')
1309
1313
  const response = await this._awsRequest1(
1310
1314
  'GET',
1311
1315
  searchParams.size > 0
1312
- ? `/${key}?${searchParams.toString()}`
1313
- : `/${key}`,
1316
+ ? `/${encodedKey}?${searchParams.toString()}`
1317
+ : `/${encodedKey}`,
1314
1318
  headers,
1315
1319
  ''
1316
1320
  )
@@ -1497,11 +1501,12 @@ class S3Bucket {
1497
1501
  searchParams.set('versionId', options.VersionId)
1498
1502
  }
1499
1503
 
1504
+ const encodedKey = encodeURIComponentRFC3986(key).replace(/%2F/g, '/')
1500
1505
  const response = await this._awsRequest1(
1501
1506
  'GET',
1502
1507
  searchParams.size > 0
1503
- ? `/${key}?acl&${searchParams.toString()}`
1504
- : `/${key}?acl`,
1508
+ ? `/${encodedKey}?acl&${searchParams.toString()}`
1509
+ : `/${encodedKey}?acl`,
1505
1510
  headers,
1506
1511
  ''
1507
1512
  )
@@ -1510,7 +1515,7 @@ class S3Bucket {
1510
1515
  const data = {}
1511
1516
 
1512
1517
  const text = await Readable.Text(response)
1513
- const xmlData = XML.parse(text)
1518
+ const xmlData = XML.parse(HTMLEntities.decode(text))
1514
1519
  let Grants = xmlData.AccessControlPolicy?.AccessControlList?.Grant
1515
1520
  if (!Array.isArray(Grants)) {
1516
1521
  Grants = [Grants]
@@ -1747,11 +1752,12 @@ class S3Bucket {
1747
1752
  searchParams.set('versionId', options.VersionId)
1748
1753
  }
1749
1754
 
1755
+ const encodedKey = encodeURIComponentRFC3986(key).replace(/%2F/g, '/')
1750
1756
  const response = await this._awsRequest1(
1751
1757
  'HEAD',
1752
1758
  searchParams.size > 0
1753
- ? `/${key}?${searchParams.toString()}`
1754
- : `/${key}`,
1759
+ ? `/${encodedKey}?${searchParams.toString()}`
1760
+ : `/${encodedKey}`,
1755
1761
  headers,
1756
1762
  ''
1757
1763
  )
@@ -1973,11 +1979,12 @@ class S3Bucket {
1973
1979
  searchParams.set('versionId', options.VersionId)
1974
1980
  }
1975
1981
 
1982
+ const encodedKey = encodeURIComponentRFC3986(key).replace(/%2F/g, '/')
1976
1983
  const response = await this._awsRequest1(
1977
1984
  'DELETE',
1978
1985
  searchParams.size > 0
1979
- ? `/${key}?${searchParams.toString()}`
1980
- : `/${key}`,
1986
+ ? `/${encodedKey}?${searchParams.toString()}`
1987
+ : `/${encodedKey}`,
1981
1988
  headers,
1982
1989
  ''
1983
1990
  )
@@ -2077,12 +2084,12 @@ class S3Bucket {
2077
2084
  ${
2078
2085
  keys.map(key => typeof key == 'string' ? `
2079
2086
  <Object>
2080
- <Key>${key}</Key>
2087
+ <Key>${HTMLEntities.encode(key)}</Key>
2081
2088
  </Object>
2082
2089
  `.trim() : `
2083
2090
  <Object>
2084
- <Key>${key.Key}</Key>
2085
- <VersionId>${key.VersionId}</VersionId>
2091
+ <Key>${HTMLEntities.encode(key.Key)}</Key>
2092
+ <VersionId>${HTMLEntities.encode(key.VersionId)}</VersionId>
2086
2093
  </Object>
2087
2094
  `.trim()).join('\n')
2088
2095
  }
@@ -2095,7 +2102,7 @@ class S3Bucket {
2095
2102
 
2096
2103
  if (response.ok) {
2097
2104
  const text = await Readable.Text(response)
2098
- const xmlData = XML.parse(text)
2105
+ const xmlData = XML.parse(HTMLEntities.decode(text))
2099
2106
 
2100
2107
  const data = {}
2101
2108
  data.Deleted = xmlData.DeleteResult.Deleted ?? []
@@ -2366,7 +2373,7 @@ class S3Bucket {
2366
2373
 
2367
2374
  if (response.ok) {
2368
2375
  const text = await Readable.Text(response)
2369
- const xmlData = XML.parse(text)
2376
+ const xmlData = XML.parse(HTMLEntities.decode(text))
2370
2377
 
2371
2378
  const data = {}
2372
2379
 
@@ -2535,7 +2542,7 @@ class S3Bucket {
2535
2542
 
2536
2543
  if (response.ok) {
2537
2544
  const text = await Readable.Text(response)
2538
- const xmlData = XML.parse(text)
2545
+ const xmlData = XML.parse(HTMLEntities.decode(text))
2539
2546
 
2540
2547
  const data = {}
2541
2548
 
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @name encodeURIComponentRFC3986
3
+ *
4
+ * @docs
5
+ * ```coffeescript [specscript]
6
+ * encodeURIComponentRFC3986(str string) -> encoded string
7
+ * ```
8
+ */
9
+ function encodeURIComponentRFC3986(str) {
10
+ return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
11
+ return '%' + c.charCodeAt(0).toString(16).toUpperCase()
12
+ })
13
+ }
14
+
15
+ module.exports = encodeURIComponentRFC3986
@@ -0,0 +1,35 @@
1
+ const Test = require('thunk-test')
2
+ const encodeURIComponentRFC3986 = require('./encodeURIComponentRFC3986')
3
+
4
+ const test = new Test('encodeURIComponentRFC3986', encodeURIComponentRFC3986)
5
+
6
+ .case('aaa', 'aaa')
7
+ .case('', '')
8
+ .case(':', '%3A')
9
+ .case('/', '%2F')
10
+ .case('?', '%3F')
11
+ .case('#', '%23')
12
+ .case('[', '%5B')
13
+ .case(']', '%5D')
14
+ .case('@', '%40')
15
+ .case('!', '%21')
16
+ .case('$', '%24')
17
+ .case('&', '%26')
18
+ .case('\'', '%27')
19
+ .case('(', '%28')
20
+ .case(')', '%29')
21
+ .case('*', '%2A')
22
+ .case('+', '%2B')
23
+ .case(',', '%2C')
24
+ .case(';', '%3B')
25
+ .case('=', '%3D')
26
+ .case('%', '%25')
27
+ .case(' ', '%20')
28
+ .case(undefined, 'undefined')
29
+ .case(null, 'null')
30
+
31
+ if (process.argv[1] == __filename) {
32
+ test()
33
+ }
34
+
35
+ module.exports = test
@@ -0,0 +1,15 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ async function* walk(dir) {
5
+ for await (const d of await fs.promises.opendir(dir)) {
6
+ const entry = path.join(dir, d.name);
7
+ if (d.isDirectory()) {
8
+ yield* walk(entry);
9
+ } else if (d.isFile()) {
10
+ yield entry;
11
+ }
12
+ }
13
+ }
14
+
15
+ module.exports = walk
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "presidium",
3
- "version": "2.0.1",
3
+ "version": "2.1.1",
4
4
  "description": "A library for creating web services",
5
5
  "author": "Richard Tong",
6
6
  "license": "MIT",
@@ -59,11 +59,10 @@
59
59
  "xml"
60
60
  ],
61
61
  "dependencies": {
62
- "bcrypt": "^5.1.0",
63
- "minimatch": "^9.0.3",
62
+ "html-entities": "^2.6.0",
64
63
  "presidium-websocket": ">=2.0.1",
65
64
  "rubico": "^2.7.7",
66
- "tar-stream": "^2.1.4"
65
+ "tar-stream": "^3.1.7"
67
66
  },
68
67
  "scripts": {
69
68
  "test": "node test.js && mocha **/*.test.js *.test.js",
@@ -73,7 +72,6 @@
73
72
  "@aws-sdk/crc64-nvme-crt": "^3.848.0",
74
73
  "eslint": "^7.13.0",
75
74
  "fast-crc32c": "^2.0.0",
76
- "glob": "^7.1.6",
77
75
  "mocha": "^10.8.2",
78
76
  "nyc": "^17.1.0",
79
77
  "thunk-test": "^1.3.1",
@@ -1,55 +0,0 @@
1
- require('rubico/global')
2
- const fs = require('fs/promises')
3
- const { minimatch } = require('minimatch')
4
- const resolvePath = require('./resolvePath')
5
- const isArray = require('./isArray')
6
-
7
- /**
8
- * @name pathWalk
9
- *
10
- * @docs
11
- * ```coffeescript [specscript]
12
- * pathWalk(path, options? {
13
- * ignore: Array<string>, // names or paths
14
- * }) -> Promise<Array<string>>
15
- * ```
16
- *
17
- * ```javascript
18
- * pathWalk('./my/path/', {
19
- * ignore: ['node_modules', '.github'],
20
- * }) // -> Promise<paths Array<string>>
21
- * ```
22
- */
23
- const pathWalk = async function (path, options = {}) {
24
- const { ignore = [] } = options
25
- const absPath = resolvePath(path)
26
- const dirents = await fs.readdir(absPath, { withFileTypes: true })
27
- const result = []
28
-
29
- for (const dirent of dirents) {
30
- const dirName = dirent.name
31
- const dirPath = resolvePath(path, dirName)
32
- let shouldIgnore = false
33
- for (const pattern of ignore) {
34
- if (minimatch(dirPath, pattern) || minimatch(dirName, pattern)) {
35
- shouldIgnore = true
36
- break
37
- }
38
- }
39
-
40
- if (shouldIgnore) {
41
- continue
42
- }
43
-
44
- if (dirent.isDirectory()) {
45
- const subPaths = await pathWalk(dirPath, options)
46
- result.push(...subPaths)
47
- } else {
48
- result.push(dirPath)
49
- }
50
- }
51
-
52
- return result
53
- }
54
-
55
- module.exports = pathWalk
@@ -1,25 +0,0 @@
1
- const assert = require('assert')
2
- const Test = require('thunk-test')
3
- const pathWalk = require('./pathWalk')
4
- const resolvePath = require('./resolvePath')
5
-
6
- const test = Test('pathWalk', pathWalk)
7
-
8
- .case(__dirname, function (paths) {
9
- assert(paths.length > 0)
10
- this.allInternalPaths = paths
11
- })
12
-
13
- .case(__dirname, { ignore: ['pathWalk.js'] }, function (paths) {
14
- assert.equal(paths.length, this.allInternalPaths.length - 1)
15
- })
16
-
17
- .case(__dirname, { ignore: [resolvePath(__dirname, 'pathWalk.js')] }, function (paths) {
18
- assert.equal(paths.length, this.allInternalPaths.length - 1)
19
- })
20
-
21
- if (process.argv[1] == __filename) {
22
- test()
23
- }
24
-
25
- module.exports = test