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 +11 -2
- package/HTTP.js +1 -0
- package/Password.js +25 -8
- package/S3Bucket.js +26 -19
- package/internal/encodeURIComponentRFC3986.js +15 -0
- package/internal/encodeURIComponentRFC3986.test.js +35 -0
- package/internal/walk.js +15 -0
- package/package.json +3 -5
- 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 = {}
|
|
@@ -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
|
-
? `/${
|
|
1313
|
-
: `/${
|
|
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
|
-
? `/${
|
|
1504
|
-
: `/${
|
|
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
|
-
? `/${
|
|
1754
|
-
: `/${
|
|
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
|
-
? `/${
|
|
1980
|
-
: `/${
|
|
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
|
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.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
|
-
"
|
|
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": "^
|
|
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
|