@decocms/blocks-admin 7.5.1 → 7.5.3
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/package.json +2 -2
- package/src/admin/decofile.test.ts +65 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/blocks-admin",
|
|
3
|
-
"version": "7.5.
|
|
3
|
+
"version": "7.5.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Deco admin protocol: /live/_meta, /.decofile, /deco/invoke, /deco/render, plus admin-facing site setup and commerce-app auto-configuration",
|
|
6
6
|
"repository": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"lint:unused": "knip"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@decocms/blocks": "7.5.
|
|
28
|
+
"@decocms/blocks": "7.5.3"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"react": "^19.0.0",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
1
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
2
2
|
import { KV_KEYS, type KVNamespace, getRevision, loadBlocks, setBlocks } from "@decocms/blocks/cms";
|
|
3
3
|
import { handleDecofileReload, setFastDeployKVGetter } from "./decofile";
|
|
4
4
|
|
|
@@ -154,3 +154,67 @@ describe("handleDecofileReload — validation", () => {
|
|
|
154
154
|
expect(res.status).toBe(400);
|
|
155
155
|
});
|
|
156
156
|
});
|
|
157
|
+
|
|
158
|
+
describe("handleDecofileReload — auth gate outside dev (fail-closed)", () => {
|
|
159
|
+
// These tests deliberately leave the file-level NODE_ENV=development
|
|
160
|
+
// override and simulate the production posture per-test: the reload
|
|
161
|
+
// endpoint is DESTRUCTIVE (a posted body fully replaces the in-memory
|
|
162
|
+
// registry), and the dev bypass predicate changed once already
|
|
163
|
+
// (import.meta.env.DEV -> NODE_ENV) — this suite pins the fail-closed
|
|
164
|
+
// side so the next predicate change can't silently open it.
|
|
165
|
+
const DEV_VALUE = process.env.NODE_ENV;
|
|
166
|
+
|
|
167
|
+
function reloadWithAuth(authHeader: string | null, env?: Record<string, unknown>) {
|
|
168
|
+
const headers: Record<string, string> = { "Content-Type": "application/json" };
|
|
169
|
+
if (authHeader !== null) headers.Authorization = authHeader;
|
|
170
|
+
const req = new Request("http://x/.decofile", {
|
|
171
|
+
method: "POST",
|
|
172
|
+
body: JSON.stringify({ Site: { name: "attacker" } }),
|
|
173
|
+
headers,
|
|
174
|
+
});
|
|
175
|
+
return handleDecofileReload(req, env);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
beforeEach(() => {
|
|
179
|
+
process.env.NODE_ENV = "production";
|
|
180
|
+
});
|
|
181
|
+
afterEach(() => {
|
|
182
|
+
process.env.NODE_ENV = DEV_VALUE;
|
|
183
|
+
delete process.env.DECO_RELEASE_RELOAD_TOKEN;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("401s with no Authorization header and leaves the registry untouched", async () => {
|
|
187
|
+
const res = await reloadWithAuth(null, {});
|
|
188
|
+
expect(res.status).toBe(401);
|
|
189
|
+
expect((loadBlocks().Site as { name: string }).name).toBe("base");
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("401s when NO reload token is configured at all (fail-closed, not fail-open)", async () => {
|
|
193
|
+
const res = await reloadWithAuth("anything", {});
|
|
194
|
+
expect(res.status).toBe(401);
|
|
195
|
+
expect((loadBlocks().Site as { name: string }).name).toBe("base");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("401s on a wrong token and leaves the registry untouched", async () => {
|
|
199
|
+
const res = await reloadWithAuth("wrong-token", {
|
|
200
|
+
DECO_RELEASE_RELOAD_TOKEN: "right-token",
|
|
201
|
+
});
|
|
202
|
+
expect(res.status).toBe(401);
|
|
203
|
+
expect((loadBlocks().Site as { name: string }).name).toBe("base");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("401s when NODE_ENV is unset (fail-closed by default)", async () => {
|
|
207
|
+
delete process.env.NODE_ENV;
|
|
208
|
+
const res = await reloadWithAuth(null, {});
|
|
209
|
+
expect(res.status).toBe(401);
|
|
210
|
+
expect((loadBlocks().Site as { name: string }).name).toBe("base");
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("accepts the correct token and performs the replacement", async () => {
|
|
214
|
+
const res = await reloadWithAuth("right-token", {
|
|
215
|
+
DECO_RELEASE_RELOAD_TOKEN: "right-token",
|
|
216
|
+
});
|
|
217
|
+
expect(res.status).toBe(200);
|
|
218
|
+
expect((loadBlocks().Site as { name: string }).name).toBe("attacker");
|
|
219
|
+
});
|
|
220
|
+
});
|