javi-forge 1.32.0 → 1.34.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.
- package/README.md +2 -0
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +6 -0
- package/dist/commands/claude-hooks.d.ts +10 -6
- package/dist/commands/claude-hooks.js +73 -11
- package/dist/commands/init/steps/security.d.ts +2 -0
- package/dist/commands/init/steps/security.js +37 -7
- package/dist/lib/claude-hook-manager.d.ts +147 -0
- package/dist/lib/claude-hook-manager.js +337 -4
- package/dist/lib/claude-hook-settings.d.ts +32 -0
- package/dist/lib/claude-hook-settings.js +40 -0
- package/dist/lib/secure-fs-posix.d.ts +43 -0
- package/dist/lib/secure-fs-posix.js +66 -7
- package/dist/lib/secure-refusal-remediation.d.ts +30 -0
- package/dist/lib/secure-refusal-remediation.js +48 -0
- package/dist/ui/App.js +3 -16
- package/dist/ui/build-init-options.d.ts +44 -0
- package/dist/ui/build-init-options.js +39 -0
- package/package.json +1 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure refusal → remediation table for the CLI layer (Linux hardening, Slice A).
|
|
3
|
+
*
|
|
4
|
+
* The secure-fs adapters stay PROVERS: they emit stable `SecureRefusal` codes
|
|
5
|
+
* plus a stable detail token and carry ZERO user copy. This module is the only
|
|
6
|
+
* place that turns one of those tokens into an actionable next step, so the
|
|
7
|
+
* proof algorithms and their refusal identities never move when the copy does.
|
|
8
|
+
*
|
|
9
|
+
* The table is deliberately narrow. A remediation is emitted ONLY when the user
|
|
10
|
+
* can actually fix the cause: an unresolvable `getfacl` is fixed by installing
|
|
11
|
+
* the `acl` package, while a REAL extended ACL (the adapter ran and found a
|
|
12
|
+
* named-user entry) is not — suggesting a package install there would be
|
|
13
|
+
* actively misleading. No I/O, no rendering, no Ink.
|
|
14
|
+
*/
|
|
15
|
+
import type { SecureRefusal } from "./secure-fs-transaction.js";
|
|
16
|
+
/** The one actionable line for a host whose POSIX ACL adapter is missing. */
|
|
17
|
+
export declare const ACL_PACKAGE_REMEDIATION = "install the acl package (provides getfacl): apt install acl \u00B7 apk add acl \u00B7 dnf install acl";
|
|
18
|
+
/**
|
|
19
|
+
* The actionable line for a refusal + detail pair, or `undefined` when nothing
|
|
20
|
+
* the user can do would change the outcome.
|
|
21
|
+
*/
|
|
22
|
+
export declare function remediationForRefusal(refusal: SecureRefusal, detail?: string): string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* The same lookup for an already-rendered refusal MESSAGE (the transaction
|
|
25
|
+
* flattens `refusal`/`detail` into strings such as `acl /path: getfacl absent`).
|
|
26
|
+
* Matching is anchored to the message TAIL so prose that merely mentions the
|
|
27
|
+
* token never triggers a wrong hint.
|
|
28
|
+
*/
|
|
29
|
+
export declare function remediationForMessage(message: string): string | undefined;
|
|
30
|
+
//# sourceMappingURL=secure-refusal-remediation.d.ts.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure refusal → remediation table for the CLI layer (Linux hardening, Slice A).
|
|
3
|
+
*
|
|
4
|
+
* The secure-fs adapters stay PROVERS: they emit stable `SecureRefusal` codes
|
|
5
|
+
* plus a stable detail token and carry ZERO user copy. This module is the only
|
|
6
|
+
* place that turns one of those tokens into an actionable next step, so the
|
|
7
|
+
* proof algorithms and their refusal identities never move when the copy does.
|
|
8
|
+
*
|
|
9
|
+
* The table is deliberately narrow. A remediation is emitted ONLY when the user
|
|
10
|
+
* can actually fix the cause: an unresolvable `getfacl` is fixed by installing
|
|
11
|
+
* the `acl` package, while a REAL extended ACL (the adapter ran and found a
|
|
12
|
+
* named-user entry) is not — suggesting a package install there would be
|
|
13
|
+
* actively misleading. No I/O, no rendering, no Ink.
|
|
14
|
+
*/
|
|
15
|
+
import { ACL_DETAIL } from "./secure-fs-posix.js";
|
|
16
|
+
/** The one actionable line for a host whose POSIX ACL adapter is missing. */
|
|
17
|
+
export const ACL_PACKAGE_REMEDIATION = "install the acl package (provides getfacl): apt install acl · apk add acl · dnf install acl";
|
|
18
|
+
/** Detail tokens that map to a remediation, keyed by refusal code. */
|
|
19
|
+
const REMEDIATION_TABLE = {
|
|
20
|
+
"unsupported-posix-acl": {
|
|
21
|
+
[ACL_DETAIL.getfaclAbsent]: ACL_PACKAGE_REMEDIATION,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The actionable line for a refusal + detail pair, or `undefined` when nothing
|
|
26
|
+
* the user can do would change the outcome.
|
|
27
|
+
*/
|
|
28
|
+
export function remediationForRefusal(refusal, detail) {
|
|
29
|
+
if (detail === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
return REMEDIATION_TABLE[refusal]?.[detail];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The same lookup for an already-rendered refusal MESSAGE (the transaction
|
|
35
|
+
* flattens `refusal`/`detail` into strings such as `acl /path: getfacl absent`).
|
|
36
|
+
* Matching is anchored to the message TAIL so prose that merely mentions the
|
|
37
|
+
* token never triggers a wrong hint.
|
|
38
|
+
*/
|
|
39
|
+
export function remediationForMessage(message) {
|
|
40
|
+
for (const details of Object.values(REMEDIATION_TABLE)) {
|
|
41
|
+
for (const [detail, line] of Object.entries(details)) {
|
|
42
|
+
if (message.endsWith(detail))
|
|
43
|
+
return line;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=secure-refusal-remediation.js.map
|
package/dist/ui/App.js
CHANGED
|
@@ -2,6 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { Box } from "ink";
|
|
3
3
|
import React, { useState } from "react";
|
|
4
4
|
import { initProject } from "../commands/init.js";
|
|
5
|
+
import { buildInitOptions } from "./build-init-options.js";
|
|
5
6
|
import CISelector from "./CISelector.js";
|
|
6
7
|
import Header from "./Header.js";
|
|
7
8
|
import HookProfileSelector from "./HookProfileSelector.js";
|
|
@@ -80,29 +81,15 @@ export default function App({ dryRun = false, presetStack, presetCI, presetMemor
|
|
|
80
81
|
};
|
|
81
82
|
const runInit = async (opts) => {
|
|
82
83
|
setStage("running");
|
|
83
|
-
await initProject({
|
|
84
|
+
await initProject(buildInitOptions(opts, {
|
|
84
85
|
projectName,
|
|
85
86
|
projectDir,
|
|
86
87
|
stack,
|
|
87
88
|
ciProvider,
|
|
88
89
|
memory,
|
|
89
|
-
aiSync: opts.aiSync,
|
|
90
|
-
sdd: opts.sdd,
|
|
91
|
-
ghagga: opts.ghagga,
|
|
92
|
-
contextDir: opts.contextDir,
|
|
93
|
-
claudeMd: opts.claudeMd,
|
|
94
|
-
securityHooks: opts.securityHooks,
|
|
95
|
-
hookProfile: opts.hookProfile,
|
|
96
|
-
// Derived from securityHooks alone — ALL profiles incl. Minimal
|
|
97
|
-
// install the managed guard when security hooks are enabled.
|
|
98
|
-
claudePreToolUseGuard: opts.securityHooks,
|
|
99
|
-
codeGraph: opts.codeGraph,
|
|
100
|
-
localAi: opts.localAi,
|
|
101
|
-
dockerDeploy: false,
|
|
102
|
-
dockerServiceName: "app",
|
|
103
90
|
mock: presetMock,
|
|
104
91
|
dryRun,
|
|
105
|
-
}, (step) => setSteps((prev) => {
|
|
92
|
+
}), (step) => setSteps((prev) => {
|
|
106
93
|
const idx = prev.findIndex((s) => s.id === step.id);
|
|
107
94
|
if (idx >= 0) {
|
|
108
95
|
const next = [...prev];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { CIProvider, HookProfile, InitOptions, MemoryOption, Stack } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Wizard-collected toggles for an init run — the subset the user answers
|
|
4
|
+
* interactively (or via presets). Mirrors the `runInit` opts parameter.
|
|
5
|
+
*/
|
|
6
|
+
export interface InitOptionsWizardResult {
|
|
7
|
+
aiSync: boolean;
|
|
8
|
+
sdd: boolean;
|
|
9
|
+
contextDir: boolean;
|
|
10
|
+
claudeMd: boolean;
|
|
11
|
+
ghagga: boolean;
|
|
12
|
+
securityHooks: boolean;
|
|
13
|
+
codeGraph: boolean;
|
|
14
|
+
localAi: boolean;
|
|
15
|
+
hookProfile: HookProfile;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Surrounding context resolved before the wizard finishes — project identity,
|
|
19
|
+
* stack/CI/memory choices, and run flags (mock/dryRun).
|
|
20
|
+
*/
|
|
21
|
+
export interface InitOptionsContext {
|
|
22
|
+
projectName: string;
|
|
23
|
+
projectDir: string;
|
|
24
|
+
stack: Stack;
|
|
25
|
+
ciProvider: CIProvider;
|
|
26
|
+
memory: MemoryOption;
|
|
27
|
+
mock: boolean;
|
|
28
|
+
dryRun: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Pure mapping from wizard result + surrounding context to the full
|
|
32
|
+
* {@link InitOptions} contract passed to `initProject`.
|
|
33
|
+
*
|
|
34
|
+
* Extracted from the single inline object literal in `App.tsx#runInit` so the
|
|
35
|
+
* derivation is unit-testable. Behavior-identical: field values must match the
|
|
36
|
+
* previous inline literal exactly.
|
|
37
|
+
*
|
|
38
|
+
* Notably, `claudePreToolUseGuard` is derived from `securityHooks` alone — ALL
|
|
39
|
+
* hook profiles (including "minimal") install the managed guard when security
|
|
40
|
+
* hooks are enabled. A regression here silently disables managed-guard
|
|
41
|
+
* installation, so it is pinned by build-init-options.test.ts.
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildInitOptions(opts: InitOptionsWizardResult, ctx: InitOptionsContext): InitOptions;
|
|
44
|
+
//# sourceMappingURL=build-init-options.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure mapping from wizard result + surrounding context to the full
|
|
3
|
+
* {@link InitOptions} contract passed to `initProject`.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from the single inline object literal in `App.tsx#runInit` so the
|
|
6
|
+
* derivation is unit-testable. Behavior-identical: field values must match the
|
|
7
|
+
* previous inline literal exactly.
|
|
8
|
+
*
|
|
9
|
+
* Notably, `claudePreToolUseGuard` is derived from `securityHooks` alone — ALL
|
|
10
|
+
* hook profiles (including "minimal") install the managed guard when security
|
|
11
|
+
* hooks are enabled. A regression here silently disables managed-guard
|
|
12
|
+
* installation, so it is pinned by build-init-options.test.ts.
|
|
13
|
+
*/
|
|
14
|
+
export function buildInitOptions(opts, ctx) {
|
|
15
|
+
return {
|
|
16
|
+
projectName: ctx.projectName,
|
|
17
|
+
projectDir: ctx.projectDir,
|
|
18
|
+
stack: ctx.stack,
|
|
19
|
+
ciProvider: ctx.ciProvider,
|
|
20
|
+
memory: ctx.memory,
|
|
21
|
+
aiSync: opts.aiSync,
|
|
22
|
+
sdd: opts.sdd,
|
|
23
|
+
ghagga: opts.ghagga,
|
|
24
|
+
contextDir: opts.contextDir,
|
|
25
|
+
claudeMd: opts.claudeMd,
|
|
26
|
+
securityHooks: opts.securityHooks,
|
|
27
|
+
hookProfile: opts.hookProfile,
|
|
28
|
+
// Derived from securityHooks alone — ALL profiles incl. Minimal
|
|
29
|
+
// install the managed guard when security hooks are enabled.
|
|
30
|
+
claudePreToolUseGuard: opts.securityHooks,
|
|
31
|
+
codeGraph: opts.codeGraph,
|
|
32
|
+
localAi: opts.localAi,
|
|
33
|
+
dockerDeploy: false,
|
|
34
|
+
dockerServiceName: "app",
|
|
35
|
+
mock: ctx.mock,
|
|
36
|
+
dryRun: ctx.dryRun,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=build-init-options.js.map
|