approval-md 0.4.3 → 0.4.6

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 (52) hide show
  1. package/SPEC.md +13 -2
  2. package/dist/src/channels/telegram.d.ts +105 -31
  3. package/dist/src/channels/telegram.js +157 -81
  4. package/dist/src/channels/telegram.js.map +1 -1
  5. package/dist/src/cli/channel-telegram.d.ts +100 -35
  6. package/dist/src/cli/channel-telegram.js +216 -119
  7. package/dist/src/cli/channel-telegram.js.map +1 -1
  8. package/dist/src/cli/doctor.js +33 -13
  9. package/dist/src/cli/doctor.js.map +1 -1
  10. package/dist/src/cli/help.js +3 -3
  11. package/dist/src/cli/hook-exec-code.d.ts +150 -0
  12. package/dist/src/cli/hook-exec-code.js +357 -0
  13. package/dist/src/cli/hook-exec-code.js.map +1 -0
  14. package/dist/src/cli/hook.d.ts +125 -1
  15. package/dist/src/cli/hook.js +2177 -107
  16. package/dist/src/cli/hook.js.map +1 -1
  17. package/dist/src/cli/wordmark.d.ts +2 -2
  18. package/dist/src/cli/wordmark.js +2 -2
  19. package/dist/src/core/command-class.d.ts +368 -1
  20. package/dist/src/core/command-class.js +2830 -93
  21. package/dist/src/core/command-class.js.map +1 -1
  22. package/dist/src/core/date-argv.d.ts +83 -0
  23. package/dist/src/core/date-argv.js +212 -0
  24. package/dist/src/core/date-argv.js.map +1 -0
  25. package/dist/src/core/exec-code.d.ts +77 -0
  26. package/dist/src/core/exec-code.js +60 -0
  27. package/dist/src/core/exec-code.js.map +1 -0
  28. package/dist/src/core/git-push-argv.d.ts +111 -0
  29. package/dist/src/core/git-push-argv.js +375 -0
  30. package/dist/src/core/git-push-argv.js.map +1 -0
  31. package/dist/src/core/harness-version.d.ts +71 -30
  32. package/dist/src/core/harness-version.js +101 -34
  33. package/dist/src/core/harness-version.js.map +1 -1
  34. package/dist/src/core/policy-load.d.ts +8 -0
  35. package/dist/src/core/policy-load.js.map +1 -1
  36. package/dist/src/core/read-scope.js +1 -0
  37. package/dist/src/core/read-scope.js.map +1 -1
  38. package/dist/src/core/sysinfo-argv.d.ts +48 -0
  39. package/dist/src/core/sysinfo-argv.js +340 -0
  40. package/dist/src/core/sysinfo-argv.js.map +1 -0
  41. package/dist/src/daemon/git-evidence.d.ts +2 -2
  42. package/dist/src/daemon/git-evidence.js +1 -1
  43. package/docs/README-extended.md +3 -2
  44. package/docs/cli-reference.md +20 -7
  45. package/package.json +2 -2
  46. package/schema/event.schema.json +1 -1
  47. package/schema/fixtures/policy/invalid/exec-code-custody-boolean.json +11 -0
  48. package/schema/fixtures/policy/invalid/exec-code-execute-code-value.json +11 -0
  49. package/schema/fixtures/policy/invalid/exec-code-unknown-key.json +12 -0
  50. package/schema/fixtures/policy/valid/exec-code-both.json +17 -0
  51. package/schema/fixtures/policy/valid/exec-code-custody.json +11 -0
  52. package/schema/policy.schema.json +23 -0
