@bli-cockpit/telemetry-core 0.1.7 → 0.1.9

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.
@@ -58,6 +58,14 @@ export declare const EvidenceCompletenessTruncationMarkerSchema: z.ZodObject<{
58
58
  included_bytes: z.ZodOptional<z.ZodNumber>;
59
59
  }, z.core.$strict>;
60
60
  export type EvidenceCompletenessTruncationMarker = z.infer<typeof EvidenceCompletenessTruncationMarkerSchema>;
61
+ export declare const EvidenceCompletenessRedactionMarkerSchema: z.ZodObject<{
62
+ source: z.ZodString;
63
+ status: z.ZodLiteral<"sanitized">;
64
+ mode: z.ZodString;
65
+ count: z.ZodNumber;
66
+ rule_ids: z.ZodDefault<z.ZodArray<z.ZodString>>;
67
+ }, z.core.$strict>;
68
+ export type EvidenceCompletenessRedactionMarker = z.infer<typeof EvidenceCompletenessRedactionMarkerSchema>;
61
69
  export declare const EvidenceCompletenessPayloadSchema: z.ZodObject<{
62
70
  schema_version: z.ZodLiteral<"evidence-completeness.v1">;
63
71
  status: z.ZodEnum<{
@@ -118,6 +126,13 @@ export declare const EvidenceCompletenessPayloadSchema: z.ZodObject<{
118
126
  observed_bytes: z.ZodOptional<z.ZodNumber>;
119
127
  included_bytes: z.ZodOptional<z.ZodNumber>;
120
128
  }, z.core.$strict>>>;
129
+ redaction_markers: z.ZodDefault<z.ZodArray<z.ZodObject<{
130
+ source: z.ZodString;
131
+ status: z.ZodLiteral<"sanitized">;
132
+ mode: z.ZodString;
133
+ count: z.ZodNumber;
134
+ rule_ids: z.ZodDefault<z.ZodArray<z.ZodString>>;
135
+ }, z.core.$strict>>>;
121
136
  notes: z.ZodDefault<z.ZodArray<z.ZodString>>;
122
137
  }, z.core.$strict>;
123
138
  export type EvidenceCompletenessPayload = z.infer<typeof EvidenceCompletenessPayloadSchema>;
@@ -49,6 +49,15 @@ export const EvidenceCompletenessTruncationMarkerSchema = z
49
49
  included_bytes: EvidenceCountSchema.optional(),
50
50
  })
51
51
  .strict();
52
+ export const EvidenceCompletenessRedactionMarkerSchema = z
53
+ .object({
54
+ source: NonEmptyStringSchema,
55
+ status: z.literal("sanitized"),
56
+ mode: NonEmptyStringSchema,
57
+ count: EvidenceCountSchema,
58
+ rule_ids: z.array(NonEmptyStringSchema).default([]),
59
+ })
60
+ .strict();
52
61
  export const EvidenceCompletenessPayloadSchema = z
53
62
  .object({
54
63
  schema_version: z.literal("evidence-completeness.v1"),
@@ -71,6 +80,9 @@ export const EvidenceCompletenessPayloadSchema = z
71
80
  truncation_markers: z
72
81
  .array(EvidenceCompletenessTruncationMarkerSchema)
73
82
  .default([]),
83
+ redaction_markers: z
84
+ .array(EvidenceCompletenessRedactionMarkerSchema)
85
+ .default([]),
74
86
  notes: z.array(NonEmptyStringSchema.max(160)).default([]),
75
87
  })
76
88
  .strict()
@@ -82,11 +94,12 @@ export const EvidenceCompletenessPayloadSchema = z
82
94
  payload.caps.some((cap) => cap.applied) ||
83
95
  payload.skip_reasons.length > 0 ||
84
96
  payload.failure_reasons.length > 0 ||
85
- payload.truncation_markers.length > 0;
97
+ payload.truncation_markers.length > 0 ||
98
+ payload.redaction_markers.length > 0;
86
99
  if (payload.status === "complete" && hasGap) {
87
100
  context.addIssue({
88
101
  code: "custom",
89
- message: "complete evidence completeness cannot include skips, caps, truncations, deferrals, or failures",
102
+ message: "complete evidence completeness cannot include skips, caps, truncations, deferrals, failures, or redactions",
90
103
  path: ["status"],
91
104
  });
92
105
  }
@@ -46,6 +46,32 @@ export declare const RawEvidenceUploadBeginObjectSchema: z.ZodObject<{
46
46
  byte_size: z.ZodOptional<z.ZodNumber>;
47
47
  media_type: z.ZodOptional<z.ZodString>;
48
48
  redacted_summary: z.ZodOptional<z.ZodString>;
49
+ redaction: z.ZodOptional<z.ZodObject<{
50
+ schema_version: z.ZodLiteral<"raw-evidence-redaction.v1">;
51
+ status: z.ZodLiteral<"sanitized">;
52
+ mode: z.ZodLiteral<"deterministic_text_replacement">;
53
+ applied_by: z.ZodArray<z.ZodEnum<{
54
+ local_collector: "local_collector";
55
+ server_commit: "server_commit";
56
+ legacy_upload: "legacy_upload";
57
+ }>>;
58
+ rule_counts: z.ZodArray<z.ZodObject<{
59
+ rule_id: z.ZodString;
60
+ match_count: z.ZodNumber;
61
+ redacted_char_count: z.ZodNumber;
62
+ }, z.core.$strict>>;
63
+ secret_like_match_count: z.ZodNumber;
64
+ redacted_fields: z.ZodDefault<z.ZodArray<z.ZodString>>;
65
+ redacted_ranges: z.ZodDefault<z.ZodArray<z.ZodObject<{
66
+ start: z.ZodNumber;
67
+ end: z.ZodNumber;
68
+ rule_id: z.ZodString;
69
+ }, z.core.$strict>>>;
70
+ original_content_hash_sha256: z.ZodOptional<z.ZodString>;
71
+ sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
72
+ original_byte_size: z.ZodOptional<z.ZodNumber>;
73
+ sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
74
+ }, z.core.$strict>>;
49
75
  }, z.core.$strict>;
