@antelopejs/dms-frontend 0.1.5 → 0.1.7
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/dist/workspace-setup.js
CHANGED
|
@@ -9,6 +9,7 @@ exports.setupWorkspace = setupWorkspace;
|
|
|
9
9
|
const node_child_process_1 = require("node:child_process");
|
|
10
10
|
const node_crypto_1 = require("node:crypto");
|
|
11
11
|
const node_fs_1 = require("node:fs");
|
|
12
|
+
const node_os_1 = require("node:os");
|
|
12
13
|
const node_path_1 = require("node:path");
|
|
13
14
|
const layers_1 = require("./layers");
|
|
14
15
|
const manifest_1 = require("./manifest");
|
|
@@ -50,15 +51,77 @@ function installDeps(workspaceDir) {
|
|
|
50
51
|
stdio: "inherit",
|
|
51
52
|
});
|
|
52
53
|
}
|
|
54
|
+
const FORWARDED_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
55
|
+
const CHILD_SHUTDOWN_TIMEOUT_MS = 5000;
|
|
56
|
+
function exitCodeForSignal(signal) {
|
|
57
|
+
const number = node_os_1.constants.signals[signal];
|
|
58
|
+
return 128 + (typeof number === "number" ? number : 15);
|
|
59
|
+
}
|
|
53
60
|
function runCommand(command, args, options) {
|
|
61
|
+
const isWindows = process.platform === "win32";
|
|
54
62
|
return new Promise((resolve, reject) => {
|
|
55
63
|
const child = (0, node_child_process_1.spawn)(command, args, {
|
|
56
64
|
stdio: "inherit",
|
|
57
|
-
shell:
|
|
65
|
+
shell: isWindows,
|
|
58
66
|
...options,
|
|
59
67
|
});
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
let escalation;
|
|
69
|
+
let forwarded;
|
|
70
|
+
const alive = () => child.exitCode === null && child.signalCode === null;
|
|
71
|
+
const stopChild = (signal) => {
|
|
72
|
+
if (!alive())
|
|
73
|
+
return;
|
|
74
|
+
try {
|
|
75
|
+
child.kill(isWindows ? "SIGTERM" : signal);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
}
|
|
79
|
+
if (escalation || isWindows)
|
|
80
|
+
return;
|
|
81
|
+
escalation = setTimeout(() => {
|
|
82
|
+
if (alive()) {
|
|
83
|
+
try {
|
|
84
|
+
child.kill("SIGKILL");
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}, CHILD_SHUTDOWN_TIMEOUT_MS);
|
|
90
|
+
escalation.unref();
|
|
91
|
+
};
|
|
92
|
+
const onSignal = (signal) => {
|
|
93
|
+
forwarded = signal;
|
|
94
|
+
stopChild(signal);
|
|
95
|
+
};
|
|
96
|
+
const onParentExit = () => {
|
|
97
|
+
if (alive()) {
|
|
98
|
+
try {
|
|
99
|
+
child.kill("SIGKILL");
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
for (const signal of FORWARDED_SIGNALS)
|
|
106
|
+
process.on(signal, onSignal);
|
|
107
|
+
process.on("exit", onParentExit);
|
|
108
|
+
const cleanup = () => {
|
|
109
|
+
if (escalation)
|
|
110
|
+
clearTimeout(escalation);
|
|
111
|
+
for (const signal of FORWARDED_SIGNALS)
|
|
112
|
+
process.off(signal, onSignal);
|
|
113
|
+
process.off("exit", onParentExit);
|
|
114
|
+
};
|
|
115
|
+
child.on("error", (error) => {
|
|
116
|
+
cleanup();
|
|
117
|
+
reject(error);
|
|
118
|
+
});
|
|
119
|
+
child.on("exit", (code, signal) => {
|
|
120
|
+
cleanup();
|
|
121
|
+
if (code !== null)
|
|
122
|
+
return resolve(code);
|
|
123
|
+
resolve(exitCodeForSignal(signal ?? forwarded ?? "SIGTERM"));
|
|
124
|
+
});
|
|
62
125
|
});
|
|
63
126
|
}
|
|
64
127
|
async function setupWorkspace(opts) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/dms-frontend",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Frontend-agnostic loader for AntelopeJS DMS, shipping the Vue 3 renderer (Vite, Inertia, SSR)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelope",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"vue-i18n": "^11.2.7"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
|
-
"@antelopejs/core": ">=1.
|
|
85
|
+
"@antelopejs/core": ">=1.7.0 <2"
|
|
86
86
|
},
|
|
87
87
|
"peerDependenciesMeta": {
|
|
88
88
|
"@antelopejs/core": {
|
|
@@ -50,3 +50,17 @@ export function clientIp(request) {
|
|
|
50
50
|
const chain = [...forwarded, socketIp];
|
|
51
51
|
return chain[Math.max(0, chain.length - hops - 1)] ?? socketIp;
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Body answered with the 403 of the CSRF origin check.
|
|
56
|
+
*
|
|
57
|
+
* The check itself is unchanged: a state-changing request must carry an
|
|
58
|
+
* `Origin` matching the frontend's own. The `reason` only makes the refusal
|
|
59
|
+
* diagnosable, since a bare `{"error":"Forbidden"}` on `POST /auth/login`
|
|
60
|
+
* looks exactly like bad credentials to an API client that simply forgot
|
|
61
|
+
* the header (curl, a server-side integration, a test harness).
|
|
62
|
+
*/
|
|
63
|
+
export const CROSS_ORIGIN_ERROR = Object.freeze({
|
|
64
|
+
error: "Forbidden",
|
|
65
|
+
reason: "missing or untrusted Origin header",
|
|
66
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { backend, body, json, UpstreamError } from "./backend.mjs";
|
|
3
|
-
import { isSameOrigin } from "./client-ip.mjs";
|
|
3
|
+
import { CROSS_ORIGIN_ERROR, isSameOrigin } from "./client-ip.mjs";
|
|
4
4
|
import {
|
|
5
5
|
oauthCallback,
|
|
6
6
|
oauthHandoff,
|
|
@@ -288,8 +288,7 @@ export async function handleAuth(request, response, url) {
|
|
|
288
288
|
response.setHeader("allow", "GET, POST, DELETE");
|
|
289
289
|
return json(response, 405, { error: "Method Not Allowed" });
|
|
290
290
|
}
|
|
291
|
-
if (!isSameOrigin(request))
|
|
292
|
-
return json(response, 403, { error: "Forbidden" });
|
|
291
|
+
if (!isSameOrigin(request)) return json(response, 403, CROSS_ORIGIN_ERROR);
|
|
293
292
|
return request.method === "DELETE"
|
|
294
293
|
? logout(request, response)
|
|
295
294
|
: refresh(request, response);
|
|
@@ -301,8 +300,9 @@ export async function handleAuth(request, response, url) {
|
|
|
301
300
|
return match[2] === "start"
|
|
302
301
|
? oauthStart(request, response, match[1], url)
|
|
303
302
|
: oauthCallback(request, response, match[1], url);
|
|
304
|
-
if (request.method !== "POST"
|
|
305
|
-
return json(response, 403, { error: "Forbidden" });
|
|
303
|
+
if (request.method !== "POST")
|
|
304
|
+
return json(response, 403, { error: "Forbidden", reason: "POST required" });
|
|
305
|
+
if (!isSameOrigin(request)) return json(response, 403, CROSS_ORIGIN_ERROR);
|
|
306
306
|
if (url.pathname === "/auth/oauth/handoff")
|
|
307
307
|
return oauthHandoff(request, response);
|
|
308
308
|
const endpoint = PASSTHROUGH.get(url.pathname);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { body, json, RequestBodyError } from "./auth/backend.mjs";
|
|
2
|
-
import { isSameOrigin } from "./auth/client-ip.mjs";
|
|
2
|
+
import { CROSS_ORIGIN_ERROR, isSameOrigin } from "./auth/client-ip.mjs";
|
|
3
3
|
import { readSession } from "./auth/session.mjs";
|
|
4
4
|
|
|
5
5
|
const RESPONSE_LIMIT = 64 * 1024;
|
|
@@ -206,8 +206,7 @@ export async function handleTester(request, response) {
|
|
|
206
206
|
response.setHeader("allow", "POST");
|
|
207
207
|
return json(response, 405, { error: "Method Not Allowed" });
|
|
208
208
|
}
|
|
209
|
-
if (!isSameOrigin(request))
|
|
210
|
-
return json(response, 403, { error: "Forbidden" });
|
|
209
|
+
if (!isSameOrigin(request)) return json(response, 403, CROSS_ORIGIN_ERROR);
|
|
211
210
|
try {
|
|
212
211
|
const session = readSession(request);
|
|
213
212
|
if (!session?.accessToken)
|
package/templates/vue/server.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "node:zlib";
|
|
12
12
|
import { ofetch } from "ofetch";
|
|
13
13
|
import { RequestBodyError, UpstreamError } from "./server/auth/backend.mjs";
|
|
14
|
-
import { isSameOrigin } from "./server/auth/client-ip.mjs";
|
|
14
|
+
import { CROSS_ORIGIN_ERROR, isSameOrigin } from "./server/auth/client-ip.mjs";
|
|
15
15
|
import {
|
|
16
16
|
handleAuth,
|
|
17
17
|
publicSession,
|
|
@@ -242,7 +242,7 @@ export function requestOwnership(method, pathname) {
|
|
|
242
242
|
async function proxy(request, response, fallbackOnNotFound = false) {
|
|
243
243
|
if (!SAFE_METHODS.has(request.method) && !isSameOrigin(request)) {
|
|
244
244
|
response.writeHead(403, { "content-type": JSON_TYPE });
|
|
245
|
-
response.end(JSON.stringify(
|
|
245
|
+
response.end(JSON.stringify(CROSS_ORIGIN_ERROR));
|
|
246
246
|
return true;
|
|
247
247
|
}
|
|
248
248
|
const target = new URL(request.url, process.env.DMS_API_BASE_URL);
|