@@ -321,6 +321,28 @@ export declare const HOOK_DENY_CODES: readonly [
321
321
  * say about bytes it could see. Here there are no bytes to see.
322
322
  */
323
323
  "hook-hermes-execute-code-unbound",
324
+ /**
325
+ * An opaque program (`python3 -c`, `node -e` and kin, an interpreter running a
326
+ * file that is not a skill script, or a Hermes `execute_code` call with
327
+ * `exec.code.execute_code: hooked`) under a policy that NAMES the `exec.code`
328
+ * class but does not declare `exec.code.custody: attested` (APRV-510).
329
+ *
330
+ * Distinct from `hook-opaque`, and the distinction is the repair: that one
331
+ * says rewrite the command, this one says the operator has asked for the
332
+ * class and the one line that opens it is missing. A policy that does not
333
+ * name `exec.code` at all keeps today's refusal byte for byte, so this code
334
+ * is never a surprise to an operator who has not opted in. Nothing appends.
335
+ */
336
+ "hook-exec-code-custody-unattested",
337
+ /**
338
+ * An `exec.code` file program this hook cannot read and hash (APRV-510): it
339
+ * is absent, unreadable by this process (EACCES under co-location), not a
340
+ * regular file, too large, or relative after a `cd` the hook cannot place.
341
+ * A record of an opaque program binds its hash, and a program whose bytes
342
+ * cannot be read is one nothing could bind, so it is refused rather than
343
+ * recorded without one. Nothing appends.
344
+ */
345
+ "hook-exec-code-unreadable",
324
346
  /** Malformed hook input, or a log/filesystem fact that stopped the check. */
325
347
  "hook-io"];
326
348
  export type HookDenyCode = (typeof HOOK_DENY_CODES)[number];
