@segmentstream-cloud/app 0.16.1 → 0.16.2
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/decision-pages.d.ts +2 -1
- package/dist/decision-pages.js +42 -5
- package/dist/handlers.js +2 -2
- package/package.json +1 -1
package/dist/decision-pages.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { type SegmentStreamProxy } from "./proxy.js";
|
|
|
7
7
|
export declare function withUnframeablePages(proxy: SegmentStreamProxy): SegmentStreamProxy;
|
|
8
8
|
/**
|
|
9
9
|
* Whether a form post came from the app's own pages. A decision must never be made from another
|
|
10
|
-
* site, which could otherwise approve an agent in the member's name.
|
|
10
|
+
* site, which could otherwise approve an agent in the member's name. Fetch Metadata describes
|
|
11
|
+
* the browser's public request even when a trusted proxy rewrites the server's request URL.
|
|
11
12
|
*/
|
|
12
13
|
export declare function isSameOrigin(request: Request): boolean;
|
package/dist/decision-pages.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isLoopbackHostname } from "./cookies.js";
|
|
1
2
|
import { AGENT_CONNECT_PAGE, CONSENT_PAGE } from "./pages.js";
|
|
2
3
|
import { matchesPathPrefix } from "./proxy.js";
|
|
3
4
|
/** Pages where a signed-in member lets an agent act for them. */
|
|
@@ -26,12 +27,48 @@ export function withUnframeablePages(proxy) {
|
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
28
29
|
* Whether a form post came from the app's own pages. A decision must never be made from another
|
|
29
|
-
* site, which could otherwise approve an agent in the member's name.
|
|
30
|
+
* site, which could otherwise approve an agent in the member's name. Fetch Metadata describes
|
|
31
|
+
* the browser's public request even when a trusted proxy rewrites the server's request URL.
|
|
30
32
|
*/
|
|
31
33
|
export function isSameOrigin(request) {
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
return
|
|
34
|
+
const site = request.headers.get("sec-fetch-site");
|
|
35
|
+
if (site !== null) {
|
|
36
|
+
return site === "same-origin";
|
|
35
37
|
}
|
|
36
|
-
|
|
38
|
+
const supplied = request.headers.get("origin");
|
|
39
|
+
const origin = supplied === null ? undefined : httpOrigin(supplied);
|
|
40
|
+
if (origin === undefined) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (origin === new URL(request.url).origin) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
const host = (request.headers.get("x-forwarded-host") ?? request.headers.get("host"))
|
|
47
|
+
?.split(",")[0]
|
|
48
|
+
?.trim();
|
|
49
|
+
if (!host) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const forwardedProto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim();
|
|
53
|
+
const protocol = forwardedProto === "http" || forwardedProto === "https"
|
|
54
|
+
? forwardedProto
|
|
55
|
+
: isLoopbackHostname(host.replace(/:\d+$/, ""))
|
|
56
|
+
? "http"
|
|
57
|
+
: "https";
|
|
58
|
+
return origin === httpOrigin(`${protocol}://${host}`);
|
|
59
|
+
}
|
|
60
|
+
/** An Origin names only an HTTP(S) authority, never credentials, a path or query. */
|
|
61
|
+
function httpOrigin(value) {
|
|
62
|
+
if (!URL.canParse(value)) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const url = new URL(value);
|
|
66
|
+
return (url.protocol === "http:" || url.protocol === "https:") &&
|
|
67
|
+
url.username === "" &&
|
|
68
|
+
url.password === "" &&
|
|
69
|
+
url.pathname === "/" &&
|
|
70
|
+
url.search === "" &&
|
|
71
|
+
url.hash === ""
|
|
72
|
+
? url.origin
|
|
73
|
+
: undefined;
|
|
37
74
|
}
|
package/dist/handlers.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
import { authFlowCookieName, clearCookie, clearSessionCookies, cookiePolicyFor, decodeAuthFlowState, pendingAuthFlowCookies, safeReturnTo, setSessionCookies, } from "./cookies.js";
|
|
3
|
+
import { isSameOrigin } from "./decision-pages.js";
|
|
3
4
|
import { constantTimeEqual } from "./pkce.js";
|
|
4
5
|
import { CALLBACK_PATH, SIGNED_OUT_PATH, SIGN_IN_PATH, SDK_ROUTE_PREFIX, signInRedirect, } from "./sign-in.js";
|
|
5
6
|
export function createHandlers(context) {
|
|
@@ -92,8 +93,7 @@ function returnToApp(request, policy, flowCookie, returnTo) {
|
|
|
92
93
|
return response;
|
|
93
94
|
}
|
|
94
95
|
async function handleSignOut(context, request) {
|
|
95
|
-
|
|
96
|
-
if (origin !== null && origin !== request.nextUrl.origin) {
|
|
96
|
+
if (!isSameOrigin(request)) {
|
|
97
97
|
return htmlResponse(403, "Sign-out refused", "Sign out from this app's own pages.", "Return to the app");
|
|
98
98
|
}
|
|
99
99
|
const policy = cookiePolicyFor(request.nextUrl, context.config.workspaceId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@segmentstream-cloud/app",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
4
4
|
"description": "Sign-in, users, permissions, the ontology client and warehouse data access for SegmentStream workspace apps built with Next.js.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "SegmentStream",
|