odac 0.9.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/.editorconfig +21 -0
- package/.github/workflows/auto-pr-description.yml +49 -0
- package/.github/workflows/release.yml +32 -0
- package/.github/workflows/test-coverage.yml +58 -0
- package/.husky/pre-commit +2 -0
- package/.kiro/steering/code-style.md +56 -0
- package/.kiro/steering/product.md +20 -0
- package/.kiro/steering/structure.md +77 -0
- package/.kiro/steering/tech.md +87 -0
- package/.prettierrc +10 -0
- package/.releaserc.js +134 -0
- package/AGENTS.md +84 -0
- package/CHANGELOG.md +181 -0
- package/CODE_OF_CONDUCT.md +83 -0
- package/CONTRIBUTING.md +63 -0
- package/LICENSE +661 -0
- package/README.md +57 -0
- package/SECURITY.md +26 -0
- package/bin/candy +10 -0
- package/bin/candypack +10 -0
- package/cli/index.js +3 -0
- package/cli/src/Cli.js +348 -0
- package/cli/src/Connector.js +93 -0
- package/cli/src/Monitor.js +416 -0
- package/core/Candy.js +87 -0
- package/core/Commands.js +239 -0
- package/core/Config.js +1094 -0
- package/core/Lang.js +52 -0
- package/core/Log.js +43 -0
- package/core/Process.js +26 -0
- package/docs/backend/01-overview/01-whats-in-the-candy-box.md +9 -0
- package/docs/backend/01-overview/02-super-handy-helper-functions.md +9 -0
- package/docs/backend/01-overview/03-development-server.md +79 -0
- package/docs/backend/02-structure/01-typical-project-layout.md +39 -0
- package/docs/backend/03-config/00-configuration-overview.md +214 -0
- package/docs/backend/03-config/01-database-connection.md +60 -0
- package/docs/backend/03-config/02-static-route-mapping-optional.md +20 -0
- package/docs/backend/03-config/03-request-timeout.md +11 -0
- package/docs/backend/03-config/04-environment-variables.md +227 -0
- package/docs/backend/03-config/05-early-hints.md +352 -0
- package/docs/backend/04-routing/01-basic-page-routes.md +28 -0
- package/docs/backend/04-routing/02-controller-less-view-routes.md +43 -0
- package/docs/backend/04-routing/03-api-and-data-routes.md +20 -0
- package/docs/backend/04-routing/04-authentication-aware-routes.md +48 -0
- package/docs/backend/04-routing/05-advanced-routing.md +14 -0
- package/docs/backend/04-routing/06-error-pages.md +101 -0
- package/docs/backend/04-routing/07-cron-jobs.md +149 -0
- package/docs/backend/05-controllers/01-how-to-build-a-controller.md +17 -0
- package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +20 -0
- package/docs/backend/05-controllers/03-controller-classes.md +93 -0
- package/docs/backend/05-forms/01-custom-forms.md +395 -0
- package/docs/backend/05-forms/02-automatic-database-insert.md +297 -0
- package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +96 -0
- package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +40 -0
- package/docs/backend/07-views/01-the-view-directory.md +73 -0
- package/docs/backend/07-views/02-rendering-a-view.md +179 -0
- package/docs/backend/07-views/03-template-syntax.md +181 -0
- package/docs/backend/07-views/03-variables.md +328 -0
- package/docs/backend/07-views/04-request-data.md +231 -0
- package/docs/backend/07-views/05-conditionals.md +290 -0
- package/docs/backend/07-views/06-loops.md +353 -0
- package/docs/backend/07-views/07-translations.md +358 -0
- package/docs/backend/07-views/08-backend-javascript.md +398 -0
- package/docs/backend/07-views/09-comments.md +297 -0
- package/docs/backend/08-database/01-database-connection.md +99 -0
- package/docs/backend/08-database/02-using-mysql.md +322 -0
- package/docs/backend/09-validation/01-the-validator-service.md +424 -0
- package/docs/backend/10-authentication/01-user-logins-with-authjs.md +53 -0
- package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +55 -0
- package/docs/backend/10-authentication/03-register.md +134 -0
- package/docs/backend/10-authentication/04-candy-register-forms.md +676 -0
- package/docs/backend/10-authentication/05-session-management.md +159 -0
- package/docs/backend/10-authentication/06-candy-login-forms.md +596 -0
- package/docs/backend/11-mail/01-the-mail-service.md +42 -0
- package/docs/backend/12-streaming/01-streaming-overview.md +300 -0
- package/docs/backend/13-utilities/01-candy-var.md +504 -0
- package/docs/frontend/01-overview/01-introduction.md +146 -0
- package/docs/frontend/02-ajax-navigation/01-quick-start.md +608 -0
- package/docs/frontend/02-ajax-navigation/02-configuration.md +370 -0
- package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +519 -0
- package/docs/frontend/03-forms/01-form-handling.md +420 -0
- package/docs/frontend/04-api-requests/01-get-post.md +443 -0
- package/docs/frontend/05-streaming/01-client-streaming.md +163 -0
- package/docs/index.json +452 -0
- package/docs/server/01-installation/01-quick-install.md +19 -0
- package/docs/server/01-installation/02-manual-installation-via-npm.md +9 -0
- package/docs/server/02-get-started/01-core-concepts.md +7 -0
- package/docs/server/02-get-started/02-basic-commands.md +57 -0
- package/docs/server/02-get-started/03-cli-reference.md +276 -0
- package/docs/server/02-get-started/04-cli-quick-reference.md +102 -0
- package/docs/server/03-service/01-start-a-new-service.md +57 -0
- package/docs/server/03-service/02-delete-a-service.md +48 -0
- package/docs/server/04-web/01-create-a-website.md +36 -0
- package/docs/server/04-web/02-list-websites.md +9 -0
- package/docs/server/04-web/03-delete-a-website.md +29 -0
- package/docs/server/05-subdomain/01-create-a-subdomain.md +32 -0
- package/docs/server/05-subdomain/02-list-subdomains.md +33 -0
- package/docs/server/05-subdomain/03-delete-a-subdomain.md +41 -0
- package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +34 -0
- package/docs/server/07-mail/01-create-a-mail-account.md +23 -0
- package/docs/server/07-mail/02-delete-a-mail-account.md +20 -0
- package/docs/server/07-mail/03-list-mail-accounts.md +20 -0
- package/docs/server/07-mail/04-change-account-password.md +23 -0
- package/eslint.config.mjs +120 -0
- package/framework/index.js +4 -0
- package/framework/src/Auth.js +309 -0
- package/framework/src/Candy.js +81 -0
- package/framework/src/Config.js +79 -0
- package/framework/src/Env.js +60 -0
- package/framework/src/Lang.js +57 -0
- package/framework/src/Mail.js +83 -0
- package/framework/src/Mysql.js +575 -0
- package/framework/src/Request.js +301 -0
- package/framework/src/Route/Cron.js +128 -0
- package/framework/src/Route/Internal.js +439 -0
- package/framework/src/Route.js +455 -0
- package/framework/src/Server.js +15 -0
- package/framework/src/Stream.js +163 -0
- package/framework/src/Token.js +37 -0
- package/framework/src/Validator.js +271 -0
- package/framework/src/Var.js +211 -0
- package/framework/src/View/EarlyHints.js +190 -0
- package/framework/src/View/Form.js +600 -0
- package/framework/src/View.js +513 -0
- package/framework/web/candy.js +838 -0
- package/jest.config.js +22 -0
- package/locale/de-DE.json +80 -0
- package/locale/en-US.json +79 -0
- package/locale/es-ES.json +80 -0
- package/locale/fr-FR.json +80 -0
- package/locale/pt-BR.json +80 -0
- package/locale/ru-RU.json +80 -0
- package/locale/tr-TR.json +85 -0
- package/locale/zh-CN.json +80 -0
- package/package.json +86 -0
- package/server/index.js +5 -0
- package/server/src/Api.js +88 -0
- package/server/src/DNS.js +940 -0
- package/server/src/Hub.js +535 -0
- package/server/src/Mail.js +571 -0
- package/server/src/SSL.js +180 -0
- package/server/src/Server.js +27 -0
- package/server/src/Service.js +248 -0
- package/server/src/Subdomain.js +64 -0
- package/server/src/Web/Firewall.js +170 -0
- package/server/src/Web/Proxy.js +134 -0
- package/server/src/Web.js +451 -0
- package/server/src/mail/imap.js +1091 -0
- package/server/src/mail/server.js +32 -0
- package/server/src/mail/smtp.js +786 -0
- package/test/cli/Cli.test.js +36 -0
- package/test/core/Candy.test.js +234 -0
- package/test/core/Commands.test.js +538 -0
- package/test/core/Config.test.js +1435 -0
- package/test/core/Lang.test.js +250 -0
- package/test/core/Process.test.js +156 -0
- package/test/framework/Route.test.js +239 -0
- package/test/framework/View/EarlyHints.test.js +282 -0
- package/test/scripts/check-coverage.js +132 -0
- package/test/server/Api.test.js +647 -0
- package/test/server/Client.test.js +338 -0
- package/test/server/DNS.test.js +2050 -0
- package/test/server/DNS.test.js.bak +2084 -0
- package/test/server/Log.test.js +73 -0
- package/test/server/Mail.account.test_.js +460 -0
- package/test/server/Mail.init.test_.js +411 -0
- package/test/server/Mail.test_.js +1340 -0
- package/test/server/SSL.test_.js +1491 -0
- package/test/server/Server.test.js +765 -0
- package/test/server/Service.test_.js +1127 -0
- package/test/server/Subdomain.test.js +440 -0
- package/test/server/Web/Firewall.test.js +175 -0
- package/test/server/Web.test_.js +1562 -0
- package/test/server/__mocks__/acme-client.js +17 -0
- package/test/server/__mocks__/bcrypt.js +50 -0
- package/test/server/__mocks__/child_process.js +389 -0
- package/test/server/__mocks__/crypto.js +432 -0
- package/test/server/__mocks__/fs.js +450 -0
- package/test/server/__mocks__/globalCandy.js +227 -0
- package/test/server/__mocks__/http-proxy.js +105 -0
- package/test/server/__mocks__/http.js +575 -0
- package/test/server/__mocks__/https.js +272 -0
- package/test/server/__mocks__/index.js +249 -0
- package/test/server/__mocks__/mail/server.js +100 -0
- package/test/server/__mocks__/mail/smtp.js +31 -0
- package/test/server/__mocks__/mailparser.js +81 -0
- package/test/server/__mocks__/net.js +369 -0
- package/test/server/__mocks__/node-forge.js +328 -0
- package/test/server/__mocks__/os.js +320 -0
- package/test/server/__mocks__/path.js +291 -0
- package/test/server/__mocks__/selfsigned.js +8 -0
- package/test/server/__mocks__/server/src/mail/server.js +100 -0
- package/test/server/__mocks__/server/src/mail/smtp.js +31 -0
- package/test/server/__mocks__/smtp-server.js +106 -0
- package/test/server/__mocks__/sqlite3.js +394 -0
- package/test/server/__mocks__/testFactories.js +299 -0
- package/test/server/__mocks__/testHelpers.js +363 -0
- package/test/server/__mocks__/tls.js +229 -0
- package/watchdog/index.js +3 -0
- package/watchdog/src/Watchdog.js +156 -0
- package/web/config.json +5 -0
- package/web/controller/page/about.js +27 -0
- package/web/controller/page/index.js +34 -0
- package/web/package.json +18 -0
- package/web/public/assets/css/style.css +1835 -0
- package/web/public/assets/js/app.js +96 -0
- package/web/route/www.js +19 -0
- package/web/skeleton/main.html +22 -0
- package/web/view/content/about.html +65 -0
- package/web/view/content/home.html +205 -0
- package/web/view/footer/main.html +11 -0
- package/web/view/head/main.html +5 -0
- package/web/view/header/main.html +14 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock implementation of the crypto module for server tests
|
|
3
|
+
* Provides comprehensive mocking of cryptographic operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const {createMockEventEmitter} = require('./testHelpers')
|
|
7
|
+
|
|
8
|
+
// Mock random data generation
|
|
9
|
+
const generateMockRandomBytes = size => {
|
|
10
|
+
const bytes = Buffer.alloc(size)
|
|
11
|
+
for (let i = 0; i < size; i++) {
|
|
12
|
+
bytes[i] = Math.floor(Math.random() * 256)
|
|
13
|
+
}
|
|
14
|
+
return bytes
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Mock hash generation
|
|
18
|
+
const generateMockHash = (algorithm, data) => {
|
|
19
|
+
// Simple mock hash based on algorithm and data
|
|
20
|
+
const hashMap = {
|
|
21
|
+
md5: '5d41402abc4b2a76b9719d911017c592',
|
|
22
|
+
sha1: 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d',
|
|
23
|
+
sha256: '2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae',
|
|
24
|
+
sha512:
|
|
25
|
+
'f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const baseHash = hashMap[algorithm] || hashMap['sha256']
|
|
29
|
+
|
|
30
|
+
// Modify hash based on data to make it somewhat unique
|
|
31
|
+
if (data) {
|
|
32
|
+
const dataStr = Buffer.isBuffer(data) ? data.toString() : String(data)
|
|
33
|
+
const modifier = dataStr.length % 16
|
|
34
|
+
return baseHash.substring(modifier) + baseHash.substring(0, modifier)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return baseHash
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const createMockHash = algorithm => {
|
|
41
|
+
const hash = createMockEventEmitter()
|
|
42
|
+
let data = ''
|
|
43
|
+
|
|
44
|
+
Object.assign(hash, {
|
|
45
|
+
update: jest.fn((chunk, encoding = 'utf8') => {
|
|
46
|
+
data += Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk)
|
|
47
|
+
return hash
|
|
48
|
+
}),
|
|
49
|
+
|
|
50
|
+
digest: jest.fn(encoding => {
|
|
51
|
+
const hashValue = generateMockHash(algorithm, data)
|
|
52
|
+
|
|
53
|
+
if (encoding === 'hex') {
|
|
54
|
+
return hashValue
|
|
55
|
+
} else if (encoding === 'base64') {
|
|
56
|
+
return Buffer.from(hashValue, 'hex').toString('base64')
|
|
57
|
+
} else if (encoding) {
|
|
58
|
+
return Buffer.from(hashValue, 'hex').toString(encoding)
|
|
59
|
+
} else {
|
|
60
|
+
return Buffer.from(hashValue, 'hex')
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
return hash
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const createMockHmac = (algorithm, key) => {
|
|
69
|
+
const hmac = createMockEventEmitter()
|
|
70
|
+
let data = ''
|
|
71
|
+
|
|
72
|
+
Object.assign(hmac, {
|
|
73
|
+
update: jest.fn((chunk, encoding = 'utf8') => {
|
|
74
|
+
data += Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk)
|
|
75
|
+
return hmac
|
|
76
|
+
}),
|
|
77
|
+
|
|
78
|
+
digest: jest.fn(encoding => {
|
|
79
|
+
// Create a mock HMAC by combining key and data
|
|
80
|
+
const keyStr = Buffer.isBuffer(key) ? key.toString() : String(key)
|
|
81
|
+
const combinedData = keyStr + data
|
|
82
|
+
const hashValue = generateMockHash(algorithm, combinedData)
|
|
83
|
+
|
|
84
|
+
if (encoding === 'hex') {
|
|
85
|
+
return hashValue
|
|
86
|
+
} else if (encoding === 'base64') {
|
|
87
|
+
return Buffer.from(hashValue, 'hex').toString('base64')
|
|
88
|
+
} else if (encoding) {
|
|
89
|
+
return Buffer.from(hashValue, 'hex').toString(encoding)
|
|
90
|
+
} else {
|
|
91
|
+
return Buffer.from(hashValue, 'hex')
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
return hmac
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const createMockCipher = (algorithm, password) => {
|
|
100
|
+
const cipher = createMockEventEmitter()
|
|
101
|
+
let data = Buffer.alloc(0)
|
|
102
|
+
|
|
103
|
+
Object.assign(cipher, {
|
|
104
|
+
update: jest.fn((chunk, inputEncoding, outputEncoding) => {
|
|
105
|
+
const input = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, inputEncoding || 'utf8')
|
|
106
|
+
data = Buffer.concat([data, input])
|
|
107
|
+
|
|
108
|
+
// Mock encryption by XORing with a simple key
|
|
109
|
+
const encrypted = Buffer.alloc(input.length)
|
|
110
|
+
const key = Buffer.from(password || 'mockkey')
|
|
111
|
+
for (let i = 0; i < input.length; i++) {
|
|
112
|
+
encrypted[i] = input[i] ^ key[i % key.length]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return outputEncoding ? encrypted.toString(outputEncoding) : encrypted
|
|
116
|
+
}),
|
|
117
|
+
|
|
118
|
+
final: jest.fn(outputEncoding => {
|
|
119
|
+
const final = Buffer.from('mockfinal')
|
|
120
|
+
return outputEncoding ? final.toString(outputEncoding) : final
|
|
121
|
+
}),
|
|
122
|
+
|
|
123
|
+
setAutoPadding: jest.fn((autoPadding = true) => {
|
|
124
|
+
cipher._autoPadding = autoPadding
|
|
125
|
+
return cipher
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return cipher
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const createMockDecipher = (algorithm, password) => {
|
|
133
|
+
const decipher = createMockEventEmitter()
|
|
134
|
+
let data = Buffer.alloc(0)
|
|
135
|
+
|
|
136
|
+
Object.assign(decipher, {
|
|
137
|
+
update: jest.fn((chunk, inputEncoding, outputEncoding) => {
|
|
138
|
+
const input = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, inputEncoding || 'hex')
|
|
139
|
+
data = Buffer.concat([data, input])
|
|
140
|
+
|
|
141
|
+
// Mock decryption by XORing with the same key (reverse of encryption)
|
|
142
|
+
const decrypted = Buffer.alloc(input.length)
|
|
143
|
+
const key = Buffer.from(password || 'mockkey')
|
|
144
|
+
for (let i = 0; i < input.length; i++) {
|
|
145
|
+
decrypted[i] = input[i] ^ key[i % key.length]
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return outputEncoding ? decrypted.toString(outputEncoding) : decrypted
|
|
149
|
+
}),
|
|
150
|
+
|
|
151
|
+
final: jest.fn(outputEncoding => {
|
|
152
|
+
const final = Buffer.from('mockfinal')
|
|
153
|
+
return outputEncoding ? final.toString(outputEncoding) : final
|
|
154
|
+
}),
|
|
155
|
+
|
|
156
|
+
setAutoPadding: jest.fn((autoPadding = true) => {
|
|
157
|
+
decipher._autoPadding = autoPadding
|
|
158
|
+
return decipher
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
return decipher
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const crypto = {
|
|
166
|
+
// Hash functions
|
|
167
|
+
createHash: jest.fn(algorithm => {
|
|
168
|
+
return createMockHash(algorithm)
|
|
169
|
+
}),
|
|
170
|
+
|
|
171
|
+
createHmac: jest.fn((algorithm, key) => {
|
|
172
|
+
return createMockHmac(algorithm, key)
|
|
173
|
+
}),
|
|
174
|
+
|
|
175
|
+
// Cipher functions
|
|
176
|
+
createCipher: jest.fn((algorithm, password) => {
|
|
177
|
+
return createMockCipher(algorithm, password)
|
|
178
|
+
}),
|
|
179
|
+
|
|
180
|
+
createDecipher: jest.fn((algorithm, password) => {
|
|
181
|
+
return createMockDecipher(algorithm, password)
|
|
182
|
+
}),
|
|
183
|
+
|
|
184
|
+
createCipheriv: jest.fn((algorithm, key, iv) => {
|
|
185
|
+
return createMockCipher(algorithm, key)
|
|
186
|
+
}),
|
|
187
|
+
|
|
188
|
+
createDecipheriv: jest.fn((algorithm, key, iv) => {
|
|
189
|
+
return createMockDecipher(algorithm, key)
|
|
190
|
+
}),
|
|
191
|
+
|
|
192
|
+
// Random functions
|
|
193
|
+
randomBytes: jest.fn((size, callback) => {
|
|
194
|
+
const bytes = generateMockRandomBytes(size)
|
|
195
|
+
|
|
196
|
+
if (callback) {
|
|
197
|
+
setTimeout(() => callback(null, bytes), 0)
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return bytes
|
|
202
|
+
}),
|
|
203
|
+
|
|
204
|
+
randomFillSync: jest.fn((buffer, offset, size) => {
|
|
205
|
+
if (!Buffer.isBuffer(buffer)) {
|
|
206
|
+
throw new TypeError('buffer must be a Buffer')
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
offset = offset || 0
|
|
210
|
+
size = size || buffer.length - offset
|
|
211
|
+
|
|
212
|
+
const randomData = generateMockRandomBytes(size)
|
|
213
|
+
randomData.copy(buffer, offset)
|
|
214
|
+
|
|
215
|
+
return buffer
|
|
216
|
+
}),
|
|
217
|
+
|
|
218
|
+
randomFill: jest.fn((buffer, offset, size, callback) => {
|
|
219
|
+
if (typeof offset === 'function') {
|
|
220
|
+
callback = offset
|
|
221
|
+
offset = 0
|
|
222
|
+
size = buffer.length
|
|
223
|
+
} else if (typeof size === 'function') {
|
|
224
|
+
callback = size
|
|
225
|
+
size = buffer.length - offset
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
setTimeout(() => {
|
|
229
|
+
try {
|
|
230
|
+
crypto.randomFillSync(buffer, offset, size)
|
|
231
|
+
callback(null, buffer)
|
|
232
|
+
} catch (error) {
|
|
233
|
+
callback(error)
|
|
234
|
+
}
|
|
235
|
+
}, 0)
|
|
236
|
+
}),
|
|
237
|
+
|
|
238
|
+
randomInt: jest.fn((min, max, callback) => {
|
|
239
|
+
if (typeof min === 'function') {
|
|
240
|
+
callback = min
|
|
241
|
+
min = 0
|
|
242
|
+
max = 2147483647
|
|
243
|
+
} else if (typeof max === 'function') {
|
|
244
|
+
callback = max
|
|
245
|
+
max = min
|
|
246
|
+
min = 0
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const randomValue = Math.floor(Math.random() * (max - min)) + min
|
|
250
|
+
|
|
251
|
+
if (callback) {
|
|
252
|
+
setTimeout(() => callback(null, randomValue), 0)
|
|
253
|
+
return
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return randomValue
|
|
257
|
+
}),
|
|
258
|
+
|
|
259
|
+
randomUUID: jest.fn(() => {
|
|
260
|
+
// Generate a mock UUID v4
|
|
261
|
+
const hex = '0123456789abcdef'
|
|
262
|
+
let uuid = ''
|
|
263
|
+
|
|
264
|
+
for (let i = 0; i < 36; i++) {
|
|
265
|
+
if (i === 8 || i === 13 || i === 18 || i === 23) {
|
|
266
|
+
uuid += '-'
|
|
267
|
+
} else if (i === 14) {
|
|
268
|
+
uuid += '4' // Version 4
|
|
269
|
+
} else if (i === 19) {
|
|
270
|
+
uuid += hex[Math.floor(Math.random() * 4) + 8] // Variant bits
|
|
271
|
+
} else {
|
|
272
|
+
uuid += hex[Math.floor(Math.random() * 16)]
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return uuid
|
|
277
|
+
}),
|
|
278
|
+
|
|
279
|
+
// Key generation
|
|
280
|
+
generateKeyPairSync: jest.fn((type, options = {}) => {
|
|
281
|
+
const mockPrivateKey = {
|
|
282
|
+
asymmetricKeyType: type,
|
|
283
|
+
asymmetricKeySize: options.modulusLength || 2048,
|
|
284
|
+
export: jest.fn((options = {}) => {
|
|
285
|
+
if (options.format === 'pem') {
|
|
286
|
+
return '-----BEGIN PRIVATE KEY-----\nMockPrivateKeyData\n-----END PRIVATE KEY-----'
|
|
287
|
+
}
|
|
288
|
+
return Buffer.from('mockprivatekeydata')
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const mockPublicKey = {
|
|
293
|
+
asymmetricKeyType: type,
|
|
294
|
+
asymmetricKeySize: options.modulusLength || 2048,
|
|
295
|
+
export: jest.fn((options = {}) => {
|
|
296
|
+
if (options.format === 'pem') {
|
|
297
|
+
return '-----BEGIN PUBLIC KEY-----\nMockPublicKeyData\n-----END PUBLIC KEY-----'
|
|
298
|
+
}
|
|
299
|
+
return Buffer.from('mockpublickeydata')
|
|
300
|
+
})
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
privateKey: mockPrivateKey,
|
|
305
|
+
publicKey: mockPublicKey
|
|
306
|
+
}
|
|
307
|
+
}),
|
|
308
|
+
|
|
309
|
+
generateKeyPair: jest.fn((type, options, callback) => {
|
|
310
|
+
if (typeof options === 'function') {
|
|
311
|
+
callback = options
|
|
312
|
+
options = {}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
setTimeout(() => {
|
|
316
|
+
try {
|
|
317
|
+
const keyPair = crypto.generateKeyPairSync(type, options)
|
|
318
|
+
callback(null, keyPair.publicKey, keyPair.privateKey)
|
|
319
|
+
} catch (error) {
|
|
320
|
+
callback(error)
|
|
321
|
+
}
|
|
322
|
+
}, 0)
|
|
323
|
+
}),
|
|
324
|
+
|
|
325
|
+
// Signing and verification
|
|
326
|
+
createSign: jest.fn(algorithm => {
|
|
327
|
+
const sign = createMockEventEmitter()
|
|
328
|
+
let data = ''
|
|
329
|
+
|
|
330
|
+
Object.assign(sign, {
|
|
331
|
+
update: jest.fn((chunk, encoding = 'utf8') => {
|
|
332
|
+
data += Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk)
|
|
333
|
+
return sign
|
|
334
|
+
}),
|
|
335
|
+
|
|
336
|
+
sign: jest.fn((privateKey, outputEncoding) => {
|
|
337
|
+
const signature = generateMockHash('sha256', data + 'signature')
|
|
338
|
+
|
|
339
|
+
if (outputEncoding === 'hex') {
|
|
340
|
+
return signature
|
|
341
|
+
} else if (outputEncoding === 'base64') {
|
|
342
|
+
return Buffer.from(signature, 'hex').toString('base64')
|
|
343
|
+
} else if (outputEncoding) {
|
|
344
|
+
return Buffer.from(signature, 'hex').toString(outputEncoding)
|
|
345
|
+
} else {
|
|
346
|
+
return Buffer.from(signature, 'hex')
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
return sign
|
|
352
|
+
}),
|
|
353
|
+
|
|
354
|
+
createVerify: jest.fn(algorithm => {
|
|
355
|
+
const verify = createMockEventEmitter()
|
|
356
|
+
let data = ''
|
|
357
|
+
|
|
358
|
+
Object.assign(verify, {
|
|
359
|
+
update: jest.fn((chunk, encoding = 'utf8') => {
|
|
360
|
+
data += Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk)
|
|
361
|
+
return verify
|
|
362
|
+
}),
|
|
363
|
+
|
|
364
|
+
verify: jest.fn((publicKey, signature, signatureEncoding) => {
|
|
365
|
+
// Mock verification - always return true for testing
|
|
366
|
+
return true
|
|
367
|
+
})
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
return verify
|
|
371
|
+
}),
|
|
372
|
+
|
|
373
|
+
// Constants
|
|
374
|
+
constants: {
|
|
375
|
+
// OpenSSL constants
|
|
376
|
+
OPENSSL_VERSION_NUMBER: 0x1010100f,
|
|
377
|
+
SSL_OP_ALL: 0x80000bff,
|
|
378
|
+
SSL_OP_NO_SSLv2: 0x01000000,
|
|
379
|
+
SSL_OP_NO_SSLv3: 0x02000000,
|
|
380
|
+
SSL_OP_NO_TLSv1: 0x04000000,
|
|
381
|
+
SSL_OP_NO_TLSv1_1: 0x10000000,
|
|
382
|
+
SSL_OP_NO_TLSv1_2: 0x08000000,
|
|
383
|
+
|
|
384
|
+
// RSA padding
|
|
385
|
+
RSA_PKCS1_PADDING: 1,
|
|
386
|
+
RSA_SSLV23_PADDING: 2,
|
|
387
|
+
RSA_NO_PADDING: 3,
|
|
388
|
+
RSA_PKCS1_OAEP_PADDING: 4,
|
|
389
|
+
RSA_X931_PADDING: 5,
|
|
390
|
+
RSA_PKCS1_PSS_PADDING: 6
|
|
391
|
+
},
|
|
392
|
+
|
|
393
|
+
// Utility functions
|
|
394
|
+
timingSafeEqual: jest.fn((a, b) => {
|
|
395
|
+
if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
|
|
396
|
+
throw new TypeError('Arguments must be Buffers')
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (a.length !== b.length) {
|
|
400
|
+
return false
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return a.equals(b)
|
|
404
|
+
}),
|
|
405
|
+
|
|
406
|
+
// Test helpers
|
|
407
|
+
__setMockRandomBytes: mockFn => {
|
|
408
|
+
crypto.randomBytes.mockImplementation(mockFn)
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
__setMockHash: (algorithm, mockHash) => {
|
|
412
|
+
const originalCreateHash = crypto.createHash
|
|
413
|
+
crypto.createHash.mockImplementation(alg => {
|
|
414
|
+
if (alg === algorithm) {
|
|
415
|
+
const hash = originalCreateHash(alg)
|
|
416
|
+
hash.digest.mockReturnValue(mockHash)
|
|
417
|
+
return hash
|
|
418
|
+
}
|
|
419
|
+
return originalCreateHash(alg)
|
|
420
|
+
})
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
__resetMocks: () => {
|
|
424
|
+
Object.values(crypto).forEach(fn => {
|
|
425
|
+
if (jest.isMockFunction(fn)) {
|
|
426
|
+
fn.mockClear()
|
|
427
|
+
}
|
|
428
|
+
})
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
module.exports = crypto
|