pinstripe 0.31.0 → 0.31.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/development.db +0 -0
- package/lib/commands/purge_used_hashes.js +14 -0
- package/lib/migrations/1708772281_create_used_hash.js +9 -0
- package/lib/migrations/_file_importer.js +2 -0
- package/lib/models/_file_importer.js +2 -0
- package/lib/models/used_hash.js +7 -0
- package/lib/proof_of_work.js +7 -16
- package/lib/services/only_once.js +24 -0
- package/lib/services/render_form.js +5 -3
- package/package.json +2 -2
package/development.db
ADDED
|
File without changes
|
package/lib/proof_of_work.js
CHANGED
|
@@ -5,6 +5,8 @@ import { Class } from './class.js';
|
|
|
5
5
|
import { Singleton } from './singleton.js';
|
|
6
6
|
|
|
7
7
|
const HASH_CASH_TARGET = Math.pow(2, 32 - 20);
|
|
8
|
+
const DEFAULT_DIFFICULTY = 1 / 20;
|
|
9
|
+
const DEFAULT_EXPIRY_IN_SECONDS = 10 * 60;
|
|
8
10
|
|
|
9
11
|
export const ProofOfWork = Class.extend().include({
|
|
10
12
|
meta(){
|
|
@@ -12,10 +14,10 @@ export const ProofOfWork = Class.extend().include({
|
|
|
12
14
|
},
|
|
13
15
|
|
|
14
16
|
async generateProofOfWork(input, options = {}){
|
|
15
|
-
const { difficulty =
|
|
17
|
+
const { difficulty = DEFAULT_DIFFICULTY, steps = 1000, onProgress = () => {}, abortSignal = { aborted: false } } = options;
|
|
16
18
|
const inputHash = await this.createSha1Hash(JSON.stringify(input));
|
|
17
19
|
const salt = await this.createSha1Hash(Math.random());
|
|
18
|
-
const timestamp =
|
|
20
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
19
21
|
const target = this.calculateTarget(difficulty, steps);
|
|
20
22
|
const solution = [];
|
|
21
23
|
|
|
@@ -42,9 +44,11 @@ export const ProofOfWork = Class.extend().include({
|
|
|
42
44
|
},
|
|
43
45
|
|
|
44
46
|
async verifyProofOfWork(input, proofOfWork, options = {}){
|
|
45
|
-
const { difficulty =
|
|
47
|
+
const { difficulty = DEFAULT_DIFFICULTY, expiryInSeconds = DEFAULT_EXPIRY_IN_SECONDS } = options;
|
|
46
48
|
const { salt, timestamp, solution } = JSON.parse(proofOfWork);
|
|
49
|
+
if(Math.floor(Date.now() / 1000) - timestamp > expiryInSeconds) return false;
|
|
47
50
|
const steps = solution.length;
|
|
51
|
+
if(typeof steps !== 'number' || steps <= 0) return false;
|
|
48
52
|
const inputHash = await this.createSha1Hash(JSON.stringify(input));
|
|
49
53
|
const target = this.calculateTarget(difficulty, steps);
|
|
50
54
|
for(let i = 0; i < steps; i++){
|
|
@@ -72,19 +76,6 @@ export const ProofOfWork = Class.extend().include({
|
|
|
72
76
|
|
|
73
77
|
calculateTarget(difficulty, steps){
|
|
74
78
|
return (HASH_CASH_TARGET / difficulty) * steps;
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
getUTCTimestamp() {
|
|
78
|
-
const now = new Date();
|
|
79
|
-
|
|
80
|
-
const year = now.getUTCFullYear() % 100;
|
|
81
|
-
const month = String(now.getUTCMonth() + 1).padStart(2, '0');
|
|
82
|
-
const day = String(now.getUTCDate()).padStart(2, '0');
|
|
83
|
-
const hours = String(now.getUTCHours()).padStart(2, '0');
|
|
84
|
-
const minutes = String(now.getUTCMinutes()).padStart(2, '0');
|
|
85
|
-
const seconds = String(now.getUTCSeconds()).padStart(2, '0');
|
|
86
|
-
|
|
87
|
-
return `${year}${month}${day}${hours}${minutes}${seconds}`;
|
|
88
79
|
}
|
|
89
80
|
});
|
|
90
81
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
create(){
|
|
6
|
+
return this;
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
async hasBeenUsed(key){
|
|
10
|
+
return await this.database.withoutTenantScope.usedHashes.where({value: this.createHash(key)}).count() > 0;
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
async markAsUsed(key, options = {}){
|
|
14
|
+
const { expiresAt = new Date(Date.now() + (1000 * 60 * 60 * 24)) } = options;
|
|
15
|
+
await this.database.usedHashes.insert({
|
|
16
|
+
value: this.createHash(key),
|
|
17
|
+
expiresAt
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
createHash(key){
|
|
22
|
+
return crypto.createHash('sha1').update(JSON.stringify(key)).digest('base64');
|
|
23
|
+
}
|
|
24
|
+
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as crypto from 'crypto';
|
|
2
1
|
|
|
3
2
|
import { ValidationError } from '../validation_error.js';
|
|
4
3
|
import { Inflector } from '../inflector.js';
|
|
@@ -25,13 +24,16 @@ export default {
|
|
|
25
24
|
try {
|
|
26
25
|
if(requiresProofOfWork){
|
|
27
26
|
if(!this.params._proofOfWork) throw new ValidationError({ _proofOfWork: 'Must not be blank' });
|
|
28
|
-
if(!verifyProofOfWork(values, this.params._proofOfWork)) throw new ValidationError({ _proofOfWork: 'Must be a valid
|
|
27
|
+
if(!await verifyProofOfWork(values, this.params._proofOfWork)) throw new ValidationError({ _proofOfWork: 'Must be a valid' });
|
|
28
|
+
if(await this.onlyOnce.hasBeenUsed({ proofOfWork: this.params._proofOfWork })) throw new ValidationError({ _proofOfWork: 'Must be unused' });
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
const out = await formAdapter.submit(values, success) || this.renderHtml`
|
|
31
31
|
<span data-component="pinstripe-anchor" data-target="_parent">
|
|
32
32
|
<script type="pinstripe">this.parent.trigger('click');</script>
|
|
33
33
|
</span>
|
|
34
34
|
`;
|
|
35
|
+
if(requiresProofOfWork) await this.onlyOnce.markAsUsed({ proofOfWork: this.params._proofOfWork });
|
|
36
|
+
return out;
|
|
35
37
|
} catch(e){
|
|
36
38
|
if(!(e instanceof ValidationError)){
|
|
37
39
|
throw e;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "pinstripe",
|
|
4
4
|
"description": "A slick web framework for Node.js.",
|
|
5
|
-
"version": "0.31.
|
|
5
|
+
"version": "0.31.1",
|
|
6
6
|
"author": "Jody Salt",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"exports": {
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"url": "git://github.com/blognami/pinstripe.git",
|
|
54
54
|
"directory": "packages/pinstripe"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "73c659004059facb56e4f96e32d2d443e989d348"
|
|
57
57
|
}
|