50
76
  chunk_size_bytes: z.ZodNumber;
51
77
  chunk_count: z.ZodNumber;
@@ -116,6 +142,32 @@ export declare const RawEvidenceUploadBeginRequestSchema: z.ZodObject<{
116
142
  byte_size: z.ZodOptional<z.ZodNumber>;
117
143
  media_type: z.ZodOptional<z.ZodString>;
118
144
  redacted_summary: z.ZodOptional<z.ZodString>;
145
+ redaction: z.ZodOptional<z.ZodObject<{
146
+ schema_version: z.ZodLiteral<"raw-evidence-redaction.v1">;
147
+ status: z.ZodLiteral<"sanitized">;
148
+ mode: z.ZodLiteral<"deterministic_text_replacement">;
149
+ applied_by: z.ZodArray<z.ZodEnum<{
150
+ local_collector: "local_collector";
151
+ server_commit: "server_commit";
152
+ legacy_upload: "legacy_upload";
153
+ }>>;
154
+ rule_counts: z.ZodArray<z.ZodObject<{
155
+ rule_id: z.ZodString;
156
+ match_count: z.ZodNumber;
157
+ redacted_char_count: z.ZodNumber;
158
+ }, z.core.$strict>>;
159
+ secret_like_match_count: z.ZodNumber;
160
+ redacted_fields: z.ZodDefault<z.ZodArray<z.ZodString>>;
161
+ redacted_ranges: z.ZodDefault<z.ZodArray<z.ZodObject<{
162
+ start: z.ZodNumber;
163
+ end: z.ZodNumber;
164
+ rule_id: z.ZodString;
165
+ }, z.core.$strict>>>;
166
+ original_content_hash_sha256: z.ZodOptional<z.ZodString>;
167
+ sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
168
+ original_byte_size: z.ZodOptional<z.ZodNumber>;
169
+ sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
170
+ }, z.core.$strict>>;
119
171
  }, z.core.$strict>;
120
172
  chunk_size_bytes: z.ZodNumber;
121
173
  chunk_count: z.ZodNumber;
@@ -253,6 +305,32 @@ export declare const RawEvidenceUploadCommitResponseSchema: z.ZodObject<{
253
305
  content_hash_sha256: z.ZodString;
254
306
  byte_size: z.ZodNumber;
255
307
  chunk_count: z.ZodNumber;
308
+ redaction: z.ZodOptional<z.ZodObject<{
309
+ schema_version: z.ZodLiteral<"raw-evidence-redaction.v1">;
310
+ status: z.ZodLiteral<"sanitized">;
311
+ mode: z.ZodLiteral<"deterministic_text_replacement">;
312
+ applied_by: z.ZodArray<z.ZodEnum<{
313
+ local_collector: "local_collector";
314
+ server_commit: "server_commit";
315
+ legacy_upload: "legacy_upload";
316
+ }>>;
317
+ rule_counts: z.ZodArray<z.ZodObject<{
318
+ rule_id: z.ZodString;
319
+ match_count: z.ZodNumber;
320
+ redacted_char_count: z.ZodNumber;
321
+ }, z.core.$strict>>;
322
+ secret_like_match_count: z.ZodNumber;
323
+ redacted_fields: z.ZodDefault<z.ZodArray<z.ZodString>>;
324
+ redacted_ranges: z.ZodDefault<z.ZodArray<z.ZodObject<{
325
+ start: z.ZodNumber;
326
+ end: z.ZodNumber;
327
+ rule_id: z.ZodString;
328
+ }, z.core.$strict>>>;
329
+ original_content_hash_sha256: z.ZodOptional<z.ZodString>;
330
+ sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
331
+ original_byte_size: z.ZodOptional<z.ZodNumber>;
332
+ sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
333
+ }, z.core.$strict>>;
256
334
  }, z.core.$strict>;
257
335
  export type RawEvidenceUploadCommitResponse = z.infer<typeof RawEvidenceUploadCommitResponseSchema>;