@@ -725,6 +747,95 @@ export declare function resolveReadRoots(cwd: string, gateRoot: string, declared
725
747
  * "absent yields today's answer" the classifier context promises.
726
748
  */
727
749
  export declare function refineReadScope(result: CommandClassification, roots: readonly string[], cwd: string): RefinedClassification;
750
+ /** The rule a credential read found by resolving its path reports. */
751
+ export declare const CREDENTIAL_RESOLVED_RULE = "credential-path-resolved";
752
+ /**
753
+ * Every credential file a command reads once its paths are resolved the way
754
+ * the shell and the filesystem will resolve them (APRV-522, M4 and M5).
755
+ *
756
+ * The pure classifier judges the words as written. A word written relative
757
+ * after a `cd`, through a symlinked parent, as a glob, or inside a `$(…)` does
758
+ * not carry the segments the credential rule matches, so it was `read.shell`
759
+ * through the Claude Code hook (a relative read after a `cd` into the approval
760
+ * home, a read through a symlinked alias of it, a glob or `<` target naming a
761
+ * file in it), and through the Hermes hook too when the reader's segment also
762
+ * wrote, or the reader sat inside a substitution.
763
+ *
764
+ * Every adapter and `hook classify` reach this through {@link classifyForHook}.
765
+ * What is judged, per segment, whatever class the segment took:
766
+ *
767
+ * - the words {@link credentialReadWords} says the binary reads (the pure
768
+ * rule's own list: positionals, option-embedded values, git `<rev>:<path>`),
769
+ * and every `<` target;
770
+ * - each word expanded ({@link expandHookWord}, brace lists included), then
771
+ * landed from every directory the command may be in: lexically
772
+ * ({@link canonicalPath}) and physically ({@link physicalLanding}), a glob
773
+ * expanded against its directory (or, where that is unknown, judged by
774
+ * whether its pattern could name a credential);
775
+ * - a directory handed to a recursive reader or a directory copier, when it
776
+ * {@link holdsCredentials}. git and diff count as recursive readers here
777
+ * (fix round 1, S): git's no-index grep and diff walk a directory, and diff
778
+ * compares a directory's files. A recursive search also reads the working
779
+ * directory (fix round 1, M-E), whether or not it names a path, because
780
+ * which of its words are option values cannot be known for every reader;
781
+ * - a one-operand link or copy keeps its only operand as a read, because the
782
+ * destination is the working directory (fix round 1, M-F);
783
+ * - `cd` and `pushd` move the directory for later segments, the directories
784
+ * before and after both kept (a `cd` may fail, or sit behind `||`), each as
785
+ * spelled and with its symlinks resolved (fix round 2). After a `cd` this
786
+ * hook cannot resolve, every later RELATIVE read word is a credential read:
787
+ * where it lands is unknown, and it may be the vault. A `cd` is
788
+ * unresolvable when its operand is a variable, `-`, a glob or a brace list,
789
+ * or a tilde prefix this hook does not expand; when it carries an
790
+ * assignment prefix; and in the argv shapes {@link cdTarget} lists. `popd`
791
+ * is always unresolvable. (A segment that reassigns `HOME` or `CDPATH` on
792
+ * its own, `export`, `unset` or `read` of either included, is refused by the
793
+ * classifier before this pass runs.)
794
+ * - a relative operand that does not open with `./` or `../` may be found
795
+ * through a `CDPATH` this hook cannot see (fix round 1, M-B). Since fix
796
+ * round 2 that does not make the directory wholly unknown: the `cd` also
797
+ * lands at `<unknown prefix>/<operand>`, and each later relative word is a
798
+ * credential read there only when {@link steeredReadCouldNameCredential}
799
+ * says that path could name credential material or a gate home. A
800
+ * recursive reader, a word holding `..` and a `**` glob are credential
801
+ * reads there, since what they reach below an unknown directory cannot be
802
+ * bounded.
803
+ * - the body of every `$(…)`, and the script of every login-shell wrapper the
804
+ * classifier unwraps (fix round 1, M-A), walked the same way from the
805
+ * directory it runs in. A known shell handed an inline script in any other
806
+ * shape is a credential read, since what it reads cannot be judged;
807
+ * - past {@link BRACE_WORD_CAP} brace words, or {@link SUBSTITUTION_DEPTH_CAP}
808
+ * levels of nesting, a credential read (fix round 1, M-D).
809
+ *
810
+ * A word opening with a tilde prefix this hook does not expand (`~+`, `~-`,
811
+ * `~name`) is a credential read (fix round 1, M-C). A relative word read after an
812
+ * unresolvable `cd` is a credential read even when it holds a `$` this hook
813
+ * cannot expand (fix round 1, S-2). Otherwise a word left with such a `$` (a
814
+ * variable, or the substitution sentinel a `$(…)` leaves) is judged here for
815
+ * every adapter (APRV-524): by its text, by its literal prefix landed from
816
+ * each working directory, and, when its last component is the unresolved
817
+ * one, as a read of whatever that prefix holds, so a home or a directory that
818
+ * holds credentials makes it a credential read. A word holding the sentinel is
819
+ * judged once more with every sentinel removed, as a resolved path (the
820
+ * reading before APRV-511), so an empty substitution is judged too. The Hermes
821
+ * disk pass also fails closed on every recursive reader of such a word; this
822
+ * pass does not. Beyond that, a path held in a variable (which may be empty)
823
+ * or produced by a substitution's output is the accepted limit.
824
+ *
825
+ * `cwd` may name several directories (fix round 1, S-1): every one is a place
826
+ * the command may start in, and each is judged.
827
+ */
828
+ export declare function credentialReadLandings(command: string, cwd: string | readonly string[], env?: NodeJS.ProcessEnv): Array<{
829
+ segment: string;
830
+ path: string;
831
+ }>;
832
+ /**
833
+ * Add an `account.credential` segment for every credential read
834
+ * {@link credentialReadLandings} finds that the text did not already name
835
+ * (APRV-522). Only ever adds: the segment's own class stays, and the
836
+ * credential read rides beside it in the shape the classifier's `also` has.
837
+ */
838
+ export declare function refineCredentialReads(result: CommandClassification, command: string, cwd: string | readonly string[], env?: NodeJS.ProcessEnv): RefinedClassification;
728
839
  /**
729
840
  * The roots a write may land in, resolved.
730
841
  *
@@ -798,7 +909,20 @@ export declare function refineSkillScripts(result: CommandClassification, cwd: s
798
909
  * resolved gate scope wants and what this classifier did before the field
799
910
  * existed.
800
911
  */
801
- export declare function classifyForHook(command: string, protectedPaths: readonly ProtectedPathEntry[], cwd: string, readRoots?: readonly string[]): RefinedClassification;
912
+ export declare function classifyForHook(command: string, protectedPaths: readonly ProtectedPathEntry[], cwd: string, readRoots?: readonly string[],
913
+ /**
914
+ * May an opaque program classify `exec.code`? (APRV-510.) `true` only when
915
+ * the loaded policy opens the class ({@link execCodeGate}); the default is
916
+ * today's answer, a refusal.
917
+ */
918
+ execCode?: boolean,
919
+ /**
920
+ * Further directories the command may start in, judged by the credential
921
+ * pass only (APRV-522 fix round 1, S-1): the envelope's `cwd` where the
922
+ * harness sends no per-call directory. It only ever adds reads, so a stale
923
+ * or absent directory costs nothing.
924
+ */
925
+ credentialCwds?: readonly string[]): RefinedClassification;
802
926
  /**
803
927
  * What the classifier made of a command (APRV-91 #9).
804
928
  *