presidium 2.0.0 → 2.1.0

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 = {}
@@ -1510,7 +1513,7 @@ class S3Bucket {
1510
1513
  const data = {}
1511
1514
 
1512
1515
  const text = await Readable.Text(response)
1513
- const xmlData = XML.parse(text)
1516
+ const xmlData = XML.parse(HTMLEntities.decode(text))
1514
1517
  let Grants = xmlData.AccessControlPolicy?.AccessControlList?.Grant
1515
1518
  if (!Array.isArray(Grants)) {
1516
1519
  Grants = [Grants]
@@ -2077,12 +2080,12 @@ class S3Bucket {
2077
2080
  ${
2078
2081
  keys.map(key => typeof key == 'string' ? `
2079
2082
  <Object>
2080
- <Key>${key}</Key>
2083
+ <Key>${HTMLEntities.encode(key)}</Key>
2081
2084
  </Object>
2082
2085
  `.trim() : `
2083
2086
  <Object>
2084
- <Key>${key.Key}</Key>
2085
- <VersionId>${key.VersionId}</VersionId>
2087
+ <Key>${HTMLEntities.encode(key.Key)}</Key>
2088
+ <VersionId>${HTMLEntities.encode(key.VersionId)}</VersionId>
2086
2089
  </Object>
2087
2090
  `.trim()).join('\n')
2088
2091
  }
@@ -2095,7 +2098,7 @@ class S3Bucket {
2095
2098
 
2096
2099
  if (response.ok) {
2097
2100
  const text = await Readable.Text(response)
2098
- const xmlData = XML.parse(text)
2101
+ const xmlData = XML.parse(HTMLEntities.decode(text))
2099
2102
 
2100
2103
  const data = {}
2101
2104
  data.Deleted = xmlData.DeleteResult.Deleted ?? []
@@ -2366,7 +2369,7 @@ class S3Bucket {
2366
2369
 
2367
2370
  if (response.ok) {
2368
2371
  const text = await Readable.Text(response)
2369
- const xmlData = XML.parse(text)
2372
+ const xmlData = XML.parse(HTMLEntities.decode(text))
2370
2373
 
2371
2374
  const data = {}
2372
2375
 
@@ -2535,7 +2538,7 @@ class S3Bucket {
2535
2538
 
2536
2539
  if (response.ok) {
2537
2540
  const text = await Readable.Text(response)
2538
- const xmlData = XML.parse(text)
2541
+ const xmlData = XML.parse(HTMLEntities.decode(text))
2539
2542
 
2540
2543
  const data = {}
2541
2544
 
@@ -14,7 +14,7 @@
14
14
  * ) -> strictValidator Validator
15
15
  * ```
16
16
  *
17
- * Creates a strict validator. A strict validator validates each field of a `data` object with the corresponding `parserValidator` function. A strict validator throws an error if any of the required fields specified by the `parserValidatorSchema` are missing.
17
+ * Creates a strict validator. A strict validator validates each field of a `data` object with the corresponding `parserValidator` function. A strict validator throws an error if any of the required fields specified by the `parserValidatorSchema` are missing (not in the data object).
18
18
  *
19
19
  * Arguments:
20
20
  * * `parserValidatorSchema` - an object of parser validator functions.
@@ -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.0",
3
+ "version": "2.1.0",
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",
64
- "presidium-websocket": ">=1.1.2",
62
+ "html-entities": "^2.6.0",
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