258
336
  /**
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { CaptureProvenanceSchema, IsoDateTimeSchema, NonEmptyStringSchema, Sha256Schema, } from "./common.js";
3
3
  import { RawEvidencePointerSchema } from "./privacy.js";
4
+ import { RawEvidenceRedactionMetadataSchema } from "./secret-guards.js";
4
5
  /**
5
6
  * Chunked raw-evidence upload wire contract.
6
7
  *
@@ -117,6 +118,7 @@ export const RawEvidenceUploadCommitResponseSchema = z
117
118
  content_hash_sha256: Sha256Schema,
118
119
  byte_size: z.number().int().nonnegative(),
119
120
  chunk_count: z.number().int().nonnegative(),
121
+ redaction: RawEvidenceRedactionMetadataSchema.optional(),
120
122
  })
121
123
  .strict();
122
124
  /**
@@ -1,4 +1,16 @@
1
1
  import { z } from "zod";
2
+ export declare const WorktreeInventoryItemSchema: z.ZodObject<{
3
+ repo: z.ZodString;
4
+ repo_label: z.ZodOptional<z.ZodString>;
5
+ repo_fingerprint: z.ZodOptional<z.ZodString>;
6
+ repo_origin_url: z.ZodOptional<z.ZodString>;
7
+ head_sha: z.ZodOptional<z.ZodString>;
8
+ worktree_label: z.ZodOptional<z.ZodString>;
9
+ worktree_fingerprint: z.ZodString;
10
+ worktree_is_primary: z.ZodOptional<z.ZodBoolean>;
11
+ branch: z.ZodOptional<z.ZodString>;
12
+ }, z.core.$strict>;
13
+ export type WorktreeInventoryItem = z.infer<typeof WorktreeInventoryItemSchema>;
2
14
  export declare const TelemetryEventTypeSchema: z.ZodEnum<{
3
15
  session_started: "session_started";
4
16
  session_ended: "session_ended";
@@ -148,6 +160,13 @@ export declare const TelemetryIngestEventDtoSchema: z.ZodObject<{
148
160
  observed_bytes: z.ZodOptional<z.ZodNumber>;
149
161
  included_bytes: z.ZodOptional<z.ZodNumber>;
150
162
  }, z.core.$strict>>>;
163
+ redaction_markers: z.ZodDefault<z.ZodArray<z.ZodObject<{
164
+ source: z.ZodString;
165
+ status: z.ZodLiteral<"sanitized">;
166
+ mode: z.ZodString;
167
+ count: z.ZodNumber;
168
+ rule_ids: z.ZodDefault<z.ZodArray<z.ZodString>>;
169
+ }, z.core.$strict>>>;
151
170
  notes: z.ZodDefault<z.ZodArray<z.ZodString>>;
152
171
  }, z.core.$strict>>;
153
172
  ticket_binding: z.ZodOptional<z.ZodObject<{
@@ -269,6 +288,32 @@ export declare const TelemetryIngestEventDtoSchema: z.ZodObject<{
269
288
  byte_size: z.ZodOptional<z.ZodNumber>;
270
289
  media_type: z.ZodOptional<z.ZodString>;
271
290
  redacted_summary: z.ZodOptional<z.ZodString>;
291
+ redaction: z.ZodOptional<z.ZodObject<{
292
+ schema_version: z.ZodLiteral<"raw-evidence-redaction.v1">;
293
+ status: z.ZodLiteral<"sanitized">;
294
+ mode: z.ZodLiteral<"deterministic_text_replacement">;
295
+ applied_by: z.ZodArray<z.ZodEnum<{
296
+ local_collector: "local_collector";
297
+ server_commit: "server_commit";
298
+ legacy_upload: "legacy_upload";
299
+ }>>;
300
+ rule_counts: z.ZodArray<z.ZodObject<{
301
+ rule_id: z.ZodString;
302
+ match_count: z.ZodNumber;
303
+ redacted_char_count: z.ZodNumber;
304
+ }, z.core.$strict>>;
305
+ secret_like_match_count: z.ZodNumber;
306
+ redacted_fields: z.ZodDefault<z.ZodArray<z.ZodString>>;
307
+ redacted_ranges: z.ZodDefault<z.ZodArray<z.ZodObject<{
308
+ start: z.ZodNumber;
309
+ end: z.ZodNumber;
310
+ rule_id: z.ZodString;
311
+ }, z.core.$strict>>>;
312
+ original_content_hash_sha256: z.ZodOptional<z.ZodString>;
313
+ sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
314
+ original_byte_size: z.ZodOptional<z.ZodNumber>;
315
+ sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
316
+ }, z.core.$strict>>;
272
317
  }, z.core.$strict>>>;
273
318
  otel: z.ZodOptional<z.ZodObject<{
274
319
  trace_id: z.ZodOptional<z.ZodString>;
@@ -442,6 +487,17 @@ export declare const TelemetryIngestEnvelopeSchema: z.ZodObject<{
442
487
  work_context_id: z.ZodString;
443
488
  }, z.core.$strict>>;
444
489
  }, z.core.$strict>;
490
+ worktree_inventory: z.ZodDefault<z.ZodArray<z.ZodObject<{
491
+ repo: z.ZodString;
492
+ repo_label: z.ZodOptional<z.ZodString>;
493
+ repo_fingerprint: z.ZodOptional<z.ZodString>;
494
+ repo_origin_url: z.ZodOptional<z.ZodString>;
495
+ head_sha: z.ZodOptional<z.ZodString>;
496
+ worktree_label: z.ZodOptional<z.ZodString>;
497
+ worktree_fingerprint: z.ZodString;
498
+ worktree_is_primary: z.ZodOptional<z.ZodBoolean>;
499
+ branch: z.ZodOptional<z.ZodString>;
500
+ }, z.core.$strict>>>;
445
501
  events: z.ZodArray<z.ZodObject<{
446
502
  event_id: z.ZodString;
447
503
  event_type: z.ZodEnum<{
@@ -577,6 +633,13 @@ export declare const TelemetryIngestEnvelopeSchema: z.ZodObject<{
577
633
  observed_bytes: z.ZodOptional<z.ZodNumber>;
578
634
  included_bytes: z.ZodOptional<z.ZodNumber>;
579
635
  }, z.core.$strict>>>;
636
+ redaction_markers: z.ZodDefault<z.ZodArray<z.ZodObject<{
637
+ source: z.ZodString;
638
+ status: z.ZodLiteral<"sanitized">;
639
+ mode: z.ZodString;
640
+ count: z.ZodNumber;
641
+ rule_ids: z.ZodDefault<z.ZodArray<z.ZodString>>;
642
+ }, z.core.$strict>>>;
580
643
  notes: z.ZodDefault<z.ZodArray<z.ZodString>>;
581
644
  }, z.core.$strict>>;
582
645
  ticket_binding: z.ZodOptional<z.ZodObject<{
@@ -698,6 +761,32 @@ export declare const TelemetryIngestEnvelopeSchema: z.ZodObject<{
698
761
  byte_size: z.ZodOptional<z.ZodNumber>;
699
762
  media_type: z.ZodOptional<z.ZodString>;
700
763
  redacted_summary: z.ZodOptional<z.ZodString>;
764
+ redaction: z.ZodOptional<z.ZodObject<{
765
+ schema_version: z.ZodLiteral<"raw-evidence-redaction.v1">;
766
+ status: z.ZodLiteral<"sanitized">;
767
+ mode: z.ZodLiteral<"deterministic_text_replacement">;
768
+ applied_by: z.ZodArray<z.ZodEnum<{
769
+ local_collector: "local_collector";
770
+ server_commit: "server_commit";
771
+ legacy_upload: "legacy_upload";
772
+ }>>;
773
+ rule_counts: z.ZodArray<z.ZodObject<{
774
+ rule_id: z.ZodString;
775
+ match_count: z.ZodNumber;
776
+ redacted_char_count: z.ZodNumber;
777
+ }, z.core.$strict>>;
778
+ secret_like_match_count: z.ZodNumber;
779
+ redacted_fields: z.ZodDefault<z.ZodArray<z.ZodString>>;
780
+ redacted_ranges: z.ZodDefault<z.ZodArray<z.ZodObject<{
781
+ start: z.ZodNumber;
782
+ end: z.ZodNumber;
783
+ rule_id: z.ZodString;
784
+ }, z.core.$strict>>>;
785
+ original_content_hash_sha256: z.ZodOptional<z.ZodString>;
786
+ sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
787
+ original_byte_size: z.ZodOptional<z.ZodNumber>;
788
+ sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
789
+ }, z.core.$strict>>;
701
790
  }, z.core.$strict>>>;
702
791
  otel: z.ZodOptional<z.ZodObject<{
703
792
  trace_id: z.ZodOptional<z.ZodString>;
@@ -822,6 +911,32 @@ export declare const TelemetryIngestEnvelopeSchema: z.ZodObject<{
822
911
  byte_size: z.ZodOptional<z.ZodNumber>;
823
912
  media_type: z.ZodOptional<z.ZodString>;
824
913
  redacted_summary: z.ZodOptional<z.ZodString>;
914
+ redaction: z.ZodOptional<z.ZodObject<{
915
+ schema_version: z.ZodLiteral<"raw-evidence-redaction.v1">;
916
+ status: z.ZodLiteral<"sanitized">;
917
+ mode: z.ZodLiteral<"deterministic_text_replacement">;
918
+ applied_by: z.ZodArray<z.ZodEnum<{
919
+ local_collector: "local_collector";
920
+ server_commit: "server_commit";
921
+ legacy_upload: "legacy_upload";
922
+ }>>;
923
+ rule_counts: z.ZodArray<z.ZodObject<{
924
+ rule_id: z.ZodString;
925
+ match_count: z.ZodNumber;
926
+ redacted_char_count: z.ZodNumber;
927
+ }, z.core.$strict>>;
928
+ secret_like_match_count: z.ZodNumber;
929
+ redacted_fields: z.ZodDefault<z.ZodArray<z.ZodString>>;
930
+ redacted_ranges: z.ZodDefault<z.ZodArray<z.ZodObject<{
931
+ start: z.ZodNumber;
932
+ end: z.ZodNumber;
933
+ rule_id: z.ZodString;
934
+ }, z.core.$strict>>>;
935
+ original_content_hash_sha256: z.ZodOptional<z.ZodString>;
936
+ sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
937
+ original_byte_size: z.ZodOptional<z.ZodNumber>;
938
+ sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
939
+ }, z.core.$strict>>;
825
940
  }, z.core.$strict>>>;
826
941
  }, z.core.$strict>>>;
827
942
  ticket_binding_candidates: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -7,6 +7,19 @@ import { SourceScanResultSchema } from "./source-adapter.js";
7
7
  import { ExternalTraceSinkPointerSchema, NormalizedOtelTelemetrySchema, } from "./trace-sink.js";
8
8
  import { EvidenceCompletenessPayloadSchema } from "./evidence-completeness.js";
9
9
  import { LocalWorkContextSchema, TicketBindingCandidateSchema, } from "./work-context.js";
10
+ export const WorktreeInventoryItemSchema = z
11
+ .object({
12
+ repo: NonEmptyStringSchema,
13
+ repo_label: NonEmptyStringSchema.optional(),
14
+ repo_fingerprint: NonEmptyStringSchema.optional(),
15
+ repo_origin_url: NonEmptyStringSchema.optional(),
16
+ head_sha: NonEmptyStringSchema.optional(),
17
+ worktree_label: NonEmptyStringSchema.optional(),
18
+ worktree_fingerprint: NonEmptyStringSchema,
19
+ worktree_is_primary: z.boolean().optional(),
20
+ branch: NonEmptyStringSchema.optional(),
21
+ })
22
+ .strict();
10
23
  export const TelemetryEventTypeSchema = z.enum([
11
24
  "session_started",
12
25
  "session_ended",
@@ -83,6 +96,7 @@ export const TelemetryIngestEnvelopeSchema = z
83
96
  collector_version: NonEmptyStringSchema,
84
97
  session_reference: LocalUserSessionReferenceSchema,
85
98
  work_context: LocalWorkContextSchema,
99
+ worktree_inventory: z.array(WorktreeInventoryItemSchema).default([]),
86
100
  events: z.array(TelemetryIngestEventDtoSchema).min(1),
87
101
  source_scan_results: z.array(SourceScanResultSchema).default([]),
88
102
  })
package/dist/privacy.d.ts CHANGED
@@ -66,6 +66,32 @@ export declare const RawEvidencePointerSchema: z.ZodObject<{
66
66
  byte_size: z.ZodOptional<z.ZodNumber>;
67
67
  media_type: z.ZodOptional<z.ZodString>;
68
68
  redacted_summary: z.ZodOptional<z.ZodString>;
69
+ redaction: z.ZodOptional<z.ZodObject<{
70
+ schema_version: z.ZodLiteral<"raw-evidence-redaction.v1">;
71
+ status: z.ZodLiteral<"sanitized">;
72
+ mode: z.ZodLiteral<"deterministic_text_replacement">;
73
+ applied_by: z.ZodArray<z.ZodEnum<{
74
+ local_collector: "local_collector";
75
+ server_commit: "server_commit";
76
+ legacy_upload: "legacy_upload";
77
+ }>>;
78
+ rule_counts: z.ZodArray<z.ZodObject<{
79
+ rule_id: z.ZodString;
80
+ match_count: z.ZodNumber;
81
+ redacted_char_count: z.ZodNumber;
82
+ }, z.core.$strict>>;
83
+ secret_like_match_count: z.ZodNumber;
84
+ redacted_fields: z.ZodDefault<z.ZodArray<z.ZodString>>;
85
+ redacted_ranges: z.ZodDefault<z.ZodArray<z.ZodObject<{
86
+ start: z.ZodNumber;
87
+ end: z.ZodNumber;
88
+ rule_id: z.ZodString;
89
+ }, z.core.$strict>>>;
90
+ original_content_hash_sha256: z.ZodOptional<z.ZodString>;
91
+ sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
92
+ original_byte_size: z.ZodOptional<z.ZodNumber>;
93
+ sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
94
+ }, z.core.$strict>>;
69
95
  }, z.core.$strict>;
70
96
  export type RawEvidencePointer = z.infer<typeof RawEvidencePointerSchema>;
71
97
  export declare const RedactionMetadataSchema: z.ZodObject<{
package/dist/privacy.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { IsoDateTimeSchema, NonEmptyStringSchema, Sha256Schema, } from "./common.js";
3
+ import { RawEvidenceRedactionMetadataSchema } from "./secret-guards.js";
3
4
  export const PrivacyClassificationSchema = z.enum([
4
5
  "metadata",
5
6
  "redacted_summary",
@@ -93,6 +94,7 @@ export const RawEvidencePointerSchema = z
93
94
  byte_size: z.number().int().nonnegative().optional(),
94
95
  media_type: NonEmptyStringSchema.optional(),
95
96
  redacted_summary: z.string().trim().min(1).max(4000).optional(),
97
+ redaction: RawEvidenceRedactionMetadataSchema.optional(),
96
98
  })
97
99
  .strict()
98
100
  .superRefine((pointer, context) => {
@@ -1,3 +1,4 @@
1
+ import { z } from "zod";
1
2
  /**
2
3
  * Secret-like content + file-name guards shared by the local collector
3
4
  * (attribution, collection) and the dashboard commit-time guard. Keeping a
@@ -24,3 +25,57 @@
24
25
  export declare const SECRET_FILE_SEGMENT_PATTERN: RegExp;
25
26
  export declare function isSecretLikePathSegment(value: string): boolean;
26
27
  export declare function containsSecretLikeContent(value: string): boolean;
28
+ export declare const RawEvidenceRedactionSourceSchema: z.ZodEnum<{
29
+ local_collector: "local_collector";
30
+ server_commit: "server_commit";
31
+ legacy_upload: "legacy_upload";
32
+ }>;
33
+ export type RawEvidenceRedactionSource = z.infer<typeof RawEvidenceRedactionSourceSchema>;
34
+ export declare const RawEvidenceRedactionRuleCountSchema: z.ZodObject<{
35
+ rule_id: z.ZodString;
36
+ match_count: z.ZodNumber;
37
+ redacted_char_count: z.ZodNumber;
38
+ }, z.core.$strict>;
39
+ export type RawEvidenceRedactionRuleCount = z.infer<typeof RawEvidenceRedactionRuleCountSchema>;
40
+ export declare const RawEvidenceRedactionRangeSchema: z.ZodObject<{
41
+ start: z.ZodNumber;
42
+ end: z.ZodNumber;
43
+ rule_id: z.ZodString;
44
+ }, z.core.$strict>;
45
+ export type RawEvidenceRedactionRange = z.infer<typeof RawEvidenceRedactionRangeSchema>;
46
+ export declare const RawEvidenceRedactionMetadataSchema: z.ZodObject<{
47
+ schema_version: z.ZodLiteral<"raw-evidence-redaction.v1">;
48
+ status: z.ZodLiteral<"sanitized">;
49
+ mode: z.ZodLiteral<"deterministic_text_replacement">;
50
+ applied_by: z.ZodArray<z.ZodEnum<{
51
+ local_collector: "local_collector";
52
+ server_commit: "server_commit";
53
+ legacy_upload: "legacy_upload";
54
+ }>>;
55
+ rule_counts: z.ZodArray<z.ZodObject<{
56
+ rule_id: z.ZodString;
57
+ match_count: z.ZodNumber;
58
+ redacted_char_count: z.ZodNumber;
59
+ }, z.core.$strict>>;
60
+ secret_like_match_count: z.ZodNumber;
61
+ redacted_fields: z.ZodDefault<z.ZodArray<z.ZodString>>;
62
+ redacted_ranges: z.ZodDefault<z.ZodArray<z.ZodObject<{
63
+ start: z.ZodNumber;
64
+ end: z.ZodNumber;
65
+ rule_id: z.ZodString;
66
+ }, z.core.$strict>>>;
67
+ original_content_hash_sha256: z.ZodOptional<z.ZodString>;
68
+ sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
69
+ original_byte_size: z.ZodOptional<z.ZodNumber>;
70
+ sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
71
+ }, z.core.$strict>;
72
+ export type RawEvidenceRedactionMetadata = z.infer<typeof RawEvidenceRedactionMetadataSchema>;
73
+ export interface SecretRedactionResult {
74
+ redacted: boolean;
75
+ text: string;
76
+ metadata: RawEvidenceRedactionMetadata | null;
77
+ }
78
+ export declare function redactSecretLikeContent(value: string, options?: {
79
+ appliedBy?: RawEvidenceRedactionSource;
80
+ redactedFields?: string[];
81
+ }): SecretRedactionResult;
@@ -1,3 +1,4 @@
1
+ import { z } from "zod";
1
2
  /**
2
3
  * Secret-like content + file-name guards shared by the local collector
3
4
  * (attribution, collection) and the dashboard commit-time guard. Keeping a
@@ -32,14 +33,7 @@ const PRIVILEGED_SUPABASE_KEY_NAME = ["SUPABASE", "SERVICE", "ROLE", "KEY"].join
32
33
  const SUPABASE_SERVICE_ROLE_NAME = ["SUPABASE", "SERVICE", "ROLE"].join("_");
33
34
  const OPENAI_KEY_NAME = ["OPENAI", "API", "KEY"].join("_");
34
35
  const MEM0_KEY_NAME = ["MEM0", "API", "KEY"].join("_");
35
- /**
36
- * Credential names that are only a leak when assigned a value (D13). The value
37
- * shape mirrors the long-standing generic pattern: an assignment operator
38
- * followed by a 12+ char opaque value. A bare env var reference (name only) and
39
- * `GRANT ... TO service_role` (no assignment) do not match; the same name with
40
- * an assigned value does.
41
- */
42
- const SECRET_NAME_WITH_VALUE_PATTERN = new RegExp(`\\b(?:${[
36
+ const SECRET_NAME_PATTERN_SOURCE = [
43
37
  "(?:NEXT_PUBLIC_)?SUPABASE_ANON_KEY",
44
38
  PRIVILEGED_SUPABASE_KEY_NAME,
45
39
  SUPABASE_SERVICE_ROLE_NAME,
@@ -53,7 +47,15 @@ const SECRET_NAME_WITH_VALUE_PATTERN = new RegExp(`\\b(?:${[
53
47
  "private[_-]?key",
54
48
  "client[_-]?secret",
55
49
  "secret[_-]?access[_-]?key",
56
- ].join("|")})\\b\\s*[:=]\\s*["']?[A-Za-z0-9_./+=-]{12,}`, "i");
50
+ ].join("|");
51
+ /**
52
+ * Credential names that are only a leak when assigned a value (D13). The value
53
+ * shape mirrors the long-standing generic pattern: an assignment operator
54
+ * followed by a 12+ char opaque value. A bare env var reference (name only) and
55
+ * `GRANT ... TO service_role` (no assignment) do not match; the same name with
56
+ * an assigned value does.
57
+ */
58
+ const SECRET_NAME_WITH_VALUE_PATTERN = new RegExp(`\\b(?:${SECRET_NAME_PATTERN_SOURCE})\\b\\s*[:=]\\s*["']?[A-Za-z0-9_./+=-]{12,}`, "i");
57
59
  /**
58
60
  * Self-identifying secret values — blocked regardless of context because the
59
61
  * token shape itself is the credential, with no benign reading.
@@ -80,3 +82,190 @@ const SECRET_LIKE_CONTENT_PATTERNS = [
80
82
  export function containsSecretLikeContent(value) {
81
83
  return SECRET_LIKE_CONTENT_PATTERNS.some((pattern) => pattern.test(value));
82
84
  }
85
+ export const RawEvidenceRedactionSourceSchema = z.enum([
86
+ "local_collector",
87
+ "server_commit",
88
+ "legacy_upload",
89
+ ]);
90
+ export const RawEvidenceRedactionRuleCountSchema = z
91
+ .object({
92
+ rule_id: z.string().trim().min(1).max(80),
93
+ match_count: z.number().int().nonnegative(),
94
+ redacted_char_count: z.number().int().nonnegative(),
95
+ })
96
+ .strict();
97
+ export const RawEvidenceRedactionRangeSchema = z
98
+ .object({
99
+ start: z.number().int().nonnegative(),
100
+ end: z.number().int().positive(),
101
+ rule_id: z.string().trim().min(1).max(80),
102
+ })
103
+ .strict()
104
+ .superRefine((range, context) => {
105
+ if (range.end <= range.start) {
106
+ context.addIssue({
107
+ code: "custom",
108
+ message: "redaction range end must be greater than start",
109
+ path: ["end"],
110
+ });
111
+ }
112
+ });
113
+ export const RawEvidenceRedactionMetadataSchema = z
114
+ .object({
115
+ schema_version: z.literal("raw-evidence-redaction.v1"),
116
+ status: z.literal("sanitized"),
117
+ mode: z.literal("deterministic_text_replacement"),
118
+ applied_by: z.array(RawEvidenceRedactionSourceSchema).min(1),
119
+ rule_counts: z.array(RawEvidenceRedactionRuleCountSchema).min(1),
120
+ secret_like_match_count: z.number().int().positive(),
121
+ redacted_fields: z.array(z.string().trim().min(1).max(160)).default([]),
122
+ redacted_ranges: z.array(RawEvidenceRedactionRangeSchema).max(200).default([]),
123
+ original_content_hash_sha256: z.string().regex(/^[a-f0-9]{64}$/).optional(),
124
+ sanitized_content_hash_sha256: z.string().regex(/^[a-f0-9]{64}$/).optional(),
125
+ original_byte_size: z.number().int().nonnegative().optional(),
126
+ sanitized_byte_size: z.number().int().nonnegative().optional(),
127
+ })
128
+ .strict();
129
+ const SECRET_ASSIGNMENT_REDACTION_PATTERN = new RegExp(`(\\b(?:${SECRET_NAME_PATTERN_SOURCE})\\b\\s*[:=]\\s*["']?)([A-Za-z0-9_./+=-]{12,})`, "gi");
130
+ const SECRET_REDACTION_RULES = [
131
+ {
132
+ ruleId: "credential_assignment",
133
+ pattern: SECRET_ASSIGNMENT_REDACTION_PATTERN,
134
+ secretGroup: 2,
135
+ },
136
+ {
137
+ ruleId: "private_key_block",
138
+ pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
139
+ },
140
+ {
141
+ ruleId: "private_key_header",
142
+ pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----/g,
143
+ },
144
+ {
145
+ ruleId: "bearer_token",
146
+ pattern: /\b(Bearer\s+)([A-Za-z0-9._~+/=-]{16,})/gi,
147
+ secretGroup: 2,
148
+ },
149
+ {
150
+ ruleId: "jwt",
151
+ pattern: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,
152
+ },
153
+ {
154
+ ruleId: "stripe_token",
155
+ pattern: /\b(?:sk|pk)_(?:live|test)_[A-Za-z0-9]{16,}/g,
156
+ },
157
+ {
158
+ ruleId: "openai_token",
159
+ pattern: /\bsk-[A-Za-z0-9_-]{16,}/g,
160
+ },
161
+ {
162
+ ruleId: "github_token",
163
+ pattern: /\b(?:gh[pousr]_[A-Za-z0-9_]{16,}|github_pat_[A-Za-z0-9_]{20,})/g,
164
+ },
165
+ {
166
+ ruleId: "linear_token",
167
+ pattern: /\blin_api_[A-Za-z0-9]{16,}/g,
168
+ },
169
+ {
170
+ ruleId: "supabase_secret_token",
171
+ pattern: /\bsb_secret_[A-Za-z0-9_]{16,}/g,
172
+ },
173
+ {
174
+ ruleId: "slack_token",
175
+ pattern: /\bxox[baprs]-[A-Za-z0-9-]{16,}/g,
176
+ },
177
+ {
178
+ ruleId: "aws_access_key_id",
179
+ pattern: /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g,
180
+ },
181
+ {
182
+ ruleId: "langfuse_token",
183
+ pattern: /\b(?:pk|sk)-lf-[A-Za-z0-9_-]{16,}\b/gi,
184
+ },
185
+ {
186
+ ruleId: "mem0_token",
187
+ pattern: /\bm0[-_][A-Za-z0-9_-]{16,}\b/gi,
188
+ },
189
+ ];
190
+ export function redactSecretLikeContent(value, options = {}) {
191
+ const matches = findSecretRedactionMatches(value);
192
+ if (matches.length === 0) {
193
+ return { redacted: false, text: value, metadata: null };
194
+ }
195
+ let text = "";
196
+ let cursor = 0;
197
+ const ruleCounts = new Map();
198
+ for (const match of matches) {
199
+ text += value.slice(cursor, match.start);
200
+ text += `[REDACTED:${match.ruleId}]`;
201
+ cursor = match.end;
202
+ const existing = ruleCounts.get(match.ruleId) ?? {
203
+ rule_id: match.ruleId,
204
+ match_count: 0,
205
+ redacted_char_count: 0,
206
+ };
207
+ existing.match_count += 1;
208
+ existing.redacted_char_count += match.end - match.start;
209
+ ruleCounts.set(match.ruleId, existing);
210
+ }
211
+ text += value.slice(cursor);
212
+ const metadata = RawEvidenceRedactionMetadataSchema.parse({
213
+ schema_version: "raw-evidence-redaction.v1",
214
+ status: "sanitized",
215
+ mode: "deterministic_text_replacement",
216
+ applied_by: [options.appliedBy ?? "local_collector"],
217
+ rule_counts: [...ruleCounts.values()].sort((a, b) => a.rule_id.localeCompare(b.rule_id)),
218
+ secret_like_match_count: matches.length,
219
+ redacted_fields: options.redactedFields ?? [],
220
+ redacted_ranges: matches.slice(0, 200).map((match) => ({
221
+ start: match.start,
222
+ end: match.end,
223
+ rule_id: match.ruleId,
224
+ })),
225
+ });
226
+ return { redacted: true, text, metadata };
227
+ }
228
+ function findSecretRedactionMatches(value) {
229
+ const candidates = [];
230
+ SECRET_REDACTION_RULES.forEach((rule, priority) => {
231
+ const pattern = cloneGlobalPattern(rule.pattern);
232
+ for (const match of value.matchAll(pattern)) {
233
+ const fullMatch = match[0];
234
+ const matchIndex = match.index;
235
+ if (!fullMatch || matchIndex === undefined)
236
+ continue;
237
+ const secret = rule.secretGroup ? match[rule.secretGroup] : fullMatch;
238
+ if (!secret)
239
+ continue;
240
+ const relativeStart = fullMatch.indexOf(secret);
241
+ if (relativeStart < 0)
242
+ continue;
243
+ candidates.push({
244
+ start: matchIndex + relativeStart,
245
+ end: matchIndex + relativeStart + secret.length,
246
+ ruleId: rule.ruleId,
247
+ priority,
248
+ });
249
+ }
250
+ });
251
+ candidates.sort((a, b) => {
252
+ if (a.start !== b.start)
253
+ return a.start - b.start;
254
+ if (a.priority !== b.priority)
255
+ return a.priority - b.priority;
256
+ return b.end - b.start - (a.end - a.start);
257
+ });
258
+ const accepted = [];
259
+ let lastEnd = -1;
260
+ for (const candidate of candidates) {
261
+ if (candidate.start < lastEnd)
262
+ continue;
263
+ accepted.push(candidate);
264
+ lastEnd = candidate.end;
265
+ }
266
+ return accepted;
267
+ }
268
+ function cloneGlobalPattern(pattern) {
269
+ const flags = pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`;
270
+ return new RegExp(pattern.source, flags);
271
+ }
@@ -74,6 +74,32 @@ export declare const SourceScanEventSummarySchema: z.ZodObject<{
74
74
  byte_size: z.ZodOptional<z.ZodNumber>;
75
75
  media_type: z.ZodOptional<z.ZodString>;
76
76
  redacted_summary: z.ZodOptional<z.ZodString>;
77
+ redaction: z.ZodOptional<z.ZodObject<{
78
+ schema_version: z.ZodLiteral<"raw-evidence-redaction.v1">;
79
+ status: z.ZodLiteral<"sanitized">;
80
+ mode: z.ZodLiteral<"deterministic_text_replacement">;
81
+ applied_by: z.ZodArray<z.ZodEnum<{
82
+ local_collector: "local_collector";
83
+ server_commit: "server_commit";
84
+ legacy_upload: "legacy_upload";
85
+ }>>;
86
+ rule_counts: z.ZodArray<z.ZodObject<{
87
+ rule_id: z.ZodString;
88
+ match_count: z.ZodNumber;
89
+ redacted_char_count: z.ZodNumber;
90
+ }, z.core.$strict>>;
91
+ secret_like_match_count: z.ZodNumber;
92
+ redacted_fields: z.ZodDefault<z.ZodArray<z.ZodString>>;
93
+ redacted_ranges: z.ZodDefault<z.ZodArray<z.ZodObject<{
94
+ start: z.ZodNumber;
95
+ end: z.ZodNumber;
96
+ rule_id: z.ZodString;
97
+ }, z.core.$strict>>>;
98
+ original_content_hash_sha256: z.ZodOptional<z.ZodString>;
99
+ sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
100
+ original_byte_size: z.ZodOptional<z.ZodNumber>;
101
+ sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
102
+ }, z.core.$strict>>;
77
103
  }, z.core.$strict>>>;
78
104
  }, z.core.$strict>;
79
105
  export type SourceScanEventSummary = z.infer<typeof SourceScanEventSummarySchema>;
@@ -162,6 +188,32 @@ export declare const SourceScanResultSchema: z.ZodObject<{
162
188
  byte_size: z.ZodOptional<z.ZodNumber>;
163
189
  media_type: z.ZodOptional<z.ZodString>;
164
190
  redacted_summary: z.ZodOptional<z.ZodString>;
191
+ redaction: z.ZodOptional<z.ZodObject<{
192
+ schema_version: z.ZodLiteral<"raw-evidence-redaction.v1">;
193
+ status: z.ZodLiteral<"sanitized">;
194
+ mode: z.ZodLiteral<"deterministic_text_replacement">;
195
+ applied_by: z.ZodArray<z.ZodEnum<{
196
+ local_collector: "local_collector";
197
+ server_commit: "server_commit";
198
+ legacy_upload: "legacy_upload";
199
+ }>>;
200
+ rule_counts: z.ZodArray<z.ZodObject<{
201
+ rule_id: z.ZodString;
202
+ match_count: z.ZodNumber;
203
+ redacted_char_count: z.ZodNumber;
204
+ }, z.core.$strict>>;
205
+ secret_like_match_count: z.ZodNumber;
206
+ redacted_fields: z.ZodDefault<z.ZodArray<z.ZodString>>;
207
+ redacted_ranges: z.ZodDefault<z.ZodArray<z.ZodObject<{
208
+ start: z.ZodNumber;
209
+ end: z.ZodNumber;
210
+ rule_id: z.ZodString;
211
+ }, z.core.$strict>>>;
212
+ original_content_hash_sha256: z.ZodOptional<z.ZodString>;
213
+ sanitized_content_hash_sha256: z.ZodOptional<z.ZodString>;
214
+ original_byte_size: z.ZodOptional<z.ZodNumber>;
215
+ sanitized_byte_size: z.ZodOptional<z.ZodNumber>;
216
+ }, z.core.$strict>>;
165
217
  }, z.core.$strict>>>;
166
218
  }, z.core.$strict>>>;
167
219
  ticket_binding_candidates: z.ZodDefault<z.ZodArray<z.ZodObject<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/telemetry-core",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",