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 +11 -2
- package/HTTP.js +1 -0
- package/Password.js +25 -8
- package/S3Bucket.js +14 -11
- package/StrictValidator.js +1 -1
- package/internal/encodeURIComponentRFC3986.js +15 -0
- package/internal/encodeURIComponentRFC3986.test.js +35 -0
- package/internal/walk.js +15 -0
- package/package.json +4 -6
- package/internal/pathWalk.js +0 -55
- package/internal/pathWalk.test.js +0 -25
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
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
|
|
332
|
+
const xmlData = XML.parse(HTMLEntities.decode(text))
|
|
331
333
|
return {
|
|
332
|
-
LocationConstraint: typeof
|
|
333
|
-
?
|
|
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
|
|
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
|
|
package/StrictValidator.js
CHANGED
|
@@ -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
|
package/internal/walk.js
ADDED
|
@@ -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.
|
|
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
|
-
"
|
|
63
|
-
"
|
|
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": "^
|
|
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",
|
package/internal/pathWalk.js
DELETED
|
@@ -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
|