@oscharko-dev/keiko-security 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/errors/audit.d.ts +26 -0
  3. package/dist/errors/audit.d.ts.map +1 -0
  4. package/dist/errors/audit.js +39 -0
  5. package/dist/errors/gateway.d.ts +84 -0
  6. package/dist/errors/gateway.d.ts.map +1 -0
  7. package/dist/errors/gateway.js +95 -0
  8. package/dist/errors/harness.d.ts +22 -0
  9. package/dist/errors/harness.d.ts.map +1 -0
  10. package/dist/errors/harness.js +39 -0
  11. package/dist/errors/index.d.ts +9 -0
  12. package/dist/errors/index.d.ts.map +1 -0
  13. package/dist/errors/index.js +12 -0
  14. package/dist/errors/promptEnhancer.d.ts +38 -0
  15. package/dist/errors/promptEnhancer.d.ts.map +1 -0
  16. package/dist/errors/promptEnhancer.js +56 -0
  17. package/dist/errors/secretbox.d.ts +5 -0
  18. package/dist/errors/secretbox.d.ts.map +1 -0
  19. package/dist/errors/secretbox.js +13 -0
  20. package/dist/errors/tools.d.ts +60 -0
  21. package/dist/errors/tools.d.ts.map +1 -0
  22. package/dist/errors/tools.js +94 -0
  23. package/dist/errors/verification.d.ts +12 -0
  24. package/dist/errors/verification.d.ts.map +1 -0
  25. package/dist/errors/verification.js +21 -0
  26. package/dist/errors/workspace.d.ts +56 -0
  27. package/dist/errors/workspace.d.ts.map +1 -0
  28. package/dist/errors/workspace.js +95 -0
  29. package/dist/hashing.d.ts +4 -0
  30. package/dist/hashing.d.ts.map +1 -0
  31. package/dist/hashing.js +38 -0
  32. package/dist/index.d.ts +11 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +13 -0
  35. package/dist/promptInjection.d.ts +29 -0
  36. package/dist/promptInjection.d.ts.map +1 -0
  37. package/dist/promptInjection.js +235 -0
  38. package/dist/redaction.d.ts +5 -0
  39. package/dist/redaction.d.ts.map +1 -0
  40. package/dist/redaction.js +123 -0
  41. package/dist/runid.d.ts +2 -0
  42. package/dist/runid.d.ts.map +1 -0
  43. package/dist/runid.js +29 -0
  44. package/dist/secret-vault.d.ts +33 -0
  45. package/dist/secret-vault.d.ts.map +1 -0
  46. package/dist/secret-vault.js +271 -0
  47. package/dist/secretbox.d.ts +6 -0
  48. package/dist/secretbox.d.ts.map +1 -0
  49. package/dist/secretbox.js +80 -0
  50. package/dist/secrets.d.ts +4 -0
  51. package/dist/secrets.d.ts.map +1 -0
  52. package/dist/secrets.js +34 -0
  53. package/dist/version.d.ts +2 -0
  54. package/dist/version.d.ts.map +1 -0
  55. package/dist/version.js +4 -0
  56. package/package.json +78 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secretbox.d.ts","sourceRoot":"","sources":["../../src/errors/secretbox.ts"],"names":[],"mappings":"AAOA,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAgB,IAAI,EAAG,uBAAuB,CAAU;gBAErC,OAAO,EAAE,MAAM;CAInC"}
