asaihost-nodejs 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +518 -0
- package/dist/InterventionLog-25EHE4QR.cjs +30 -0
- package/dist/InterventionLog-25EHE4QR.cjs.map +1 -0
- package/dist/InterventionLog-R6CCPU2B.es.js +30 -0
- package/dist/InterventionLog-R6CCPU2B.es.js.map +1 -0
- package/dist/LogStore-ECXX34WC.es.js +54 -0
- package/dist/LogStore-ECXX34WC.es.js.map +1 -0
- package/dist/LogStore-M2AUBNGO.cjs +54 -0
- package/dist/LogStore-M2AUBNGO.cjs.map +1 -0
- package/dist/asaihost-nodejs.cjs +6761 -0
- package/dist/asaihost-nodejs.cjs.map +1 -0
- package/dist/asaihost-nodejs.d.cts +406 -0
- package/dist/asaihost-nodejs.d.ts +406 -0
- package/dist/asaihost-nodejs.es.js +6761 -0
- package/dist/asaihost-nodejs.es.js.map +1 -0
- package/dist/chunk-4WJDC4M4.es.js +311 -0
- package/dist/chunk-4WJDC4M4.es.js.map +1 -0
- package/dist/chunk-6U2I5EON.es.js +12 -0
- package/dist/chunk-6U2I5EON.es.js.map +1 -0
- package/dist/chunk-DROOHKB3.es.js +87 -0
- package/dist/chunk-DROOHKB3.es.js.map +1 -0
- package/dist/chunk-E7V3EBDB.es.js +435 -0
- package/dist/chunk-E7V3EBDB.es.js.map +1 -0
- package/dist/chunk-EOGZPRML.es.js +740 -0
- package/dist/chunk-EOGZPRML.es.js.map +1 -0
- package/dist/chunk-G4WVDUBH.cjs +463 -0
- package/dist/chunk-G4WVDUBH.cjs.map +1 -0
- package/dist/chunk-HHPGUJ75.cjs +162 -0
- package/dist/chunk-HHPGUJ75.cjs.map +1 -0
- package/dist/chunk-KOFCBB3N.cjs +87 -0
- package/dist/chunk-KOFCBB3N.cjs.map +1 -0
- package/dist/chunk-KUZQ36IJ.es.js +162 -0
- package/dist/chunk-KUZQ36IJ.es.js.map +1 -0
- package/dist/chunk-LOMSUPPU.es.js +1202 -0
- package/dist/chunk-LOMSUPPU.es.js.map +1 -0
- package/dist/chunk-MCMADJQR.es.js +463 -0
- package/dist/chunk-MCMADJQR.es.js.map +1 -0
- package/dist/chunk-NLBFIRDB.cjs +105 -0
- package/dist/chunk-NLBFIRDB.cjs.map +1 -0
- package/dist/chunk-O6LVLSNB.cjs +40 -0
- package/dist/chunk-O6LVLSNB.cjs.map +1 -0
- package/dist/chunk-QWVR2HNT.cjs +435 -0
- package/dist/chunk-QWVR2HNT.cjs.map +1 -0
- package/dist/chunk-SMVZ3ZDJ.cjs +12 -0
- package/dist/chunk-SMVZ3ZDJ.cjs.map +1 -0
- package/dist/chunk-UTKVJE7N.es.js +40 -0
- package/dist/chunk-UTKVJE7N.es.js.map +1 -0
- package/dist/chunk-VLMM735Q.cjs +1202 -0
- package/dist/chunk-VLMM735Q.cjs.map +1 -0
- package/dist/chunk-W7DVKGX5.cjs +311 -0
- package/dist/chunk-W7DVKGX5.cjs.map +1 -0
- package/dist/chunk-X7VHW2TD.cjs +740 -0
- package/dist/chunk-X7VHW2TD.cjs.map +1 -0
- package/dist/chunk-XBIN4C2V.es.js +105 -0
- package/dist/chunk-XBIN4C2V.es.js.map +1 -0
- package/dist/intervention-hooks-FI4EHOL5.es.js +21 -0
- package/dist/intervention-hooks-FI4EHOL5.es.js.map +1 -0
- package/dist/intervention-hooks-LE6X4C6N.cjs +21 -0
- package/dist/intervention-hooks-LE6X4C6N.cjs.map +1 -0
- package/dist/log-alert-GLCULMQP.es.js +51 -0
- package/dist/log-alert-GLCULMQP.es.js.map +1 -0
- package/dist/log-alert-LAJ6VSAG.cjs +51 -0
- package/dist/log-alert-LAJ6VSAG.cjs.map +1 -0
- package/dist/log-formats-FKFMQKNO.cjs +31 -0
- package/dist/log-formats-FKFMQKNO.cjs.map +1 -0
- package/dist/log-formats-I5E32H5F.es.js +31 -0
- package/dist/log-formats-I5E32H5F.es.js.map +1 -0
- package/dist/password-63OY27RX.es.js +20 -0
- package/dist/password-63OY27RX.es.js.map +1 -0
- package/dist/password-NZ5EGDWG.cjs +20 -0
- package/dist/password-NZ5EGDWG.cjs.map +1 -0
- package/dist/user-store-J7LWEXSW.es.js +12 -0
- package/dist/user-store-J7LWEXSW.es.js.map +1 -0
- package/dist/user-store-SX4N6PQ4.cjs +12 -0
- package/dist/user-store-SX4N6PQ4.cjs.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getNowTime
|
|
3
|
+
} from "./chunk-UTKVJE7N.es.js";
|
|
4
|
+
import {
|
|
5
|
+
__name
|
|
6
|
+
} from "./chunk-6U2I5EON.es.js";
|
|
7
|
+
|
|
8
|
+
// src/sysserver/lib/common/log-formats.ts
|
|
9
|
+
function formatClfDate(date = /* @__PURE__ */ new Date()) {
|
|
10
|
+
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
11
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
12
|
+
const mon = months[date.getMonth()];
|
|
13
|
+
const year = date.getFullYear();
|
|
14
|
+
const h = String(date.getHours()).padStart(2, "0");
|
|
15
|
+
const m = String(date.getMinutes()).padStart(2, "0");
|
|
16
|
+
const s = String(date.getSeconds()).padStart(2, "0");
|
|
17
|
+
const tzOffset = -date.getTimezoneOffset();
|
|
18
|
+
const tzSign = tzOffset >= 0 ? "+" : "-";
|
|
19
|
+
const tzH = String(Math.floor(Math.abs(tzOffset) / 60)).padStart(2, "0");
|
|
20
|
+
const tzM = String(Math.abs(tzOffset) % 60).padStart(2, "0");
|
|
21
|
+
return `[${day}/${mon}/${year}:${h}:${m}:${s} ${tzSign}${tzH}${tzM}]`;
|
|
22
|
+
}
|
|
23
|
+
__name(formatClfDate, "formatClfDate");
|
|
24
|
+
function formatCLF(entry) {
|
|
25
|
+
const ident = entry.ident ?? "-";
|
|
26
|
+
const authuser = entry.authuser ?? "-";
|
|
27
|
+
const proto = entry.protocol || "HTTP/1.1";
|
|
28
|
+
const requestLine = `${entry.method} ${entry.url} ${proto}`;
|
|
29
|
+
const dateStr = formatClfDate(entry.date || /* @__PURE__ */ new Date());
|
|
30
|
+
return `${entry.host} ${ident} ${authuser} ${dateStr} "${requestLine}" ${entry.status} ${entry.bytes}
|
|
31
|
+
`;
|
|
32
|
+
}
|
|
33
|
+
__name(formatCLF, "formatCLF");
|
|
34
|
+
function formatW3CHeader(opts) {
|
|
35
|
+
const date = opts.date || /* @__PURE__ */ new Date();
|
|
36
|
+
const iso = date.toISOString();
|
|
37
|
+
return [
|
|
38
|
+
"#Version: 1.0",
|
|
39
|
+
`#Software: ${opts.software} v${opts.version}`,
|
|
40
|
+
`#Date: ${iso}`,
|
|
41
|
+
`#Fields: ${opts.fields.join(" ")}`,
|
|
42
|
+
""
|
|
43
|
+
].join("\n");
|
|
44
|
+
}
|
|
45
|
+
__name(formatW3CHeader, "formatW3CHeader");
|
|
46
|
+
var W3C_ACCESS_FIELDS = [
|
|
47
|
+
"date",
|
|
48
|
+
"time",
|
|
49
|
+
"c-ip",
|
|
50
|
+
"cs-method",
|
|
51
|
+
"cs-uri-stem",
|
|
52
|
+
"sc-status",
|
|
53
|
+
"sc-bytes",
|
|
54
|
+
"cs(User-Agent)",
|
|
55
|
+
"cs(Referer)",
|
|
56
|
+
"x-log-type"
|
|
57
|
+
];
|
|
58
|
+
function formatW3CAccessRecord(entry) {
|
|
59
|
+
const date = entry.date || /* @__PURE__ */ new Date();
|
|
60
|
+
const dateStr = getNowTime(2, date);
|
|
61
|
+
const timeStr = getNowTime(1, date);
|
|
62
|
+
const cols = [
|
|
63
|
+
dateStr,
|
|
64
|
+
timeStr,
|
|
65
|
+
entry.host,
|
|
66
|
+
entry.method,
|
|
67
|
+
entry.url,
|
|
68
|
+
String(entry.status),
|
|
69
|
+
String(entry.bytes),
|
|
70
|
+
entry.userAgent || "-",
|
|
71
|
+
entry.referer || "-",
|
|
72
|
+
entry.logType || "HTTP"
|
|
73
|
+
];
|
|
74
|
+
return cols.map(escW3CField).join(" ") + "\n";
|
|
75
|
+
}
|
|
76
|
+
__name(formatW3CAccessRecord, "formatW3CAccessRecord");
|
|
77
|
+
function escW3CField(v) {
|
|
78
|
+
if (v.includes(" ") || v.includes(" ")) return `"${v.replace(/"/g, '""')}"`;
|
|
79
|
+
return v;
|
|
80
|
+
}
|
|
81
|
+
__name(escW3CField, "escW3CField");
|
|
82
|
+
var W3C_SERVER_FIELDS = ["date", "time", "x-log-type", "cs-method", "cs-uri-stem", "x-message"];
|
|
83
|
+
function formatW3CServerRecord(opts) {
|
|
84
|
+
const date = opts.date || /* @__PURE__ */ new Date();
|
|
85
|
+
const cols = [
|
|
86
|
+
getNowTime(2, date),
|
|
87
|
+
getNowTime(1, date),
|
|
88
|
+
opts.logType,
|
|
89
|
+
opts.method || "-",
|
|
90
|
+
opts.url || "-",
|
|
91
|
+
opts.message.replace(/[\n\r]/g, " ").slice(0, 2e3)
|
|
92
|
+
];
|
|
93
|
+
return cols.map(escW3CField).join(" ") + "\n";
|
|
94
|
+
}
|
|
95
|
+
__name(formatW3CServerRecord, "formatW3CServerRecord");
|
|
96
|
+
function buildAccessEntry(req, res, logType) {
|
|
97
|
+
const forwarded = req.headers?.["x-forwarded-for"];
|
|
98
|
+
const host = (typeof forwarded === "string" ? forwarded.split(",")[0].trim() : "") || req.socket?.remoteAddress || req.connection?.remoteAddress || "-";
|
|
99
|
+
const url = (req.url || "/").split("?")[0];
|
|
100
|
+
return {
|
|
101
|
+
host: host.replace(/^::ffff:/, ""),
|
|
102
|
+
method: req.method || "GET",
|
|
103
|
+
url,
|
|
104
|
+
status: res.statusCode || 0,
|
|
105
|
+
bytes: Number(res.getHeader?.("content-length")) || 0,
|
|
106
|
+
userAgent: req.headers?.["user-agent"] || "-",
|
|
107
|
+
referer: req.headers?.referer || req.headers?.referrer || "-",
|
|
108
|
+
logType: logType || "HTTP",
|
|
109
|
+
date: /* @__PURE__ */ new Date()
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
__name(buildAccessEntry, "buildAccessEntry");
|
|
113
|
+
function parseCLF(line) {
|
|
114
|
+
const m = line.trim().match(
|
|
115
|
+
/^(\S+)\s+(\S+)\s+(\S+)\s+\[([^\]]+)\]\s+"([^"]+)"\s+(\d+)\s+(\S+)/
|
|
116
|
+
);
|
|
117
|
+
if (!m) return null;
|
|
118
|
+
const [, host, ident, authuser, date, request, status, bytes] = m;
|
|
119
|
+
const reqParts = request.split(" ");
|
|
120
|
+
return {
|
|
121
|
+
host,
|
|
122
|
+
ident,
|
|
123
|
+
authuser,
|
|
124
|
+
date,
|
|
125
|
+
method: reqParts[0] || "",
|
|
126
|
+
url: reqParts[1] || "",
|
|
127
|
+
protocol: reqParts[2] || "",
|
|
128
|
+
status: Number(status),
|
|
129
|
+
bytes: bytes === "-" ? 0 : Number(bytes)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
__name(parseCLF, "parseCLF");
|
|
133
|
+
function parseW3CRecord(line, fields) {
|
|
134
|
+
const trimmed = line.trim();
|
|
135
|
+
if (!trimmed || trimmed.startsWith("#")) return null;
|
|
136
|
+
const cols = splitW3CFields(trimmed);
|
|
137
|
+
const fieldNames = fields || [...W3C_ACCESS_FIELDS];
|
|
138
|
+
const out = {};
|
|
139
|
+
for (let i = 0; i < fieldNames.length && i < cols.length; i++) {
|
|
140
|
+
out[fieldNames[i]] = cols[i];
|
|
141
|
+
}
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
__name(parseW3CRecord, "parseW3CRecord");
|
|
145
|
+
function splitW3CFields(line) {
|
|
146
|
+
const cols = [];
|
|
147
|
+
let cur = "";
|
|
148
|
+
let inQuote = false;
|
|
149
|
+
for (let i = 0; i < line.length; i++) {
|
|
150
|
+
const ch = line[i];
|
|
151
|
+
if (ch === '"') {
|
|
152
|
+
inQuote = !inQuote;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (!inQuote && (ch === " " || ch === " ")) {
|
|
156
|
+
if (cur) cols.push(cur);
|
|
157
|
+
cur = "";
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
cur += ch;
|
|
161
|
+
}
|
|
162
|
+
if (cur) cols.push(cur);
|
|
163
|
+
return cols;
|
|
164
|
+
}
|
|
165
|
+
__name(splitW3CFields, "splitW3CFields");
|
|
166
|
+
function extractW3CFields(content) {
|
|
167
|
+
for (const line of content.split("\n")) {
|
|
168
|
+
const m = line.match(/^#Fields:\s*(.+)$/);
|
|
169
|
+
if (m) return m[1].trim().split(/\s+/);
|
|
170
|
+
}
|
|
171
|
+
return [...W3C_ACCESS_FIELDS];
|
|
172
|
+
}
|
|
173
|
+
__name(extractW3CFields, "extractW3CFields");
|
|
174
|
+
function getLogFormatTaxonomy($asai) {
|
|
175
|
+
const vendor = $asai?.hostconfig?.website?.title || "AsaiCMS";
|
|
176
|
+
const product = "AsaiHost";
|
|
177
|
+
const version = $asai?.hostconfig?.website?.ver || "1.0.0.0";
|
|
178
|
+
return {
|
|
179
|
+
standards: ["IEC 62443-4-2 CR2.8", "RFC 6872 (CLF)", "W3C Extended Log File Format", "CEF (ArcSight/OpenText)"],
|
|
180
|
+
formats: [
|
|
181
|
+
{
|
|
182
|
+
key: "clf",
|
|
183
|
+
name: "CLF \u2014 Common Log Format",
|
|
184
|
+
nameZh: "\u901A\u7528\u65E5\u5FD7\u683C\u5F0F",
|
|
185
|
+
ref: "RFC 6872",
|
|
186
|
+
refUrl: "https://www.rfc-editor.org/rfc/rfc6872",
|
|
187
|
+
pattern: 'host ident authuser [date] "request" status bytes',
|
|
188
|
+
example: `${vendor} - - ${formatClfDate()} "GET /api/asailog/store/stats/ HTTP/1.1" 200 512`,
|
|
189
|
+
fields: [
|
|
190
|
+
{ field: "host", desc: "\u5BA2\u6237\u7AEF IP \u6216\u4E3B\u673A\u540D", example: "192.168.1.10" },
|
|
191
|
+
{ field: "ident", desc: "RFC 1413 \u8EAB\u4EFD\u6807\u8BC6\uFF08\u901A\u5E38\u4E3A -\uFF09", example: "-" },
|
|
192
|
+
{ field: "authuser", desc: "HTTP \u8BA4\u8BC1\u7528\u6237\u540D\uFF08\u672A\u8BA4\u8BC1\u4E3A -\uFF09", example: "-" },
|
|
193
|
+
{ field: "date", desc: "\u8BF7\u6C42\u65F6\u95F4\u6233", example: formatClfDate() },
|
|
194
|
+
{ field: "request", desc: "\u8BF7\u6C42\u884C\uFF08\u65B9\u6CD5 URI \u534F\u8BAE\uFF09", example: "GET /api/ HTTP/1.1" },
|
|
195
|
+
{ field: "status", desc: "HTTP \u54CD\u5E94\u72B6\u6001\u7801", example: "200" },
|
|
196
|
+
{ field: "bytes", desc: "\u54CD\u5E94\u4F53\u5B57\u8282\u6570", example: "1234" }
|
|
197
|
+
],
|
|
198
|
+
usage: "WEB \u670D\u52A1\u5668 HTTP \u8BBF\u95EE\u65E5\u5FD7\uFF0C\u6309\u65E5\u5199\u5165 access-clf-YYYY-MM-DD.log"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
key: "w3c",
|
|
202
|
+
name: "W3C Extended Log File Format",
|
|
203
|
+
nameZh: "W3C \u6269\u5C55\u65E5\u5FD7\u6587\u4EF6\u683C\u5F0F",
|
|
204
|
+
ref: "W3C WD-logfile",
|
|
205
|
+
refUrl: "https://www.w3.org/TR/WD-logfile",
|
|
206
|
+
pattern: "#Version / #Software / #Date / #Fields + tab/space-separated records",
|
|
207
|
+
example: [
|
|
208
|
+
"#Version: 1.0",
|
|
209
|
+
`#Software: ${product}`,
|
|
210
|
+
`#Version: ${version}`,
|
|
211
|
+
`#Date: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
212
|
+
`#Fields: ${W3C_ACCESS_FIELDS.join(" ")}`,
|
|
213
|
+
formatW3CAccessRecord({
|
|
214
|
+
host: "192.168.1.10",
|
|
215
|
+
method: "GET",
|
|
216
|
+
url: "/api/user/login/profile/",
|
|
217
|
+
status: 200,
|
|
218
|
+
bytes: 1234,
|
|
219
|
+
userAgent: "Mozilla/5.0",
|
|
220
|
+
logType: "API"
|
|
221
|
+
}).trim()
|
|
222
|
+
].join("\n"),
|
|
223
|
+
fields: W3C_ACCESS_FIELDS.map((f) => ({
|
|
224
|
+
field: f,
|
|
225
|
+
desc: w3cFieldDesc(f),
|
|
226
|
+
example: w3cFieldExample(f)
|
|
227
|
+
})),
|
|
228
|
+
usage: "HTTP \u8BBF\u95EE\u65E5\u5FD7\uFF08access-w3c-*.log\uFF09\u4E0E\u670D\u52A1\u7AEF\u8FD0\u884C\u65E5\u5FD7\uFF08log-*.txt \u5E26 #Fields \u5934\uFF09"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
key: "cef",
|
|
232
|
+
name: "CEF \u2014 Common Event Format",
|
|
233
|
+
nameZh: "\u901A\u7528\u4E8B\u4EF6\u683C\u5F0F\uFF08SIEM\uFF09",
|
|
234
|
+
ref: "ArcSight CEF",
|
|
235
|
+
refUrl: "https://www.microfocus.com/en-us/cyberres/secops/arcsight",
|
|
236
|
+
pattern: "CEF:Version|DeviceVendor|DeviceProduct|DeviceVersion|SignatureID|Name|Severity|Extension",
|
|
237
|
+
example: `CEF:0|${vendor}|${product}|${version}|ASAI.SAFETY.PARAM.001|Safety parameter changed: torque_limit|8|rt=${(/* @__PURE__ */ new Date()).toISOString()} src=192.168.1.10 dst=127.0.0.1 proto=HTTPS cs1=ConfigurationChanges cs1Label=EventCategory cs2=SAFETY_PARAM cs2Label=EventType cs3=a1b2c3d4 cs3Label=Identification cn1=1 cn1Label=BootCounter cn2=42 cn2Label=RunningCounter outcome=Success`,
|
|
238
|
+
fields: [
|
|
239
|
+
{ field: "CEF:Version", desc: "CEF \u683C\u5F0F\u7248\u672C\u53F7", example: "CEF:0" },
|
|
240
|
+
{ field: "DeviceVendor", desc: "\u751F\u6210\u4E8B\u4EF6\u7684\u8BBE\u5907/\u7CFB\u7EDF\u5382\u5546", example: vendor },
|
|
241
|
+
{ field: "DeviceProduct", desc: "\u4EA7\u54C1\u540D\u79F0", example: product },
|
|
242
|
+
{ field: "DeviceVersion", desc: "\u4EA7\u54C1\u7248\u672C", example: version },
|
|
243
|
+
{ field: "SignatureID", desc: "\u4E8B\u4EF6\u552F\u4E00\u6807\u8BC6\u7B26\uFF08\u5BF9\u5E94 CR2.8 \u4E8B\u4EF6 ID\uFF09", example: "ASAI.SAFETY.PARAM.001" },
|
|
244
|
+
{ field: "Name", desc: "\u4EBA\u7C7B\u53EF\u8BFB\u7684\u4E8B\u4EF6\u540D\u79F0", example: "Safety parameter changed: torque_limit" },
|
|
245
|
+
{ field: "Severity", desc: "\u4E25\u91CD\u7EA7\u522B 0\u201310", example: "8" },
|
|
246
|
+
{ field: "Extension", desc: "\u952E\u503C\u5BF9\u6269\u5C55\uFF1Art/src/dst/proto/cs1/cs2/cs3/cn1/cn2/outcome \u7B49", example: "rt=... outcome=Success" }
|
|
247
|
+
],
|
|
248
|
+
severityGuide: [
|
|
249
|
+
{ category: "Access Control \u8BBF\u95EE\u63A7\u5236", range: "7\u20138", note: "\u767B\u5F55\u5931\u8D25\u3001\u6743\u9650\u63D0\u5347\u3001\u672A\u6388\u6743\u8BBF\u95EE" },
|
|
250
|
+
{ category: "Request Errors \u8BF7\u6C42\u9519\u8BEF", range: "2\u20134", note: "\u534F\u8BAE\u683C\u5F0F\u9519\u8BEF\u3001\u65E0\u6548\u547D\u4EE4\u3001\u901A\u4FE1\u5F02\u5E38" },
|
|
251
|
+
{ category: "Control System Events \u63A7\u5236\u7CFB\u7EDF\u4E8B\u4EF6", range: "8\u201310", note: "\u5B89\u5168\u529F\u80FD\u89E6\u53D1\u3001PLC \u6A21\u5F0F\u5207\u6362\u3001I/O \u5F3A\u5236\u503C" },
|
|
252
|
+
{ category: "Backup and Restore \u5907\u4EFD\u4E0E\u6062\u590D", range: "6\u20137", note: "\u5907\u4EFD/\u6062\u590D\u64CD\u4F5C\uFF0C\u6062\u590D\u98CE\u9669\u66F4\u9AD8" },
|
|
253
|
+
{ category: "Configuration Changes \u914D\u7F6E\u66F4\u6539", range: "7\u20139", note: "\u5B89\u5168\u53C2\u6570\u3001\u63A7\u5236\u903B\u8F91\u3001\u7F51\u7EDC\u8BBE\u7F6E\u53D8\u66F4" },
|
|
254
|
+
{ category: "Audit Log Events \u5BA1\u8BA1\u65E5\u5FD7\u4E8B\u4EF6", range: "7\u20138", note: "\u65E5\u5FD7\u6E05\u9664\u3001\u7BE1\u6539\u3001\u7CFB\u7EDF\u6545\u969C" }
|
|
255
|
+
],
|
|
256
|
+
usage: "\u5E72\u9884\u65E5\u5FD7\uFF08intervention-*.cef.log\uFF09\uFF0C\u54C8\u5E0C\u94FE\u9632\u7BE1\u6539\uFF0C\u7559\u5B58 \u22655 \u5E74"
|
|
257
|
+
}
|
|
258
|
+
]
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
__name(getLogFormatTaxonomy, "getLogFormatTaxonomy");
|
|
262
|
+
function w3cFieldDesc(field) {
|
|
263
|
+
const map = {
|
|
264
|
+
date: "\u65E5\u671F YYYY-MM-DD",
|
|
265
|
+
time: "\u65F6\u95F4 HH:MM:SS",
|
|
266
|
+
"c-ip": "\u5BA2\u6237\u7AEF IP",
|
|
267
|
+
"cs-method": "HTTP \u65B9\u6CD5",
|
|
268
|
+
"cs-uri-stem": "\u8BF7\u6C42 URI",
|
|
269
|
+
"sc-status": "\u54CD\u5E94\u72B6\u6001\u7801",
|
|
270
|
+
"sc-bytes": "\u54CD\u5E94\u5B57\u8282\u6570",
|
|
271
|
+
"cs(User-Agent)": "User-Agent \u8BF7\u6C42\u5934",
|
|
272
|
+
"cs(Referer)": "Referer \u8BF7\u6C42\u5934",
|
|
273
|
+
"x-log-type": "\u65E5\u5FD7\u7C7B\u578B\uFF08API/SYS/JSONP/HTTP \u7B49\uFF09",
|
|
274
|
+
"x-message": "\u65E5\u5FD7\u6D88\u606F\u5185\u5BB9"
|
|
275
|
+
};
|
|
276
|
+
return map[field] || field;
|
|
277
|
+
}
|
|
278
|
+
__name(w3cFieldDesc, "w3cFieldDesc");
|
|
279
|
+
function w3cFieldExample(field) {
|
|
280
|
+
const map = {
|
|
281
|
+
date: "2026-07-02",
|
|
282
|
+
time: "15:42:10",
|
|
283
|
+
"c-ip": "192.168.1.10",
|
|
284
|
+
"cs-method": "GET",
|
|
285
|
+
"cs-uri-stem": "/api/asailog/store/stats/",
|
|
286
|
+
"sc-status": "200",
|
|
287
|
+
"sc-bytes": "1234",
|
|
288
|
+
"cs(User-Agent)": "Mozilla/5.0",
|
|
289
|
+
"cs(Referer)": "-",
|
|
290
|
+
"x-log-type": "API",
|
|
291
|
+
"x-message": "profile updated"
|
|
292
|
+
};
|
|
293
|
+
return map[field] || "-";
|
|
294
|
+
}
|
|
295
|
+
__name(w3cFieldExample, "w3cFieldExample");
|
|
296
|
+
|
|
297
|
+
export {
|
|
298
|
+
formatClfDate,
|
|
299
|
+
formatCLF,
|
|
300
|
+
formatW3CHeader,
|
|
301
|
+
W3C_ACCESS_FIELDS,
|
|
302
|
+
formatW3CAccessRecord,
|
|
303
|
+
W3C_SERVER_FIELDS,
|
|
304
|
+
formatW3CServerRecord,
|
|
305
|
+
buildAccessEntry,
|
|
306
|
+
parseCLF,
|
|
307
|
+
parseW3CRecord,
|
|
308
|
+
extractW3CFields,
|
|
309
|
+
getLogFormatTaxonomy
|
|
310
|
+
};
|
|
311
|
+
//# sourceMappingURL=chunk-4WJDC4M4.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sysserver/lib/common/log-formats.ts"],"sourcesContent":["/**\r\n * IEC 62443-4-2 CR2.8 日志格式规范 — CLF / W3C Extended / CEF\r\n *\r\n * 1) CLF — Common Log Format (RFC 6872)\r\n * 2) W3C Extended Log File Format\r\n * 3) CEF — Common Event Format (ArcSight / SIEM)\r\n */\r\nimport { getNowTime } from '../../../core/time';\r\n\r\n/** CLF 日期格式:[02/Jul/2026:15:42:10 +0800] */\r\nexport function formatClfDate(date: Date = new Date()): string {\r\n\tconst months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\r\n\tconst day = String(date.getDate()).padStart(2, '0');\r\n\tconst mon = months[date.getMonth()];\r\n\tconst year = date.getFullYear();\r\n\tconst h = String(date.getHours()).padStart(2, '0');\r\n\tconst m = String(date.getMinutes()).padStart(2, '0');\r\n\tconst s = String(date.getSeconds()).padStart(2, '0');\r\n\tconst tzOffset = -date.getTimezoneOffset();\r\n\tconst tzSign = tzOffset >= 0 ? '+' : '-';\r\n\tconst tzH = String(Math.floor(Math.abs(tzOffset) / 60)).padStart(2, '0');\r\n\tconst tzM = String(Math.abs(tzOffset) % 60).padStart(2, '0');\r\n\treturn `[${day}/${mon}/${year}:${h}:${m}:${s} ${tzSign}${tzH}${tzM}]`;\r\n}\r\n\r\nexport interface AccessLogEntry {\r\n\thost: string;\r\n\tident?: string;\r\n\tauthuser?: string;\r\n\tmethod: string;\r\n\turl: string;\r\n\tprotocol?: string;\r\n\tstatus: number;\r\n\tbytes: number;\r\n\tuserAgent?: string;\r\n\treferer?: string;\r\n\tlogType?: string;\r\n\tdate?: Date;\r\n}\r\n\r\n/** CLF: host ident authuser [date] \"request\" status bytes */\r\nexport function formatCLF(entry: AccessLogEntry): string {\r\n\tconst ident = entry.ident ?? '-';\r\n\tconst authuser = entry.authuser ?? '-';\r\n\tconst proto = entry.protocol || 'HTTP/1.1';\r\n\tconst requestLine = `${entry.method} ${entry.url} ${proto}`;\r\n\tconst dateStr = formatClfDate(entry.date || new Date());\r\n\treturn `${entry.host} ${ident} ${authuser} ${dateStr} \"${requestLine}\" ${entry.status} ${entry.bytes}\\n`;\r\n}\r\n\r\nexport interface W3CHeaderOpts {\r\n\tsoftware: string;\r\n\tversion: string;\r\n\tfields: string[];\r\n\tdate?: Date;\r\n}\r\n\r\n/** W3C Extended Log 文件头(新文件首行写入) */\r\nexport function formatW3CHeader(opts: W3CHeaderOpts): string {\r\n\tconst date = opts.date || new Date();\r\n\tconst iso = date.toISOString();\r\n\treturn [\r\n\t\t'#Version: 1.0',\r\n\t\t`#Software: ${opts.software} v${opts.version}`,\r\n\t\t`#Date: ${iso}`,\r\n\t\t`#Fields: ${opts.fields.join(' ')}`,\r\n\t\t'',\r\n\t].join('\\n');\r\n}\r\n\r\n/** W3C Extended Log 默认访问日志字段(参照 W3C WD-logfile) */\r\nexport const W3C_ACCESS_FIELDS = [\r\n\t'date',\r\n\t'time',\r\n\t'c-ip',\r\n\t'cs-method',\r\n\t'cs-uri-stem',\r\n\t'sc-status',\r\n\t'sc-bytes',\r\n\t'cs(User-Agent)',\r\n\t'cs(Referer)',\r\n\t'x-log-type',\r\n] as const;\r\n\r\n/** W3C Extended Log 单行记录 */\r\nexport function formatW3CAccessRecord(entry: AccessLogEntry): string {\r\n\tconst date = entry.date || new Date();\r\n\tconst dateStr = getNowTime(2, date);\r\n\tconst timeStr = getNowTime(1, date);\r\n\tconst cols = [\r\n\t\tdateStr,\r\n\t\ttimeStr,\r\n\t\tentry.host,\r\n\t\tentry.method,\r\n\t\tentry.url,\r\n\t\tString(entry.status),\r\n\t\tString(entry.bytes),\r\n\t\tentry.userAgent || '-',\r\n\t\tentry.referer || '-',\r\n\t\tentry.logType || 'HTTP',\r\n\t];\r\n\treturn cols.map(escW3CField).join(' ') + '\\n';\r\n}\r\n\r\nfunction escW3CField(v: string): string {\r\n\tif (v.includes(' ') || v.includes('\\t')) return `\"${v.replace(/\"/g, '\"\"')}\"`;\r\n\treturn v;\r\n}\r\n\r\n/** W3C 运行日志字段(server/client 业务日志) */\r\nexport const W3C_SERVER_FIELDS = ['date', 'time', 'x-log-type', 'cs-method', 'cs-uri-stem', 'x-message'] as const;\r\n\r\nexport function formatW3CServerRecord(opts: {\r\n\tdate?: Date;\r\n\tlogType: string;\r\n\tmethod?: string;\r\n\turl?: string;\r\n\tmessage: string;\r\n}): string {\r\n\tconst date = opts.date || new Date();\r\n\tconst cols = [\r\n\t\tgetNowTime(2, date),\r\n\t\tgetNowTime(1, date),\r\n\t\topts.logType,\r\n\t\topts.method || '-',\r\n\t\topts.url || '-',\r\n\t\topts.message.replace(/[\\n\\r]/g, ' ').slice(0, 2000),\r\n\t];\r\n\treturn cols.map(escW3CField).join(' ') + '\\n';\r\n}\r\n\r\n/** 从 HTTP req/res 构建访问日志条目 */\r\nexport function buildAccessEntry(req: any, res: any, logType?: string): AccessLogEntry {\r\n\tconst forwarded = req.headers?.['x-forwarded-for'];\r\n\tconst host = (typeof forwarded === 'string' ? forwarded.split(',')[0].trim() : '') ||\r\n\t\treq.socket?.remoteAddress ||\r\n\t\treq.connection?.remoteAddress ||\r\n\t\t'-';\r\n\tconst url = (req.url || '/').split('?')[0];\r\n\treturn {\r\n\t\thost: host.replace(/^::ffff:/, ''),\r\n\t\tmethod: req.method || 'GET',\r\n\t\turl,\r\n\t\tstatus: res.statusCode || 0,\r\n\t\tbytes: Number(res.getHeader?.('content-length')) || 0,\r\n\t\tuserAgent: req.headers?.['user-agent'] || '-',\r\n\t\treferer: req.headers?.referer || req.headers?.referrer || '-',\r\n\t\tlogType: logType || 'HTTP',\r\n\t\tdate: new Date(),\r\n\t};\r\n}\r\n\r\n/** 解析 CLF 行 */\r\nexport function parseCLF(line: string): Record<string, string | number> | null {\r\n\tconst m = line.trim().match(\r\n\t\t/^(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+\\[([^\\]]+)\\]\\s+\"([^\"]+)\"\\s+(\\d+)\\s+(\\S+)/,\r\n\t);\r\n\tif (!m) return null;\r\n\tconst [, host, ident, authuser, date, request, status, bytes] = m;\r\n\tconst reqParts = request.split(' ');\r\n\treturn {\r\n\t\thost,\r\n\t\tident,\r\n\t\tauthuser,\r\n\t\tdate,\r\n\t\tmethod: reqParts[0] || '',\r\n\t\turl: reqParts[1] || '',\r\n\t\tprotocol: reqParts[2] || '',\r\n\t\tstatus: Number(status),\r\n\t\tbytes: bytes === '-' ? 0 : Number(bytes),\r\n\t};\r\n}\r\n\r\n/** 解析 W3C 数据行(跳过 # 开头注释行) */\r\nexport function parseW3CRecord(line: string, fields?: string[]): Record<string, string> | null {\r\n\tconst trimmed = line.trim();\r\n\tif (!trimmed || trimmed.startsWith('#')) return null;\r\n\tconst cols = splitW3CFields(trimmed);\r\n\tconst fieldNames = fields || [...W3C_ACCESS_FIELDS];\r\n\tconst out: Record<string, string> = {};\r\n\tfor (let i = 0; i < fieldNames.length && i < cols.length; i++) {\r\n\t\tout[fieldNames[i]] = cols[i];\r\n\t}\r\n\treturn out;\r\n}\r\n\r\nfunction splitW3CFields(line: string): string[] {\r\n\tconst cols: string[] = [];\r\n\tlet cur = '';\r\n\tlet inQuote = false;\r\n\tfor (let i = 0; i < line.length; i++) {\r\n\t\tconst ch = line[i];\r\n\t\tif (ch === '\"') {\r\n\t\t\tinQuote = !inQuote;\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tif (!inQuote && (ch === ' ' || ch === '\\t')) {\r\n\t\t\tif (cur) cols.push(cur);\r\n\t\t\tcur = '';\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tcur += ch;\r\n\t}\r\n\tif (cur) cols.push(cur);\r\n\treturn cols;\r\n}\r\n\r\n/** 从 W3C 文件内容提取 #Fields 定义 */\r\nexport function extractW3CFields(content: string): string[] {\r\n\tfor (const line of content.split('\\n')) {\r\n\t\tconst m = line.match(/^#Fields:\\s*(.+)$/);\r\n\t\tif (m) return m[1].trim().split(/\\s+/);\r\n\t}\r\n\treturn [...W3C_ACCESS_FIELDS];\r\n}\r\n\r\n/** IEC 62443-4-2 CR2.8 三种日志格式完整规范(供前端展示与合规核查) */\r\nexport function getLogFormatTaxonomy($asai?: any) {\r\n\tconst vendor = $asai?.hostconfig?.website?.title || 'AsaiCMS';\r\n\tconst product = 'AsaiHost';\r\n\tconst version = $asai?.hostconfig?.website?.ver || '1.0.0.0';\r\n\r\n\treturn {\r\n\t\tstandards: ['IEC 62443-4-2 CR2.8', 'RFC 6872 (CLF)', 'W3C Extended Log File Format', 'CEF (ArcSight/OpenText)'],\r\n\t\tformats: [\r\n\t\t\t{\r\n\t\t\t\tkey: 'clf',\r\n\t\t\t\tname: 'CLF — Common Log Format',\r\n\t\t\t\tnameZh: '通用日志格式',\r\n\t\t\t\tref: 'RFC 6872',\r\n\t\t\t\trefUrl: 'https://www.rfc-editor.org/rfc/rfc6872',\r\n\t\t\t\tpattern: 'host ident authuser [date] \"request\" status bytes',\r\n\t\t\t\texample: `${vendor} - - ${formatClfDate()} \"GET /api/asailog/store/stats/ HTTP/1.1\" 200 512`,\r\n\t\t\t\tfields: [\r\n\t\t\t\t\t{ field: 'host', desc: '客户端 IP 或主机名', example: '192.168.1.10' },\r\n\t\t\t\t\t{ field: 'ident', desc: 'RFC 1413 身份标识(通常为 -)', example: '-' },\r\n\t\t\t\t\t{ field: 'authuser', desc: 'HTTP 认证用户名(未认证为 -)', example: '-' },\r\n\t\t\t\t\t{ field: 'date', desc: '请求时间戳', example: formatClfDate() },\r\n\t\t\t\t\t{ field: 'request', desc: '请求行(方法 URI 协议)', example: 'GET /api/ HTTP/1.1' },\r\n\t\t\t\t\t{ field: 'status', desc: 'HTTP 响应状态码', example: '200' },\r\n\t\t\t\t\t{ field: 'bytes', desc: '响应体字节数', example: '1234' },\r\n\t\t\t\t],\r\n\t\t\t\tusage: 'WEB 服务器 HTTP 访问日志,按日写入 access-clf-YYYY-MM-DD.log',\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tkey: 'w3c',\r\n\t\t\t\tname: 'W3C Extended Log File Format',\r\n\t\t\t\tnameZh: 'W3C 扩展日志文件格式',\r\n\t\t\t\tref: 'W3C WD-logfile',\r\n\t\t\t\trefUrl: 'https://www.w3.org/TR/WD-logfile',\r\n\t\t\t\tpattern: '#Version / #Software / #Date / #Fields + tab/space-separated records',\r\n\t\t\t\texample: [\r\n\t\t\t\t\t'#Version: 1.0',\r\n\t\t\t\t\t`#Software: ${product}`,\r\n\t\t\t\t\t`#Version: ${version}`,\r\n\t\t\t\t\t`#Date: ${new Date().toISOString()}`,\r\n\t\t\t\t\t`#Fields: ${W3C_ACCESS_FIELDS.join(' ')}`,\r\n\t\t\t\t\tformatW3CAccessRecord({\r\n\t\t\t\t\t\thost: '192.168.1.10',\r\n\t\t\t\t\t\tmethod: 'GET',\r\n\t\t\t\t\t\turl: '/api/user/login/profile/',\r\n\t\t\t\t\t\tstatus: 200,\r\n\t\t\t\t\t\tbytes: 1234,\r\n\t\t\t\t\t\tuserAgent: 'Mozilla/5.0',\r\n\t\t\t\t\t\tlogType: 'API',\r\n\t\t\t\t\t}).trim(),\r\n\t\t\t\t].join('\\n'),\r\n\t\t\t\tfields: W3C_ACCESS_FIELDS.map((f) => ({\r\n\t\t\t\t\tfield: f,\r\n\t\t\t\t\tdesc: w3cFieldDesc(f),\r\n\t\t\t\t\texample: w3cFieldExample(f),\r\n\t\t\t\t})),\r\n\t\t\t\tusage: 'HTTP 访问日志(access-w3c-*.log)与服务端运行日志(log-*.txt 带 #Fields 头)',\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tkey: 'cef',\r\n\t\t\t\tname: 'CEF — Common Event Format',\r\n\t\t\t\tnameZh: '通用事件格式(SIEM)',\r\n\t\t\t\tref: 'ArcSight CEF',\r\n\t\t\t\trefUrl: 'https://www.microfocus.com/en-us/cyberres/secops/arcsight',\r\n\t\t\t\tpattern: 'CEF:Version|DeviceVendor|DeviceProduct|DeviceVersion|SignatureID|Name|Severity|Extension',\r\n\t\t\t\texample: `CEF:0|${vendor}|${product}|${version}|ASAI.SAFETY.PARAM.001|Safety parameter changed: torque_limit|8|rt=${new Date().toISOString()} src=192.168.1.10 dst=127.0.0.1 proto=HTTPS cs1=ConfigurationChanges cs1Label=EventCategory cs2=SAFETY_PARAM cs2Label=EventType cs3=a1b2c3d4 cs3Label=Identification cn1=1 cn1Label=BootCounter cn2=42 cn2Label=RunningCounter outcome=Success`,\r\n\t\t\t\tfields: [\r\n\t\t\t\t\t{ field: 'CEF:Version', desc: 'CEF 格式版本号', example: 'CEF:0' },\r\n\t\t\t\t\t{ field: 'DeviceVendor', desc: '生成事件的设备/系统厂商', example: vendor },\r\n\t\t\t\t\t{ field: 'DeviceProduct', desc: '产品名称', example: product },\r\n\t\t\t\t\t{ field: 'DeviceVersion', desc: '产品版本', example: version },\r\n\t\t\t\t\t{ field: 'SignatureID', desc: '事件唯一标识符(对应 CR2.8 事件 ID)', example: 'ASAI.SAFETY.PARAM.001' },\r\n\t\t\t\t\t{ field: 'Name', desc: '人类可读的事件名称', example: 'Safety parameter changed: torque_limit' },\r\n\t\t\t\t\t{ field: 'Severity', desc: '严重级别 0–10', example: '8' },\r\n\t\t\t\t\t{ field: 'Extension', desc: '键值对扩展:rt/src/dst/proto/cs1/cs2/cs3/cn1/cn2/outcome 等', example: 'rt=... outcome=Success' },\r\n\t\t\t\t],\r\n\t\t\t\tseverityGuide: [\r\n\t\t\t\t\t{ category: 'Access Control 访问控制', range: '7–8', note: '登录失败、权限提升、未授权访问' },\r\n\t\t\t\t\t{ category: 'Request Errors 请求错误', range: '2–4', note: '协议格式错误、无效命令、通信异常' },\r\n\t\t\t\t\t{ category: 'Control System Events 控制系统事件', range: '8–10', note: '安全功能触发、PLC 模式切换、I/O 强制值' },\r\n\t\t\t\t\t{ category: 'Backup and Restore 备份与恢复', range: '6–7', note: '备份/恢复操作,恢复风险更高' },\r\n\t\t\t\t\t{ category: 'Configuration Changes 配置更改', range: '7–9', note: '安全参数、控制逻辑、网络设置变更' },\r\n\t\t\t\t\t{ category: 'Audit Log Events 审计日志事件', range: '7–8', note: '日志清除、篡改、系统故障' },\r\n\t\t\t\t],\r\n\t\t\t\tusage: '干预日志(intervention-*.cef.log),哈希链防篡改,留存 ≥5 年',\r\n\t\t\t},\r\n\t\t],\r\n\t};\r\n}\r\n\r\nfunction w3cFieldDesc(field: string): string {\r\n\tconst map: Record<string, string> = {\r\n\t\tdate: '日期 YYYY-MM-DD',\r\n\t\ttime: '时间 HH:MM:SS',\r\n\t\t'c-ip': '客户端 IP',\r\n\t\t'cs-method': 'HTTP 方法',\r\n\t\t'cs-uri-stem': '请求 URI',\r\n\t\t'sc-status': '响应状态码',\r\n\t\t'sc-bytes': '响应字节数',\r\n\t\t'cs(User-Agent)': 'User-Agent 请求头',\r\n\t\t'cs(Referer)': 'Referer 请求头',\r\n\t\t'x-log-type': '日志类型(API/SYS/JSONP/HTTP 等)',\r\n\t\t'x-message': '日志消息内容',\r\n\t};\r\n\treturn map[field] || field;\r\n}\r\n\r\nfunction w3cFieldExample(field: string): string {\r\n\tconst map: Record<string, string> = {\r\n\t\tdate: '2026-07-02',\r\n\t\ttime: '15:42:10',\r\n\t\t'c-ip': '192.168.1.10',\r\n\t\t'cs-method': 'GET',\r\n\t\t'cs-uri-stem': '/api/asailog/store/stats/',\r\n\t\t'sc-status': '200',\r\n\t\t'sc-bytes': '1234',\r\n\t\t'cs(User-Agent)': 'Mozilla/5.0',\r\n\t\t'cs(Referer)': '-',\r\n\t\t'x-log-type': 'API',\r\n\t\t'x-message': 'profile updated',\r\n\t};\r\n\treturn map[field] || '-';\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__name,
|
|
10
|
+
__export
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=chunk-6U2I5EON.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getPasswordPolicy,
|
|
3
|
+
hashPasswordForHistory,
|
|
4
|
+
isPasswordInHistory,
|
|
5
|
+
parsePassHistory,
|
|
6
|
+
validatePasswordWithPolicy
|
|
7
|
+
} from "./chunk-EOGZPRML.es.js";
|
|
8
|
+
import {
|
|
9
|
+
__name
|
|
10
|
+
} from "./chunk-6U2I5EON.es.js";
|
|
11
|
+
|
|
12
|
+
// src/sysserver/api/login/service/password.ts
|
|
13
|
+
import { randomBytes, scryptSync, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
14
|
+
|
|
15
|
+
// src/infra/secure-compare.ts
|
|
16
|
+
import { timingSafeEqual } from "crypto";
|
|
17
|
+
function secureCompare(a, b) {
|
|
18
|
+
if (typeof a !== "string" || typeof b !== "string") return false;
|
|
19
|
+
const bufA = Buffer.from(a);
|
|
20
|
+
const bufB = Buffer.from(b);
|
|
21
|
+
if (bufA.length !== bufB.length) {
|
|
22
|
+
if (bufA.length > 0) timingSafeEqual(bufA, bufA);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return timingSafeEqual(bufA, bufB);
|
|
26
|
+
}
|
|
27
|
+
__name(secureCompare, "secureCompare");
|
|
28
|
+
|
|
29
|
+
// src/sysserver/api/login/service/password.ts
|
|
30
|
+
var SCRYPT_OPTS = { N: 16384, r: 8, p: 1, maxmem: 64 * 1024 * 1024 };
|
|
31
|
+
function hashPassword(password) {
|
|
32
|
+
const passSalt = randomBytes(16).toString("hex");
|
|
33
|
+
const passHash = scryptSync(password, passSalt, 64, SCRYPT_OPTS).toString("hex");
|
|
34
|
+
return { passHash, passSalt };
|
|
35
|
+
}
|
|
36
|
+
__name(hashPassword, "hashPassword");
|
|
37
|
+
function verifyPassword(password, passHash, passSalt) {
|
|
38
|
+
try {
|
|
39
|
+
const derived = scryptSync(password, passSalt, 64, SCRYPT_OPTS);
|
|
40
|
+
const expected = Buffer.from(passHash, "hex");
|
|
41
|
+
if (derived.length !== expected.length) return false;
|
|
42
|
+
return timingSafeEqual2(derived, expected);
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
__name(verifyPassword, "verifyPassword");
|
|
48
|
+
function validatePasswordPolicy(password, minLen = 8, policy) {
|
|
49
|
+
const p = policy || { minLength: minLen, requireLower: true, requireDigit: true, requireUpper: false, requireSpecial: false, maxAgeDays: 0, historyCount: 0 };
|
|
50
|
+
return validatePasswordWithPolicy(password, p);
|
|
51
|
+
}
|
|
52
|
+
__name(validatePasswordPolicy, "validatePasswordPolicy");
|
|
53
|
+
function validatePasswordChange(password, $asai, passHistory) {
|
|
54
|
+
const policy = getPasswordPolicy($asai);
|
|
55
|
+
const err = validatePasswordWithPolicy(password, policy);
|
|
56
|
+
if (err) return err;
|
|
57
|
+
if (policy.historyCount > 0) {
|
|
58
|
+
const history = parsePassHistory(passHistory);
|
|
59
|
+
if (isPasswordInHistory(password, history)) return "PASSWORD_REUSED";
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
__name(validatePasswordChange, "validatePasswordChange");
|
|
64
|
+
function nextPassHistory(password, passHistory, historyCount) {
|
|
65
|
+
if (!historyCount || historyCount <= 0) return "[]";
|
|
66
|
+
const history = parsePassHistory(passHistory);
|
|
67
|
+
history.unshift(hashPasswordForHistory(password));
|
|
68
|
+
return JSON.stringify(history.slice(0, historyCount));
|
|
69
|
+
}
|
|
70
|
+
__name(nextPassHistory, "nextPassHistory");
|
|
71
|
+
function verifyHostPassword($asai, password) {
|
|
72
|
+
const expected = $asai?.hostconfig?.password;
|
|
73
|
+
if (!expected) return false;
|
|
74
|
+
return secureCompare(password, expected);
|
|
75
|
+
}
|
|
76
|
+
__name(verifyHostPassword, "verifyHostPassword");
|
|
77
|
+
|
|
78
|
+
export {
|
|
79
|
+
secureCompare,
|
|
80
|
+
hashPassword,
|
|
81
|
+
verifyPassword,
|
|
82
|
+
validatePasswordPolicy,
|
|
83
|
+
validatePasswordChange,
|
|
84
|
+
nextPassHistory,
|
|
85
|
+
verifyHostPassword
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=chunk-DROOHKB3.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sysserver/api/login/service/password.ts","../src/infra/secure-compare.ts"],"sourcesContent":["import { randomBytes, scryptSync, timingSafeEqual } from 'node:crypto';\r\nimport { secureCompare } from '../../../../infra/secure-compare';\r\nimport {\r\n\tgetPasswordPolicy,\r\n\thashPasswordForHistory,\r\n\tisPasswordInHistory,\r\n\tparsePassHistory,\r\n\tvalidatePasswordWithPolicy,\r\n\ttype PasswordPolicy,\r\n} from '../../../../infra/security-policy';\r\n\r\nconst SCRYPT_OPTS = { N: 16384, r: 8, p: 1, maxmem: 64 * 1024 * 1024 };\r\n\r\nexport function hashPassword(password: string): { passHash: string; passSalt: string } {\r\n\tconst passSalt = randomBytes(16).toString('hex');\r\n\tconst passHash = scryptSync(password, passSalt, 64, SCRYPT_OPTS).toString('hex');\r\n\treturn { passHash, passSalt };\r\n}\r\n\r\nexport function verifyPassword(password: string, passHash: string, passSalt: string): boolean {\r\n\ttry {\r\n\t\tconst derived = scryptSync(password, passSalt, 64, SCRYPT_OPTS);\r\n\t\tconst expected = Buffer.from(passHash, 'hex');\r\n\t\tif (derived.length !== expected.length) return false;\r\n\t\treturn timingSafeEqual(derived, expected);\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\nexport function validatePasswordPolicy(password: string, minLen = 8, policy?: PasswordPolicy): string | null {\r\n\tconst p = policy || { minLength: minLen, requireLower: true, requireDigit: true, requireUpper: false, requireSpecial: false, maxAgeDays: 0, historyCount: 0 };\r\n\treturn validatePasswordWithPolicy(password, p);\r\n}\r\n\r\nexport function validatePasswordChange(\r\n\tpassword: string,\r\n\t$asai: any,\r\n\tpassHistory?: unknown,\r\n): string | null {\r\n\tconst policy = getPasswordPolicy($asai);\r\n\tconst err = validatePasswordWithPolicy(password, policy);\r\n\tif (err) return err;\r\n\tif (policy.historyCount > 0) {\r\n\t\tconst history = parsePassHistory(passHistory);\r\n\t\tif (isPasswordInHistory(password, history)) return 'PASSWORD_REUSED';\r\n\t}\r\n\treturn null;\r\n}\r\n\r\nexport function nextPassHistory(password: string, passHistory: unknown, historyCount: number): string {\r\n\tif (!historyCount || historyCount <= 0) return '[]';\r\n\tconst history = parsePassHistory(passHistory);\r\n\thistory.unshift(hashPasswordForHistory(password));\r\n\treturn JSON.stringify(history.slice(0, historyCount));\r\n}\r\n\r\nexport function verifyHostPassword($asai: any, password: string): boolean {\r\n\tconst expected = $asai?.hostconfig?.password;\r\n\tif (!expected) return false;\r\n\treturn secureCompare(password, expected);\r\n}\r\n","/**\r\n * 恒定时间字符串比较 — 降低密码/令牌 timing attack 风险(OWASP)\r\n */\r\nimport { timingSafeEqual } from 'crypto';\r\n\r\nexport function secureCompare(a: unknown, b: unknown): boolean {\r\n\tif (typeof a !== 'string' || typeof b !== 'string') return false;\r\n\tconst bufA = Buffer.from(a);\r\n\tconst bufB = Buffer.from(b);\r\n\tif (bufA.length !== bufB.length) {\r\n\t\t// 恒定时间伪比较,避免通过响应时序推断长度(IEC 62443-4-2 CR 1.7)\r\n\t\tif (bufA.length > 0) timingSafeEqual(bufA, bufA);\r\n\t\treturn false;\r\n\t}\r\n\treturn timingSafeEqual(bufA, bufB);\r\n}\r\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,aAAa,YAAY,mBAAAA,wBAAuB;;;ACGzD,SAAS,uBAAuB;AAEzB,SAAS,cAAc,GAAY,GAAqB;AAC9D,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SAAU,QAAO;AAC3D,QAAM,OAAO,OAAO,KAAK,CAAC;AAC1B,QAAM,OAAO,OAAO,KAAK,CAAC;AAC1B,MAAI,KAAK,WAAW,KAAK,QAAQ;AAEhC,QAAI,KAAK,SAAS,EAAG,iBAAgB,MAAM,IAAI;AAC/C,WAAO;AAAA,EACR;AACA,SAAO,gBAAgB,MAAM,IAAI;AAClC;AAVgB;;;ADMhB,IAAM,cAAc,EAAE,GAAG,OAAO,GAAG,GAAG,GAAG,GAAG,QAAQ,KAAK,OAAO,KAAK;AAE9D,SAAS,aAAa,UAA0D;AACtF,QAAM,WAAW,YAAY,EAAE,EAAE,SAAS,KAAK;AAC/C,QAAM,WAAW,WAAW,UAAU,UAAU,IAAI,WAAW,EAAE,SAAS,KAAK;AAC/E,SAAO,EAAE,UAAU,SAAS;AAC7B;AAJgB;AAMT,SAAS,eAAe,UAAkB,UAAkB,UAA2B;AAC7F,MAAI;AACH,UAAM,UAAU,WAAW,UAAU,UAAU,IAAI,WAAW;AAC9D,UAAM,WAAW,OAAO,KAAK,UAAU,KAAK;AAC5C,QAAI,QAAQ,WAAW,SAAS,OAAQ,QAAO;AAC/C,WAAOC,iBAAgB,SAAS,QAAQ;AAAA,EACzC,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AATgB;AAWT,SAAS,uBAAuB,UAAkB,SAAS,GAAG,QAAwC;AAC5G,QAAM,IAAI,UAAU,EAAE,WAAW,QAAQ,cAAc,MAAM,cAAc,MAAM,cAAc,OAAO,gBAAgB,OAAO,YAAY,GAAG,cAAc,EAAE;AAC5J,SAAO,2BAA2B,UAAU,CAAC;AAC9C;AAHgB;AAKT,SAAS,uBACf,UACA,OACA,aACgB;AAChB,QAAM,SAAS,kBAAkB,KAAK;AACtC,QAAM,MAAM,2BAA2B,UAAU,MAAM;AACvD,MAAI,IAAK,QAAO;AAChB,MAAI,OAAO,eAAe,GAAG;AAC5B,UAAM,UAAU,iBAAiB,WAAW;AAC5C,QAAI,oBAAoB,UAAU,OAAO,EAAG,QAAO;AAAA,EACpD;AACA,SAAO;AACR;AAbgB;AAeT,SAAS,gBAAgB,UAAkB,aAAsB,cAA8B;AACrG,MAAI,CAAC,gBAAgB,gBAAgB,EAAG,QAAO;AAC/C,QAAM,UAAU,iBAAiB,WAAW;AAC5C,UAAQ,QAAQ,uBAAuB,QAAQ,CAAC;AAChD,SAAO,KAAK,UAAU,QAAQ,MAAM,GAAG,YAAY,CAAC;AACrD;AALgB;AAOT,SAAS,mBAAmB,OAAY,UAA2B;AACzE,QAAM,WAAW,OAAO,YAAY;AACpC,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,cAAc,UAAU,QAAQ;AACxC;AAJgB;","names":["timingSafeEqual","timingSafeEqual"]}
|