@t0mer/n8n-nodes-ssllabs 2026.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/LICENSE +21 -0
- package/README.md +234 -0
- package/dist/assets/demo/ssllabs-demo.png +0 -0
- package/dist/credentials/SslLabsApi.credentials.d.ts +10 -0
- package/dist/credentials/SslLabsApi.credentials.js +46 -0
- package/dist/credentials/SslLabsApi.credentials.js.map +1 -0
- package/dist/credentials/sslLabs.dark.svg +9 -0
- package/dist/credentials/sslLabs.svg +9 -0
- package/dist/nodes/SslLabs/SslLabs.node.d.ts +5 -0
- package/dist/nodes/SslLabs/SslLabs.node.js +91 -0
- package/dist/nodes/SslLabs/SslLabs.node.js.map +1 -0
- package/dist/nodes/SslLabs/SslLabs.node.json +18 -0
- package/dist/nodes/SslLabs/resources/assessment.d.ts +11 -0
- package/dist/nodes/SslLabs/resources/assessment.js +258 -0
- package/dist/nodes/SslLabs/resources/assessment.js.map +1 -0
- package/dist/nodes/SslLabs/resources/endpoint.d.ts +2 -0
- package/dist/nodes/SslLabs/resources/endpoint.js +77 -0
- package/dist/nodes/SslLabs/resources/endpoint.js.map +1 -0
- package/dist/nodes/SslLabs/resources/registration.d.ts +2 -0
- package/dist/nodes/SslLabs/resources/registration.js +71 -0
- package/dist/nodes/SslLabs/resources/registration.js.map +1 -0
- package/dist/nodes/SslLabs/resources/service.d.ts +3 -0
- package/dist/nodes/SslLabs/resources/service.js +86 -0
- package/dist/nodes/SslLabs/resources/service.js.map +1 -0
- package/dist/nodes/SslLabs/shared.d.ts +11 -0
- package/dist/nodes/SslLabs/shared.js +26 -0
- package/dist/nodes/SslLabs/shared.js.map +1 -0
- package/dist/nodes/SslLabs/sslLabs.dark.svg +9 -0
- package/dist/nodes/SslLabs/sslLabs.svg +9 -0
- package/dist/nodes/SslLabsTrigger/SslLabsTrigger.node.d.ts +5 -0
- package/dist/nodes/SslLabsTrigger/SslLabsTrigger.node.js +237 -0
- package/dist/nodes/SslLabsTrigger/SslLabsTrigger.node.js.map +1 -0
- package/dist/nodes/SslLabsTrigger/SslLabsTrigger.node.json +18 -0
- package/dist/nodes/SslLabsTrigger/sslLabs.dark.svg +9 -0
- package/dist/nodes/SslLabsTrigger/sslLabs.svg +9 -0
- package/dist/nodes/SslLabsTrigger/state.d.ts +35 -0
- package/dist/nodes/SslLabsTrigger/state.js +122 -0
- package/dist/nodes/SslLabsTrigger/state.js.map +1 -0
- package/dist/package.json +73 -0
- package/dist/shared/batch.d.ts +24 -0
- package/dist/shared/batch.js +66 -0
- package/dist/shared/batch.js.map +1 -0
- package/dist/shared/grades.d.ts +8 -0
- package/dist/shared/grades.js +38 -0
- package/dist/shared/grades.js.map +1 -0
- package/dist/shared/host.d.ts +2 -0
- package/dist/shared/host.js +38 -0
- package/dist/shared/host.js.map +1 -0
- package/dist/shared/poll.d.ts +29 -0
- package/dist/shared/poll.js +81 -0
- package/dist/shared/poll.js.map +1 -0
- package/dist/shared/summary.d.ts +8 -0
- package/dist/shared/summary.js +87 -0
- package/dist/shared/summary.js.map +1 -0
- package/dist/shared/transport.d.ts +37 -0
- package/dist/shared/transport.js +161 -0
- package/dist/shared/transport.js.map +1 -0
- package/dist/shared/types.d.ts +88 -0
- package/dist/shared/types.js +3 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SslLabsTrigger = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const grades_1 = require("../../shared/grades");
|
|
6
|
+
const batch_1 = require("../../shared/batch");
|
|
7
|
+
const host_1 = require("../../shared/host");
|
|
8
|
+
const poll_1 = require("../../shared/poll");
|
|
9
|
+
const summary_1 = require("../../shared/summary");
|
|
10
|
+
const transport_1 = require("../../shared/transport");
|
|
11
|
+
const state_1 = require("./state");
|
|
12
|
+
const BUDGET_SHARE = 0.8;
|
|
13
|
+
const properties = [
|
|
14
|
+
{
|
|
15
|
+
displayName: 'Hosts',
|
|
16
|
+
name: 'hosts',
|
|
17
|
+
type: 'string',
|
|
18
|
+
typeOptions: { multipleValues: true, multipleValueButtonText: 'Add Host' },
|
|
19
|
+
default: [],
|
|
20
|
+
placeholder: 'example.com',
|
|
21
|
+
description: 'Hostnames to watch. Only watch hosts you own or are authorized to test.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
displayName: 'Event',
|
|
25
|
+
name: 'event',
|
|
26
|
+
type: 'options',
|
|
27
|
+
options: [
|
|
28
|
+
{
|
|
29
|
+
name: 'Certificate Expiring',
|
|
30
|
+
value: 'certificateExpiring',
|
|
31
|
+
description: 'Fires once per certificate when it expires within the given number of days',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'Grade Below Threshold',
|
|
35
|
+
value: 'gradeBelowThreshold',
|
|
36
|
+
description: 'Fires once when the grade drops below the threshold; re-arms when the grade recovers',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Grade Changed',
|
|
40
|
+
value: 'gradeChanged',
|
|
41
|
+
description: "Fires when a host's grade differs from the last seen grade",
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
default: 'gradeChanged',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
displayName: 'Threshold Grade',
|
|
48
|
+
name: 'thresholdGrade',
|
|
49
|
+
type: 'options',
|
|
50
|
+
options: grades_1.GRADE_ORDER.map((grade) => ({ name: grade, value: grade })),
|
|
51
|
+
default: 'A',
|
|
52
|
+
description: 'Fire when the grade is worse than this. T (trust issues) and M (name mismatch) rank below F.',
|
|
53
|
+
displayOptions: { show: { event: ['gradeBelowThreshold'] } },
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
displayName: 'Days Before Expiry',
|
|
57
|
+
name: 'daysBeforeExpiry',
|
|
58
|
+
type: 'number',
|
|
59
|
+
default: 21,
|
|
60
|
+
typeOptions: { minValue: 0 },
|
|
61
|
+
description: 'Fire when the certificate expires within this many days',
|
|
62
|
+
displayOptions: { show: { event: ['certificateExpiring'] } },
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
displayName: 'Max Result Age (Hours)',
|
|
66
|
+
name: 'maxAge',
|
|
67
|
+
type: 'number',
|
|
68
|
+
default: 24,
|
|
69
|
+
typeOptions: { minValue: 1 },
|
|
70
|
+
description: 'Cached SSL Labs results up to this old are reused; a host is re-assessed at most this often',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
displayName: 'Options',
|
|
74
|
+
name: 'options',
|
|
75
|
+
type: 'collection',
|
|
76
|
+
placeholder: 'Add Option',
|
|
77
|
+
default: {},
|
|
78
|
+
options: [
|
|
79
|
+
{
|
|
80
|
+
displayName: 'Emit Errors',
|
|
81
|
+
name: 'emitErrors',
|
|
82
|
+
type: 'boolean',
|
|
83
|
+
default: false,
|
|
84
|
+
description: 'Whether to emit an item when an assessment ends in ERROR',
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
function readHosts(ctx) {
|
|
90
|
+
const raw = ctx.getNodeParameter('hosts', []);
|
|
91
|
+
const values = (Array.isArray(raw) ? raw : [raw]).flatMap((v) => String(v).split(/[\s,]+/));
|
|
92
|
+
const hosts = values.filter((v) => v.trim() !== '').map((v) => (0, host_1.normalizeHost)(v, ctx.getNode()));
|
|
93
|
+
return [...new Set(hosts)];
|
|
94
|
+
}
|
|
95
|
+
function classify(error) {
|
|
96
|
+
if (error instanceof n8n_workflow_1.NodeApiError) {
|
|
97
|
+
if (error.message === transport_1.UNREGISTERED_EMAIL_MESSAGE)
|
|
98
|
+
return 'fatal';
|
|
99
|
+
if ((0, transport_1.isBusyError)(error))
|
|
100
|
+
return 'busy';
|
|
101
|
+
const status = Number(error.httpCode);
|
|
102
|
+
if (status === 400)
|
|
103
|
+
return 'host';
|
|
104
|
+
return status >= 400 && status < 500 ? 'fatal' : 'transient';
|
|
105
|
+
}
|
|
106
|
+
const code = error === null || error === void 0 ? void 0 : error.code;
|
|
107
|
+
const isNetwork = (error === null || error === void 0 ? void 0 : error.isAxiosError) === true;
|
|
108
|
+
return isNetwork || (typeof code === 'string' && /^E[A-Z]+/.test(code)) ? 'transient' : 'fatal';
|
|
109
|
+
}
|
|
110
|
+
class SslLabsTrigger {
|
|
111
|
+
constructor() {
|
|
112
|
+
this.description = {
|
|
113
|
+
displayName: 'SSL Labs Trigger',
|
|
114
|
+
name: 'sslLabsTrigger',
|
|
115
|
+
icon: { light: 'file:sslLabs.svg', dark: 'file:sslLabs.dark.svg' },
|
|
116
|
+
group: ['trigger'],
|
|
117
|
+
version: 1,
|
|
118
|
+
subtitle: '={{$parameter["event"]}}',
|
|
119
|
+
description: 'Watch SSL Labs grades and certificates of your hosts',
|
|
120
|
+
defaults: { name: 'SSL Labs Trigger' },
|
|
121
|
+
polling: true,
|
|
122
|
+
inputs: [],
|
|
123
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
124
|
+
credentials: [{ name: 'sslLabsApi', required: true }],
|
|
125
|
+
properties: [...properties],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async poll() {
|
|
129
|
+
var _a;
|
|
130
|
+
const hosts = readHosts(this);
|
|
131
|
+
const maxAge = this.getNodeParameter('maxAge', 24);
|
|
132
|
+
const options = this.getNodeParameter('options', {});
|
|
133
|
+
const threshold = this.getNodeParameter('thresholdGrade', 'A');
|
|
134
|
+
const config = {
|
|
135
|
+
event: this.getNodeParameter('event', 'gradeChanged'),
|
|
136
|
+
emitErrors: options.emitErrors === true,
|
|
137
|
+
thresholdGrade: (0, grades_1.isGrade)(threshold) ? threshold : 'A',
|
|
138
|
+
daysBeforeExpiry: this.getNodeParameter('daysBeforeExpiry', 21),
|
|
139
|
+
};
|
|
140
|
+
const analyzeOnce = async (host) => await (0, poll_1.analyze)(this, { host, fromCache: true, maxAge, all: 'done' }, true, { retry: false });
|
|
141
|
+
let coolOffMs;
|
|
142
|
+
const waitCoolOff = async () => {
|
|
143
|
+
if (coolOffMs === undefined) {
|
|
144
|
+
coolOffMs = batch_1.DEFAULT_COOL_OFF_MS;
|
|
145
|
+
try {
|
|
146
|
+
const { body } = await (0, transport_1.sslLabsRequest)(this, { path: 'info', retry: false });
|
|
147
|
+
const value = Number(body === null || body === void 0 ? void 0 : body.newAssessmentCoolOff);
|
|
148
|
+
if (value > 0)
|
|
149
|
+
coolOffMs = value + 100;
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
await (0, n8n_workflow_1.sleep)(coolOffMs);
|
|
155
|
+
};
|
|
156
|
+
if (this.getMode() === 'manual') {
|
|
157
|
+
const items = [];
|
|
158
|
+
for (const [index, host] of hosts.entries()) {
|
|
159
|
+
const result = await analyzeOnce(host);
|
|
160
|
+
items.push({ json: { ...(0, summary_1.toSummary)({ ...result, host }), event: 'test' } });
|
|
161
|
+
if (!(0, poll_1.isFinished)(result) && index < hosts.length - 1)
|
|
162
|
+
await waitCoolOff();
|
|
163
|
+
}
|
|
164
|
+
return items.length ? [items] : null;
|
|
165
|
+
}
|
|
166
|
+
const state = (0, state_1.loadState)(this.getWorkflowStaticData('node'));
|
|
167
|
+
const started = Date.now();
|
|
168
|
+
const budgetMs = typeof this.getPollBudgetMs === 'function'
|
|
169
|
+
? this.getPollBudgetMs()
|
|
170
|
+
: Number.POSITIVE_INFINITY;
|
|
171
|
+
const ordered = [...hosts].sort((a, b) => { var _a, _b, _c, _d; return ((_b = (_a = state.hosts[a]) === null || _a === void 0 ? void 0 : _a.lastCheckedAt) !== null && _b !== void 0 ? _b : 0) - ((_d = (_c = state.hosts[b]) === null || _c === void 0 ? void 0 : _c.lastCheckedAt) !== null && _d !== void 0 ? _d : 0); });
|
|
172
|
+
const emitted = [];
|
|
173
|
+
let fatal;
|
|
174
|
+
let checked = 0;
|
|
175
|
+
let transientFailures = 0;
|
|
176
|
+
let lastTransient;
|
|
177
|
+
for (const [index, host] of ordered.entries()) {
|
|
178
|
+
if (checked++ > 0 && Date.now() - started >= budgetMs * BUDGET_SHARE)
|
|
179
|
+
break;
|
|
180
|
+
let result;
|
|
181
|
+
try {
|
|
182
|
+
result = await analyzeOnce(host);
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
const failure = classify(error);
|
|
186
|
+
if (failure === 'fatal') {
|
|
187
|
+
fatal =
|
|
188
|
+
error instanceof n8n_workflow_1.NodeApiError || error instanceof n8n_workflow_1.NodeOperationError
|
|
189
|
+
? error
|
|
190
|
+
: new n8n_workflow_1.NodeOperationError(this.getNode(), error);
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
(0, state_1.touch)(state, host, Date.now());
|
|
194
|
+
if (failure === 'busy')
|
|
195
|
+
break;
|
|
196
|
+
if (failure === 'transient') {
|
|
197
|
+
transientFailures++;
|
|
198
|
+
lastTransient = error;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const summary = (0, summary_1.toSummary)({
|
|
202
|
+
host,
|
|
203
|
+
status: 'ERROR',
|
|
204
|
+
statusMessage: error.message,
|
|
205
|
+
});
|
|
206
|
+
emitted.push(...(0, state_1.applyResult)(state, summary, config, Date.now()));
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const summary = (0, summary_1.toSummary)({ ...result, host });
|
|
210
|
+
if (!(0, poll_1.isFinished)(result)) {
|
|
211
|
+
const wasPending = ((_a = state.hosts[host]) === null || _a === void 0 ? void 0 : _a.pending) === true;
|
|
212
|
+
(0, state_1.markPending)(state, host, Date.now());
|
|
213
|
+
if (!wasPending && index < ordered.length - 1)
|
|
214
|
+
await waitCoolOff();
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
emitted.push(...(0, state_1.applyResult)(state, summary, config, Date.now()));
|
|
218
|
+
}
|
|
219
|
+
if (!fatal && ordered.length > 0 && transientFailures === ordered.length) {
|
|
220
|
+
fatal =
|
|
221
|
+
lastTransient instanceof n8n_workflow_1.NodeApiError
|
|
222
|
+
? lastTransient
|
|
223
|
+
: new n8n_workflow_1.NodeOperationError(this.getNode(), lastTransient, {
|
|
224
|
+
description: 'Check the Base URL in the SSL Labs API credential and your network.',
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
if (fatal)
|
|
228
|
+
throw fatal;
|
|
229
|
+
for (const host of Object.keys(state.hosts)) {
|
|
230
|
+
if (!hosts.includes(host))
|
|
231
|
+
delete state.hosts[host];
|
|
232
|
+
}
|
|
233
|
+
return emitted.length ? [emitted.map((e) => ({ json: e }))] : null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
exports.SslLabsTrigger = SslLabsTrigger;
|
|
237
|
+
//# sourceMappingURL=SslLabsTrigger.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SslLabsTrigger.node.js","sourceRoot":"","sources":["../../../nodes/SslLabsTrigger/SslLabsTrigger.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"node": "@t0mer/n8n-nodes-ssllabs.sslLabsTrigger",
|
|
3
|
+
"nodeVersion": "1.0",
|
|
4
|
+
"codexVersion": "1.0",
|
|
5
|
+
"categories": ["Development", "Developer Tools"],
|
|
6
|
+
"resources": {
|
|
7
|
+
"credentialDocumentation": [
|
|
8
|
+
{
|
|
9
|
+
"url": "https://github.com/t0mer/n8n-nodes-ssllabs#credentials"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"primaryDocumentation": [
|
|
13
|
+
{
|
|
14
|
+
"url": "https://github.com/t0mer/n8n-nodes-ssllabs#readme"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
|
|
2
|
+
<path d="M20 28v-7a12 12 0 0 1 24 0v7" fill="none" stroke="#58a6ff" stroke-width="5" stroke-linecap="round"/>
|
|
3
|
+
<rect x="10" y="27" width="44" height="31" rx="6" fill="#58a6ff"/>
|
|
4
|
+
<path d="M19 51a13 13 0 0 1 26 0" fill="none" stroke="#0d1117" stroke-width="3" stroke-linecap="round"/>
|
|
5
|
+
<path d="M19 51a13 13 0 0 1 6.5-11.26" fill="none" stroke="#e5534b" stroke-width="3" stroke-linecap="round"/>
|
|
6
|
+
<path d="M38.5 39.74A13 13 0 0 1 45 51" fill="none" stroke="#3fb950" stroke-width="3" stroke-linecap="round"/>
|
|
7
|
+
<line x1="32" y1="51" x2="39" y2="42" stroke="#0d1117" stroke-width="3" stroke-linecap="round"/>
|
|
8
|
+
<circle cx="32" cy="51" r="3" fill="#0d1117"/>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
|
|
2
|
+
<path d="M20 28v-7a12 12 0 0 1 24 0v7" fill="none" stroke="#1f5f8b" stroke-width="5" stroke-linecap="round"/>
|
|
3
|
+
<rect x="10" y="27" width="44" height="31" rx="6" fill="#1f5f8b"/>
|
|
4
|
+
<path d="M19 51a13 13 0 0 1 26 0" fill="none" stroke="#ffffff" stroke-width="3" stroke-linecap="round"/>
|
|
5
|
+
<path d="M19 51a13 13 0 0 1 6.5-11.26" fill="none" stroke="#e5534b" stroke-width="3" stroke-linecap="round"/>
|
|
6
|
+
<path d="M38.5 39.74A13 13 0 0 1 45 51" fill="none" stroke="#3fb950" stroke-width="3" stroke-linecap="round"/>
|
|
7
|
+
<line x1="32" y1="51" x2="39" y2="42" stroke="#ffffff" stroke-width="3" stroke-linecap="round"/>
|
|
8
|
+
<circle cx="32" cy="51" r="3" fill="#ffffff"/>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Grade } from '../../shared/grades';
|
|
2
|
+
import type { Summary, SummaryCertificate } from '../../shared/types';
|
|
3
|
+
export declare const STATE_VERSION = 1;
|
|
4
|
+
export type TriggerEvent = 'gradeChanged' | 'gradeBelowThreshold' | 'certificateExpiring';
|
|
5
|
+
export interface HostState {
|
|
6
|
+
grade: string | null;
|
|
7
|
+
testTime: string | null;
|
|
8
|
+
threshold: string | null;
|
|
9
|
+
belowThreshold: boolean;
|
|
10
|
+
certificate: SummaryCertificate | null;
|
|
11
|
+
certAlerted: boolean;
|
|
12
|
+
pending: boolean;
|
|
13
|
+
error: string | null;
|
|
14
|
+
lastCheckedAt: number;
|
|
15
|
+
}
|
|
16
|
+
export interface TriggerState {
|
|
17
|
+
stateVersion: typeof STATE_VERSION;
|
|
18
|
+
hosts: Record<string, HostState>;
|
|
19
|
+
}
|
|
20
|
+
export interface EventConfig {
|
|
21
|
+
event: TriggerEvent;
|
|
22
|
+
emitErrors: boolean;
|
|
23
|
+
thresholdGrade: Grade;
|
|
24
|
+
daysBeforeExpiry: number;
|
|
25
|
+
}
|
|
26
|
+
export type EventItem = Summary & {
|
|
27
|
+
event: TriggerEvent | 'assessmentError';
|
|
28
|
+
previousGrade?: string | null;
|
|
29
|
+
thresholdGrade?: string;
|
|
30
|
+
previousCertificate?: SummaryCertificate | null;
|
|
31
|
+
};
|
|
32
|
+
export declare function loadState(staticData: Record<string, unknown>): TriggerState;
|
|
33
|
+
export declare function touch(state: TriggerState, host: string, now: number): void;
|
|
34
|
+
export declare function markPending(state: TriggerState, host: string, now: number): void;
|
|
35
|
+
export declare function applyResult(state: TriggerState, summary: Summary, config: EventConfig, now: number): EventItem[];
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.STATE_VERSION = void 0;
|
|
4
|
+
exports.loadState = loadState;
|
|
5
|
+
exports.touch = touch;
|
|
6
|
+
exports.markPending = markPending;
|
|
7
|
+
exports.applyResult = applyResult;
|
|
8
|
+
const grades_1 = require("../../shared/grades");
|
|
9
|
+
exports.STATE_VERSION = 1;
|
|
10
|
+
const DAY_MS = 86400000;
|
|
11
|
+
function daysUntil(iso, now) {
|
|
12
|
+
const ms = iso ? Date.parse(iso) : Number.NaN;
|
|
13
|
+
return Number.isNaN(ms) ? null : Math.floor((ms - now) / DAY_MS);
|
|
14
|
+
}
|
|
15
|
+
const EMPTY_HOST = {
|
|
16
|
+
grade: null,
|
|
17
|
+
testTime: null,
|
|
18
|
+
threshold: null,
|
|
19
|
+
belowThreshold: false,
|
|
20
|
+
certificate: null,
|
|
21
|
+
certAlerted: false,
|
|
22
|
+
pending: false,
|
|
23
|
+
error: null,
|
|
24
|
+
};
|
|
25
|
+
function loadState(staticData) {
|
|
26
|
+
if (staticData.stateVersion !== exports.STATE_VERSION ||
|
|
27
|
+
typeof staticData.hosts !== 'object' ||
|
|
28
|
+
!staticData.hosts) {
|
|
29
|
+
staticData.stateVersion = exports.STATE_VERSION;
|
|
30
|
+
staticData.hosts = {};
|
|
31
|
+
}
|
|
32
|
+
return staticData;
|
|
33
|
+
}
|
|
34
|
+
function touch(state, host, now) {
|
|
35
|
+
var _a;
|
|
36
|
+
state.hosts[host] = { ...((_a = state.hosts[host]) !== null && _a !== void 0 ? _a : EMPTY_HOST), lastCheckedAt: now };
|
|
37
|
+
}
|
|
38
|
+
function markPending(state, host, now) {
|
|
39
|
+
const prev = state.hosts[host];
|
|
40
|
+
state.hosts[host] = prev
|
|
41
|
+
? { ...prev, pending: true, lastCheckedAt: now }
|
|
42
|
+
: { ...EMPTY_HOST, pending: true, lastCheckedAt: now };
|
|
43
|
+
}
|
|
44
|
+
function applyResult(state, summary, config, now) {
|
|
45
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
46
|
+
const prev = state.hosts[summary.host];
|
|
47
|
+
const baseline = !prev || (prev.grade === null && prev.testTime === null);
|
|
48
|
+
const events = [];
|
|
49
|
+
if (summary.status === 'ERROR') {
|
|
50
|
+
const error = (_a = summary.statusMessage) !== null && _a !== void 0 ? _a : 'Assessment failed';
|
|
51
|
+
const hadResult = !!prev && (prev.testTime !== null || prev.error !== null);
|
|
52
|
+
if (config.emitErrors && hadResult && prev.error !== error) {
|
|
53
|
+
events.push({ ...summary, event: 'assessmentError' });
|
|
54
|
+
}
|
|
55
|
+
state.hosts[summary.host] = {
|
|
56
|
+
...(prev !== null && prev !== void 0 ? prev : EMPTY_HOST),
|
|
57
|
+
pending: false,
|
|
58
|
+
error,
|
|
59
|
+
lastCheckedAt: now,
|
|
60
|
+
};
|
|
61
|
+
return events;
|
|
62
|
+
}
|
|
63
|
+
if (!baseline &&
|
|
64
|
+
config.event === 'gradeChanged' &&
|
|
65
|
+
prev.grade !== null &&
|
|
66
|
+
summary.grade !== null &&
|
|
67
|
+
prev.grade !== summary.grade) {
|
|
68
|
+
events.push({ ...summary, event: 'gradeChanged', previousGrade: prev.grade });
|
|
69
|
+
}
|
|
70
|
+
let threshold = null;
|
|
71
|
+
let belowThreshold = false;
|
|
72
|
+
if (config.event === 'gradeBelowThreshold') {
|
|
73
|
+
const tracked = !baseline && prev.threshold === config.thresholdGrade;
|
|
74
|
+
if (summary.grade !== null) {
|
|
75
|
+
const below = (0, grades_1.isWorseThan)(summary.grade, config.thresholdGrade);
|
|
76
|
+
if (tracked && below && !prev.belowThreshold) {
|
|
77
|
+
events.push({
|
|
78
|
+
...summary,
|
|
79
|
+
event: 'gradeBelowThreshold',
|
|
80
|
+
previousGrade: prev.grade,
|
|
81
|
+
thresholdGrade: config.thresholdGrade,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
threshold = config.thresholdGrade;
|
|
85
|
+
belowThreshold = below;
|
|
86
|
+
}
|
|
87
|
+
else if (tracked) {
|
|
88
|
+
threshold = config.thresholdGrade;
|
|
89
|
+
belowThreshold = prev.belowThreshold;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const certificate = (_c = (_b = summary.certificate) !== null && _b !== void 0 ? _b : prev === null || prev === void 0 ? void 0 : prev.certificate) !== null && _c !== void 0 ? _c : null;
|
|
93
|
+
const newCert = ((_d = certificate === null || certificate === void 0 ? void 0 : certificate.fingerprint) !== null && _d !== void 0 ? _d : null) !== ((_f = (_e = prev === null || prev === void 0 ? void 0 : prev.certificate) === null || _e === void 0 ? void 0 : _e.fingerprint) !== null && _f !== void 0 ? _f : null);
|
|
94
|
+
let certAlerted = newCert ? false : ((_g = prev === null || prev === void 0 ? void 0 : prev.certAlerted) !== null && _g !== void 0 ? _g : false);
|
|
95
|
+
const days = daysUntil(certificate === null || certificate === void 0 ? void 0 : certificate.notAfter, now);
|
|
96
|
+
if (!baseline &&
|
|
97
|
+
config.event === 'certificateExpiring' &&
|
|
98
|
+
!certAlerted &&
|
|
99
|
+
days !== null &&
|
|
100
|
+
days <= config.daysBeforeExpiry) {
|
|
101
|
+
events.push({
|
|
102
|
+
...summary,
|
|
103
|
+
certificate: certificate && { ...certificate, daysUntilExpiry: days },
|
|
104
|
+
event: 'certificateExpiring',
|
|
105
|
+
...(newCert && (prev === null || prev === void 0 ? void 0 : prev.certificate) ? { previousCertificate: prev.certificate } : {}),
|
|
106
|
+
});
|
|
107
|
+
certAlerted = true;
|
|
108
|
+
}
|
|
109
|
+
state.hosts[summary.host] = {
|
|
110
|
+
grade: (_j = (_h = summary.grade) !== null && _h !== void 0 ? _h : prev === null || prev === void 0 ? void 0 : prev.grade) !== null && _j !== void 0 ? _j : null,
|
|
111
|
+
testTime: summary.testTime,
|
|
112
|
+
threshold,
|
|
113
|
+
belowThreshold,
|
|
114
|
+
certificate,
|
|
115
|
+
certAlerted,
|
|
116
|
+
pending: false,
|
|
117
|
+
error: null,
|
|
118
|
+
lastCheckedAt: now,
|
|
119
|
+
};
|
|
120
|
+
return events;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../nodes/SslLabsTrigger/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@t0mer/n8n-nodes-ssllabs",
|
|
3
|
+
"version": "2026.9.0",
|
|
4
|
+
"description": "n8n community node for the Qualys SSL Labs API v4: run SSL/TLS assessments, fetch grades, and watch for grade or certificate changes.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/t0mer/n8n-nodes-ssllabs#readme",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/t0mer/n8n-nodes-ssllabs/issues"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public",
|
|
12
|
+
"provenance": true
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"n8n-community-node-package",
|
|
16
|
+
"n8n",
|
|
17
|
+
"ssllabs",
|
|
18
|
+
"ssl",
|
|
19
|
+
"tls",
|
|
20
|
+
"certificate",
|
|
21
|
+
"security"
|
|
22
|
+
],
|
|
23
|
+
"author": {
|
|
24
|
+
"name": "t0mer",
|
|
25
|
+
"email": "tomer.klein@gmail.com"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/t0mer/n8n-nodes-ssllabs.git"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "n8n-node build",
|
|
33
|
+
"build:watch": "tsc --watch",
|
|
34
|
+
"dev": "n8n-node dev",
|
|
35
|
+
"lint": "n8n-node lint",
|
|
36
|
+
"lint:fix": "n8n-node lint --fix",
|
|
37
|
+
"release": "n8n-node release",
|
|
38
|
+
"prepublishOnly": "n8n-node prerelease",
|
|
39
|
+
"test": "vitest run"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist"
|
|
43
|
+
],
|
|
44
|
+
"n8n": {
|
|
45
|
+
"n8nNodesApiVersion": 1,
|
|
46
|
+
"strict": true,
|
|
47
|
+
"credentials": [
|
|
48
|
+
"dist/credentials/SslLabsApi.credentials.js"
|
|
49
|
+
],
|
|
50
|
+
"nodes": [
|
|
51
|
+
"dist/nodes/SslLabs/SslLabs.node.js",
|
|
52
|
+
"dist/nodes/SslLabsTrigger/SslLabsTrigger.node.js"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@n8n/node-cli": "*",
|
|
57
|
+
"eslint": "9.32.0",
|
|
58
|
+
"prettier": "3.6.2",
|
|
59
|
+
"release-it": "^19.0.4",
|
|
60
|
+
"typescript": "5.9.2",
|
|
61
|
+
"vitest": "^3.2.7"
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"n8n-workflow": "*"
|
|
65
|
+
},
|
|
66
|
+
"allowScripts": {
|
|
67
|
+
"isolated-vm@7.0.1": true,
|
|
68
|
+
"cpu-features@0.0.10": true,
|
|
69
|
+
"ssh2@1.15.0": true,
|
|
70
|
+
"unrs-resolver@1.12.2": true,
|
|
71
|
+
"eslint-plugin-n8n-nodes-base": false
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type PoolResult<T> = {
|
|
2
|
+
ok: true;
|
|
3
|
+
value: T;
|
|
4
|
+
} | {
|
|
5
|
+
ok: false;
|
|
6
|
+
error: Error;
|
|
7
|
+
};
|
|
8
|
+
export interface PoolOptions {
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
stopOnError: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface PoolOutcome<T> {
|
|
13
|
+
results: Array<PoolResult<T> | undefined>;
|
|
14
|
+
firstError?: {
|
|
15
|
+
index: number;
|
|
16
|
+
error: Error;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export declare function runPool<T>(count: number, concurrency: number, worker: (index: number, signal: AbortSignal) => Promise<T>, options: PoolOptions): Promise<PoolOutcome<T>>;
|
|
20
|
+
export declare function createStartGate(coolOffMs: number, sleep?: (ms: number, signal?: AbortSignal) => Promise<void>, now?: () => number): (signal?: AbortSignal) => Promise<void>;
|
|
21
|
+
export declare const DEFAULT_BATCH_CONCURRENCY = 3;
|
|
22
|
+
export declare const MAX_BATCH_CONCURRENCY = 10;
|
|
23
|
+
export declare const DEFAULT_COOL_OFF_MS = 1100;
|
|
24
|
+
export declare function batchConcurrency(requested: unknown, maxAssessments: unknown, currentAssessments: unknown): number;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_COOL_OFF_MS = exports.MAX_BATCH_CONCURRENCY = exports.DEFAULT_BATCH_CONCURRENCY = void 0;
|
|
4
|
+
exports.runPool = runPool;
|
|
5
|
+
exports.createStartGate = createStartGate;
|
|
6
|
+
exports.batchConcurrency = batchConcurrency;
|
|
7
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
|
+
async function runPool(count, concurrency, worker, options) {
|
|
9
|
+
var _a, _b, _c;
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
const onParentAbort = () => controller.abort();
|
|
12
|
+
if ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted)
|
|
13
|
+
controller.abort();
|
|
14
|
+
(_b = options.signal) === null || _b === void 0 ? void 0 : _b.addEventListener('abort', onParentAbort, { once: true });
|
|
15
|
+
const outcome = { results: new Array(count).fill(undefined) };
|
|
16
|
+
let next = 0;
|
|
17
|
+
const runWorker = async () => {
|
|
18
|
+
while (next < count && !controller.signal.aborted) {
|
|
19
|
+
const index = next++;
|
|
20
|
+
try {
|
|
21
|
+
outcome.results[index] = { ok: true, value: await worker(index, controller.signal) };
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
outcome.results[index] = { ok: false, error: error };
|
|
25
|
+
if (options.stopOnError && !outcome.firstError) {
|
|
26
|
+
outcome.firstError = { index, error: error };
|
|
27
|
+
controller.abort();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
try {
|
|
33
|
+
const workers = Math.max(1, Math.min(concurrency, count));
|
|
34
|
+
await Promise.all(Array.from({ length: workers }, runWorker));
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
(_c = options.signal) === null || _c === void 0 ? void 0 : _c.removeEventListener('abort', onParentAbort);
|
|
38
|
+
}
|
|
39
|
+
return outcome;
|
|
40
|
+
}
|
|
41
|
+
function createStartGate(coolOffMs, sleep = n8n_workflow_1.sleep, now = Date.now) {
|
|
42
|
+
let lastStart = Number.NEGATIVE_INFINITY;
|
|
43
|
+
let queue = Promise.resolve();
|
|
44
|
+
return async (signal) => {
|
|
45
|
+
const turn = queue.then(async () => {
|
|
46
|
+
const wait = lastStart + coolOffMs - now();
|
|
47
|
+
if (wait > 0)
|
|
48
|
+
await sleep(wait, signal);
|
|
49
|
+
lastStart = now();
|
|
50
|
+
});
|
|
51
|
+
queue = turn.catch(() => undefined);
|
|
52
|
+
return await turn;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
exports.DEFAULT_BATCH_CONCURRENCY = 3;
|
|
56
|
+
exports.MAX_BATCH_CONCURRENCY = 10;
|
|
57
|
+
exports.DEFAULT_COOL_OFF_MS = 1100;
|
|
58
|
+
function batchConcurrency(requested, maxAssessments, currentAssessments) {
|
|
59
|
+
const wanted = Math.min(exports.MAX_BATCH_CONCURRENCY, Math.max(1, Math.floor(Number(requested) || exports.DEFAULT_BATCH_CONCURRENCY)));
|
|
60
|
+
const max = Number(maxAssessments);
|
|
61
|
+
if (!Number.isFinite(max) || max <= 0)
|
|
62
|
+
return wanted;
|
|
63
|
+
const free = max - (Number(currentAssessments) || 0);
|
|
64
|
+
return Math.max(1, Math.min(wanted, free));
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=batch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.js","sourceRoot":"","sources":["../../shared/batch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const GRADE_ORDER: readonly ["A+", "A", "A-", "B", "C", "D", "E", "F", "T", "M"];
|
|
2
|
+
export type Grade = (typeof GRADE_ORDER)[number];
|
|
3
|
+
export declare function isGrade(value: unknown): value is Grade;
|
|
4
|
+
export declare function gradeRank(grade: unknown): number | undefined;
|
|
5
|
+
export declare function compareGrades(a: unknown, b: unknown): number;
|
|
6
|
+
export declare function worstGrade(grades: unknown[]): Grade | null;
|
|
7
|
+
export declare function bestGrade(grades: unknown[]): Grade | null;
|
|
8
|
+
export declare function isWorseThan(grade: unknown, threshold: Grade): boolean;
|