@hasna/logs 0.3.29 → 0.3.31
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/dist/cli/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
runJob,
|
|
9
9
|
structuredLogToEntry,
|
|
10
10
|
validateStructuredLogReferences
|
|
11
|
-
} from "../index-
|
|
11
|
+
} from "../index-a0gz0zzc.js";
|
|
12
12
|
import {
|
|
13
13
|
PACKAGE_VERSION,
|
|
14
14
|
createPage,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
searchTestReports,
|
|
31
31
|
summarizeLogs,
|
|
32
32
|
validateUniversalEventInput
|
|
33
|
-
} from "../index-
|
|
33
|
+
} from "../index-he072p17.js";
|
|
34
34
|
import {
|
|
35
35
|
getStorageStatus,
|
|
36
36
|
storagePull,
|
|
@@ -237,7 +237,7 @@ async function fireAlert(db, rule, count) {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
// src/lib/ingest.ts
|
|
240
|
-
import { randomBytes } from "crypto";
|
|
240
|
+
import { createHash, randomBytes } from "crypto";
|
|
241
241
|
|
|
242
242
|
// src/lib/event-bus.ts
|
|
243
243
|
class LogEventBus {
|
|
@@ -511,6 +511,23 @@ var REDACTED = "[REDACTED]";
|
|
|
511
511
|
var SENSITIVE_KEY = /(?:authorization|cookie|set-cookie|credentials?\b|api[_-]?key|token|secret|password|passwd|pwd|private[_-]?key|access[_-]?token|refresh[_-]?token|session[_-]?secret|client[_-]?(?:secret|credentials?))/i;
|
|
512
512
|
var SENSITIVE_FLAG = /^(?:authorization|auth|credentials?|api[-_]?key|token|secret|password|passwd|pwd|private[-_]?key|access[-_]?token|refresh[-_]?token|session[-_]?secret|client[-_]?(?:secret|credentials?))$/i;
|
|
513
513
|
var SENSITIVE_FLAG_NAME = /(?:authorization|credentials?\b|api[-_]?key|token|secret|password|passwd|pwd|private[-_]?key|access[-_]?token|refresh[-_]?token|session[-_]?secret|client[-_]?(?:secret|credentials?))/i;
|
|
514
|
+
var LOG_ENTRY_REDACTABLE_TOP_LEVEL_FIELDS = [
|
|
515
|
+
"id",
|
|
516
|
+
"source_event_id",
|
|
517
|
+
"service",
|
|
518
|
+
"machine_id",
|
|
519
|
+
"repo_id",
|
|
520
|
+
"app_id",
|
|
521
|
+
"process_id",
|
|
522
|
+
"run_id",
|
|
523
|
+
"trace_id",
|
|
524
|
+
"span_id",
|
|
525
|
+
"parent_span_id",
|
|
526
|
+
"session_id",
|
|
527
|
+
"release_id",
|
|
528
|
+
"environment",
|
|
529
|
+
"agent"
|
|
530
|
+
];
|
|
514
531
|
var STRING_PATTERNS = [
|
|
515
532
|
{
|
|
516
533
|
label: "openlogs_canary",
|
|
@@ -586,6 +603,14 @@ var STRING_PATTERNS = [
|
|
|
586
603
|
function redactLogEntry(entry) {
|
|
587
604
|
const reports = [];
|
|
588
605
|
const next = { ...entry };
|
|
606
|
+
for (const field of LOG_ENTRY_REDACTABLE_TOP_LEVEL_FIELDS) {
|
|
607
|
+
const value = entry[field];
|
|
608
|
+
if (typeof value !== "string")
|
|
609
|
+
continue;
|
|
610
|
+
const result = redactString(value, field);
|
|
611
|
+
next[field] = result.value;
|
|
612
|
+
reports.push(result.report);
|
|
613
|
+
}
|
|
589
614
|
if (typeof entry.message === "string") {
|
|
590
615
|
const result = redactString(entry.message, "message");
|
|
591
616
|
next.message = result.value;
|
|
@@ -633,6 +658,12 @@ function redactString(input, path = "$") {
|
|
|
633
658
|
if (matched)
|
|
634
659
|
fields.push(`${path}:${label}`);
|
|
635
660
|
}
|
|
661
|
+
const cookieResult = redactCookieHeaderText(output);
|
|
662
|
+
if (cookieResult.replacements > 0) {
|
|
663
|
+
output = cookieResult.value;
|
|
664
|
+
fields.push(`${path}:cookie_header`);
|
|
665
|
+
replacements += cookieResult.replacements;
|
|
666
|
+
}
|
|
636
667
|
return {
|
|
637
668
|
value: output,
|
|
638
669
|
report: { applied: replacements > 0, fields, replacements }
|
|
@@ -701,6 +732,191 @@ function redactionMetadata(report) {
|
|
|
701
732
|
function emptyReport() {
|
|
702
733
|
return { applied: false, fields: [], replacements: 0 };
|
|
703
734
|
}
|
|
735
|
+
function redactCookieHeaderText(input) {
|
|
736
|
+
const ranges = [];
|
|
737
|
+
const keyPattern = /set-cookie|cookie/gi;
|
|
738
|
+
let match = keyPattern.exec(input);
|
|
739
|
+
while (match) {
|
|
740
|
+
const keyStart = match.index;
|
|
741
|
+
const keyEnd = keyStart + match[0].length;
|
|
742
|
+
if (hasCookieKeyBoundary(input, keyStart, keyEnd)) {
|
|
743
|
+
const quotedKey = parseQuotedKeyContext(input, keyStart, keyEnd);
|
|
744
|
+
if (quotedKey) {
|
|
745
|
+
const afterKey = skipHorizontalWhitespace(input, quotedKey.afterKey);
|
|
746
|
+
if (input[afterKey] === ":") {
|
|
747
|
+
collectCookieMapValueRanges(input, afterKey + 1, ranges);
|
|
748
|
+
match = keyPattern.exec(input);
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
if (input[afterKey] === ",") {
|
|
752
|
+
const value = parseQuotedString(input, skipHorizontalWhitespace(input, afterKey + 1));
|
|
753
|
+
if (value)
|
|
754
|
+
ranges.push(quotedStringRange(value));
|
|
755
|
+
match = keyPattern.exec(input);
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
collectPlainCookieHeaderRange(input, keyEnd, ranges);
|
|
760
|
+
}
|
|
761
|
+
match = keyPattern.exec(input);
|
|
762
|
+
}
|
|
763
|
+
return applyReplacementRanges(input, ranges);
|
|
764
|
+
}
|
|
765
|
+
function hasCookieKeyBoundary(input, start, end) {
|
|
766
|
+
return !isCookieKeyCharacter(input[start - 1]) && !isCookieKeyCharacter(input[end]);
|
|
767
|
+
}
|
|
768
|
+
function isCookieKeyCharacter(value) {
|
|
769
|
+
return value !== undefined && /[A-Za-z0-9_-]/.test(value);
|
|
770
|
+
}
|
|
771
|
+
function parseQuotedKeyContext(input, start, end) {
|
|
772
|
+
const before = readQuoteBefore(input, start);
|
|
773
|
+
const after = readQuoteAt(input, end);
|
|
774
|
+
if (!before || !after)
|
|
775
|
+
return null;
|
|
776
|
+
if (before.quote !== after.quote || before.escaped !== after.escaped) {
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
return { afterKey: end + after.length };
|
|
780
|
+
}
|
|
781
|
+
function collectCookieMapValueRanges(input, start, ranges) {
|
|
782
|
+
const valueStart = skipHorizontalWhitespace(input, start);
|
|
783
|
+
if (input[valueStart] === "[") {
|
|
784
|
+
collectQuotedArrayValueRanges(input, valueStart, ranges);
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
const value = parseQuotedString(input, valueStart);
|
|
788
|
+
if (value)
|
|
789
|
+
ranges.push(quotedStringRange(value));
|
|
790
|
+
}
|
|
791
|
+
function collectQuotedArrayValueRanges(input, start, ranges) {
|
|
792
|
+
let index = start + 1;
|
|
793
|
+
while (index < input.length) {
|
|
794
|
+
index = skipHorizontalWhitespace(input, index);
|
|
795
|
+
if (input[index] === "]" || isLineBreak(input[index]))
|
|
796
|
+
return;
|
|
797
|
+
const value = parseQuotedString(input, index);
|
|
798
|
+
if (value) {
|
|
799
|
+
ranges.push(quotedStringRange(value));
|
|
800
|
+
index = value.end;
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
index += 1;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
function collectPlainCookieHeaderRange(input, start, ranges) {
|
|
807
|
+
let index = skipHorizontalWhitespace(input, start);
|
|
808
|
+
if (input[index] !== ":" && input[index] !== "=")
|
|
809
|
+
return;
|
|
810
|
+
index = skipHorizontalWhitespace(input, index + 1);
|
|
811
|
+
const end = findLineEnd(input, index);
|
|
812
|
+
if (end > index)
|
|
813
|
+
ranges.push({ start: index, end });
|
|
814
|
+
}
|
|
815
|
+
function parseQuotedString(input, start) {
|
|
816
|
+
const open = readQuoteAt(input, start);
|
|
817
|
+
if (!open)
|
|
818
|
+
return null;
|
|
819
|
+
const contentStart = start + open.length;
|
|
820
|
+
const closeStart = findClosingQuote(input, contentStart, open);
|
|
821
|
+
if (closeStart === null)
|
|
822
|
+
return null;
|
|
823
|
+
return {
|
|
824
|
+
contentStart,
|
|
825
|
+
contentEnd: closeStart,
|
|
826
|
+
end: closeStart + open.length
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
function quotedStringRange(value) {
|
|
830
|
+
return { start: value.contentStart, end: value.contentEnd };
|
|
831
|
+
}
|
|
832
|
+
function findClosingQuote(input, start, token) {
|
|
833
|
+
let index = start;
|
|
834
|
+
while (index < input.length) {
|
|
835
|
+
if (isLineBreak(input[index]))
|
|
836
|
+
return null;
|
|
837
|
+
if (token.escaped) {
|
|
838
|
+
if (input[index] === "\\" && input[index + 1] === token.quote) {
|
|
839
|
+
const slashCount = countContiguousBackslashesEndingAt(input, index);
|
|
840
|
+
if (slashCount === 1)
|
|
841
|
+
return index;
|
|
842
|
+
index += 2;
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
index += 1;
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
if (input[index] === "\\") {
|
|
849
|
+
index += 2;
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
if (input[index] === token.quote)
|
|
853
|
+
return index;
|
|
854
|
+
index += 1;
|
|
855
|
+
}
|
|
856
|
+
return null;
|
|
857
|
+
}
|
|
858
|
+
function readQuoteBefore(input, index) {
|
|
859
|
+
const escapedQuote = input.slice(index - 2, index);
|
|
860
|
+
if (escapedQuote === "\\\"" || escapedQuote === "\\'") {
|
|
861
|
+
return { quote: escapedQuote[1], escaped: true, length: 2 };
|
|
862
|
+
}
|
|
863
|
+
const quote = input[index - 1];
|
|
864
|
+
return quote === '"' || quote === "'" ? { quote, escaped: false, length: 1 } : null;
|
|
865
|
+
}
|
|
866
|
+
function readQuoteAt(input, index) {
|
|
867
|
+
const escapedQuote = input.slice(index, index + 2);
|
|
868
|
+
if (escapedQuote === "\\\"" || escapedQuote === "\\'") {
|
|
869
|
+
return { quote: escapedQuote[1], escaped: true, length: 2 };
|
|
870
|
+
}
|
|
871
|
+
const quote = input[index];
|
|
872
|
+
return quote === '"' || quote === "'" ? { quote, escaped: false, length: 1 } : null;
|
|
873
|
+
}
|
|
874
|
+
function skipHorizontalWhitespace(input, start) {
|
|
875
|
+
let index = start;
|
|
876
|
+
while (input[index] === " " || input[index] === "\t")
|
|
877
|
+
index += 1;
|
|
878
|
+
return index;
|
|
879
|
+
}
|
|
880
|
+
function findLineEnd(input, start) {
|
|
881
|
+
let index = start;
|
|
882
|
+
while (index < input.length && !isLineBreak(input[index]))
|
|
883
|
+
index += 1;
|
|
884
|
+
return index;
|
|
885
|
+
}
|
|
886
|
+
function isLineBreak(value) {
|
|
887
|
+
return value === `
|
|
888
|
+
` || value === "\r";
|
|
889
|
+
}
|
|
890
|
+
function countContiguousBackslashesEndingAt(input, index) {
|
|
891
|
+
let count = 0;
|
|
892
|
+
let cursor = index;
|
|
893
|
+
while (cursor >= 0 && input[cursor] === "\\") {
|
|
894
|
+
count += 1;
|
|
895
|
+
cursor -= 1;
|
|
896
|
+
}
|
|
897
|
+
return count;
|
|
898
|
+
}
|
|
899
|
+
function applyReplacementRanges(input, ranges) {
|
|
900
|
+
const sorted = ranges.filter((range) => range.end > range.start).sort((a, b) => a.start - b.start);
|
|
901
|
+
const deduped = [];
|
|
902
|
+
let lastEnd = -1;
|
|
903
|
+
for (const range of sorted) {
|
|
904
|
+
if (range.start < lastEnd)
|
|
905
|
+
continue;
|
|
906
|
+
deduped.push(range);
|
|
907
|
+
lastEnd = range.end;
|
|
908
|
+
}
|
|
909
|
+
let value = input;
|
|
910
|
+
let replacements = 0;
|
|
911
|
+
for (let index = deduped.length - 1;index >= 0; index -= 1) {
|
|
912
|
+
const range = deduped[index];
|
|
913
|
+
if (value.slice(range.start, range.end) === REDACTED)
|
|
914
|
+
continue;
|
|
915
|
+
value = `${value.slice(0, range.start)}${REDACTED}${value.slice(range.end)}`;
|
|
916
|
+
replacements += 1;
|
|
917
|
+
}
|
|
918
|
+
return { value, replacements };
|
|
919
|
+
}
|
|
704
920
|
function isSensitiveFlag(value) {
|
|
705
921
|
const normalized = value.trim().replace(/^-+/, "");
|
|
706
922
|
if (!normalized || normalized.includes("="))
|
|
@@ -732,7 +948,8 @@ function ingestLog(db, entry) {
|
|
|
732
948
|
return withEventStoreLock(db, () => ingestLogLocked(db, entry));
|
|
733
949
|
}
|
|
734
950
|
function ingestLogLocked(db, entry) {
|
|
735
|
-
const
|
|
951
|
+
const eventIdRedaction = typeof entry.id === "string" ? redactString(entry.id, "id") : null;
|
|
952
|
+
const eventId = entry.id ? eventIdRedaction?.report.applied ? createRedactedEventId(entry.id) : entry.id : createEventId();
|
|
736
953
|
const existing = db.prepare("SELECT * FROM logs WHERE id = ?").get(eventId);
|
|
737
954
|
if (existing)
|
|
738
955
|
return existing;
|
|
@@ -750,6 +967,14 @@ function ingestLogLocked(db, entry) {
|
|
|
750
967
|
};
|
|
751
968
|
const redacted = redactLogEntry(normalized);
|
|
752
969
|
const safeEntry = redacted.value;
|
|
970
|
+
if (eventIdRedaction?.report.applied) {
|
|
971
|
+
const report = mergeRedactionReports(eventIdRedaction.report, redacted.report);
|
|
972
|
+
safeEntry.metadata = {
|
|
973
|
+
...safeEntry.metadata ?? {},
|
|
974
|
+
redaction: redactionMetadata(report)
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
const safeSourceEventId = safeEntry.source_event_id ?? null;
|
|
753
978
|
const identity = extractIdentity(safeEntry);
|
|
754
979
|
const envelope = createLogEnvelope(safeEntry, eventId, eventTime, ingestTime, identity);
|
|
755
980
|
const write = appendRawEvent(db, envelope);
|
|
@@ -778,7 +1003,7 @@ function ingestLogLocked(db, entry) {
|
|
|
778
1003
|
indexRawEvent(db, {
|
|
779
1004
|
event_id: eventId,
|
|
780
1005
|
schema_version: envelope.schema_version,
|
|
781
|
-
source_event_id:
|
|
1006
|
+
source_event_id: safeSourceEventId,
|
|
782
1007
|
event_type: envelope.type,
|
|
783
1008
|
event_time: eventTime,
|
|
784
1009
|
ingest_time: ingestTime,
|
|
@@ -918,6 +1143,10 @@ function stringMetadata(metadata, key) {
|
|
|
918
1143
|
function createEventId() {
|
|
919
1144
|
return randomBytes(16).toString("hex");
|
|
920
1145
|
}
|
|
1146
|
+
function createRedactedEventId(value) {
|
|
1147
|
+
const digest = createHash("sha256").update(value).digest("hex").slice(0, 32);
|
|
1148
|
+
return `log_redacted_${digest}`;
|
|
1149
|
+
}
|
|
921
1150
|
|
|
922
1151
|
// src/lib/package-meta.ts
|
|
923
1152
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -1302,7 +1531,7 @@ function clampNonNegativeInt2(value, fallback) {
|
|
|
1302
1531
|
}
|
|
1303
1532
|
|
|
1304
1533
|
// src/lib/universal-ingest.ts
|
|
1305
|
-
import { createHash, randomBytes as randomBytes2 } from "crypto";
|
|
1534
|
+
import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
|
|
1306
1535
|
var UNIVERSAL_EVENT_TYPES = [
|
|
1307
1536
|
"log",
|
|
1308
1537
|
"exception",
|
|
@@ -1822,7 +2051,7 @@ function normalizeIsoTime(value, field) {
|
|
|
1822
2051
|
function deterministicSourceEventId(source, sourceEventId) {
|
|
1823
2052
|
if (!sourceEventId)
|
|
1824
2053
|
return;
|
|
1825
|
-
const digest =
|
|
2054
|
+
const digest = createHash2("sha256").update(source).update("\x00").update(sourceEventId).digest("hex").slice(0, 32);
|
|
1826
2055
|
return `evt_src_${digest}`;
|
|
1827
2056
|
}
|
|
1828
2057
|
function compactObject(value) {
|
package/dist/mcp/index.js
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
startScheduler,
|
|
9
9
|
structuredLogPayloadToEntries,
|
|
10
10
|
validateStructuredLogReferences
|
|
11
|
-
} from "../index-
|
|
11
|
+
} from "../index-a0gz0zzc.js";
|
|
12
12
|
import {
|
|
13
13
|
countLogs
|
|
14
14
|
} from "../index-gcd14q2f.js";
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
updateAlertRule,
|
|
51
51
|
updateProject,
|
|
52
52
|
validateUniversalEventInput
|
|
53
|
-
} from "../index-
|
|
53
|
+
} from "../index-he072p17.js";
|
|
54
54
|
import {
|
|
55
55
|
getDb,
|
|
56
56
|
getIssue,
|