recker 1.0.79 → 1.0.80-next.70ea84d

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.
Files changed (196) hide show
  1. package/dist/browser/browser/mini.d.ts +2 -2
  2. package/dist/browser/browser/recker-mini.d.ts +8 -8
  3. package/dist/browser/browser/recker.d.ts +11 -8
  4. package/dist/browser/browser/recker.js +54 -6
  5. package/dist/browser/core/client.d.ts +15 -10
  6. package/dist/browser/core/client.js +54 -38
  7. package/dist/browser/index.iife.min.js +129 -130
  8. package/dist/browser/index.min.js +129 -130
  9. package/dist/browser/index.mini.iife.js +5697 -636
  10. package/dist/browser/index.mini.iife.min.js +47 -48
  11. package/dist/browser/index.mini.min.js +47 -48
  12. package/dist/browser/index.mini.umd.js +5697 -636
  13. package/dist/browser/index.mini.umd.min.js +47 -48
  14. package/dist/browser/index.umd.min.js +129 -130
  15. package/dist/browser/mini.d.ts +2 -2
  16. package/dist/browser/plugins/proxy-rotator.d.ts +2 -2
  17. package/dist/browser/plugins/proxy-rotator.js +6 -28
  18. package/dist/browser/recker-mini.d.ts +8 -8
  19. package/dist/browser/recker.d.ts +11 -8
  20. package/dist/browser/recker.js +54 -6
  21. package/dist/browser/scrape/document.js +2 -2
  22. package/dist/browser/scrape/element.js +7 -1
  23. package/dist/browser/scrape/parser/nodes/html.js +1 -1
  24. package/dist/browser/scrape/spider.d.ts +52 -0
  25. package/dist/browser/scrape/spider.js +620 -38
  26. package/dist/browser/scrape/types.d.ts +2 -0
  27. package/dist/browser/search/google.d.ts +26 -1
  28. package/dist/browser/search/google.js +427 -45
  29. package/dist/browser/seo/analyzer.d.ts +1 -0
  30. package/dist/browser/seo/analyzer.js +144 -1
  31. package/dist/browser/seo/index.d.ts +1 -1
  32. package/dist/browser/seo/keyword-campaign-analyzer.d.ts +2 -0
  33. package/dist/browser/seo/keyword-campaign-analyzer.js +538 -0
  34. package/dist/browser/seo/keyword-campaign-seed-advanced.d.ts +17 -0
  35. package/dist/browser/seo/keyword-campaign-seed-advanced.js +269 -0
  36. package/dist/browser/seo/keyword-campaign-seed-core.d.ts +29 -0
  37. package/dist/browser/seo/keyword-campaign-seed-core.js +525 -0
  38. package/dist/browser/seo/keyword-campaign-shared.d.ts +165 -0
  39. package/dist/browser/seo/keyword-campaign-shared.js +59 -0
  40. package/dist/browser/seo/keyword-campaign.d.ts +4 -107
  41. package/dist/browser/seo/keyword-campaign.js +2 -380
  42. package/dist/browser/seo/keywords.js +5 -22
  43. package/dist/browser/seo/types.d.ts +19 -0
  44. package/dist/browser/transport/curl.d.ts +5 -1
  45. package/dist/browser/transport/curl.js +207 -50
  46. package/dist/browser/transport/undici.d.ts +4 -3
  47. package/dist/browser/transport/undici.js +123 -49
  48. package/dist/browser/types/index.d.ts +9 -3
  49. package/dist/browser/utils/binary-manager.js +26 -3
  50. package/dist/browser/utils/block-detector.d.ts +8 -0
  51. package/dist/browser/utils/block-detector.js +542 -7
  52. package/dist/cli/commands/hls-runner.js +5 -4
  53. package/dist/cli/commands/live-runner.js +5 -4
  54. package/dist/cli/commands/loadtest-runner.js +3 -2
  55. package/dist/cli/commands/search.d.ts +2 -0
  56. package/dist/cli/commands/search.js +105 -0
  57. package/dist/cli/commands/seo-runner.js +9 -7
  58. package/dist/cli/commands/seo.js +140 -1
  59. package/dist/cli/commands/serve.js +75 -131
  60. package/dist/cli/commands/server-runner.js +59 -82
  61. package/dist/cli/commands/spider-runner.d.ts +37 -1
  62. package/dist/cli/commands/spider-runner.js +134 -10
  63. package/dist/cli/commands/spider.d.ts +18 -1
  64. package/dist/cli/commands/spider.js +457 -27
  65. package/dist/cli/events/handlers/cli.js +30 -1
  66. package/dist/cli/events/handlers/tui.js +26 -0
  67. package/dist/cli/events/types.d.ts +27 -0
  68. package/dist/cli/handler.d.ts +2 -12
  69. package/dist/cli/handler.js +20 -15
  70. package/dist/cli/handlers/protocols.js +39 -12
  71. package/dist/cli/handlers/search.d.ts +2 -0
  72. package/dist/cli/handlers/search.js +171 -0
  73. package/dist/cli/handlers/seo-analyze.d.ts +1 -0
  74. package/dist/cli/handlers/seo-analyze.js +666 -0
  75. package/dist/cli/handlers/seo-robots.d.ts +1 -0
  76. package/dist/cli/handlers/seo-robots.js +76 -0
  77. package/dist/cli/handlers/seo-serp.d.ts +54 -0
  78. package/dist/cli/handlers/seo-serp.js +243 -0
  79. package/dist/cli/handlers/seo-sitemap.d.ts +1 -0
  80. package/dist/cli/handlers/seo-sitemap.js +55 -0
  81. package/dist/cli/handlers/seo-spider.d.ts +1 -0
  82. package/dist/cli/handlers/seo-spider.js +334 -0
  83. package/dist/cli/handlers/seo.d.ts +8 -4
  84. package/dist/cli/handlers/seo.js +294 -442
  85. package/dist/cli/handlers/spider.js +94 -17
  86. package/dist/cli/handlers/streaming.js +5 -1
  87. package/dist/cli/index.js +11 -2
  88. package/dist/cli/presets.d.ts +1 -1
  89. package/dist/cli/presets.js +15 -4
  90. package/dist/cli/tui/app.js +6 -1
  91. package/dist/cli/tui/components/rich-response.d.ts +72 -0
  92. package/dist/cli/tui/components/rich-response.js +117 -0
  93. package/dist/cli/tui/executor-commands/background.js +30 -24
  94. package/dist/cli/tui/executor-commands/testing.js +3 -2
  95. package/dist/cli/tui/hooks/useDomains.d.ts +17 -0
  96. package/dist/cli/tui/hooks/useHelp.js +15 -2
  97. package/dist/cli/tui/job-manager.d.ts +4 -4
  98. package/dist/cli/tui/job-manager.js +5 -1
  99. package/dist/cli/tui/spider-tui.d.ts +63 -0
  100. package/dist/cli/tui/spider-tui.js +120 -2
  101. package/dist/cli/types.d.ts +12 -0
  102. package/dist/cli/types.js +1 -0
  103. package/dist/cli/utils/option-helpers.d.ts +10 -0
  104. package/dist/cli/utils/option-helpers.js +63 -0
  105. package/dist/cli/utils/score-color.d.ts +11 -0
  106. package/dist/cli/utils/score-color.js +11 -0
  107. package/dist/cli/utils/serp-campaign.d.ts +53 -0
  108. package/dist/cli/utils/serp-campaign.js +53 -0
  109. package/dist/cli/utils/serp-config.d.ts +26 -0
  110. package/dist/cli/utils/serp-config.js +125 -0
  111. package/dist/core/client.d.ts +15 -10
  112. package/dist/core/client.js +54 -38
  113. package/dist/index.d.ts +1 -0
  114. package/dist/index.js +1 -0
  115. package/dist/mcp/cli.js +35 -34
  116. package/dist/mcp/client.d.ts +2 -2
  117. package/dist/mcp/client.js +20 -2
  118. package/dist/mcp/contract.d.ts +1 -1
  119. package/dist/mcp/profiles.js +5 -1
  120. package/dist/mcp/prompts/index.js +8 -4
  121. package/dist/mcp/resources/index.js +46 -23
  122. package/dist/mcp/server.js +9 -6
  123. package/dist/mcp/tools/protocols.js +9 -2
  124. package/dist/mcp/tools/registry.js +13 -3
  125. package/dist/mcp/tools/seo.js +427 -2
  126. package/dist/mcp/types.d.ts +5 -1
  127. package/dist/plugins/proxy-rotator.d.ts +2 -2
  128. package/dist/plugins/proxy-rotator.js +6 -28
  129. package/dist/raffel/client.d.ts +38 -0
  130. package/dist/raffel/client.js +282 -0
  131. package/dist/raffel/index.d.ts +2 -0
  132. package/dist/raffel/index.js +2 -0
  133. package/dist/raffel/types.d.ts +40 -0
  134. package/dist/raffel/types.js +14 -0
  135. package/dist/recker.d.ts +13 -7
  136. package/dist/recker.js +58 -6
  137. package/dist/scrape/document.js +2 -2
  138. package/dist/scrape/element.js +7 -1
  139. package/dist/scrape/parser/nodes/html.js +1 -1
  140. package/dist/scrape/spider.d.ts +52 -0
  141. package/dist/scrape/spider.js +620 -38
  142. package/dist/scrape/types.d.ts +2 -0
  143. package/dist/search/google.d.ts +26 -1
  144. package/dist/search/google.js +427 -45
  145. package/dist/search/index.d.ts +1 -1
  146. package/dist/seo/analyzer.d.ts +1 -0
  147. package/dist/seo/analyzer.js +144 -1
  148. package/dist/seo/index.d.ts +1 -1
  149. package/dist/seo/keyword-campaign-analyzer.d.ts +2 -0
  150. package/dist/seo/keyword-campaign-analyzer.js +538 -0
  151. package/dist/seo/keyword-campaign-seed-advanced.d.ts +17 -0
  152. package/dist/seo/keyword-campaign-seed-advanced.js +269 -0
  153. package/dist/seo/keyword-campaign-seed-core.d.ts +29 -0
  154. package/dist/seo/keyword-campaign-seed-core.js +525 -0
  155. package/dist/seo/keyword-campaign-shared.d.ts +165 -0
  156. package/dist/seo/keyword-campaign-shared.js +59 -0
  157. package/dist/seo/keyword-campaign.d.ts +4 -107
  158. package/dist/seo/keyword-campaign.js +2 -380
  159. package/dist/seo/keywords.js +5 -22
  160. package/dist/seo/types.d.ts +19 -0
  161. package/dist/template/index.d.ts +1 -1
  162. package/dist/template/types.d.ts +0 -2
  163. package/dist/testing/index.d.ts +1 -22
  164. package/dist/testing/index.js +1 -11
  165. package/dist/transport/curl.d.ts +5 -1
  166. package/dist/transport/curl.js +207 -50
  167. package/dist/transport/undici.d.ts +4 -3
  168. package/dist/transport/undici.js +123 -49
  169. package/dist/types/index.d.ts +9 -3
  170. package/dist/utils/binary-manager.js +26 -3
  171. package/dist/utils/block-detector.d.ts +8 -0
  172. package/dist/utils/block-detector.js +542 -7
  173. package/dist/version.js +1 -1
  174. package/package.json +12 -1
  175. package/dist/testing/mock-dns-server.d.ts +0 -69
  176. package/dist/testing/mock-dns-server.js +0 -269
  177. package/dist/testing/mock-ftp-server.d.ts +0 -89
  178. package/dist/testing/mock-ftp-server.js +0 -562
  179. package/dist/testing/mock-hls-server.d.ts +0 -80
  180. package/dist/testing/mock-hls-server.js +0 -381
  181. package/dist/testing/mock-http-server.d.ts +0 -124
  182. package/dist/testing/mock-http-server.js +0 -343
  183. package/dist/testing/mock-proxy-server.d.ts +0 -108
  184. package/dist/testing/mock-proxy-server.js +0 -615
  185. package/dist/testing/mock-sse-server.d.ts +0 -76
  186. package/dist/testing/mock-sse-server.js +0 -291
  187. package/dist/testing/mock-telnet-server.d.ts +0 -59
  188. package/dist/testing/mock-telnet-server.js +0 -274
  189. package/dist/testing/mock-udp-server.d.ts +0 -43
  190. package/dist/testing/mock-udp-server.js +0 -188
  191. package/dist/testing/mock-websocket-server.d.ts +0 -76
  192. package/dist/testing/mock-websocket-server.js +0 -334
  193. package/dist/testing/mock-whois-server.d.ts +0 -56
  194. package/dist/testing/mock-whois-server.js +0 -234
  195. package/dist/testing/proxy-certs.d.ts +0 -19
  196. package/dist/testing/proxy-certs.js +0 -208
