@oneuptime/common 12.0.27 → 12.0.29
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/Models/AnalyticsModels/RumSessionChunk.ts +52 -0
- package/Models/DatabaseModels/CloudResource.ts +17 -1
- package/Models/DatabaseModels/DashboardDomain.ts +43 -0
- package/Models/DatabaseModels/MonitorTemplate.ts +15 -3
- package/Models/DatabaseModels/RumApplication.ts +35 -2
- package/Models/DatabaseModels/StatusPageDomain.ts +43 -0
- package/Server/API/DashboardDomainAPI.ts +74 -0
- package/Server/API/MicrosoftTeamsAPI.ts +81 -180
- package/Server/API/StatusPageDomainAPI.ts +74 -0
- package/Server/API/TelemetryAPI.ts +104 -15
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.ts +43 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/ProjectAuthorization.ts +6 -1
- package/Server/Services/DashboardDomainService.ts +131 -0
- package/Server/Services/LogPipelineProcessorService.ts +88 -0
- package/Server/Services/MonitorTemplateService.ts +70 -11
- package/Server/Services/StatusPageDomainService.ts +163 -3
- package/Server/Services/StatusPagePrivateUserSessionService.ts +197 -56
- package/Server/Types/Domain.ts +17 -0
- package/Server/Utils/APIKey/AccessPermission.ts +24 -0
- package/Server/Utils/Greenlock/Greenlock.ts +156 -58
- package/Server/Utils/LogPipelineProcessorValidation.ts +112 -0
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +1094 -45
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +153 -17
- package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +12 -5
- package/Server/Utils/SessionReplay/SessionReplayIdentity.ts +102 -0
- package/Server/Utils/SessionReplay/SessionReplayReadService.ts +11 -2
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +23 -11
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +0 -170
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.ts +158 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +23 -11
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +35 -13
- package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +505 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +106 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeUtil.test.ts +84 -0
- package/Tests/App/StatusPage/StatusPageRedirectSafety.test.ts +119 -0
- package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +81 -0
- package/Tests/Models/MonitorTemplateMonitorNameColumn.test.ts +258 -0
- package/Tests/Server/API/CustomDomainReissueSslAPI.test.ts +469 -0
- package/Tests/Server/API/MicrosoftTeamsBotMessagesEndpoint.test.ts +729 -0
- package/Tests/Server/API/MicrosoftTeamsBotTestEndpoint.test.ts +26 -0
- package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +402 -0
- package/Tests/Server/API/SessionReplayAPI.test.ts +232 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -0
- package/Tests/Server/Services/AddCertificateReissueRequestedAtToDomainsMigration.test.ts +287 -0
- package/Tests/Server/Services/CustomDomainCertificateReissue.test.ts +477 -0
- package/Tests/Server/Services/LogPipelineProcessorSaveValidation.test.ts +249 -0
- package/Tests/Server/Services/MonitorTemplateServiceBulkSync.test.ts +549 -0
- package/Tests/Server/Services/MonitorTemplateServiceNetworkDeviceSync.test.ts +16 -3
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +57 -0
- package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +151 -15
- package/Tests/Server/Services/StatusPagePrivateUserSessionService.test.ts +587 -0
- package/Tests/Server/Types/Database/Permissions/ApiKeyFileAccess.test.ts +286 -0
- package/Tests/Server/Types/Database/Permissions/MonitorTemplateColumnAccess.test.ts +328 -0
- package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAndIsNull.test.ts +83 -0
- package/Tests/Server/Utils/AI/SRE/ConfidenceSignal.test.ts +377 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +16 -1
- package/Tests/Server/Utils/Greenlock/CertificateRenewalSchedule.test.ts +250 -0
- package/Tests/Server/Utils/LogPipelineProcessorValidation.test.ts +198 -0
- package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +48 -9
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsAuth.test.ts +726 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +74 -11
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +1107 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +20 -3
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +51 -11
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +1246 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +961 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +653 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +880 -0
- package/Tests/Types/API/HTTPResponse.test.ts +175 -0
- package/Tests/Types/API/Route.test.ts +36 -4
- package/Tests/Types/Database/DatabaseProperty.test.ts +171 -0
- package/Tests/Types/DateLocalShortDateTimeString.test.ts +518 -0
- package/Tests/Types/DateLocalTimeString.test.ts +259 -0
- package/Tests/Types/DateUserPrefers12HourFormat.test.ts +316 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +41 -2
- package/Tests/UI/Components/LogsViewerEmptyStateTimeRange.test.tsx +212 -0
- package/Tests/UI/Components/TelemetryChartClockFormat.test.tsx +359 -0
- package/Tests/UI/Components/TimeRangePickerClockFormat.test.tsx +422 -0
- package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +146 -8
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +520 -0
- package/Tests/UI/Utils/Navigation.test.ts +82 -0
- package/Tests/Utils/Array.test.ts +100 -0
- package/Tests/Utils/CertificateReissue.test.ts +225 -0
- package/Tests/Utils/Grok/Grok.test.ts +492 -0
- package/Tests/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.test.ts +181 -13
- package/Types/API/Route.ts +20 -5
- package/Types/Date.ts +143 -13
- package/Types/Rum/SessionReplay.ts +76 -1
- package/UI/Components/Date/TimeRangePickerDropdown.tsx +9 -5
- package/UI/Components/LogsViewer/LogsViewer.tsx +12 -7
- package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +18 -17
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +21 -28
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +2 -4
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +2 -4
- package/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.tsx +17 -16
- package/UI/Utils/Navigation.ts +42 -0
- package/Utils/Array.ts +37 -0
- package/Utils/CertificateReissue.ts +128 -0
- package/Utils/Grok/Grok.ts +461 -0
- package/Utils/Grok/GrokPatterns.ts +118 -0
- package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +58 -6
- package/build/dist/Models/AnalyticsModels/RumSessionChunk.js +48 -0
- package/build/dist/Models/AnalyticsModels/RumSessionChunk.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CloudResource.js +19 -2
- package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DashboardDomain.js +44 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js +17 -4
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +37 -3
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js +44 -0
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
- package/build/dist/Server/API/DashboardDomainAPI.js +43 -1
- package/build/dist/Server/API/DashboardDomainAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +74 -118
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageDomainAPI.js +43 -1
- package/build/dist/Server/API/StatusPageDomainAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +67 -14
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js +28 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +6 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Services/DashboardDomainService.js +104 -0
- package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
- package/build/dist/Server/Services/LogPipelineProcessorService.js +65 -0
- package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +57 -11
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageDomainService.js +132 -3
- package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js +112 -32
- package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js.map +1 -1
- package/build/dist/Server/Types/Domain.js +16 -0
- package/build/dist/Server/Types/Domain.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +24 -0
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/Greenlock/Greenlock.js +110 -35
- package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
- package/build/dist/Server/Utils/LogPipelineProcessorValidation.js +77 -0
- package/build/dist/Server/Utils/LogPipelineProcessorValidation.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +777 -36
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +108 -17
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayIdentity.js +59 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayIdentity.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +9 -2
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +17 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +0 -159
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js +128 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +17 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -13
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Route.js +16 -5
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/Date.js +115 -13
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplay.js +38 -0
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +9 -5
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +12 -7
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +15 -14
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +17 -22
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +2 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +2 -4
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js +15 -14
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +30 -0
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/Utils/Array.js +25 -0
- package/build/dist/Utils/Array.js.map +1 -1
- package/build/dist/Utils/CertificateReissue.js +97 -0
- package/build/dist/Utils/CertificateReissue.js.map +1 -0
- package/build/dist/Utils/Grok/Grok.js +324 -0
- package/build/dist/Utils/Grok/Grok.js.map +1 -0
- package/build/dist/Utils/Grok/GrokPatterns.js +112 -0
- package/build/dist/Utils/Grok/GrokPatterns.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +63 -3
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
- package/package.json +1 -1
- package/UI/Utils/JsonWebToken.ts +0 -14
- package/build/dist/UI/Utils/JsonWebToken.js +0 -10
- package/build/dist/UI/Utils/JsonWebToken.js.map +0 -1
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
2
|
+
import GrokPatterns from "./GrokPatterns";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Grok engine.
|
|
6
|
+
*
|
|
7
|
+
* A grok pattern is a regex with named references into a pattern
|
|
8
|
+
* library: `%{IPV4:client_ip} - %{WORD:verb}` means "the IPv4 regex,
|
|
9
|
+
* captured as client_ip, then a literal ' - ', then a word captured as
|
|
10
|
+
* verb". Anything that is not a `%{...}` reference is passed through to
|
|
11
|
+
* the regex verbatim, so raw regex and grok references can be mixed.
|
|
12
|
+
*
|
|
13
|
+
* Supported reference forms:
|
|
14
|
+
*
|
|
15
|
+
* %{NAME} match, capture nothing
|
|
16
|
+
* %{NAME:field} capture into `field` as a string
|
|
17
|
+
* %{NAME:field:type} capture into `field`, coerced (int/float/boolean)
|
|
18
|
+
*
|
|
19
|
+
* Compilation expands references recursively into one JavaScript RegExp
|
|
20
|
+
* and records, for each captured field, the internal group name that
|
|
21
|
+
* holds it. Only the groups this engine injects capture — every library
|
|
22
|
+
* definition uses `(?:...)` — so extraction is a `match.groups` lookup
|
|
23
|
+
* with no index arithmetic.
|
|
24
|
+
*
|
|
25
|
+
* Matching is deliberately UNANCHORED, like Logstash: a pattern
|
|
26
|
+
* describes a fragment of the line unless the author anchors it with
|
|
27
|
+
* `^`/`$` themselves.
|
|
28
|
+
*
|
|
29
|
+
* This runs once per ingested log record on the telemetry hot path
|
|
30
|
+
* against text the customer's users control, so the compiler enforces
|
|
31
|
+
* hard ceilings on expansion (depth, count, final source length) and
|
|
32
|
+
* the matcher refuses inputs above MAX_GROK_INPUT_LENGTH rather than
|
|
33
|
+
* handing an unbounded string to a backtracking engine.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
export enum GrokFieldType {
|
|
37
|
+
String = "string",
|
|
38
|
+
Integer = "integer",
|
|
39
|
+
Float = "float",
|
|
40
|
+
Boolean = "boolean",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type GrokValue = string | number | boolean;
|
|
44
|
+
|
|
45
|
+
export interface GrokCapture {
|
|
46
|
+
/** Internal RegExp group name, e.g. "oug0". Never surfaced to users. */
|
|
47
|
+
groupName: string;
|
|
48
|
+
/** Attribute name the user asked for, e.g. "client_ip". */
|
|
49
|
+
fieldName: string;
|
|
50
|
+
fieldType: GrokFieldType;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface CompiledGrokPattern {
|
|
54
|
+
regex: RegExp;
|
|
55
|
+
captures: Array<GrokCapture>;
|
|
56
|
+
/** Expanded regex source. Useful for debugging and for the UI tester. */
|
|
57
|
+
source: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Longest raw pattern a user may save. */
|
|
61
|
+
export const MAX_GROK_PATTERN_LENGTH: number = 4000;
|
|
62
|
+
|
|
63
|
+
/** Longest expanded regex source a pattern may compile to. */
|
|
64
|
+
export const MAX_GROK_SOURCE_LENGTH: number = 20000;
|
|
65
|
+
|
|
66
|
+
/** How deep `%{A}` -> `%{B}` -> `%{C}` nesting may go. */
|
|
67
|
+
export const MAX_GROK_EXPANSION_DEPTH: number = 20;
|
|
68
|
+
|
|
69
|
+
/** Total number of references expanded while compiling one pattern. */
|
|
70
|
+
export const MAX_GROK_EXPANSIONS: number = 500;
|
|
71
|
+
|
|
72
|
+
/** Fields one pattern may capture. */
|
|
73
|
+
export const MAX_GROK_CAPTURES: number = 100;
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
* Inputs longer than this are not parsed. A backtracking regex cannot be
|
|
77
|
+
* interrupted once started, so the only bound available on the ingest
|
|
78
|
+
* hot path is the length of what we feed it.
|
|
79
|
+
*/
|
|
80
|
+
export const MAX_GROK_INPUT_LENGTH: number = 32768;
|
|
81
|
+
|
|
82
|
+
/*
|
|
83
|
+
* `%{NAME}`, `%{NAME:field}` or `%{NAME:field:type}`. The field and type
|
|
84
|
+
* segments exclude `{`, `}` and `:` so a malformed reference fails to
|
|
85
|
+
* match here and is reported as unparsable rather than being silently
|
|
86
|
+
* split at the wrong colon.
|
|
87
|
+
*/
|
|
88
|
+
const GROK_REFERENCE_SOURCE: string =
|
|
89
|
+
"%\\{([A-Za-z0-9_]+)(?::([^:{}]*))?(?::([^:{}]*))?\\}";
|
|
90
|
+
|
|
91
|
+
/*
|
|
92
|
+
* Field names become log attribute keys. Dots are allowed because OTel
|
|
93
|
+
* semantic-convention keys use them (`http.request.method`).
|
|
94
|
+
*/
|
|
95
|
+
const FIELD_NAME_REGEX: RegExp = /^[A-Za-z_][A-Za-z0-9_.@-]*$/;
|
|
96
|
+
|
|
97
|
+
interface CompileContext {
|
|
98
|
+
patterns: Record<string, string>;
|
|
99
|
+
captures: Array<GrokCapture>;
|
|
100
|
+
/** Names currently being expanded — used to reject circular references. */
|
|
101
|
+
stack: Array<string>;
|
|
102
|
+
expansions: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function parseFieldType(rawType: string | undefined): GrokFieldType {
|
|
106
|
+
if (!rawType) {
|
|
107
|
+
return GrokFieldType.String;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
switch (rawType.trim().toLowerCase()) {
|
|
111
|
+
case "int":
|
|
112
|
+
case "integer":
|
|
113
|
+
case "long":
|
|
114
|
+
return GrokFieldType.Integer;
|
|
115
|
+
case "float":
|
|
116
|
+
case "double":
|
|
117
|
+
case "number":
|
|
118
|
+
return GrokFieldType.Float;
|
|
119
|
+
case "bool":
|
|
120
|
+
case "boolean":
|
|
121
|
+
return GrokFieldType.Boolean;
|
|
122
|
+
case "string":
|
|
123
|
+
case "text":
|
|
124
|
+
return GrokFieldType.String;
|
|
125
|
+
default:
|
|
126
|
+
throw new BadDataException(
|
|
127
|
+
`Unknown grok field type "${rawType}". Supported types are: int, long, float, double, boolean, string.`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function resolveReference(
|
|
133
|
+
match: RegExpExecArray,
|
|
134
|
+
context: CompileContext,
|
|
135
|
+
): string {
|
|
136
|
+
const patternName: string = match[1] as string;
|
|
137
|
+
const rawFieldName: string | undefined = match[2];
|
|
138
|
+
const rawFieldType: string | undefined = match[3];
|
|
139
|
+
|
|
140
|
+
context.expansions++;
|
|
141
|
+
|
|
142
|
+
if (context.expansions > MAX_GROK_EXPANSIONS) {
|
|
143
|
+
throw new BadDataException(
|
|
144
|
+
`This grok pattern expands to more than ${MAX_GROK_EXPANSIONS} sub-patterns. Please simplify it.`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const definition: string | undefined = context.patterns[patternName];
|
|
149
|
+
|
|
150
|
+
if (definition === undefined) {
|
|
151
|
+
throw new BadDataException(
|
|
152
|
+
`Unknown grok pattern "%{${patternName}}". Check the list of supported patterns.`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (context.stack.includes(patternName)) {
|
|
157
|
+
throw new BadDataException(
|
|
158
|
+
`Grok pattern "%{${patternName}}" refers to itself. Circular pattern references are not supported.`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (context.stack.length >= MAX_GROK_EXPANSION_DEPTH) {
|
|
163
|
+
throw new BadDataException(
|
|
164
|
+
`Grok pattern "%{${patternName}}" nests more than ${MAX_GROK_EXPANSION_DEPTH} levels deep. Please simplify it.`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
context.stack.push(patternName);
|
|
169
|
+
const expanded: string = expandPattern(definition, context);
|
|
170
|
+
context.stack.pop();
|
|
171
|
+
|
|
172
|
+
if (rawFieldName === undefined) {
|
|
173
|
+
return `(?:${expanded})`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const fieldName: string = rawFieldName.trim();
|
|
177
|
+
|
|
178
|
+
if (!FIELD_NAME_REGEX.test(fieldName)) {
|
|
179
|
+
throw new BadDataException(
|
|
180
|
+
`"${rawFieldName}" is not a valid grok field name. Use letters, digits, and . _ - @ (starting with a letter or underscore).`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (context.captures.length >= MAX_GROK_CAPTURES) {
|
|
185
|
+
throw new BadDataException(
|
|
186
|
+
`This grok pattern captures more than ${MAX_GROK_CAPTURES} fields. Please capture fewer fields.`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const groupName: string = `oug${context.captures.length}`;
|
|
191
|
+
|
|
192
|
+
context.captures.push({
|
|
193
|
+
groupName: groupName,
|
|
194
|
+
fieldName: fieldName,
|
|
195
|
+
fieldType: parseFieldType(rawFieldType),
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return `(?<${groupName}>${expanded})`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function expandPattern(source: string, context: CompileContext): string {
|
|
202
|
+
/*
|
|
203
|
+
* A fresh RegExp per call: `lastIndex` is per-object state and
|
|
204
|
+
* expansion recurses, so a shared instance would have nested calls
|
|
205
|
+
* stepping on each other's scan position.
|
|
206
|
+
*/
|
|
207
|
+
const referenceRegex: RegExp = new RegExp(GROK_REFERENCE_SOURCE, "g");
|
|
208
|
+
|
|
209
|
+
let output: string = "";
|
|
210
|
+
let cursor: number = 0;
|
|
211
|
+
let match: RegExpExecArray | null = referenceRegex.exec(source);
|
|
212
|
+
|
|
213
|
+
while (match !== null) {
|
|
214
|
+
output += source.slice(cursor, match.index);
|
|
215
|
+
output += resolveReference(match, context);
|
|
216
|
+
cursor = match.index + match[0].length;
|
|
217
|
+
|
|
218
|
+
if (output.length > MAX_GROK_SOURCE_LENGTH) {
|
|
219
|
+
throw new BadDataException(
|
|
220
|
+
`This grok pattern expands to more than ${MAX_GROK_SOURCE_LENGTH} characters of regex. Please simplify it.`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
match = referenceRegex.exec(source);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
output += source.slice(cursor);
|
|
228
|
+
|
|
229
|
+
return output;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/*
|
|
233
|
+
* Compile a grok pattern. Throws BadDataException with a message meant
|
|
234
|
+
* for the person who typed the pattern — this is what save-time
|
|
235
|
+
* validation and the pattern tester in the dashboard show.
|
|
236
|
+
*/
|
|
237
|
+
export function compileGrokPattern(
|
|
238
|
+
pattern: string,
|
|
239
|
+
extraPatterns?: Record<string, string> | undefined,
|
|
240
|
+
): CompiledGrokPattern {
|
|
241
|
+
if (typeof pattern !== "string" || pattern.trim().length === 0) {
|
|
242
|
+
throw new BadDataException("Grok pattern cannot be empty.");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (pattern.length > MAX_GROK_PATTERN_LENGTH) {
|
|
246
|
+
throw new BadDataException(
|
|
247
|
+
`Grok pattern cannot be longer than ${MAX_GROK_PATTERN_LENGTH} characters.`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const context: CompileContext = {
|
|
252
|
+
patterns: extraPatterns
|
|
253
|
+
? { ...GrokPatterns, ...extraPatterns }
|
|
254
|
+
: GrokPatterns,
|
|
255
|
+
captures: [],
|
|
256
|
+
stack: [],
|
|
257
|
+
expansions: 0,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const source: string = expandPattern(pattern, context);
|
|
261
|
+
|
|
262
|
+
if (source.length > MAX_GROK_SOURCE_LENGTH) {
|
|
263
|
+
throw new BadDataException(
|
|
264
|
+
`This grok pattern expands to more than ${MAX_GROK_SOURCE_LENGTH} characters of regex. Please simplify it.`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let regex: RegExp;
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
/*
|
|
272
|
+
* No `g`/`y` flag on purpose: those carry `lastIndex` between calls
|
|
273
|
+
* and a compiled pattern is reused for every record.
|
|
274
|
+
*/
|
|
275
|
+
regex = new RegExp(source);
|
|
276
|
+
} catch (err) {
|
|
277
|
+
throw new BadDataException(
|
|
278
|
+
`This grok pattern is not valid: ${err instanceof Error ? err.message : String(err)}`,
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
regex: regex,
|
|
284
|
+
captures: context.captures,
|
|
285
|
+
source: source,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function coerceValue(raw: string, fieldType: GrokFieldType): GrokValue {
|
|
290
|
+
if (fieldType === GrokFieldType.Integer) {
|
|
291
|
+
const parsed: number = parseInt(raw, 10);
|
|
292
|
+
return Number.isFinite(parsed) ? parsed : raw;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (fieldType === GrokFieldType.Float) {
|
|
296
|
+
const parsed: number = parseFloat(raw);
|
|
297
|
+
return Number.isFinite(parsed) ? parsed : raw;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (fieldType === GrokFieldType.Boolean) {
|
|
301
|
+
const normalized: string = raw.trim().toLowerCase();
|
|
302
|
+
|
|
303
|
+
if (
|
|
304
|
+
normalized === "true" ||
|
|
305
|
+
normalized === "yes" ||
|
|
306
|
+
normalized === "y" ||
|
|
307
|
+
normalized === "1"
|
|
308
|
+
) {
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (
|
|
313
|
+
normalized === "false" ||
|
|
314
|
+
normalized === "no" ||
|
|
315
|
+
normalized === "n" ||
|
|
316
|
+
normalized === "0"
|
|
317
|
+
) {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/*
|
|
322
|
+
* Not recognisably a boolean — keep the raw text rather than
|
|
323
|
+
* inventing a `false` the log never said.
|
|
324
|
+
*/
|
|
325
|
+
return raw;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return raw;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/*
|
|
332
|
+
* Run a compiled pattern against one string. Returns null when the
|
|
333
|
+
* pattern does not match; returns the captured fields otherwise (which
|
|
334
|
+
* is an empty object for a pattern that matches but captures nothing).
|
|
335
|
+
*
|
|
336
|
+
* Empty captures are dropped, matching Logstash's default
|
|
337
|
+
* `keep_empty_captures => false`: an optional group that did not
|
|
338
|
+
* participate should not write a blank attribute onto the log.
|
|
339
|
+
*/
|
|
340
|
+
export function matchGrokPattern(
|
|
341
|
+
compiled: CompiledGrokPattern,
|
|
342
|
+
input: string,
|
|
343
|
+
): Record<string, GrokValue> | null {
|
|
344
|
+
if (typeof input !== "string" || input.length === 0) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (input.length > MAX_GROK_INPUT_LENGTH) {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const match: RegExpExecArray | null = compiled.regex.exec(input);
|
|
353
|
+
|
|
354
|
+
if (!match) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const groups: Record<string, string | undefined> = match.groups || {};
|
|
359
|
+
const extracted: Record<string, GrokValue> = {};
|
|
360
|
+
|
|
361
|
+
for (const capture of compiled.captures) {
|
|
362
|
+
const raw: string | undefined = groups[capture.groupName];
|
|
363
|
+
|
|
364
|
+
if (raw === undefined || raw === "") {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/*
|
|
369
|
+
* The same field name can legitimately appear twice (alternation
|
|
370
|
+
* branches, or a library pattern that already names it). Whichever
|
|
371
|
+
* group actually matched first wins, so a later branch that did not
|
|
372
|
+
* participate cannot blank out a real value.
|
|
373
|
+
*/
|
|
374
|
+
if (extracted[capture.fieldName] !== undefined) {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
extracted[capture.fieldName] = coerceValue(raw, capture.fieldType);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return extracted;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/*
|
|
385
|
+
* Process-wide compile cache for the ingest path.
|
|
386
|
+
*
|
|
387
|
+
* Compiling walks the pattern library and builds a RegExp; doing that
|
|
388
|
+
* per log record would dominate the cost of the processor. Compiled
|
|
389
|
+
* patterns are immutable and carry no `lastIndex` state (no `g` flag),
|
|
390
|
+
* so one instance is safely shared by every project using the same
|
|
391
|
+
* pattern text.
|
|
392
|
+
*
|
|
393
|
+
* Failures are cached too. An invalid pattern that reaches ingest would
|
|
394
|
+
* otherwise be recompiled — and rejected — once per record.
|
|
395
|
+
*/
|
|
396
|
+
const compileCache: Map<string, CompiledGrokPattern | BadDataException> =
|
|
397
|
+
new Map<string, CompiledGrokPattern | BadDataException>();
|
|
398
|
+
|
|
399
|
+
export const MAX_CACHED_GROK_PATTERNS: number = 500;
|
|
400
|
+
|
|
401
|
+
export function compileGrokPatternCached(pattern: string): CompiledGrokPattern {
|
|
402
|
+
const cached: CompiledGrokPattern | BadDataException | undefined =
|
|
403
|
+
compileCache.get(pattern);
|
|
404
|
+
|
|
405
|
+
if (cached !== undefined) {
|
|
406
|
+
if (cached instanceof BadDataException) {
|
|
407
|
+
throw cached;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return cached;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
let result: CompiledGrokPattern | BadDataException;
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
result = compileGrokPattern(pattern);
|
|
417
|
+
} catch (err) {
|
|
418
|
+
result =
|
|
419
|
+
err instanceof BadDataException ? err : new BadDataException(String(err));
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (compileCache.size >= MAX_CACHED_GROK_PATTERNS) {
|
|
423
|
+
// Insertion-ordered: drop the oldest entry to bound the cache.
|
|
424
|
+
const oldestKey: string | undefined = compileCache.keys().next().value;
|
|
425
|
+
|
|
426
|
+
if (oldestKey !== undefined) {
|
|
427
|
+
compileCache.delete(oldestKey);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
compileCache.set(pattern, result);
|
|
432
|
+
|
|
433
|
+
if (result instanceof BadDataException) {
|
|
434
|
+
throw result;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return result;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** Test seam: forget every cached compilation. */
|
|
441
|
+
export function clearGrokCompileCache(): void {
|
|
442
|
+
compileCache.clear();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/*
|
|
446
|
+
* Compile + match in one call. Convenience for the dashboard's pattern
|
|
447
|
+
* tester and for tests; ingest compiles once and reuses the result.
|
|
448
|
+
*/
|
|
449
|
+
export function parseWithGrokPattern(
|
|
450
|
+
pattern: string,
|
|
451
|
+
input: string,
|
|
452
|
+
): Record<string, GrokValue> | null {
|
|
453
|
+
return matchGrokPattern(compileGrokPattern(pattern), input);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
export default {
|
|
457
|
+
compileGrokPattern,
|
|
458
|
+
compileGrokPatternCached,
|
|
459
|
+
matchGrokPattern,
|
|
460
|
+
parseWithGrokPattern,
|
|
461
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Built-in grok pattern library.
|
|
3
|
+
*
|
|
4
|
+
* Grok is regex with names: `%{IPV4:client_ip}` is just the IPv4 regex
|
|
5
|
+
* plus "put what it matched in client_ip". This file holds the named
|
|
6
|
+
* regex halves; Grok.ts does the naming.
|
|
7
|
+
*
|
|
8
|
+
* Two rules every entry here must follow:
|
|
9
|
+
*
|
|
10
|
+
* 1. NO capturing groups. Use `(?:...)`. Grok.ts injects its own
|
|
11
|
+
* named groups around the pattern it is asked to capture and reads
|
|
12
|
+
* results back through `match.groups`, so a stray `(...)` in a
|
|
13
|
+
* definition would silently shift nothing but still cost a capture
|
|
14
|
+
* slot on every record.
|
|
15
|
+
*
|
|
16
|
+
* 2. JavaScript-compatible syntax only. The upstream Logstash
|
|
17
|
+
* definitions are Oniguruma and use possessive quantifiers (`++`)
|
|
18
|
+
* and atomic groups (`(?>...)`), neither of which V8 accepts —
|
|
19
|
+
* those have been rewritten here rather than copied.
|
|
20
|
+
*
|
|
21
|
+
* Every repetition of a group consumes at least one character, which is
|
|
22
|
+
* what keeps `(?:/[\w]*)+`-shaped patterns linear instead of
|
|
23
|
+
* exponential. Keep it that way when adding patterns: these run once
|
|
24
|
+
* per ingested log record against attacker-supplied text.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const GrokPatterns: Record<string, string> = {
|
|
28
|
+
/* --- Primitives --- */
|
|
29
|
+
USERNAME: String.raw`[a-zA-Z0-9._-]+`,
|
|
30
|
+
USER: String.raw`%{USERNAME}`,
|
|
31
|
+
INT: String.raw`(?:[+-]?(?:[0-9]+))`,
|
|
32
|
+
BASE10NUM: String.raw`(?:[+-]?(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+))`,
|
|
33
|
+
NUMBER: String.raw`(?:%{BASE10NUM})`,
|
|
34
|
+
BASE16NUM: String.raw`(?:0[xX])?(?:[0-9A-Fa-f]+)`,
|
|
35
|
+
POSINT: String.raw`\b(?:[1-9][0-9]*)\b`,
|
|
36
|
+
NONNEGINT: String.raw`\b(?:[0-9]+)\b`,
|
|
37
|
+
WORD: String.raw`\b\w+\b`,
|
|
38
|
+
NOTSPACE: String.raw`\S+`,
|
|
39
|
+
SPACE: String.raw`\s*`,
|
|
40
|
+
DATA: String.raw`.*?`,
|
|
41
|
+
GREEDYDATA: String.raw`.*`,
|
|
42
|
+
QUOTEDSTRING: String.raw`(?:"(?:\\.|[^\\"])*"|'(?:\\.|[^\\'])*')`,
|
|
43
|
+
QS: String.raw`%{QUOTEDSTRING}`,
|
|
44
|
+
UUID: String.raw`[A-Fa-f0-9]{8}-(?:[A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}`,
|
|
45
|
+
|
|
46
|
+
/* --- Network --- */
|
|
47
|
+
IPV4: String.raw`(?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?![0-9])`,
|
|
48
|
+
/*
|
|
49
|
+
* Alternation is first-match, not longest-match, so order carries
|
|
50
|
+
* meaning here: the IPv4-embedded forms come first (a generic hex form
|
|
51
|
+
* would otherwise stop before the dotted quad), and the "::"-elision
|
|
52
|
+
* forms are ordered so a shape that can only match a prefix never sits
|
|
53
|
+
* ahead of one that matches the whole address.
|
|
54
|
+
*/
|
|
55
|
+
IPV6: String.raw`(?:::(?:[Ff]{4}(?::0{1,4})?:)?%{IPV4}|(?:[0-9A-Fa-f]{1,4}:){1,4}:%{IPV4}|(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|[0-9A-Fa-f]{1,4}:(?::[0-9A-Fa-f]{1,4}){1,6}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:))`,
|
|
56
|
+
IP: String.raw`(?:%{IPV6}|%{IPV4})`,
|
|
57
|
+
HOSTNAME: String.raw`\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*\.?`,
|
|
58
|
+
IPORHOST: String.raw`(?:%{IP}|%{HOSTNAME})`,
|
|
59
|
+
HOSTPORT: String.raw`%{IPORHOST}:%{POSINT}`,
|
|
60
|
+
COMMONMAC: String.raw`(?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2})`,
|
|
61
|
+
CISCOMAC: String.raw`(?:(?:[A-Fa-f0-9]{4}\.){2}[A-Fa-f0-9]{4})`,
|
|
62
|
+
WINDOWSMAC: String.raw`(?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2})`,
|
|
63
|
+
MAC: String.raw`(?:%{CISCOMAC}|%{WINDOWSMAC}|%{COMMONMAC})`,
|
|
64
|
+
EMAILLOCALPART: String.raw`[a-zA-Z0-9!#$%&'*+\-/=?^_{|}~]{1,64}(?:\.[a-zA-Z0-9!#$%&'*+\-/=?^_{|}~]{1,62}){0,63}`,
|
|
65
|
+
EMAILADDRESS: String.raw`%{EMAILLOCALPART}@%{HOSTNAME}`,
|
|
66
|
+
|
|
67
|
+
/* --- Paths and URIs --- */
|
|
68
|
+
UNIXPATH: String.raw`(?:/[\w_%!$@:.,+~-]*)+`,
|
|
69
|
+
WINPATH: String.raw`(?:[A-Za-z]+:|\\)(?:\\[^\\?*]*)+`,
|
|
70
|
+
PATH: String.raw`(?:%{UNIXPATH}|%{WINPATH})`,
|
|
71
|
+
URIPROTO: String.raw`[A-Za-z][A-Za-z0-9+.-]+`,
|
|
72
|
+
URIHOST: String.raw`%{IPORHOST}(?::%{POSINT})?`,
|
|
73
|
+
URIPATH: String.raw`(?:/[A-Za-z0-9$.+!*'(){},~:;=@#%&_-]*)+`,
|
|
74
|
+
URIPARAM: String.raw`\?[A-Za-z0-9$.+!*'|(){},~@#%&/=:;_?<>\[\]-]*`,
|
|
75
|
+
URIPATHPARAM: String.raw`%{URIPATH}(?:%{URIPARAM})?`,
|
|
76
|
+
URI: String.raw`%{URIPROTO}://(?:%{USER}(?::[^@]*)?@)?(?:%{URIHOST})?(?:%{URIPATHPARAM})?`,
|
|
77
|
+
|
|
78
|
+
/* --- Dates and times --- */
|
|
79
|
+
MONTH: String.raw`\b(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\b`,
|
|
80
|
+
MONTHNUM: String.raw`(?:0?[1-9]|1[0-2])`,
|
|
81
|
+
MONTHNUM2: String.raw`(?:0[1-9]|1[0-2])`,
|
|
82
|
+
MONTHDAY: String.raw`(?:0[1-9]|[12][0-9]|3[01]|[1-9])`,
|
|
83
|
+
DAY: String.raw`(?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?)`,
|
|
84
|
+
YEAR: String.raw`(?:\d\d){1,2}`,
|
|
85
|
+
HOUR: String.raw`(?:2[0123]|[01]?[0-9])`,
|
|
86
|
+
MINUTE: String.raw`(?:[0-5][0-9])`,
|
|
87
|
+
SECOND: String.raw`(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?)`,
|
|
88
|
+
TIME: String.raw`(?:%{HOUR}:%{MINUTE}(?::%{SECOND})?)`,
|
|
89
|
+
DATE_US: String.raw`%{MONTHNUM}[/-]%{MONTHDAY}[/-]%{YEAR}`,
|
|
90
|
+
DATE_EU: String.raw`%{MONTHDAY}[./-]%{MONTHNUM}[./-]%{YEAR}`,
|
|
91
|
+
DATE: String.raw`(?:%{DATE_US}|%{DATE_EU})`,
|
|
92
|
+
ISO8601_TIMEZONE: String.raw`(?:Z|[+-]%{HOUR}(?::?%{MINUTE})?)`,
|
|
93
|
+
TIMESTAMP_ISO8601: String.raw`%{YEAR}-%{MONTHNUM2}-%{MONTHDAY}[T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})?(?:%{ISO8601_TIMEZONE})?`,
|
|
94
|
+
DATESTAMP: String.raw`%{DATE}[- ]%{TIME}`,
|
|
95
|
+
TZ: String.raw`(?:[APMCE][SD]T|UTC|GMT)`,
|
|
96
|
+
HTTPDATE: String.raw`%{MONTHDAY}/%{MONTH}/%{YEAR}:%{TIME} %{INT}`,
|
|
97
|
+
SYSLOGTIMESTAMP: String.raw`%{MONTH} +%{MONTHDAY} %{TIME}`,
|
|
98
|
+
|
|
99
|
+
/* --- Log shapes --- */
|
|
100
|
+
LOGLEVEL: String.raw`(?:[Aa]lert|ALERT|[Tt]race|TRACE|[Dd]ebug|DEBUG|[Nn]otice|NOTICE|[Ii]nfo(?:rmation)?|INFO(?:RMATION)?|[Ww]arn(?:ing)?|WARN(?:ING)?|[Ee]rr(?:or)?|ERR(?:OR)?|[Cc]rit(?:ical)?|CRIT(?:ICAL)?|[Ff]atal|FATAL|[Ss]evere|SEVERE|[Ee]merg(?:ency)?|EMERG(?:ENCY)?)`,
|
|
101
|
+
PROG: String.raw`[\x21-\x5a\x5c\x5e-\x7e]+`,
|
|
102
|
+
SYSLOGPROG: String.raw`%{PROG:program}(?:\[%{POSINT:pid}\])?`,
|
|
103
|
+
SYSLOGFACILITY: String.raw`<%{NONNEGINT:facility}\.%{NONNEGINT:priority}>`,
|
|
104
|
+
SYSLOGBASE: String.raw`%{SYSLOGTIMESTAMP:timestamp} (?:%{SYSLOGFACILITY} )?%{IPORHOST:logsource} %{SYSLOGPROG}:`,
|
|
105
|
+
JAVACLASS: String.raw`(?:[a-zA-Z$_][a-zA-Z$_0-9]*\.)*[a-zA-Z$_][a-zA-Z$_0-9]*`,
|
|
106
|
+
COMMONAPACHELOG: String.raw`%{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})" %{NUMBER:response} (?:%{NUMBER:bytes}|-)`,
|
|
107
|
+
COMBINEDAPACHELOG: String.raw`%{COMMONAPACHELOG} %{QS:referrer} %{QS:agent}`,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/*
|
|
111
|
+
* Names only, sorted — the pattern picker in the processor form lists
|
|
112
|
+
* these so a user can see what is available without leaving the page.
|
|
113
|
+
*/
|
|
114
|
+
export function getGrokPatternNames(): Array<string> {
|
|
115
|
+
return Object.keys(GrokPatterns).sort();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export default GrokPatterns;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import Monitor from "../../Models/DatabaseModels/Monitor";
|
|
2
2
|
import MonitorTemplate from "../../Models/DatabaseModels/MonitorTemplate";
|
|
3
3
|
import NetworkDevice from "../../Models/DatabaseModels/NetworkDevice";
|
|
4
|
-
import ColumnLength from "../../Types/Database/ColumnLength";
|
|
5
4
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
6
5
|
import { JSONObject } from "../../Types/JSON";
|
|
7
6
|
import JSONFunctions from "../../Types/JSONFunctions";
|
|
@@ -25,6 +24,29 @@ import ObjectID from "../../Types/ObjectID";
|
|
|
25
24
|
* rule-engine, and audit hooks still run.
|
|
26
25
|
*/
|
|
27
26
|
|
|
27
|
+
/*
|
|
28
|
+
* The widest name a provisioned monitor may carry.
|
|
29
|
+
*
|
|
30
|
+
* Monitor.name is varchar(100), but the real ceiling is the SLUG: Monitor is
|
|
31
|
+
* @SlugifyColumn("name", "slug") and Slug.getSlug appends a dash plus ten
|
|
32
|
+
* random digits into its own varchar(100), and the create path THROWS on
|
|
33
|
+
* overflow rather than truncating. A name that fills the name column
|
|
34
|
+
* therefore produces a 111-character slug and fails the insert — the monitor
|
|
35
|
+
* is counted into `monitorsFailed` and the device silently never gets one.
|
|
36
|
+
*
|
|
37
|
+
* 88 leaves exactly the slug's 11-character tail, the same cap
|
|
38
|
+
* MAX_PING_MONITOR_NAME_LENGTH uses on the other provisioning path. Device
|
|
39
|
+
* names arrive clamped to 80 by DiscoveredDeviceBuilder, so this only bites a
|
|
40
|
+
* device renamed by hand or a long template suffix.
|
|
41
|
+
*/
|
|
42
|
+
export const MAX_PROVISIONED_MONITOR_NAME_LENGTH: number = 88;
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
* What sits between the device's own identity and the template's default
|
|
46
|
+
* monitor name when the template supplies one.
|
|
47
|
+
*/
|
|
48
|
+
const MONITOR_NAME_SEPARATOR: string = " - ";
|
|
49
|
+
|
|
28
50
|
type NetworkDeviceId = ObjectID | string | null | undefined;
|
|
29
51
|
|
|
30
52
|
export interface RebindNetworkDeviceMonitorStepsData {
|
|
@@ -300,12 +322,42 @@ export default class NetworkDeviceMonitorTemplateUtil {
|
|
|
300
322
|
data.networkDevice.hostname?.trim() ||
|
|
301
323
|
`Network Device ${networkDeviceId}`;
|
|
302
324
|
const templateMonitorName: string =
|
|
303
|
-
data.monitorTemplate.monitorName?.trim() || "
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
325
|
+
data.monitorTemplate.monitorName?.trim() || "";
|
|
326
|
+
|
|
327
|
+
/*
|
|
328
|
+
* The suffix is OPT-IN (issue #3486). A template with no default monitor
|
|
329
|
+
* name names its monitors after the device and nothing else, because the
|
|
330
|
+
* alternative — the old `|| "Monitor"` fallback — was an invented suffix
|
|
331
|
+
* on an estate the operator never asked to have suffixed. A template that
|
|
332
|
+
* DOES carry a name keeps the composed form, which is what tells two
|
|
333
|
+
* templates apart on the same device.
|
|
334
|
+
*/
|
|
335
|
+
const composedName: string = templateMonitorName
|
|
336
|
+
? `${deviceIdentity}${MONITOR_NAME_SEPARATOR}${templateMonitorName}`
|
|
337
|
+
: deviceIdentity;
|
|
338
|
+
|
|
339
|
+
const truncatedName: string = this.truncateWithoutSplittingSurrogatePair(
|
|
340
|
+
composedName,
|
|
341
|
+
MAX_PROVISIONED_MONITOR_NAME_LENGTH,
|
|
308
342
|
);
|
|
343
|
+
|
|
344
|
+
/*
|
|
345
|
+
* The device identity leads, so a cap reached inside the separator leaves
|
|
346
|
+
* the name trailing " ", " -" or " - " — a suffix that is visibly half
|
|
347
|
+
* there. Everything that survived such a cut IS the device identity, so
|
|
348
|
+
* return that rather than the remnant. Checked by LENGTH rather than by
|
|
349
|
+
* trailing characters, so a device genuinely named "Router A -" keeps its
|
|
350
|
+
* own trailing hyphen when nothing was cut at all.
|
|
351
|
+
*/
|
|
352
|
+
if (
|
|
353
|
+
truncatedName.length > deviceIdentity.length &&
|
|
354
|
+
truncatedName.length <=
|
|
355
|
+
deviceIdentity.length + MONITOR_NAME_SEPARATOR.length
|
|
356
|
+
) {
|
|
357
|
+
return deviceIdentity;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return truncatedName;
|
|
309
361
|
}
|
|
310
362
|
|
|
311
363
|
private static truncateWithoutSplittingSurrogatePair(
|