haechi 1.0.0 → 1.1.1
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.ko.md +14 -8
- package/README.md +14 -8
- package/SECURITY.md +1 -1
- package/docs/README.md +1 -1
- package/docs/current/configuration.ko.md +23 -4
- package/docs/current/configuration.md +23 -4
- package/docs/current/open-source-modular-architecture.ko.md +1 -1
- package/docs/current/open-source-modular-architecture.md +1 -1
- package/docs/current/release-1.1-implementation-scope.ko.md +128 -0
- package/docs/current/release-1.1-implementation-scope.md +128 -0
- package/docs/current/risk-register-release-gate.ko.md +9 -1
- package/docs/current/risk-register-release-gate.md +9 -1
- package/docs/current/threat-model.ko.md +11 -5
- package/docs/current/threat-model.md +11 -5
- package/haechi.config.example.json +1 -1
- package/package.json +4 -3
- package/packages/cli/bin/haechi.mjs +2 -2
- package/packages/cli/runtime.mjs +55 -9
- package/packages/plugin/index.mjs +27 -17
- package/packages/plugin/process-sandbox.mjs +629 -0
- package/packages/plugin/sandbox-common.mjs +243 -0
- package/packages/plugin/sandbox.mjs +24 -217
- package/packages/proxy/index.mjs +1 -1
- package/packages/ssrf/index.mjs +189 -0
|
@@ -12,6 +12,11 @@ export {
|
|
|
12
12
|
createSandboxedAuthProviderSync
|
|
13
13
|
} from "./sandbox.mjs";
|
|
14
14
|
|
|
15
|
+
export {
|
|
16
|
+
createProcessIsolatedAuthProvider,
|
|
17
|
+
createProcessIsolatedAuthProviderSync
|
|
18
|
+
} from "./process-sandbox.mjs";
|
|
19
|
+
|
|
15
20
|
const VALID_KINDS = new Set([
|
|
16
21
|
"crypto-provider",
|
|
17
22
|
"key-provider",
|
|
@@ -34,10 +39,13 @@ const CAPABILITY_KEYS = [
|
|
|
34
39
|
"auditWrite",
|
|
35
40
|
"externalSecrets"
|
|
36
41
|
];
|
|
37
|
-
// manifest-only is the historical, behavior-preserving path. worker-isolated
|
|
38
|
-
//
|
|
39
|
-
// only with the Ed25519 signed envelope
|
|
40
|
-
|
|
42
|
+
// manifest-only is the historical, behavior-preserving path. worker-isolated
|
|
43
|
+
// (1.0) and process-isolated (1.1) are the dynamic-loading runtimes — both
|
|
44
|
+
// permitted ONLY for kind authProvider and only with the Ed25519 signed envelope
|
|
45
|
+
// (see validateSignedDynamicManifest). They share the same manifest contract; the
|
|
46
|
+
// difference is the isolation mechanism (worker_threads vs a --permission child).
|
|
47
|
+
const VALID_RUNTIMES = new Set(["manifest-only", "worker-isolated", "process-isolated"]);
|
|
48
|
+
const SIGNED_DYNAMIC_RUNTIMES = new Set(["worker-isolated", "process-isolated"]);
|
|
41
49
|
|
|
42
50
|
export async function validatePluginManifestFile(path) {
|
|
43
51
|
const manifest = JSON.parse(await readFile(path, "utf8"));
|
|
@@ -65,11 +73,12 @@ export function validatePluginManifest(manifest) {
|
|
|
65
73
|
errors.push("dynamic plugin execution is not supported; set runtime to manifest-only");
|
|
66
74
|
}
|
|
67
75
|
|
|
68
|
-
if (plugin.runtime
|
|
69
|
-
// The
|
|
70
|
-
// Ed25519 envelope + a validity window +
|
|
71
|
-
// from the manifest-only checks so the
|
|
72
|
-
|
|
76
|
+
if (SIGNED_DYNAMIC_RUNTIMES.has(plugin.runtime)) {
|
|
77
|
+
// The dynamic-loading path (worker-isolated 1.0 / process-isolated 1.1): a
|
|
78
|
+
// separate, stricter contract (signed Ed25519 envelope + a validity window +
|
|
79
|
+
// authProvider-only). Kept apart from the manifest-only checks so the
|
|
80
|
+
// historical path is untouched.
|
|
81
|
+
validateSignedDynamicManifest(plugin, errors);
|
|
73
82
|
} else {
|
|
74
83
|
// manifest-only (and any other declared-but-rejected runtime): the
|
|
75
84
|
// historical, behavior-preserving contract — UNCHANGED.
|
|
@@ -103,13 +112,14 @@ export function validatePluginManifest(manifest) {
|
|
|
103
112
|
};
|
|
104
113
|
}
|
|
105
114
|
|
|
106
|
-
// The worker-isolated
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
// fields / validity window / readsCredentials, is
|
|
110
|
-
|
|
115
|
+
// The signed-dynamic runtimes (worker-isolated 1.0 / process-isolated 1.1) are
|
|
116
|
+
// dynamic code-loading; both are permitted ONLY for kind authProvider and ONLY
|
|
117
|
+
// with the Ed25519 signed envelope fields. A manifest that is not an authProvider,
|
|
118
|
+
// or is missing the signed fields / validity window / readsCredentials, is
|
|
119
|
+
// rejected with a clear error. The two runtimes share this identical contract.
|
|
120
|
+
function validateSignedDynamicManifest(plugin, errors) {
|
|
111
121
|
if (plugin.kind !== "authProvider") {
|
|
112
|
-
errors.push(
|
|
122
|
+
errors.push(`${plugin.runtime} runtime is only supported for kind authProvider`);
|
|
113
123
|
}
|
|
114
124
|
|
|
115
125
|
// The signed-envelope fields that bind authorship and the exact entry bytes.
|
|
@@ -131,14 +141,14 @@ function validateWorkerIsolatedManifest(plugin, errors) {
|
|
|
131
141
|
const hasNotBefore = plugin.notBefore !== undefined && plugin.notBefore !== null;
|
|
132
142
|
const hasNotAfter = plugin.notAfter !== undefined && plugin.notAfter !== null;
|
|
133
143
|
if (!hasNotBefore && !hasNotAfter) {
|
|
134
|
-
errors.push(
|
|
144
|
+
errors.push(`${plugin.runtime} manifest requires a validity window (notBefore and/or notAfter)`);
|
|
135
145
|
}
|
|
136
146
|
|
|
137
147
|
if (!plugin.capabilities || typeof plugin.capabilities !== "object" || Array.isArray(plugin.capabilities)) {
|
|
138
148
|
errors.push("missing capabilities");
|
|
139
149
|
} else if (plugin.capabilities.readsCredentials !== true) {
|
|
140
150
|
// An authProvider sees the bearer token, so it MUST declare readsCredentials.
|
|
141
|
-
errors.push(
|
|
151
|
+
errors.push(`${plugin.runtime} authProvider must declare capabilities.readsCredentials = true`);
|
|
142
152
|
}
|
|
143
153
|
}
|
|
144
154
|
|