@@ -1,234 +0,0 @@
1
- import { EventEmitter } from 'node:events';
2
- import * as net from 'node:net';
3
- export class MockWhoisServer extends EventEmitter {
4
- options;
5
- server = null;
6
- domains = new Map();
7
- started = false;
8
- stats = {
9
- queriesReceived: 0,
10
- responseSent: 0,
11
- queryLog: [],
12
- };
13
- constructor(options = {}) {
14
- super();
15
- this.options = {
16
- port: 4343,
17
- host: '127.0.0.1',
18
- delay: 0,
19
- ...options,
20
- };
21
- this.addDefaultDomains();
22
- }
23
- get isRunning() {
24
- return this.started;
25
- }
26
- get port() {
27
- return this.options.port;
28
- }
29
- get host() {
30
- return this.options.host;
31
- }
32
- get url() {
33
- return `${this.options.host}:${this.options.port}`;
34
- }
35
- get statistics() {
36
- return { ...this.stats };
37
- }
38
- addDomain(domain, data) {
39
- this.domains.set(domain.toLowerCase(), data);
40
- }
41
- removeDomain(domain) {
42
- this.domains.delete(domain.toLowerCase());
43
- }
44
- getDomain(domain) {
45
- return this.domains.get(domain.toLowerCase());
46
- }
47
- clearDomains() {
48
- this.domains.clear();
49
- this.addDefaultDomains();
50
- }
51
- addDefaultDomains() {
52
- this.addDomain('example.com', {
53
- registrar: 'RESERVED-Internet Assigned Numbers Authority',
54
- registrarUrl: 'http://www.iana.org',
55
- createdDate: '1995-08-14T04:00:00Z',
56
- updatedDate: '2023-08-14T07:01:38Z',
57
- expiryDate: '2024-08-13T04:00:00Z',
58
- status: ['client delete prohibited', 'client transfer prohibited', 'client update prohibited'],
59
- nameservers: ['a.iana-servers.net', 'b.iana-servers.net'],
60
- dnssec: 'signedDelegation',
61
- });
62
- this.addDomain('google.com', {
63
- registrar: 'MarkMonitor Inc.',
64
- registrarUrl: 'http://www.markmonitor.com',
65
- createdDate: '1997-09-15T04:00:00Z',
66
- updatedDate: '2019-09-09T15:39:04Z',
67
- expiryDate: '2028-09-14T04:00:00Z',
68
- status: ['client delete prohibited', 'client transfer prohibited', 'client update prohibited', 'server delete prohibited', 'server transfer prohibited', 'server update prohibited'],
69
- nameservers: ['ns1.google.com', 'ns2.google.com', 'ns3.google.com', 'ns4.google.com'],
70
- registrantOrg: 'Google LLC',
71
- registrantEmail: 'Select Request Email Form at https://domains.markmonitor.com/whois/google.com',
72
- dnssec: 'unsigned',
73
- });
74
- this.addDomain('test.local', {
75
- registrar: 'Mock Registrar',
76
- registrarUrl: 'http://localhost',
77
- createdDate: '2020-01-01T00:00:00Z',
78
- updatedDate: '2024-01-01T00:00:00Z',
79
- expiryDate: '2030-01-01T00:00:00Z',
80
- status: ['ok'],
81
- nameservers: ['ns1.test.local', 'ns2.test.local'],
82
- registrantName: 'Test User',
83
- registrantOrg: 'Test Organization',
84
- registrantEmail: 'admin@test.local',
85
- });
86
- }
87
- async start() {
88
- if (this.started) {
89
- throw new Error('Server already started');
90
- }
91
- return new Promise((resolve, reject) => {
92
- this.server = net.createServer((socket) => {
93
- this.handleConnection(socket);
94
- });
95
- this.server.on('error', (err) => {
96
- this.emit('error', err);
97
- if (!this.started) {
98
- reject(err);
99
- }
100
- });
101
- this.server.listen(this.options.port, this.options.host, () => {
102
- this.started = true;
103
- this.emit('start');
104
- resolve();
105
- });
106
- });
107
- }
108
- async stop() {
109
- if (!this.started || !this.server)
110
- return;
111
- return new Promise((resolve) => {
112
- this.server.close(() => {
113
- this.server = null;
114
- this.started = false;
115
- this.emit('stop');
116
- resolve();
117
- });
118
- });
119
- }
120
- reset() {
121
- this.stats = {
122
- queriesReceived: 0,
123
- responseSent: 0,
124
- queryLog: [],
125
- };
126
- this.clearDomains();
127
- this.emit('reset');
128
- }
129
- handleConnection(socket) {
130
- let data = '';
131
- socket.on('data', (chunk) => {
132
- data += chunk.toString();
133
- if (data.includes('\n') || data.includes('\r')) {
134
- this.handleQuery(data.trim(), socket);
135
- }
136
- });
137
- socket.on('error', (err) => {
138
- this.emit('error', err);
139
- });
140
- }
141
- async handleQuery(query, socket) {
142
- this.stats.queriesReceived++;
143
- this.stats.queryLog.push({ query, timestamp: Date.now() });
144
- this.emit('query', query);
145
- if (this.options.delay > 0) {
146
- await new Promise((resolve) => setTimeout(resolve, this.options.delay));
147
- }
148
- const response = this.buildResponse(query);
149
- socket.write(response, () => {
150
- this.stats.responseSent++;
151
- this.emit('response', query);
152
- socket.end();
153
- });
154
- }
155
- buildResponse(query) {
156
- const domain = query.toLowerCase().trim();
157
- const data = this.domains.get(domain);
158
- if (!data) {
159
- return this.buildNotFoundResponse(domain);
160
- }
161
- return this.buildDomainResponse(domain, data);
162
- }
163
- buildNotFoundResponse(domain) {
164
- return `
165
- % WHOIS Mock Server
166
-
167
- No match for domain "${domain}".
168
-
169
- >>> Last update of WHOIS database: ${new Date().toISOString()} <<<
170
-
171
- NOTICE: This is a mock WHOIS server for testing purposes.
172
- `.trim() + '\n';
173
- }
174
- buildDomainResponse(domain, data) {
175
- const lines = [
176
- '% WHOIS Mock Server',
177
- '',
178
- `Domain Name: ${domain.toUpperCase()}`,
179
- ];
180
- if (data.registrar) {
181
- lines.push(`Registrar: ${data.registrar}`);
182
- }
183
- if (data.registrarUrl) {
184
- lines.push(`Registrar URL: ${data.registrarUrl}`);
185
- }
186
- if (data.createdDate) {
187
- lines.push(`Creation Date: ${data.createdDate}`);
188
- }
189
- if (data.updatedDate) {
190
- lines.push(`Updated Date: ${data.updatedDate}`);
191
- }
192
- if (data.expiryDate) {
193
- lines.push(`Registry Expiry Date: ${data.expiryDate}`);
194
- }
195
- if (data.status && data.status.length > 0) {
196
- for (const status of data.status) {
197
- lines.push(`Domain Status: ${status}`);
198
- }
199
- }
200
- if (data.nameservers && data.nameservers.length > 0) {
201
- for (const ns of data.nameservers) {
202
- lines.push(`Name Server: ${ns}`);
203
- }
204
- }
205
- if (data.registrantName) {
206
- lines.push(`Registrant Name: ${data.registrantName}`);
207
- }
208
- if (data.registrantOrg) {
209
- lines.push(`Registrant Organization: ${data.registrantOrg}`);
210
- }
211
- if (data.registrantEmail) {
212
- lines.push(`Registrant Email: ${data.registrantEmail}`);
213
- }
214
- if (data.adminEmail) {
215
- lines.push(`Admin Email: ${data.adminEmail}`);
216
- }
217
- if (data.techEmail) {
218
- lines.push(`Tech Email: ${data.techEmail}`);
219
- }
220
- if (data.dnssec) {
221
- lines.push(`DNSSEC: ${data.dnssec}`);
222
- }
223
- lines.push('');
224
- lines.push(`>>> Last update of WHOIS database: ${new Date().toISOString()} <<<`);
225
- lines.push('');
226
- lines.push('NOTICE: This is a mock WHOIS server for testing purposes.');
227
- return lines.join('\n') + '\n';
228
- }
229
- static async create(options = {}) {
230
- const server = new MockWhoisServer(options);
231
- await server.start();
232
- return server;
233
- }
234
- }
@@ -1,19 +0,0 @@
1
- export interface CertificateInfo {
2
- key: string;
3
- cert: string;
4
- ca?: string;
5
- }
6
- export interface CertificateOptions {
7
- caCert?: string;
8
- caKey?: string;
9
- validityDays?: number;
10
- }
11
- export declare function generateCA(): {
12
- key: string;
13
- cert: string;
14
- };
15
- export declare function generateCertificate(host: string, options?: CertificateOptions): Promise<CertificateInfo>;
16
- export declare function getDefaultCA(): {
17
- key: string;
18
- cert: string;
19
- };
@@ -1,208 +0,0 @@
1
- import { generateKeyPairSync, createSign, randomBytes } from 'node:crypto';
2
- let defaultCA = null;
3
- export function generateCA() {
4
- const { publicKey, privateKey } = generateKeyPairSync('rsa', {
5
- modulusLength: 2048,
6
- });
7
- const privateKeyPem = privateKey.export({ type: 'pkcs8', format: 'pem' });
8
- const publicKeyPem = publicKey.export({ type: 'spki', format: 'pem' });
9
- const cert = createSelfSignedCert({
10
- commonName: 'Recker Proxy CA',
11
- isCA: true,
12
- privateKey: privateKeyPem,
13
- publicKey: publicKeyPem,
14
- validityDays: 3650,
15
- });
16
- return {
17
- key: privateKeyPem,
18
- cert,
19
- };
20
- }
21
- export async function generateCertificate(host, options = {}) {
22
- let caKey = options.caKey;
23
- let caCert = options.caCert;
24
- if (!caKey || !caCert) {
25
- if (!defaultCA) {
26
- defaultCA = generateCA();
27
- }
28
- caKey = defaultCA.key;
29
- caCert = defaultCA.cert;
30
- }
31
- const { publicKey, privateKey } = generateKeyPairSync('rsa', {
32
- modulusLength: 2048,
33
- });
34
- const privateKeyPem = privateKey.export({ type: 'pkcs8', format: 'pem' });
35
- const publicKeyPem = publicKey.export({ type: 'spki', format: 'pem' });
36
- const cert = createSignedCert({
37
- commonName: host,
38
- sans: [host],
39
- privateKey: privateKeyPem,
40
- publicKey: publicKeyPem,
41
- caKey,
42
- caCert,
43
- validityDays: options.validityDays ?? 365,
44
- });
45
- return {
46
- key: privateKeyPem,
47
- cert,
48
- ca: caCert,
49
- };
50
- }
51
- function createSelfSignedCert(options) {
52
- const now = new Date();
53
- const notBefore = formatDate(now);
54
- const notAfter = formatDate(new Date(now.getTime() + options.validityDays * 24 * 60 * 60 * 1000));
55
- const serialNumber = randomBytes(8).toString('hex').toUpperCase();
56
- const tbsCertificate = buildTBSCertificate({
57
- serialNumber,
58
- issuer: { CN: options.commonName },
59
- subject: { CN: options.commonName },
60
- notBefore,
61
- notAfter,
62
- publicKey: options.publicKey,
63
- isCA: options.isCA,
64
- sans: [],
65
- });
66
- const signature = signData(tbsCertificate, options.privateKey);
67
- return buildCertificate(tbsCertificate, signature);
68
- }
69
- function createSignedCert(options) {
70
- const now = new Date();
71
- const notBefore = formatDate(now);
72
- const notAfter = formatDate(new Date(now.getTime() + options.validityDays * 24 * 60 * 60 * 1000));
73
- const serialNumber = randomBytes(8).toString('hex').toUpperCase();
74
- const issuerCN = extractCN(options.caCert);
75
- const tbsCertificate = buildTBSCertificate({
76
- serialNumber,
77
- issuer: { CN: issuerCN },
78
- subject: { CN: options.commonName },
79
- notBefore,
80
- notAfter,
81
- publicKey: options.publicKey,
82
- isCA: false,
83
- sans: options.sans,
84
- });
85
- const signature = signData(tbsCertificate, options.caKey);
86
- return buildCertificate(tbsCertificate, signature);
87
- }
88
- function buildTBSCertificate(options) {
89
- const parts = [];
90
- parts.push(wrapTag(0xa0, wrapTag(0x02, Buffer.from([0x02]))));
91
- const serialBytes = Buffer.from(options.serialNumber, 'hex');
92
- parts.push(wrapTag(0x02, serialBytes));
93
- parts.push(buildAlgorithmIdentifier());
94
- parts.push(buildName(options.issuer.CN));
95
- parts.push(buildValidity(options.notBefore, options.notAfter));
96
- parts.push(buildName(options.subject.CN));
97
- parts.push(extractPublicKeyInfo(options.publicKey));
98
- const extensions = [];
99
- if (options.isCA) {
100
- extensions.push(buildBasicConstraintsCA());
101
- }
102
- if (options.sans.length > 0) {
103
- extensions.push(buildSANExtension(options.sans));
104
- }
105
- if (extensions.length > 0) {
106
- const extsSeq = wrapTag(0x30, Buffer.concat(extensions));
107
- parts.push(wrapTag(0xa3, extsSeq));
108
- }
109
- return wrapTag(0x30, Buffer.concat(parts));
110
- }
111
- function buildCertificate(tbsCertificate, signature) {
112
- const parts = [];
113
- parts.push(tbsCertificate);
114
- parts.push(buildAlgorithmIdentifier());
115
- const sigBitString = Buffer.concat([Buffer.from([0x00]), signature]);
116
- parts.push(wrapTag(0x03, sigBitString));
117
- const certDer = wrapTag(0x30, Buffer.concat(parts));
118
- const certBase64 = certDer.toString('base64');
119
- const lines = ['-----BEGIN CERTIFICATE-----'];
120
- for (let i = 0; i < certBase64.length; i += 64) {
121
- lines.push(certBase64.slice(i, i + 64));
122
- }
123
- lines.push('-----END CERTIFICATE-----');
124
- return lines.join('\n');
125
- }
126
- function buildAlgorithmIdentifier() {
127
- const oid = Buffer.from([
128
- 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
129
- ]);
130
- const nullParam = Buffer.from([0x05, 0x00]);
131
- return wrapTag(0x30, Buffer.concat([oid, nullParam]));
132
- }
133
- function buildName(cn) {
134
- const cnOid = Buffer.from([0x06, 0x03, 0x55, 0x04, 0x03]);
135
- const cnValue = wrapTag(0x0c, Buffer.from(cn, 'utf8'));
136
- const atv = wrapTag(0x30, Buffer.concat([cnOid, cnValue]));
137
- const rdn = wrapTag(0x31, atv);
138
- return wrapTag(0x30, rdn);
139
- }
140
- function buildValidity(notBefore, notAfter) {
141
- const notBeforeUtc = wrapTag(0x17, Buffer.from(notBefore, 'ascii'));
142
- const notAfterUtc = wrapTag(0x17, Buffer.from(notAfter, 'ascii'));
143
- return wrapTag(0x30, Buffer.concat([notBeforeUtc, notAfterUtc]));
144
- }
145
- function buildBasicConstraintsCA() {
146
- const oid = Buffer.from([0x06, 0x03, 0x55, 0x1d, 0x13]);
147
- const critical = Buffer.from([0x01, 0x01, 0xff]);
148
- const caTrue = wrapTag(0x01, Buffer.from([0xff]));
149
- const bcValue = wrapTag(0x30, caTrue);
150
- const bcOctet = wrapTag(0x04, bcValue);
151
- return wrapTag(0x30, Buffer.concat([oid, critical, bcOctet]));
152
- }
153
- function buildSANExtension(sans) {
154
- const oid = Buffer.from([0x06, 0x03, 0x55, 0x1d, 0x11]);
155
- const names = sans.map((name) => {
156
- return wrapTag(0x82, Buffer.from(name, 'ascii'));
157
- });
158
- const sanSeq = wrapTag(0x30, Buffer.concat(names));
159
- const sanOctet = wrapTag(0x04, sanSeq);
160
- return wrapTag(0x30, Buffer.concat([oid, sanOctet]));
161
- }
162
- function extractPublicKeyInfo(publicKeyPem) {
163
- const base64 = publicKeyPem
164
- .replace(/-----BEGIN PUBLIC KEY-----/, '')
165
- .replace(/-----END PUBLIC KEY-----/, '')
166
- .replace(/\s/g, '');
167
- return Buffer.from(base64, 'base64');
168
- }
169
- function extractCN(certPem) {
170
- const match = certPem.match(/CN=([^,\/\n]+)/i);
171
- return match ? match[1] : 'Unknown';
172
- }
173
- function signData(data, privateKeyPem) {
174
- const sign = createSign('SHA256');
175
- sign.update(data);
176
- return sign.sign(privateKeyPem);
177
- }
178
- function wrapTag(tag, content) {
179
- const length = encodeLength(content.length);
180
- return Buffer.concat([Buffer.from([tag]), length, content]);
181
- }
182
- function encodeLength(len) {
183
- if (len < 128) {
184
- return Buffer.from([len]);
185
- }
186
- const bytes = [];
187
- let temp = len;
188
- while (temp > 0) {
189
- bytes.unshift(temp & 0xff);
190
- temp >>= 8;
191
- }
192
- return Buffer.from([0x80 | bytes.length, ...bytes]);
193
- }
194
- function formatDate(date) {
195
- const y = date.getUTCFullYear().toString().slice(-2);
196
- const m = (date.getUTCMonth() + 1).toString().padStart(2, '0');
197
- const d = date.getUTCDate().toString().padStart(2, '0');
198
- const h = date.getUTCHours().toString().padStart(2, '0');
199
- const min = date.getUTCMinutes().toString().padStart(2, '0');
200
- const s = date.getUTCSeconds().toString().padStart(2, '0');
201
- return `${y}${m}${d}${h}${min}${s}Z`;
202
- }
203
- export function getDefaultCA() {
204
- if (!defaultCA) {
205
- defaultCA = generateCA();
206
- }
207
- return defaultCA;
208
- }