@@ -0,0 +1,13 @@
1
+ // Typed error for the secretbox authenticated-encryption primitive. A single stable `code`
2
+ // discriminant so callers can branch on the failure class without parsing the message. The
3
+ // message is intentionally generic — it never echoes key material, plaintext, or ciphertext, so
4
+ // it is always safe to log across a trust boundary. An auth-tag mismatch (tampered ciphertext OR
5
+ // wrong key) and a malformed envelope both surface here; we do NOT distinguish "wrong key" from
6
+ // "tampered" by design, because telling an attacker which one failed is an oracle.
7
+ export class SecretboxError extends Error {
8
+ code = "SECRETBOX_OPEN_FAILED";
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "SecretboxError";
12
+ }
13
+ }
@@ -0,0 +1,60 @@
1
+ import type { PatchConflict, PatchRejection } from "@oscharko-dev/keiko-contracts";
2
+ export declare const TOOL_CODES: {
3
+ readonly ARGUMENT: "TOOL_ARGUMENT";
4
+ readonly UNKNOWN: "TOOL_UNKNOWN";
5
+ readonly COMMAND_DENIED: "TOOL_COMMAND_DENIED";
6
+ readonly COMMAND_TIMEOUT: "TOOL_COMMAND_TIMEOUT";
7
+ readonly COMMAND_CANCELLED: "TOOL_COMMAND_CANCELLED";
8
+ readonly OUTPUT_LIMIT: "TOOL_OUTPUT_LIMIT";
9
+ readonly PATCH_INVALID: "TOOL_PATCH_INVALID";
10
+ readonly PATCH_APPLY_DISABLED: "TOOL_PATCH_APPLY_DISABLED";
11
+ readonly PATCH_APPLY_FAILED: "TOOL_PATCH_APPLY_FAILED";
12
+ };
13
+ export type ToolCode = (typeof TOOL_CODES)[keyof typeof TOOL_CODES];
14
+ export declare abstract class ToolError extends Error {
15
+ abstract readonly code: ToolCode;
16
+ constructor(message: string, secrets?: readonly string[]);
17
+ }
18
+ export declare class ToolArgumentError extends ToolError {
19
+ readonly code: "TOOL_ARGUMENT";
20
+ readonly toolName: string;
21
+ constructor(message: string, toolName: string, secrets?: readonly string[]);
22
+ }
23
+ export declare class UnknownToolError extends ToolError {
24
+ readonly code: "TOOL_UNKNOWN";
25
+ readonly toolName: string;
26
+ constructor(message: string, toolName: string, secrets?: readonly string[]);
27
+ }
28
+ export declare class CommandDeniedError extends ToolError {
29
+ readonly code: "TOOL_COMMAND_DENIED";
30
+ readonly executable: string;
31
+ constructor(message: string, executable: string, secrets?: readonly string[]);
32
+ }
33
+ export declare class CommandTimeoutError extends ToolError {
34
+ readonly code: "TOOL_COMMAND_TIMEOUT";
35
+ readonly timeoutMs: number;
36
+ constructor(message: string, timeoutMs: number, secrets?: readonly string[]);
37
+ }
38
+ export declare class CommandCancelledError extends ToolError {
39
+ readonly code: "TOOL_COMMAND_CANCELLED";
40
+ }
41
+ export declare class OutputLimitError extends ToolError {
42
+ readonly code: "TOOL_OUTPUT_LIMIT";
43
+ readonly limitBytes: number;
44
+ constructor(message: string, limitBytes: number, secrets?: readonly string[]);
45
+ }
46
+ export declare class PatchValidationError extends ToolError {
47
+ readonly code: "TOOL_PATCH_INVALID";
48
+ readonly reasons: readonly PatchRejection[];
49
+ readonly conflicts: readonly PatchConflict[];
50
+ constructor(message: string, reasons: readonly PatchRejection[], conflicts: readonly PatchConflict[], secrets?: readonly string[]);
51
+ }
52
+ export declare class PatchApplyDisabledError extends ToolError {
53
+ readonly code: "TOOL_PATCH_APPLY_DISABLED";
54
+ }
55
+ export declare class PatchApplyError extends ToolError {
56
+ readonly code: "TOOL_PATCH_APPLY_FAILED";
57
+ readonly path: string;
58
+ constructor(message: string, path: string, secrets?: readonly string[]);
59
+ }
60
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/errors/tools.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAGnF,eAAO,MAAM,UAAU;;;;;;;;;;CAUb,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAEpE,8BAAsB,SAAU,SAAQ,KAAK;IAC3C,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;gBAErB,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAI7D;AAGD,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,QAAQ,CAAC,IAAI,kBAAuB;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAI/E;AAGD,qBAAa,gBAAiB,SAAQ,SAAS;IAC7C,QAAQ,CAAC,IAAI,iBAAsB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAI/E;AAGD,qBAAa,kBAAmB,SAAQ,SAAS;IAC/C,QAAQ,CAAC,IAAI,wBAA6B;IAC1C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAIjF;AAGD,qBAAa,mBAAoB,SAAQ,SAAS;IAChD,QAAQ,CAAC,IAAI,yBAA8B;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAIhF;AAGD,qBAAa,qBAAsB,SAAQ,SAAS;IAClD,QAAQ,CAAC,IAAI,2BAAgC;CAC9C;AAGD,qBAAa,gBAAiB,SAAQ,SAAS;IAC7C,QAAQ,CAAC,IAAI,sBAA2B;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAIjF;AAGD,qBAAa,oBAAqB,SAAQ,SAAS;IACjD,QAAQ,CAAC,IAAI,uBAA4B;IACzC,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,SAAS,aAAa,EAAE,CAAC;gBAG3C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,SAAS,EAAE,SAAS,aAAa,EAAE,EACnC,OAAO,GAAE,SAAS,MAAM,EAAO;CAMlC;AAGD,qBAAa,uBAAwB,SAAQ,SAAS;IACpD,QAAQ,CAAC,IAAI,8BAAmC;CACjD;AAGD,qBAAa,eAAgB,SAAQ,SAAS;IAC5C,QAAQ,CAAC,IAAI,4BAAiC;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAI3E"}
@@ -0,0 +1,94 @@
1
+ // Tool error taxonomy, mirroring gateway/harness/workspace (ADR-0003/0004/0005/0006).
2
+ // Errors carry a stable `code` discriminant; callers switch on `code`, never parse `message`.
3
+ // Every message is redacted at construction so errors are always safe to log or surface.
4
+ import { redact } from "../redaction.js";
5
+ export const TOOL_CODES = {
6
+ ARGUMENT: "TOOL_ARGUMENT",
7
+ UNKNOWN: "TOOL_UNKNOWN",
8
+ COMMAND_DENIED: "TOOL_COMMAND_DENIED",
9
+ COMMAND_TIMEOUT: "TOOL_COMMAND_TIMEOUT",
10
+ COMMAND_CANCELLED: "TOOL_COMMAND_CANCELLED",
11
+ OUTPUT_LIMIT: "TOOL_OUTPUT_LIMIT",
12
+ PATCH_INVALID: "TOOL_PATCH_INVALID",
13
+ PATCH_APPLY_DISABLED: "TOOL_PATCH_APPLY_DISABLED",
14
+ PATCH_APPLY_FAILED: "TOOL_PATCH_APPLY_FAILED",
15
+ };
16
+ export class ToolError extends Error {
17
+ constructor(message, secrets = []) {
18
+ super(redact(message, secrets));
19
+ this.name = new.target.name;
20
+ }
21
+ }
22
+ // Malformed tool arguments (a required field missing or of the wrong type).
23
+ export class ToolArgumentError extends ToolError {
24
+ code = TOOL_CODES.ARGUMENT;
25
+ toolName;
26
+ constructor(message, toolName, secrets = []) {
27
+ super(message, secrets);
28
+ this.toolName = toolName;
29
+ }
30
+ }
31
+ // No such tool in the host's dispatch map.
32
+ export class UnknownToolError extends ToolError {
33
+ code = TOOL_CODES.UNKNOWN;
34
+ toolName;
35
+ constructor(message, toolName, secrets = []) {
36
+ super(message, secrets);
37
+ this.toolName = toolName;
38
+ }
39
+ }
40
+ // Executable or subcommand not on the allowlist (deny-by-default). Raised BEFORE any spawn.
41
+ export class CommandDeniedError extends ToolError {
42
+ code = TOOL_CODES.COMMAND_DENIED;
43
+ executable;
44
+ constructor(message, executable, secrets = []) {
45
+ super(message, secrets);
46
+ this.executable = executable;
47
+ }
48
+ }
49
+ // The command exceeded its wall-time budget and was terminated.
50
+ export class CommandTimeoutError extends ToolError {
51
+ code = TOOL_CODES.COMMAND_TIMEOUT;
52
+ timeoutMs;
53
+ constructor(message, timeoutMs, secrets = []) {
54
+ super(message, secrets);
55
+ this.timeoutMs = timeoutMs;
56
+ }
57
+ }
58
+ // Abort-driven cancellation of a command or a patch apply.
59
+ export class CommandCancelledError extends ToolError {
60
+ code = TOOL_CODES.COMMAND_CANCELLED;
61
+ }
62
+ // A hard output-limit breach (used where truncate-and-flag is not acceptable, e.g. patch input).
63
+ export class OutputLimitError extends ToolError {
64
+ code = TOOL_CODES.OUTPUT_LIMIT;
65
+ limitBytes;
66
+ constructor(message, limitBytes, secrets = []) {
67
+ super(message, secrets);
68
+ this.limitBytes = limitBytes;
69
+ }
70
+ }
71
+ // Patch validation failed; carries the structured rejection reasons. Nothing was written.
72
+ export class PatchValidationError extends ToolError {
73
+ code = TOOL_CODES.PATCH_INVALID;
74
+ reasons;
75
+ conflicts;
76
+ constructor(message, reasons, conflicts, secrets = []) {
77
+ super(message, secrets);
78
+ this.reasons = reasons;
79
+ this.conflicts = conflicts;
80
+ }
81
+ }
82
+ // Fail-closed: apply requested while applyEnabled is false. Nothing was written.
83
+ export class PatchApplyDisabledError extends ToolError {
84
+ code = TOOL_CODES.PATCH_APPLY_DISABLED;
85
+ }
86
+ // A write (or rollback) failed during the apply phase at the filesystem boundary.
87
+ export class PatchApplyError extends ToolError {
88
+ code = TOOL_CODES.PATCH_APPLY_FAILED;
89
+ path;
90
+ constructor(message, path, secrets = []) {
91
+ super(message, secrets);
92
+ this.path = path;
93
+ }
94
+ }
@@ -0,0 +1,12 @@
1
+ export declare const VERIFICATION_CODES: {
2
+ readonly PLAN_EMPTY: "VERIFICATION_PLAN_EMPTY";
3
+ };
4
+ export type VerificationCode = (typeof VERIFICATION_CODES)[keyof typeof VERIFICATION_CODES];
5
+ export declare abstract class VerificationError extends Error {
6
+ abstract readonly code: VerificationCode;
7
+ constructor(message: string, secrets?: readonly string[]);
8
+ }
9
+ export declare class EmptyPlanError extends VerificationError {
10
+ readonly code: "VERIFICATION_PLAN_EMPTY";
11
+ }
12
+ //# sourceMappingURL=verification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification.d.ts","sourceRoot":"","sources":["../../src/errors/verification.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,kBAAkB;;CAErB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC;AAE5F,8BAAsB,iBAAkB,SAAQ,KAAK;IACnD,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;gBAE7B,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAI7D;AAID,qBAAa,cAAe,SAAQ,iBAAiB;IACnD,QAAQ,CAAC,IAAI,4BAAiC;CAC/C"}
@@ -0,0 +1,21 @@
1
+ // Verification error taxonomy, mirroring the workspace/tools pattern (ADR-0005/0006). Errors
2
+ // carry a stable `code` discriminant; callers switch on `code`, never parse `message`. Every
3
+ // message is redacted at construction so errors are always safe to log. The only verification
4
+ // error surfaced today is at the CLI/IO boundary (workspace detection failures are surfaced by
5
+ // the workspace layer's own WorkspaceError); this base exists so later boundary failures have a
6
+ // typed home without re-deriving the pattern.
7
+ import { redact } from "../redaction.js";
8
+ export const VERIFICATION_CODES = {
9
+ PLAN_EMPTY: "VERIFICATION_PLAN_EMPTY",
10
+ };
11
+ export class VerificationError extends Error {
12
+ constructor(message, secrets = []) {
13
+ super(redact(message, secrets));
14
+ this.name = new.target.name;
15
+ }
16
+ }
17
+ // Raised when a verification run is requested but the plan contains no steps (e.g. --only with an
18
+ // empty selection). Surfaced as a non-green verification error at the CLI boundary.
19
+ export class EmptyPlanError extends VerificationError {
20
+ code = VERIFICATION_CODES.PLAN_EMPTY;
21
+ }
@@ -0,0 +1,56 @@
1
+ export declare const WORKSPACE_CODES: {
2
+ readonly PATH_ESCAPE: "WORKSPACE_PATH_ESCAPE";
3
+ readonly PATH_DENIED: "WORKSPACE_PATH_DENIED";
4
+ readonly NOT_FOUND: "WORKSPACE_NOT_FOUND";
5
+ readonly FILE_TOO_LARGE: "WORKSPACE_FILE_TOO_LARGE";
6
+ readonly READ_FAILED: "WORKSPACE_READ_FAILED";
7
+ readonly REPO_SEARCH_INVALID_QUERY: "WORKSPACE_REPO_SEARCH_INVALID_QUERY";
8
+ readonly REPO_SEARCH_INVALID_RANGE: "WORKSPACE_REPO_SEARCH_INVALID_RANGE";
9
+ readonly REPO_SEARCH_UNSUPPORTED_FILE: "WORKSPACE_REPO_SEARCH_UNSUPPORTED_FILE";
10
+ };
11
+ export type WorkspaceCode = (typeof WORKSPACE_CODES)[keyof typeof WORKSPACE_CODES];
12
+ export declare abstract class WorkspaceError extends Error {
13
+ abstract readonly code: WorkspaceCode;
14
+ constructor(message: string, secrets?: readonly string[]);
15
+ }
16
+ export declare class PathEscapeError extends WorkspaceError {
17
+ readonly code: "WORKSPACE_PATH_ESCAPE";
18
+ readonly requestedPath: string;
19
+ constructor(message: string, requestedPath: string, secrets?: readonly string[]);
20
+ }
21
+ export declare class PathDeniedError extends WorkspaceError {
22
+ readonly code: "WORKSPACE_PATH_DENIED";
23
+ readonly requestedPath: string;
24
+ constructor(message: string, requestedPath: string, secrets?: readonly string[]);
25
+ }
26
+ export declare class WorkspaceNotFoundError extends WorkspaceError {
27
+ readonly code: "WORKSPACE_NOT_FOUND";
28
+ readonly startDir: string;
29
+ constructor(message: string, startDir: string, secrets?: readonly string[]);
30
+ }
31
+ export declare class FileTooLargeError extends WorkspaceError {
32
+ readonly code: "WORKSPACE_FILE_TOO_LARGE";
33
+ readonly requestedPath: string;
34
+ readonly sizeBytes: number;
35
+ readonly limitBytes: number;
36
+ constructor(message: string, requestedPath: string, sizeBytes: number, limitBytes: number, secrets?: readonly string[]);
37
+ }
38
+ export declare class WorkspaceReadError extends WorkspaceError {
39
+ readonly code: "WORKSPACE_READ_FAILED";
40
+ readonly requestedPath: string;
41
+ constructor(message: string, requestedPath: string, secrets?: readonly string[]);
42
+ }
43
+ export declare class RepoSearchInvalidQueryError extends WorkspaceError {
44
+ readonly code: "WORKSPACE_REPO_SEARCH_INVALID_QUERY";
45
+ constructor(message: string, secrets?: readonly string[]);
46
+ }
47
+ export declare class RepoSearchInvalidRangeError extends WorkspaceError {
48
+ readonly code: "WORKSPACE_REPO_SEARCH_INVALID_RANGE";
49
+ constructor(message: string, secrets?: readonly string[]);
50
+ }
51
+ export declare class RepoSearchUnsupportedFileError extends WorkspaceError {
52
+ readonly code: "WORKSPACE_REPO_SEARCH_UNSUPPORTED_FILE";
53
+ readonly reason: string;
54
+ constructor(message: string, reason: string, secrets?: readonly string[]);
55
+ }
56
+ //# sourceMappingURL=workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/errors/workspace.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,eAAe;;;;;;;;;CASlB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,OAAO,eAAe,CAAC,CAAC;AAEnF,8BAAsB,cAAe,SAAQ,KAAK;IAChD,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;gBAE1B,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAI7D;AAGD,qBAAa,eAAgB,SAAQ,cAAc;IACjD,QAAQ,CAAC,IAAI,0BAA+B;IAC5C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBAEnB,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAIpF;AAGD,qBAAa,eAAgB,SAAQ,cAAc;IACjD,QAAQ,CAAC,IAAI,0BAA+B;IAC5C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBAEnB,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAIpF;AAGD,qBAAa,sBAAuB,SAAQ,cAAc;IACxD,QAAQ,CAAC,IAAI,wBAA6B;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAI/E;AAGD,qBAAa,iBAAkB,SAAQ,cAAc;IACnD,QAAQ,CAAC,IAAI,6BAAkC;IAC/C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAG1B,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,SAAS,MAAM,EAAO;CAOlC;AAGD,qBAAa,kBAAmB,SAAQ,cAAc;IACpD,QAAQ,CAAC,IAAI,0BAA+B;IAC5C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBAEnB,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAIpF;AAID,qBAAa,2BAA4B,SAAQ,cAAc;IAC7D,QAAQ,CAAC,IAAI,wCAA6C;gBAE9C,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAG7D;AAID,qBAAa,2BAA4B,SAAQ,cAAc;IAC7D,QAAQ,CAAC,IAAI,wCAA6C;gBAE9C,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAG7D;AAID,qBAAa,8BAA+B,SAAQ,cAAc;IAChE,QAAQ,CAAC,IAAI,2CAAgD;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,MAAM,EAAO;CAI7E"}
@@ -0,0 +1,95 @@
1
+ // Workspace error taxonomy, mirroring the gateway/harness pattern (ADR-0003, ADR-0004).
2
+ // Errors carry a stable `code` discriminant; callers switch on `code`, never parse
3
+ // `message`. Every message is redacted at construction so errors are always safe to log.
4
+ import { redact } from "../redaction.js";
5
+ export const WORKSPACE_CODES = {
6
+ PATH_ESCAPE: "WORKSPACE_PATH_ESCAPE",
7
+ PATH_DENIED: "WORKSPACE_PATH_DENIED",
8
+ NOT_FOUND: "WORKSPACE_NOT_FOUND",
9
+ FILE_TOO_LARGE: "WORKSPACE_FILE_TOO_LARGE",
10
+ READ_FAILED: "WORKSPACE_READ_FAILED",
11
+ REPO_SEARCH_INVALID_QUERY: "WORKSPACE_REPO_SEARCH_INVALID_QUERY",
12
+ REPO_SEARCH_INVALID_RANGE: "WORKSPACE_REPO_SEARCH_INVALID_RANGE",
13
+ REPO_SEARCH_UNSUPPORTED_FILE: "WORKSPACE_REPO_SEARCH_UNSUPPORTED_FILE",
14
+ };
15
+ export class WorkspaceError extends Error {
16
+ constructor(message, secrets = []) {
17
+ super(redact(message, secrets));
18
+ this.name = new.target.name;
19
+ }
20
+ }
21
+ // Raised when a candidate path escapes the workspace root (NUL, `..`, or absolute escape).
22
+ export class PathEscapeError extends WorkspaceError {
23
+ code = WORKSPACE_CODES.PATH_ESCAPE;
24
+ requestedPath;
25
+ constructor(message, requestedPath, secrets = []) {
26
+ super(message, secrets);
27
+ this.requestedPath = requestedPath;
28
+ }
29
+ }
30
+ // Raised when a path matches an always-on deny pattern (secrets, deps, build, vcs).
31
+ export class PathDeniedError extends WorkspaceError {
32
+ code = WORKSPACE_CODES.PATH_DENIED;
33
+ requestedPath;
34
+ constructor(message, requestedPath, secrets = []) {
35
+ super(message, secrets);
36
+ this.requestedPath = requestedPath;
37
+ }
38
+ }
39
+ // Raised when no workspace root (`.git` or `package.json`) is found above startDir.
40
+ export class WorkspaceNotFoundError extends WorkspaceError {
41
+ code = WORKSPACE_CODES.NOT_FOUND;
42
+ startDir;
43
+ constructor(message, startDir, secrets = []) {
44
+ super(message, secrets);
45
+ this.startDir = startDir;
46
+ }
47
+ }
48
+ // Raised when a file exceeds the configured read size cap.
49
+ export class FileTooLargeError extends WorkspaceError {
50
+ code = WORKSPACE_CODES.FILE_TOO_LARGE;
51
+ requestedPath;
52
+ sizeBytes;
53
+ limitBytes;
54
+ constructor(message, requestedPath, sizeBytes, limitBytes, secrets = []) {
55
+ super(message, secrets);
56
+ this.requestedPath = requestedPath;
57
+ this.sizeBytes = sizeBytes;
58
+ this.limitBytes = limitBytes;
59
+ }
60
+ }
61
+ // Raised for an underlying filesystem read failure at the IO boundary.
62
+ export class WorkspaceReadError extends WorkspaceError {
63
+ code = WORKSPACE_CODES.READ_FAILED;
64
+ requestedPath;
65
+ constructor(message, requestedPath, secrets = []) {
66
+ super(message, secrets);
67
+ this.requestedPath = requestedPath;
68
+ }
69
+ }
70
+ // Raised at the repo-search API boundary when the RetrievalQuery fails validation, has the
71
+ // wrong `kind` for the entry point called, or carries a syntactically invalid regex.
72
+ export class RepoSearchInvalidQueryError extends WorkspaceError {
73
+ code = WORKSPACE_CODES.REPO_SEARCH_INVALID_QUERY;
74
+ constructor(message, secrets = []) {
75
+ super(message, secrets);
76
+ }
77
+ }
78
+ // Raised when a readExcerpt request specifies a line range that is not a positive,
79
+ // increasing pair of integers, or a scopePath that fails the contracts validator.
80
+ export class RepoSearchInvalidRangeError extends WorkspaceError {
81
+ code = WORKSPACE_CODES.REPO_SEARCH_INVALID_RANGE;
82
+ constructor(message, secrets = []) {
83
+ super(message, secrets);
84
+ }
85
+ }
86
+ // Raised when readExcerpt is called on a file the facade refuses to read or interpret
87
+ // (for example: outside the selected scope, denied/ignored by policy, or binary content).
88
+ export class RepoSearchUnsupportedFileError extends WorkspaceError {
89
+ code = WORKSPACE_CODES.REPO_SEARCH_UNSUPPORTED_FILE;
90
+ reason;
91
+ constructor(message, reason, secrets = []) {
92
+ super(message, secrets);
93
+ this.reason = reason;
94
+ }
95
+ }
@@ -0,0 +1,4 @@
1
+ export declare function canonicalise(value: unknown): string;
2
+ export declare function sha256Hex(input: string): string;
3
+ export declare function sha256Base64(input: string): string;
4
+ //# sourceMappingURL=hashing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashing.d.ts","sourceRoot":"","sources":["../src/hashing.ts"],"names":[],"mappings":"AAcA,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAenD;AAID,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAID,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD"}
@@ -0,0 +1,38 @@
1
+ // Pure, deterministic hashing primitives shared across the security trust boundary. Callers in
2
+ // harness, audit, and UI compose these into higher-level fingerprints, evidence digests, and CSP
3
+ // hashes without re-deriving the cryptographic boundary in each layer.
4
+ //
5
+ // Why these helpers live here, not in node:crypto callers: the security package is the leaf trust
6
+ // boundary, so the canonical-JSON serialiser and the SHA-256 wrappers belong with redact() and the
7
+ // safe-error taxonomy — any future regulated-delivery audit only needs to inspect one module to
8
+ // confirm hashing semantics are stable.
9
+ import { createHash } from "node:crypto";
10
+ // Canonical JSON: object keys sorted recursively, array order preserved, undefined values omitted
11
+ // (matching JSON.stringify semantics). Two structurally equal inputs serialise to byte-identical
12
+ // strings regardless of key insertion order, so SHA-256 over the canonical form is order-stable.
13
+ export function canonicalise(value) {
14
+ if (value === undefined) {
15
+ return "null";
16
+ }
17
+ if (value === null || typeof value !== "object") {
18
+ return JSON.stringify(value);
19
+ }
20
+ if (Array.isArray(value)) {
21
+ return `[${value.map((item) => canonicalise(item)).join(",")}]`;
22
+ }
23
+ const entries = Object.entries(value)
24
+ .filter(([, v]) => v !== undefined)
25
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
26
+ .map(([key, v]) => `${JSON.stringify(key)}:${canonicalise(v)}`);
27
+ return `{${entries.join(",")}}`;
28
+ }
29
+ // SHA-256 of a UTF-8 string, hex-encoded. Used for run-fingerprints and any other identifier-style
30
+ // digest where hex is the expected format.
31
+ export function sha256Hex(input) {
32
+ return createHash("sha256").update(input, "utf8").digest("hex");
33
+ }
34
+ // SHA-256 of a UTF-8 string, base64-encoded. Used for CSP `'sha256-...'` source tokens (RFC 4648
35
+ // standard base64, which is what browsers compare CSP hashes against).
36
+ export function sha256Base64(input) {
37
+ return createHash("sha256").update(input, "utf8").digest("base64");
38
+ }
@@ -0,0 +1,11 @@
1
+ export { KEIKO_SECURITY_VERSION } from "./version.js";
2
+ export { redact, createAuditRedactor, deepRedactStrings } from "./redaction.js";
3
+ export { assertValidRunId } from "./runid.js";
4
+ export type { EnvSource } from "./secrets.js";
5
+ export { isKeikoApiKeyEnvName, keikoApiKeySecretValues } from "./secrets.js";
6
+ export { canonicalise, sha256Hex, sha256Base64 } from "./hashing.js";
7
+ export { sealString, openString, sealBytes, openBytes, isSealed } from "./secretbox.js";
8
+ export type { PromptInjectionSignalCode, PromptInjectionSeverity, PromptInjectionSignal, } from "./promptInjection.js";
9
+ export { PROMPT_INJECTION_SIGNAL_CODES, isPromptInjectionSignalCode, detectPromptInjectionSignals, containsRedactableSecret, hasCriticalInjectionSignal, } from "./promptInjection.js";
10
+ export * from "./errors/index.js";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEhF,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAE7E,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAErE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAGxF,YAAY,EACV,yBAAyB,EACzB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,6BAA6B,EAC7B,2BAA2B,EAC3B,4BAA4B,EAC5B,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAE9B,cAAc,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ // Public barrel for @oscharko-dev/keiko-security. Re-exports every shared primitive so callers
2
+ // can `import { redact, GatewayError, assertValidRunId, sha256Hex } from "@oscharko-dev/keiko-security"`
3
+ // without knowing which sub-module owns the symbol. Subpath imports (`./errors/gateway`, etc.) are
4
+ // available for callers who want to pull in a narrower surface — both flat and subpath imports
5
+ // resolve to the same module instance.
6
+ export { KEIKO_SECURITY_VERSION } from "./version.js";
7
+ export { redact, createAuditRedactor, deepRedactStrings } from "./redaction.js";
8
+ export { assertValidRunId } from "./runid.js";
9
+ export { isKeikoApiKeyEnvName, keikoApiKeySecretValues } from "./secrets.js";
10
+ export { canonicalise, sha256Hex, sha256Base64 } from "./hashing.js";
11
+ export { sealString, openString, sealBytes, openBytes, isSealed } from "./secretbox.js";
12
+ export { PROMPT_INJECTION_SIGNAL_CODES, isPromptInjectionSignalCode, detectPromptInjectionSignals, containsRedactableSecret, hasCriticalInjectionSignal, } from "./promptInjection.js";
13
+ export * from "./errors/index.js";
@@ -0,0 +1,29 @@
1
+ export type PromptInjectionSignalCode = "instruction-override" | "system-prompt-disclosure" | "secret-exfiltration" | "tool-authority-request" | "egress-request" | "manipulative-framing" | "embedded-secret-material";
2
+ export declare const PROMPT_INJECTION_SIGNAL_CODES: readonly PromptInjectionSignalCode[];
3
+ export declare const isPromptInjectionSignalCode: (value: unknown) => value is PromptInjectionSignalCode;
4
+ export type PromptInjectionSeverity = "elevated" | "critical";
5
+ export interface PromptInjectionSignal {
6
+ readonly code: PromptInjectionSignalCode;
7
+ readonly severity: PromptInjectionSeverity;
8
+ readonly matchCount: number;
9
+ }
10
+ /**
11
+ * Detect unsafe-content signals in a piece of UNTRUSTED text (raw user draft, retrieved snippet, or
12
+ * tool output). Pure and content-free: returns a stable list of `{ code, severity, matchCount }`
13
+ * signals and never echoes the matched text. ReDoS-safe (substring containment + the bounded `redact`).
14
+ *
15
+ * `embedded-secret-material` fires when the text itself contains a redactable secret shape — relevant
16
+ * both to secret-exfiltration detection and to the redaction guarantee for persisted evidence.
17
+ */
18
+ export declare function detectPromptInjectionSignals(untrustedText: string): readonly PromptInjectionSignal[];
19
+ /**
20
+ * Whether the text contains redactable secret material (a known token/key/credential shape). Reuses
21
+ * the authoritative `redact` so the detector and the redactor agree by construction. Pure.
22
+ */
23
+ export declare function containsRedactableSecret(text: string): boolean;
24
+ /**
25
+ * Whether any detected signal is `critical` — an active attack attempt (override, disclosure,
26
+ * exfiltration, unsafe tool, or egress). Pure helper for callers gating on attack severity.
27
+ */
28
+ export declare function hasCriticalInjectionSignal(signals: readonly PromptInjectionSignal[]): boolean;
29
+ //# sourceMappingURL=promptInjection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promptInjection.d.ts","sourceRoot":"","sources":["../src/promptInjection.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,yBAAyB,GAEjC,sBAAsB,GAEtB,0BAA0B,GAE1B,qBAAqB,GAErB,wBAAwB,GAExB,gBAAgB,GAEhB,sBAAsB,GAEtB,0BAA0B,CAAC;AAE/B,eAAO,MAAM,6BAA6B,EAAE,SAAS,yBAAyB,EAQpE,CAAC;AAEX,eAAO,MAAM,2BAA2B,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,yBAC6B,CAAC;AAKpG,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,UAAU,CAAC;AAI9D,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAkLD;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,aAAa,EAAE,MAAM,GACpB,SAAS,qBAAqB,EAAE,CAgBlC;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,GAAG,OAAO,CAE7F"}