@tak-ps/node-tak 12.24.0 → 12.26.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.
@@ -0,0 +1,172 @@
1
+ import Err from '@openaddresses/batch-error';
2
+ import xmljs from '@tak-ps/xml-js';
3
+ const HIDDEN = new Set(['style', 'script', 'head']);
4
+ function collapse(input) {
5
+ return input.replace(/\s+/g, ' ').trim();
6
+ }
7
+ /** Linear-time tag stripper for malformed HTML - drops the contents of HIDDEN elements */
8
+ function stripTags(html) {
9
+ const lower = html.toLowerCase();
10
+ let out = '';
11
+ let i = 0;
12
+ while (i < html.length) {
13
+ const open = html.indexOf('<', i);
14
+ if (open === -1) {
15
+ out += html.slice(i);
16
+ break;
17
+ }
18
+ out += html.slice(i, open) + ' ';
19
+ const close = html.indexOf('>', open);
20
+ if (close === -1)
21
+ break;
22
+ const tag = lower.slice(open + 1, close).trim().split(/\s/, 1)[0];
23
+ i = close + 1;
24
+ if (HIDDEN.has(tag)) {
25
+ const end = lower.indexOf(`</${tag}`, i);
26
+ if (end === -1)
27
+ break;
28
+ i = html.indexOf('>', end);
29
+ if (i === -1)
30
+ break;
31
+ i += 1;
32
+ }
33
+ }
34
+ return collapse(out);
35
+ }
36
+ function textOf(el, opts = {}) {
37
+ if (el.type === 'text')
38
+ return String(el.text ?? '');
39
+ if (el.type !== 'element' || !el.elements)
40
+ return '';
41
+ if (opts.skipPre && el.name === 'pre')
42
+ return '';
43
+ return el.elements.map((child) => textOf(child, opts)).join('');
44
+ }
45
+ function* walk(el, skipHidden = true) {
46
+ if (el.type === 'element' && el.name && skipHidden && HIDDEN.has(el.name.toLowerCase()))
47
+ return;
48
+ if (el.type === 'element')
49
+ yield el;
50
+ for (const child of el.elements || [])
51
+ yield* walk(child, skipHidden);
52
+ }
53
+ function find(root, name) {
54
+ for (const el of walk(root, false)) {
55
+ if (el.name?.toLowerCase() === name)
56
+ return el;
57
+ }
58
+ }
59
+ export function isHTML(body) {
60
+ if (typeof body !== 'string')
61
+ return false;
62
+ const head = body.slice(0, 512).trimStart().toLowerCase();
63
+ return head.startsWith('<!doctype html')
64
+ || head.startsWith('<html')
65
+ || /^<(head|body|title|h1)[\s>]/.test(head);
66
+ }
67
+ /** Returns null if body is not HTML */
68
+ export function parseHTMLError(body) {
69
+ if (!isHTML(body))
70
+ return null;
71
+ const details = {
72
+ fields: {},
73
+ text: '',
74
+ };
75
+ let root;
76
+ try {
77
+ root = xmljs.xml2js(body, { compact: false });
78
+ }
79
+ catch {
80
+ // Not well-formed XHTML - fall back to visible text
81
+ details.text = stripTags(body);
82
+ return details;
83
+ }
84
+ const title = find(root, 'title') || find(root, 'h1');
85
+ if (title && collapse(textOf(title)))
86
+ details.title = collapse(textOf(title));
87
+ const pres = [];
88
+ let text = '';
89
+ for (const el of walk(root)) {
90
+ const name = el.name?.toLowerCase();
91
+ if (name === 'p' && el.elements?.[0]?.name?.toLowerCase() === 'b') {
92
+ const label = collapse(textOf(el.elements[0]));
93
+ const value = collapse(el.elements.slice(1).map((child) => textOf(child)).join(''));
94
+ if (label && value)
95
+ details.fields[label] = value;
96
+ }
97
+ else if (name === 'pre') {
98
+ const value = textOf(el)
99
+ .split('\n')
100
+ .map((line) => line.trimEnd())
101
+ .join('\n')
102
+ .trim();
103
+ if (value)
104
+ pres.push(value);
105
+ }
106
+ else if (name === 'body') {
107
+ text = collapse(textOf(el, { skipPre: true }));
108
+ }
109
+ }
110
+ if (pres.length)
111
+ details.exception = pres.join('\n\n');
112
+ details.text = text || collapse(textOf(root, { skipPre: true }));
113
+ return details;
114
+ }
115
+ /** Most useful human readable line from the page */
116
+ export function summarizeHTMLError(details, status) {
117
+ const message = details.fields.Message
118
+ || details.fields.message
119
+ || details.fields.Description
120
+ || details.fields.description;
121
+ if (message)
122
+ return message;
123
+ if (details.exception) {
124
+ // "<ExceptionClass>: <message>"
125
+ const first = details.exception.split('\n')[0].trim();
126
+ const colon = first.indexOf(': ');
127
+ if (colon !== -1 && colon < first.length - 2)
128
+ return first.slice(colon + 2).trim();
129
+ }
130
+ if (details.title)
131
+ return details.title;
132
+ if (details.text)
133
+ return details.text.length > 200 ? details.text.slice(0, 197) + '...' : details.text;
134
+ return status ? `Status Code: ${status}` : 'Unknown TAK Server Error';
135
+ }
136
+ /** Plain-text breakdown for an "Advanced" details pane */
137
+ export function formatHTMLError(details) {
138
+ const lines = [];
139
+ if (details.title)
140
+ lines.push(details.title);
141
+ for (const [label, value] of Object.entries(details.fields)) {
142
+ if (value)
143
+ lines.push(`${label}: ${value}`);
144
+ }
145
+ if (details.exception) {
146
+ if (lines.length)
147
+ lines.push('');
148
+ lines.push(details.exception);
149
+ }
150
+ if (!lines.length && details.text)
151
+ lines.push(details.text);
152
+ return lines.join('\n');
153
+ }
154
+ /**
155
+ * TAK Server HTML error page - `safe` is the summary, `details` the full breakdown
156
+ */
157
+ export class TAKServerError extends Err {
158
+ details;
159
+ html;
160
+ parsed;
161
+ constructor(status, html, parsed) {
162
+ const details = parsed || parseHTMLError(html) || {
163
+ fields: {},
164
+ text: stripTags(html)
165
+ };
166
+ super(status, null, summarizeHTMLError(details, status));
167
+ this.parsed = details;
168
+ this.details = formatHTMLError(details);
169
+ this.html = html;
170
+ }
171
+ }
172
+ //# sourceMappingURL=html-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-error.js","sourceRoot":"","sources":["../../../lib/utils/html-error.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,301 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { Readable } from 'node:stream';
4
+ import { Client } from 'undici';
5
+ import TAKAPI from '../lib/api.js';
6
+ import { APIAuthCertificate } from '../lib/auth.js';
7
+ function mockResponse(statusCode, contentType, body) {
8
+ return {
9
+ statusCode,
10
+ headers: { 'content-type': contentType },
11
+ body: Readable.from([Buffer.from(body)]),
12
+ trailers: {}
13
+ };
14
+ }
15
+ // Self-signed test certificate: O=CO-TAK, OU=WILDFIRE, CN=alice@example.com, expires 2036-08-22
16
+ const FIXTURE_PEM = `-----BEGIN CERTIFICATE-----
17
+ MIIB1TCCAXugAwIBAgIUHPumta9q/l2whKqG1nGD9pJ/uogwCgYIKoZIzj0EAwIw
18
+ QDEPMA0GA1UECgwGQ08tVEFLMREwDwYDVQQLDAhXSUxERklSRTEaMBgGA1UEAwwR
19
+ YWxpY2VAZXhhbXBsZS5jb20wHhcNMjYwODI1MDEwNTQxWhcNMzYwODIyMDEwNTQx
20
+ WjBAMQ8wDQYDVQQKDAZDTy1UQUsxETAPBgNVBAsMCFdJTERGSVJFMRowGAYDVQQD
21
+ DBFhbGljZUBleGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABB45
22
+ OnCT2nVQVxlgKDLttbhLW9Yjs2/elFjAZkil+iI0QLM+vtRjdoCdI+NhADggvJ65
23
+ +++JRYr8Kv2zwv2joSKjUzBRMB0GA1UdDgQWBBS8Depzqkj88/9GNHWY8GmyreFB
24
+ VTAfBgNVHSMEGDAWgBS8Depzqkj88/9GNHWY8GmyreFBVTAPBgNVHRMBAf8EBTAD
25
+ AQH/MAoGCCqGSM49BAMCA0gAMEUCIQCU1mg7e/Eu4A84SemDc0bIQHTnM9aVP8h1
26
+ LaburcgKWwIgYeKSRCQGrM1cEhJQbqJC6UWpJUGxGcclcMzGjqx7WpQ=
27
+ -----END CERTIFICATE-----`;
28
+ const FIXTURE_FINGERPRINT = '37:CA:5A:CD:2E:AC:F5:41:09:4B:DE:21:34:9F:A8:83:C4:43:20:71:7E:7F:CC:EC:97:2C:9E:AE:E9:5F:83:40';
29
+ const GET_PATH = `/Marti/api/certadmin/cert/${FIXTURE_FINGERPRINT}`;
30
+ const LIST_PATH = '/Marti/api/certadmin/cert?username=alice%40example.com';
31
+ function certList(certs) {
32
+ return JSON.stringify({ version: '3', type: 'TakCert', data: certs, nodeId: 'node' });
33
+ }
34
+ function certItem(cert) {
35
+ return JSON.stringify({ version: '3', type: 'TakCert', data: cert, nodeId: 'node' });
36
+ }
37
+ // Tomcat's default page for response.sendError(500) - what the TAK Server
38
+ // returns from getCertificate() when the hash is unknown
39
+ const TOMCAT_500 = '<!doctype html><html lang="en"><head><title>HTTP Status 500 – Internal Server Error</title></head><body><h1>HTTP Status 500 – Internal Server Error</h1></body></html>';
40
+ async function collect(stream) {
41
+ const chunks = [];
42
+ for await (const chunk of stream)
43
+ chunks.push(Buffer.from(chunk));
44
+ return Buffer.concat(chunks);
45
+ }
46
+ /**
47
+ * Install a path-keyed mock; returns the list of paths requested in order
48
+ */
49
+ function mockRoutes(routes) {
50
+ const originalRequest = Client.prototype.request;
51
+ const paths = [];
52
+ Client.prototype.request = async function (opts) {
53
+ const path = String(opts.path);
54
+ paths.push(path);
55
+ const route = routes[path];
56
+ if (!route)
57
+ throw new Error(`Unexpected request: ${opts.method ?? 'GET'} ${path}`);
58
+ return route();
59
+ };
60
+ return { paths, restore: () => { Client.prototype.request = originalRequest; } };
61
+ }
62
+ function api() {
63
+ return new TAKAPI(new URL('https://tak.example.com'), new APIAuthCertificate('cert', 'key'));
64
+ }
65
+ test('Certificate.listActive requests the active certificate list', async () => {
66
+ const mock = mockRoutes({
67
+ '/Marti/api/certadmin/cert/active': () => mockResponse(200, 'application/json', certList([{ id: 1, hash: 'abc', userDn: 'alice' }]))
68
+ });
69
+ try {
70
+ const res = await api().Certificate.listActive();
71
+ assert.deepEqual(mock.paths, ['/Marti/api/certadmin/cert/active']);
72
+ assert.equal(res.data.length, 1);
73
+ assert.equal(res.data[0].hash, 'abc');
74
+ }
75
+ finally {
76
+ mock.restore();
77
+ }
78
+ });
79
+ test('Certificate.validate resolves a known certificate with a single get(hash) call', async () => {
80
+ const mock = mockRoutes({
81
+ [GET_PATH]: () => mockResponse(200, 'application/json', certItem({ id: 2, hash: FIXTURE_FINGERPRINT, userDn: 'alice@example.com' }))
82
+ });
83
+ try {
84
+ const res = await api().Certificate.validate(FIXTURE_PEM);
85
+ assert.deepEqual(mock.paths, [GET_PATH]);
86
+ assert.equal(res.fingerprint, FIXTURE_FINGERPRINT);
87
+ assert.equal(res.username, 'alice@example.com');
88
+ assert.equal(res.expired, false);
89
+ assert.equal(res.known, true);
90
+ assert.equal(res.revoked, false);
91
+ assert.equal(res.revocationDate, undefined);
92
+ assert.equal(res.valid, true);
93
+ assert.equal(res.certificate?.id, 2);
94
+ }
95
+ finally {
96
+ mock.restore();
97
+ }
98
+ });
99
+ test('Certificate.validate reports a revoked certificate', async () => {
100
+ const mock = mockRoutes({
101
+ [GET_PATH]: () => mockResponse(200, 'application/json', certItem({ id: 2, hash: FIXTURE_FINGERPRINT, userDn: 'alice@example.com', revocationDate: '2026-08-24T21:00:00.000Z' }))
102
+ });
103
+ try {
104
+ const res = await api().Certificate.validate(FIXTURE_PEM);
105
+ assert.equal(res.known, true);
106
+ assert.equal(res.revoked, true);
107
+ assert.equal(res.revocationDate, '2026-08-24T21:00:00.000Z');
108
+ assert.equal(res.expired, false);
109
+ assert.equal(res.valid, false);
110
+ }
111
+ finally {
112
+ mock.restore();
113
+ }
114
+ });
115
+ test('Certificate.validate falls back to the user list when get(hash) answers 500 and matches case-insensitively', async () => {
116
+ const mock = mockRoutes({
117
+ [GET_PATH]: () => mockResponse(500, 'text/html', TOMCAT_500),
118
+ [LIST_PATH]: () => mockResponse(200, 'application/json', certList([
119
+ { id: 1, hash: 'AA:BB', userDn: 'alice@example.com', revocationDate: '2026-01-01T00:00:00.000Z' },
120
+ { id: 2, hash: FIXTURE_FINGERPRINT.toLowerCase(), userDn: 'alice@example.com' }
121
+ ]))
122
+ });
123
+ try {
124
+ const res = await api().Certificate.validate(FIXTURE_PEM);
125
+ assert.deepEqual(mock.paths, [GET_PATH, LIST_PATH]);
126
+ assert.equal(res.known, true);
127
+ assert.equal(res.revoked, false);
128
+ assert.equal(res.valid, true);
129
+ assert.equal(res.certificate?.id, 2);
130
+ }
131
+ finally {
132
+ mock.restore();
133
+ }
134
+ });
135
+ test('Certificate.validate reports an unknown certificate as not revoked but not known', async () => {
136
+ const mock = mockRoutes({
137
+ [GET_PATH]: () => mockResponse(500, 'text/html', TOMCAT_500),
138
+ [LIST_PATH]: () => mockResponse(200, 'application/json', certList([]))
139
+ });
140
+ try {
141
+ const res = await api().Certificate.validate(FIXTURE_PEM);
142
+ assert.deepEqual(mock.paths, [GET_PATH, LIST_PATH]);
143
+ assert.equal(res.known, false);
144
+ assert.equal(res.revoked, false);
145
+ assert.equal(res.certificate, undefined);
146
+ assert.equal(res.valid, true);
147
+ }
148
+ finally {
149
+ mock.restore();
150
+ }
151
+ });
152
+ test('Certificate.validate rethrows non-500 failures from get(hash) without consulting the list', async () => {
153
+ const mock = mockRoutes({
154
+ [GET_PATH]: () => mockResponse(403, 'text/plain', 'Forbidden')
155
+ });
156
+ try {
157
+ await assert.rejects(() => api().Certificate.validate(FIXTURE_PEM), (err) => {
158
+ assert.equal(err.status, 403);
159
+ return true;
160
+ });
161
+ assert.deepEqual(mock.paths, [GET_PATH]);
162
+ }
163
+ finally {
164
+ mock.restore();
165
+ }
166
+ });
167
+ test('Certificate.validate evaluates expiry locally against the supplied clock', async () => {
168
+ const mock = mockRoutes({
169
+ [GET_PATH]: () => mockResponse(200, 'application/json', certItem({ id: 2, hash: FIXTURE_FINGERPRINT, userDn: 'alice@example.com' }))
170
+ });
171
+ try {
172
+ const res = await api().Certificate.validate(FIXTURE_PEM, { now: new Date('2040-01-01T00:00:00Z') });
173
+ assert.equal(res.expired, true);
174
+ assert.equal(res.revoked, false);
175
+ assert.equal(res.valid, false);
176
+ }
177
+ finally {
178
+ mock.restore();
179
+ }
180
+ });
181
+ test('Certificate.validate rejects a PEM that is not a certificate', async () => {
182
+ await assert.rejects(() => api().Certificate.validate('not a certificate'));
183
+ });
184
+ test('Certificate.probe reports accepted credentials with the server version', async () => {
185
+ const mock = mockRoutes({
186
+ '/Marti/api/version': () => mockResponse(200, 'text/plain', 'TAK Server 5.4-RELEASE-1\n')
187
+ });
188
+ try {
189
+ const res = await api().Certificate.probe();
190
+ assert.deepEqual(mock.paths, ['/Marti/api/version']);
191
+ assert.deepEqual(res, { accepted: true, version: 'TAK Server 5.4-RELEASE-1' });
192
+ }
193
+ finally {
194
+ mock.restore();
195
+ }
196
+ });
197
+ test('Certificate.probe classifies a RevokedException error page as revoked', async () => {
198
+ const mock = mockRoutes({
199
+ '/Marti/api/version': () => mockResponse(500, 'text/html', [
200
+ '<!doctype html><html lang="en"><head><title>HTTP Status 500 – Internal Server Error</title></head><body>',
201
+ '<h1>HTTP Status 500 – Internal Server Error</h1><p><b>Type</b> Exception Report</p>',
202
+ '<p><b>Message</b> Exception performing TAK Server authentication</p>',
203
+ '<p><b>Exception</b></p><pre>org.springframework.security.authentication.BadCredentialsException: Exception performing TAK Server authentication</pre>',
204
+ '<p><b>Root Cause</b></p><pre>com.bbn.marti.remote.exception.RevokedException: Attempt to use revoked certificate : CN=alice@example.com,OU=WILDFIRE,O=CO-TAK</pre>',
205
+ '</body></html>'
206
+ ].join(''))
207
+ });
208
+ try {
209
+ const res = await api().Certificate.probe();
210
+ assert.equal(res.accepted, false);
211
+ assert.equal(res.reason, 'revoked');
212
+ assert.ok(res.message);
213
+ }
214
+ finally {
215
+ mock.restore();
216
+ }
217
+ });
218
+ test('Certificate.probe classifies a BadCredentialsException without a revocation cause as rejected', async () => {
219
+ const mock = mockRoutes({
220
+ '/Marti/api/version': () => mockResponse(500, 'text/plain', 'org.springframework.security.authentication.BadCredentialsException: Exception performing TAK Server authentication')
221
+ });
222
+ try {
223
+ const res = await api().Certificate.probe();
224
+ assert.equal(res.accepted, false);
225
+ assert.equal(res.reason, 'rejected');
226
+ }
227
+ finally {
228
+ mock.restore();
229
+ }
230
+ });
231
+ test('Certificate.probe classifies a TLS handshake failure as tls', async () => {
232
+ const originalRequest = Client.prototype.request;
233
+ Client.prototype.request = async function () {
234
+ const err = new Error('fetch failed');
235
+ err.cause = Object.assign(new Error('write EPROTO SSL routines:ssl3_read_bytes:sslv3 alert bad certificate'), { code: 'EPROTO' });
236
+ throw err;
237
+ };
238
+ try {
239
+ const res = await api().Certificate.probe();
240
+ assert.equal(res.accepted, false);
241
+ assert.equal(res.reason, 'tls');
242
+ assert.match(res.message ?? '', /EPROTO/);
243
+ }
244
+ finally {
245
+ Client.prototype.request = originalRequest;
246
+ }
247
+ });
248
+ test('Certificate.probe rethrows errors that are not authentication failures', async () => {
249
+ const mock = mockRoutes({
250
+ '/Marti/api/version': () => mockResponse(503, 'text/plain', 'Service Unavailable')
251
+ });
252
+ try {
253
+ await assert.rejects(() => api().Certificate.probe(), (err) => {
254
+ assert.equal(err.status, 503);
255
+ return true;
256
+ });
257
+ }
258
+ finally {
259
+ mock.restore();
260
+ }
261
+ });
262
+ test('Certificate.downloadIds streams the ZIP archive for the given IDs', async () => {
263
+ const mock = mockRoutes({
264
+ '/Marti/api/certadmin/cert/download/1,2,3': () => mockResponse(200, 'application/zip', Buffer.from('zip-bytes'))
265
+ });
266
+ try {
267
+ const body = await collect(await api().Certificate.downloadIds([1, '2', 3]));
268
+ assert.deepEqual(mock.paths, ['/Marti/api/certadmin/cert/download/1,2,3']);
269
+ assert.equal(body.toString(), 'zip-bytes');
270
+ }
271
+ finally {
272
+ mock.restore();
273
+ }
274
+ });
275
+ test('Certificate.downloadIds rejects an empty ID list without a request', async () => {
276
+ const mock = mockRoutes({});
277
+ try {
278
+ await assert.rejects(() => api().Certificate.downloadIds([]), /At least one ID must be provided/);
279
+ assert.deepEqual(mock.paths, []);
280
+ }
281
+ finally {
282
+ mock.restore();
283
+ }
284
+ });
285
+ test('Certificate.downloadIds throws on a TAK Server error instead of streaming the error page', async () => {
286
+ const mock = mockRoutes({
287
+ '/Marti/api/certadmin/cert/download/1': () => mockResponse(500, 'text/plain', 'exception in downloadCertificates!')
288
+ });
289
+ try {
290
+ await assert.rejects(() => api().Certificate.downloadIds([1]), (err) => {
291
+ assert.ok(err instanceof Error);
292
+ assert.equal(err.status, 500);
293
+ assert.match(err.message, /downloadCertificates/);
294
+ return true;
295
+ });
296
+ }
297
+ finally {
298
+ mock.restore();
299
+ }
300
+ });
301
+ //# sourceMappingURL=certificate.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"certificate.test.js","sourceRoot":"","sources":["../../test/certificate.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,115 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import Err from '@openaddresses/batch-error';
4
+ import { TAKServerError, isHTML, parseHTMLError, summarizeHTMLError, formatHTMLError, } from '../lib/utils/html-error.js';
5
+ const TOMCAT = `<!doctype html><html lang="en"><head><title>HTTP Status 500 &ndash; Internal Server Error</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 500 &ndash; Internal Server Error</h1><hr class="line" /><p><b>Type</b> Exception Report</p><p><b>Message</b> Exception performing TAK Server authentication</p><p><b>Description</b> The server encountered an unexpected condition that prevented it from fulfilling the request.</p><p><b>Exception</b></p><pre>org.springframework.security.authentication.BadCredentialsException: Exception performing TAK Server authentication
6
+ \tcom.bbn.marti.util.spring.TakAuthenticationProvider.authenticateCore(TakAuthenticationProvider.java:190)
7
+ \tcom.bbn.marti.util.spring.TakAuthenticationProvider.authenticate(TakAuthenticationProvider.java:244)
8
+ \torg.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:181)
9
+ </pre><p><b>Note</b> The full stack trace of the root cause is available in the server logs.</p><hr class="line" /><h3>Apache Tomcat/9.0.86</h3></body></html>`;
10
+ test('html-error: isHTML', () => {
11
+ assert.equal(isHTML(TOMCAT), true);
12
+ assert.equal(isHTML(' <html><body>x</body></html>'), true);
13
+ assert.equal(isHTML('<h1>HTTP Status 404 – Not Found</h1>'), true);
14
+ assert.equal(isHTML('{"message":"nope"}'), false);
15
+ assert.equal(isHTML('Plain text failure'), false);
16
+ assert.equal(isHTML(null), false);
17
+ assert.equal(isHTML({}), false);
18
+ });
19
+ test('html-error: entities are decoded', () => {
20
+ const parsed = parseHTMLError('<html><body><p><b>Message</b> a &ndash; b &amp; &lt;c&gt; &#39;d&#39; &#x41;</p></body></html>');
21
+ assert.ok(parsed);
22
+ assert.equal(parsed.fields.Message, 'a – b & <c> \'d\' A');
23
+ });
24
+ test('html-error: malformed HTML strips hidden blocks & unterminated tags', () => {
25
+ const parsed = parseHTMLError('<html><script>let a = "<b>";</script><body><STYLE>b {x:y}</STYLE><p class="x">Gateway <b>Timeout</b><script>never closed');
26
+ assert.ok(parsed);
27
+ assert.equal(parsed.text, 'Gateway Timeout');
28
+ assert.equal(parseHTMLError('<html><body>Broken <tag')?.text, 'Broken');
29
+ // Pathological input scales linearly (CodeQL polynomial regex finding)
30
+ const time = (n) => {
31
+ const big = '<html>' + '<head\t'.repeat(n) + '<'.repeat(n);
32
+ const start = performance.now();
33
+ assert.ok(parseHTMLError(big));
34
+ return performance.now() - start;
35
+ };
36
+ time(1000); // warm up
37
+ const small = time(10000);
38
+ const large = time(40000);
39
+ assert.ok(large < Math.max(small * 10, 50), `4x input took ${large}ms vs ${small}ms`);
40
+ });
41
+ test('html-error: TAKServerError from non-HTML body', () => {
42
+ const err = new TAKServerError(502, 'upstream <b>down</b>');
43
+ assert.equal(err.message, 'upstream down');
44
+ assert.equal(err.details, 'upstream down');
45
+ });
46
+ test('html-error: malformed HTML falls back to visible text', () => {
47
+ const parsed = parseHTMLError('<html><head><style>b {x:y}</style></head><body><p>Bad Gateway<br>upstream unavailable</body></html>');
48
+ assert.ok(parsed);
49
+ assert.equal(parsed.title, undefined);
50
+ assert.equal(parsed.text, 'Bad Gateway upstream unavailable');
51
+ assert.equal(summarizeHTMLError(parsed, 502), 'Bad Gateway upstream unavailable');
52
+ });
53
+ test('html-error: parseHTMLError - Tomcat Exception Report', () => {
54
+ const parsed = parseHTMLError(TOMCAT);
55
+ assert.ok(parsed);
56
+ assert.equal(parsed.title, 'HTTP Status 500 – Internal Server Error');
57
+ assert.deepEqual(parsed.fields, {
58
+ Type: 'Exception Report',
59
+ Message: 'Exception performing TAK Server authentication',
60
+ Description: 'The server encountered an unexpected condition that prevented it from fulfilling the request.',
61
+ Note: 'The full stack trace of the root cause is available in the server logs.',
62
+ });
63
+ assert.ok(parsed.exception);
64
+ assert.ok(parsed.exception.startsWith('org.springframework.security.authentication.BadCredentialsException: Exception performing TAK Server authentication\n'));
65
+ assert.ok(parsed.exception.includes('TakAuthenticationProvider.java:244'));
66
+ assert.ok(!parsed.exception.endsWith('\n'));
67
+ // Styles & exception body are excluded from the text fallback
68
+ assert.ok(!parsed.text.includes('font-family'));
69
+ assert.ok(!parsed.text.includes('TakAuthenticationProvider'));
70
+ assert.ok(parsed.text.includes('Apache Tomcat/9.0.86'));
71
+ assert.equal(summarizeHTMLError(parsed), 'Exception performing TAK Server authentication');
72
+ const formatted = formatHTMLError(parsed);
73
+ assert.equal(formatted.split('\n').slice(0, 6).join('\n'), [
74
+ 'HTTP Status 500 – Internal Server Error',
75
+ 'Type: Exception Report',
76
+ 'Message: Exception performing TAK Server authentication',
77
+ 'Description: The server encountered an unexpected condition that prevented it from fulfilling the request.',
78
+ 'Note: The full stack trace of the root cause is available in the server logs.',
79
+ '',
80
+ ].join('\n'));
81
+ assert.ok(formatted.endsWith('ProviderManager.authenticate(ProviderManager.java:181)'));
82
+ });
83
+ test('html-error: parseHTMLError - status page without Message', () => {
84
+ const parsed = parseHTMLError('<!doctype html><html><head><title>HTTP Status 404 &ndash; Not Found</title></head><body><h1>HTTP Status 404 &ndash; Not Found</h1><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server did not find a current representation for the target resource.</p></body></html>');
85
+ assert.ok(parsed);
86
+ assert.equal(summarizeHTMLError(parsed, 404), 'The origin server did not find a current representation for the target resource.');
87
+ });
88
+ test('html-error: parseHTMLError - exception only', () => {
89
+ const parsed = parseHTMLError('<html><body><pre>java.lang.IllegalStateException: Mission already exists</pre></body></html>');
90
+ assert.ok(parsed);
91
+ assert.equal(summarizeHTMLError(parsed), 'Mission already exists');
92
+ assert.equal(formatHTMLError(parsed), 'java.lang.IllegalStateException: Mission already exists');
93
+ });
94
+ test('html-error: parseHTMLError - unstructured HTML', () => {
95
+ const parsed = parseHTMLError('<html><body><div>Service Unavailable</div></body></html>');
96
+ assert.ok(parsed);
97
+ assert.equal(parsed.title, undefined);
98
+ assert.equal(summarizeHTMLError(parsed, 503), 'Service Unavailable');
99
+ assert.equal(formatHTMLError(parsed), 'Service Unavailable');
100
+ assert.equal(parseHTMLError('not html'), null);
101
+ });
102
+ test('html-error: TAKServerError', () => {
103
+ const err = new TAKServerError(500, TOMCAT);
104
+ assert.ok(err instanceof Err);
105
+ assert.ok(err instanceof Error);
106
+ assert.equal(err.name, 'PublicError');
107
+ assert.equal(err.status, 500);
108
+ assert.equal(err.message, 'Exception performing TAK Server authentication');
109
+ assert.equal(err.safe, 'Exception performing TAK Server authentication');
110
+ assert.ok(err.details.startsWith('HTTP Status 500 – Internal Server Error\nType: Exception Report\n'));
111
+ assert.ok(err.details.includes('BadCredentialsException'));
112
+ assert.equal(err.html, TOMCAT);
113
+ assert.equal(err.parsed.fields.Type, 'Exception Report');
114
+ });
115
+ //# sourceMappingURL=html-error.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-error.test.js","sourceRoot":"","sources":["../../test/html-error.test.ts"],"names":[],"mappings":"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"}