@baique/pi-web-sky 0.1.48 → 0.1.49
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/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +17 -17
- package/.next/build-manifest.json +2 -2
- package/.next/required-server-files.js +1 -1
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/api/agent/[id]/bash-output/route.js +2 -2
- package/.next/server/app/api/app-update/route.js +1 -1
- package/.next/server/app/api/board-assets/[...path]/route.js +2 -2
- package/.next/server/app/api/board-assets/route.js +2 -2
- package/.next/server/app/api/boards/reorder/route.js +2 -2
- package/.next/server/app/api/boards/route.js +2 -2
- package/.next/server/app/api/cwd/validate/route.js +2 -2
- package/.next/server/app/api/default-cwd/route.js +2 -2
- package/.next/server/app/api/file-index/route.js +2 -2
- package/.next/server/app/api/files/[...path]/route.js +2 -2
- package/.next/server/app/api/mcp/route.js +2 -2
- package/.next/server/app/api/models/route.js +2 -2
- package/.next/server/app/api/plugins/route.js +2 -2
- package/.next/server/app/api/search/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/export/route.js +2 -2
- package/.next/server/app/api/sessions/summary/route.js +2 -2
- package/.next/server/app/api/skills/install/route.js +2 -2
- package/.next/server/app/api/task-card-questions/[id]/answer/route.js +1 -1
- package/.next/server/app/api/task-card-questions/route.js +1 -1
- package/.next/server/app/api/tasks/[id]/assign-session/route.js +2 -2
- package/.next/server/app/api/tasks/reorder/route.js +2 -2
- package/.next/server/app/api/tasks/route.js +2 -2
- package/.next/server/app/api/worktrees/route.js +2 -2
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +4 -4
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +17 -17
- package/.next/server/chunks/3500.js +2 -2
- package/.next/server/chunks/4226.js +2 -2
- package/.next/server/chunks/5794.js +2 -2
- package/.next/server/chunks/5940.js +2 -2
- package/.next/server/chunks/6525.js +2 -2
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/static/chunks/app/{layout-34c3afa7e57644fe.js → layout-eb4df7493f8710df.js} +1 -1
- package/.next/static/chunks/app/{page-72aa0bf9f2c8b93d.js → page-6ff5ffc29de8ba5f.js} +1 -1
- package/.next/trace +7 -7
- package/.next/trace-build +1 -1
- package/bin/pi-web-options.js +1 -1
- package/lib/pi-web-options.test.mjs +3 -3
- package/lib/project-command-env.test.mjs +4 -4
- package/lib/request-security.test.mjs +50 -50
- package/lib/request-security.ts +1 -1
- package/lib/sqlite-db.ts +5 -0
- package/package.json +1 -1
- /package/.next/static/{m9gWh8uFYWdcMxBdc3tY5 → -gLK6QyBXk9SPd4ajO5tR}/_buildManifest.js +0 -0
- /package/.next/static/{m9gWh8uFYWdcMxBdc3tY5 → -gLK6QyBXk9SPd4ajO5tR}/_ssgManifest.js +0 -0
package/.next/trace-build
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"name":"run-webpack","duration":
|
|
1
|
+
[{"name":"run-webpack","duration":8981522,"timestamp":768123845995,"id":14,"parentId":1,"tags":{},"startTime":1789030615100,"traceId":"ff0372cbd3f70d5f"},{"name":"run-typescript","duration":8270408,"timestamp":768132829087,"id":772,"parentId":1,"tags":{},"startTime":1789030624083,"traceId":"ff0372cbd3f70d5f"},{"name":"static-check","duration":1960226,"timestamp":768141106442,"id":775,"parentId":1,"tags":{},"startTime":1789030632360,"traceId":"ff0372cbd3f70d5f"},{"name":"static-generation","duration":2740074,"timestamp":768143204314,"id":919,"parentId":1,"tags":{},"startTime":1789030634458,"traceId":"ff0372cbd3f70d5f"},{"name":"collect-build-traces","duration":16174753,"timestamp":768143068236,"id":916,"parentId":1,"tags":{},"startTime":1789030634322,"traceId":"ff0372cbd3f70d5f"},{"name":"telemetry-flush","duration":20,"timestamp":768159244994,"id":928,"parentId":1,"tags":{},"startTime":1789030650499,"traceId":"ff0372cbd3f70d5f"},{"name":"next-build","duration":35492403,"timestamp":768123752616,"id":1,"tags":{"buildMode":"default","version":"16.3.1","bundler":"webpack","has-custom-webpack-config":"false","use-build-worker":"true"},"startTime":1789030615007,"traceId":"ff0372cbd3f70d5f"}]
|
package/bin/pi-web-options.js
CHANGED
|
@@ -34,7 +34,7 @@ function parseLaunchOptions(args = process.argv.slice(2), env = process.env) {
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
return {
|
|
37
|
-
port: normalizePort(cliArgs.port ?? env.PORT ?? "
|
|
37
|
+
port: normalizePort(cliArgs.port ?? env.PORT ?? "30143"),
|
|
38
38
|
hostname: cliArgs.hostname ?? env.PI_WEB_HOSTNAME ?? "127.0.0.1",
|
|
39
39
|
openBrowser: !cliArgs["no-open"] && !isEnabled(env.PI_WEB_NO_OPEN),
|
|
40
40
|
};
|
|
@@ -7,7 +7,7 @@ const { parseLaunchOptions } = require("../bin/pi-web-options.js");
|
|
|
7
7
|
|
|
8
8
|
test("opens the browser by default", () => {
|
|
9
9
|
assert.deepEqual(parseLaunchOptions([], {}), {
|
|
10
|
-
port: "
|
|
10
|
+
port: "30143",
|
|
11
11
|
hostname: "127.0.0.1",
|
|
12
12
|
openBrowser: true,
|
|
13
13
|
});
|
|
@@ -42,11 +42,11 @@ test("preserves port and hostname options", () => {
|
|
|
42
42
|
|
|
43
43
|
test("rejects port values that could inject cmd arguments", () => {
|
|
44
44
|
assert.throws(
|
|
45
|
-
() => parseLaunchOptions(["-p", "
|
|
45
|
+
() => parseLaunchOptions(["-p", "30143&whoami"], {}),
|
|
46
46
|
/Port must be a non-negative integer/,
|
|
47
47
|
);
|
|
48
48
|
assert.throws(
|
|
49
|
-
() => parseLaunchOptions([], { PORT: "
|
|
49
|
+
() => parseLaunchOptions([], { PORT: "30143&whoami" }),
|
|
50
50
|
/Port must be a non-negative integer/,
|
|
51
51
|
);
|
|
52
52
|
});
|
|
@@ -16,7 +16,7 @@ const {
|
|
|
16
16
|
} = await createJiti(import.meta.url).import("./project-command-env.ts");
|
|
17
17
|
|
|
18
18
|
const HOST_ENVIRONMENT = {
|
|
19
|
-
PORT: "
|
|
19
|
+
PORT: "30143",
|
|
20
20
|
NODE_ENV: "production",
|
|
21
21
|
NEXT_RUNTIME: "nodejs",
|
|
22
22
|
NEXT_PRIVATE_WORKER: "1",
|
|
@@ -41,7 +41,7 @@ test("sanitizes host variables using platform casing rules", () => {
|
|
|
41
41
|
assert.deepEqual(
|
|
42
42
|
sanitizeProjectCommandEnvironment(
|
|
43
43
|
{
|
|
44
|
-
Port: "
|
|
44
|
+
Port: "30143",
|
|
45
45
|
node_env: "production",
|
|
46
46
|
Next_Runtime: "nodejs",
|
|
47
47
|
NEXT_PUBLIC_FLAG: "1",
|
|
@@ -54,7 +54,7 @@ test("sanitizes host variables using platform casing rules", () => {
|
|
|
54
54
|
assert.deepEqual(
|
|
55
55
|
sanitizeProjectCommandEnvironment(
|
|
56
56
|
{
|
|
57
|
-
PORT: "
|
|
57
|
+
PORT: "30143",
|
|
58
58
|
Port: "project-value",
|
|
59
59
|
NODE_ENV: "production",
|
|
60
60
|
node_env: "project-mode",
|
|
@@ -80,7 +80,7 @@ test("agent bash removes host variables while preserving SDK and user environmen
|
|
|
80
80
|
PI_USER_SETTING: process.env.PI_USER_SETTING,
|
|
81
81
|
};
|
|
82
82
|
Object.assign(process.env, {
|
|
83
|
-
PORT: "
|
|
83
|
+
PORT: "30143",
|
|
84
84
|
NODE_ENV: "production",
|
|
85
85
|
NEXT_RUNTIME: "nodejs",
|
|
86
86
|
NEXT_PRIVATE_WORKER: "1",
|
|
@@ -7,27 +7,27 @@ async function loadSubject() {
|
|
|
7
7
|
|
|
8
8
|
test("allows same-origin and non-browser API requests", async () => {
|
|
9
9
|
const { isApiRequestAllowed } = await loadSubject();
|
|
10
|
-
assert.equal(isApiRequestAllowed(new Request("http://localhost:
|
|
10
|
+
assert.equal(isApiRequestAllowed(new Request("http://localhost:30143/api/test", {
|
|
11
11
|
method: "POST",
|
|
12
12
|
headers: {
|
|
13
|
-
host: "localhost:
|
|
14
|
-
origin: "http://localhost:
|
|
13
|
+
host: "localhost:30143",
|
|
14
|
+
origin: "http://localhost:30143",
|
|
15
15
|
"sec-fetch-site": "same-origin",
|
|
16
16
|
},
|
|
17
17
|
})), true);
|
|
18
|
-
assert.equal(isApiRequestAllowed(new Request("http://localhost:
|
|
18
|
+
assert.equal(isApiRequestAllowed(new Request("http://localhost:30143/api/test", {
|
|
19
19
|
method: "POST",
|
|
20
|
-
headers: { host: "localhost:
|
|
20
|
+
headers: { host: "localhost:30143" },
|
|
21
21
|
})), true);
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
test("allows LAN same-origin requests when Next.js uses an internal localhost URL", async () => {
|
|
25
25
|
const { isApiRequestAllowed } = await loadSubject();
|
|
26
|
-
const request = new Request("http://localhost:
|
|
26
|
+
const request = new Request("http://localhost:30143/api/test", {
|
|
27
27
|
method: "POST",
|
|
28
28
|
headers: {
|
|
29
|
-
host: "192.168.32.7:
|
|
30
|
-
origin: "http://192.168.32.7:
|
|
29
|
+
host: "192.168.32.7:30143",
|
|
30
|
+
origin: "http://192.168.32.7:30143",
|
|
31
31
|
"sec-fetch-site": "same-origin",
|
|
32
32
|
},
|
|
33
33
|
});
|
|
@@ -36,19 +36,19 @@ test("allows LAN same-origin requests when Next.js uses an internal localhost UR
|
|
|
36
36
|
|
|
37
37
|
test("allows IPv6 and an explicitly configured hostname", async () => {
|
|
38
38
|
const { isApiRequestAllowed } = await loadSubject();
|
|
39
|
-
const ipv6 = new Request("http://localhost:
|
|
39
|
+
const ipv6 = new Request("http://localhost:30143/api/test", {
|
|
40
40
|
method: "POST",
|
|
41
41
|
headers: {
|
|
42
|
-
host: "[::1]:
|
|
43
|
-
origin: "http://[::1]:
|
|
42
|
+
host: "[::1]:30143",
|
|
43
|
+
origin: "http://[::1]:30143",
|
|
44
44
|
"sec-fetch-site": "same-origin",
|
|
45
45
|
},
|
|
46
46
|
});
|
|
47
|
-
const configured = new Request("http://localhost:
|
|
47
|
+
const configured = new Request("http://localhost:30143/api/test", {
|
|
48
48
|
method: "POST",
|
|
49
49
|
headers: {
|
|
50
|
-
host: "pi-web.internal:
|
|
51
|
-
origin: "http://pi-web.internal:
|
|
50
|
+
host: "pi-web.internal:30143",
|
|
51
|
+
origin: "http://pi-web.internal:30143",
|
|
52
52
|
"sec-fetch-site": "same-origin",
|
|
53
53
|
},
|
|
54
54
|
});
|
|
@@ -58,16 +58,16 @@ test("allows IPv6 and an explicitly configured hostname", async () => {
|
|
|
58
58
|
|
|
59
59
|
test("rejects cross-origin browser API requests", async () => {
|
|
60
60
|
const { isApiRequestAllowed, shouldCheckApiRequestOrigin } = await loadSubject();
|
|
61
|
-
const post = new Request("http://localhost:
|
|
61
|
+
const post = new Request("http://localhost:30143/api/test", {
|
|
62
62
|
method: "POST",
|
|
63
63
|
headers: {
|
|
64
|
-
host: "localhost:
|
|
64
|
+
host: "localhost:30143",
|
|
65
65
|
origin: "https://attacker.example",
|
|
66
66
|
"sec-fetch-site": "cross-site",
|
|
67
67
|
},
|
|
68
68
|
});
|
|
69
|
-
const crossSiteGet = new Request("http://localhost:
|
|
70
|
-
headers: { host: "localhost:
|
|
69
|
+
const crossSiteGet = new Request("http://localhost:30143/api/sessions", {
|
|
70
|
+
headers: { host: "localhost:30143", "sec-fetch-site": "cross-site" },
|
|
71
71
|
});
|
|
72
72
|
assert.equal(shouldCheckApiRequestOrigin(post), true);
|
|
73
73
|
assert.equal(isApiRequestAllowed(post), false);
|
|
@@ -77,10 +77,10 @@ test("rejects cross-origin browser API requests", async () => {
|
|
|
77
77
|
|
|
78
78
|
test("does not globally trust opaque iframe or alternate loopback origins", async () => {
|
|
79
79
|
const { isApiRequestAllowed } = await loadSubject();
|
|
80
|
-
const previewUrl = "http://localhost:
|
|
80
|
+
const previewUrl = "http://localhost:30143/api/files/tmp/test.docx?type=preview";
|
|
81
81
|
const opaqueIframe = new Request(previewUrl, {
|
|
82
82
|
headers: {
|
|
83
|
-
host: "localhost:
|
|
83
|
+
host: "localhost:30143",
|
|
84
84
|
origin: "null",
|
|
85
85
|
"sec-fetch-site": "cross-site",
|
|
86
86
|
"sec-fetch-mode": "navigate",
|
|
@@ -89,8 +89,8 @@ test("does not globally trust opaque iframe or alternate loopback origins", asyn
|
|
|
89
89
|
});
|
|
90
90
|
const alternateLoopback = new Request(previewUrl, {
|
|
91
91
|
headers: {
|
|
92
|
-
host: "localhost:
|
|
93
|
-
origin: "http://127.0.0.1:
|
|
92
|
+
host: "localhost:30143",
|
|
93
|
+
origin: "http://127.0.0.1:30143",
|
|
94
94
|
"sec-fetch-site": "cross-site",
|
|
95
95
|
},
|
|
96
96
|
});
|
|
@@ -102,7 +102,7 @@ test("does not globally trust opaque iframe or alternate loopback origins", asyn
|
|
|
102
102
|
test("allows only user-initiated session export document navigations from a PWA", async () => {
|
|
103
103
|
const { isApiRequestAllowed } = await loadSubject();
|
|
104
104
|
const navigationHeaders = {
|
|
105
|
-
host: "127.0.0.1:
|
|
105
|
+
host: "127.0.0.1:30143",
|
|
106
106
|
"sec-fetch-site": "cross-site",
|
|
107
107
|
"sec-fetch-mode": "navigate",
|
|
108
108
|
"sec-fetch-dest": "document",
|
|
@@ -110,19 +110,19 @@ test("allows only user-initiated session export document navigations from a PWA"
|
|
|
110
110
|
};
|
|
111
111
|
|
|
112
112
|
assert.equal(isApiRequestAllowed(new Request(
|
|
113
|
-
"http://127.0.0.1:
|
|
113
|
+
"http://127.0.0.1:30143/api/sessions/session-id/export?inline=1",
|
|
114
114
|
{ headers: navigationHeaders },
|
|
115
115
|
)), true);
|
|
116
116
|
assert.equal(isApiRequestAllowed(new Request(
|
|
117
|
-
"http://127.0.0.1:
|
|
117
|
+
"http://127.0.0.1:30143/api/sessions",
|
|
118
118
|
{ headers: navigationHeaders },
|
|
119
119
|
)), false);
|
|
120
120
|
assert.equal(isApiRequestAllowed(new Request(
|
|
121
|
-
"http://127.0.0.1:
|
|
121
|
+
"http://127.0.0.1:30143/api/sessions/session-id/export?inline=1",
|
|
122
122
|
{ headers: { ...navigationHeaders, "sec-fetch-dest": "empty" } },
|
|
123
123
|
)), false);
|
|
124
124
|
assert.equal(isApiRequestAllowed(new Request(
|
|
125
|
-
"http://127.0.0.1:
|
|
125
|
+
"http://127.0.0.1:30143/api/sessions/session-id/export?inline=1",
|
|
126
126
|
{
|
|
127
127
|
headers: {
|
|
128
128
|
...navigationHeaders,
|
|
@@ -131,21 +131,21 @@ test("allows only user-initiated session export document navigations from a PWA"
|
|
|
131
131
|
},
|
|
132
132
|
)), false);
|
|
133
133
|
assert.equal(isApiRequestAllowed(new Request(
|
|
134
|
-
"http://127.0.0.1:
|
|
134
|
+
"http://127.0.0.1:30143/api/sessions/session-id/export?inline=1",
|
|
135
135
|
{ method: "POST", headers: navigationHeaders },
|
|
136
136
|
)), false);
|
|
137
137
|
assert.equal(isApiRequestAllowed(new Request(
|
|
138
|
-
"http://127.0.0.1:
|
|
139
|
-
{ headers: { ...navigationHeaders, host: "attacker.example:
|
|
138
|
+
"http://127.0.0.1:30143/api/sessions/session-id/export?inline=1",
|
|
139
|
+
{ headers: { ...navigationHeaders, host: "attacker.example:30143" } },
|
|
140
140
|
)), false);
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
test("rejects an origin that does not match the external request host", async () => {
|
|
144
144
|
const { isApiRequestAllowed } = await loadSubject();
|
|
145
|
-
const request = new Request("http://localhost:
|
|
145
|
+
const request = new Request("http://localhost:30143/api/test", {
|
|
146
146
|
method: "POST",
|
|
147
147
|
headers: {
|
|
148
|
-
host: "192.168.32.7:
|
|
148
|
+
host: "192.168.32.7:30143",
|
|
149
149
|
origin: "http://attacker.example",
|
|
150
150
|
"sec-fetch-site": "same-site",
|
|
151
151
|
},
|
|
@@ -155,11 +155,11 @@ test("rejects an origin that does not match the external request host", async ()
|
|
|
155
155
|
|
|
156
156
|
test("rejects DNS rebinding even when browser headers say same-origin", async () => {
|
|
157
157
|
const { isApiRequestAllowed } = await loadSubject();
|
|
158
|
-
const request = new Request("http://localhost:
|
|
158
|
+
const request = new Request("http://localhost:30143/api/skills/install", {
|
|
159
159
|
method: "POST",
|
|
160
160
|
headers: {
|
|
161
|
-
host: "attacker.example:
|
|
162
|
-
origin: "http://attacker.example:
|
|
161
|
+
host: "attacker.example:30143",
|
|
162
|
+
origin: "http://attacker.example:30143",
|
|
163
163
|
"sec-fetch-site": "same-origin",
|
|
164
164
|
"content-type": "application/json",
|
|
165
165
|
},
|
|
@@ -169,21 +169,21 @@ test("rejects DNS rebinding even when browser headers say same-origin", async ()
|
|
|
169
169
|
|
|
170
170
|
test("rejects missing, malformed, and unconfigured Host headers", async () => {
|
|
171
171
|
const { isApiRequestAllowed } = await loadSubject();
|
|
172
|
-
assert.equal(isApiRequestAllowed(new Request("http://localhost:
|
|
173
|
-
assert.equal(isApiRequestAllowed(new Request("http://localhost:
|
|
174
|
-
headers: { host: "localhost@attacker.example:
|
|
172
|
+
assert.equal(isApiRequestAllowed(new Request("http://localhost:30143/api/test")), false);
|
|
173
|
+
assert.equal(isApiRequestAllowed(new Request("http://localhost:30143/api/test", {
|
|
174
|
+
headers: { host: "localhost@attacker.example:30143" },
|
|
175
175
|
})), false);
|
|
176
|
-
assert.equal(isApiRequestAllowed(new Request("http://localhost:
|
|
177
|
-
headers: { host: "pi-web.internal:
|
|
176
|
+
assert.equal(isApiRequestAllowed(new Request("http://localhost:30143/api/test", {
|
|
177
|
+
headers: { host: "pi-web.internal:30143" },
|
|
178
178
|
})), false);
|
|
179
179
|
});
|
|
180
180
|
|
|
181
181
|
test("allows same-origin requests when Chromium strips the port from Origin", async () => {
|
|
182
182
|
const { isApiRequestAllowed } = await loadSubject();
|
|
183
183
|
// Loopback IP with the port stripped from Origin (Chromium 150+ behavior).
|
|
184
|
-
const loopback = new Request("http://127.0.0.1:
|
|
184
|
+
const loopback = new Request("http://127.0.0.1:30143/api/agent/running", {
|
|
185
185
|
headers: {
|
|
186
|
-
host: "127.0.0.1:
|
|
186
|
+
host: "127.0.0.1:30143",
|
|
187
187
|
origin: "http://127.0.0.1",
|
|
188
188
|
"sec-fetch-site": "same-origin",
|
|
189
189
|
},
|
|
@@ -191,10 +191,10 @@ test("allows same-origin requests when Chromium strips the port from Origin", as
|
|
|
191
191
|
assert.equal(isApiRequestAllowed(loopback), true);
|
|
192
192
|
|
|
193
193
|
// LAN IP with the port stripped from Origin.
|
|
194
|
-
const lan = new Request("http://192.168.32.7:
|
|
194
|
+
const lan = new Request("http://192.168.32.7:30143/api/test", {
|
|
195
195
|
method: "POST",
|
|
196
196
|
headers: {
|
|
197
|
-
host: "192.168.32.7:
|
|
197
|
+
host: "192.168.32.7:30143",
|
|
198
198
|
origin: "http://192.168.32.7",
|
|
199
199
|
"sec-fetch-site": "same-origin",
|
|
200
200
|
"content-type": "application/json",
|
|
@@ -203,10 +203,10 @@ test("allows same-origin requests when Chromium strips the port from Origin", as
|
|
|
203
203
|
assert.equal(isApiRequestAllowed(lan), true);
|
|
204
204
|
|
|
205
205
|
// Loopback name with the port stripped from Origin.
|
|
206
|
-
const named = new Request("http://localhost:
|
|
206
|
+
const named = new Request("http://localhost:30143/api/test", {
|
|
207
207
|
method: "POST",
|
|
208
208
|
headers: {
|
|
209
|
-
host: "localhost:
|
|
209
|
+
host: "localhost:30143",
|
|
210
210
|
origin: "http://localhost",
|
|
211
211
|
"sec-fetch-site": "same-origin",
|
|
212
212
|
"content-type": "application/json",
|
|
@@ -219,9 +219,9 @@ test("still rejects when Origin hostname differs from Host even with port stripp
|
|
|
219
219
|
const { isApiRequestAllowed } = await loadSubject();
|
|
220
220
|
// Cross-loopback-name origin should not slip through just because both
|
|
221
221
|
// sides happen to be loopback addresses.
|
|
222
|
-
const crossName = new Request("http://localhost:
|
|
222
|
+
const crossName = new Request("http://localhost:30143/api/test", {
|
|
223
223
|
headers: {
|
|
224
|
-
host: "localhost:
|
|
224
|
+
host: "localhost:30143",
|
|
225
225
|
origin: "http://127.0.0.1",
|
|
226
226
|
"sec-fetch-site": "same-origin",
|
|
227
227
|
},
|
|
@@ -230,10 +230,10 @@ test("still rejects when Origin hostname differs from Host even with port stripp
|
|
|
230
230
|
|
|
231
231
|
// Attacker host matching its own Origin must still be blocked by the
|
|
232
232
|
// Host allowlist, not the Origin comparison.
|
|
233
|
-
const dnsRebind = new Request("http://localhost:
|
|
233
|
+
const dnsRebind = new Request("http://localhost:30143/api/skills/install", {
|
|
234
234
|
method: "POST",
|
|
235
235
|
headers: {
|
|
236
|
-
host: "attacker.example:
|
|
236
|
+
host: "attacker.example:30143",
|
|
237
237
|
origin: "http://attacker.example",
|
|
238
238
|
"sec-fetch-site": "same-origin",
|
|
239
239
|
"content-type": "application/json",
|
package/lib/request-security.ts
CHANGED
|
@@ -104,7 +104,7 @@ export function isApiRequestOriginAllowed(request: Request): boolean {
|
|
|
104
104
|
|
|
105
105
|
// Chromium 150+ strips the port from the Origin header for same-origin
|
|
106
106
|
// requests on non-default ports. Strict canonical-origin comparison would
|
|
107
|
-
// therefore reject those legitimate requests ("http://127.0.0.1:
|
|
107
|
+
// therefore reject those legitimate requests ("http://127.0.0.1:30143"
|
|
108
108
|
// vs. the browser-sent "http://127.0.0.1"). The Host header is the
|
|
109
109
|
// authoritative source for where the request actually went, so accept any
|
|
110
110
|
// Origin whose hostname matches it. Hostnames are case-insensitive, which
|
package/lib/sqlite-db.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { mkdirSync } from "fs";
|
|
2
3
|
import { homedir } from "os";
|
|
3
4
|
import { join, normalize } from "path";
|
|
4
5
|
|
|
@@ -40,6 +41,10 @@ export function setDbForTesting(db: DatabaseSync): void {
|
|
|
40
41
|
*/
|
|
41
42
|
export function getDb(): DatabaseSync {
|
|
42
43
|
if (globalThis.__piWebDb) return globalThis.__piWebDb;
|
|
44
|
+
// SQLite 打不开不存在的父目录(unable to open database file)。全新机器上
|
|
45
|
+
// ~/.pi/agent 尚未被 SDK 创建,惰性打开也会炸——与 lib/yjs-room-server.mjs
|
|
46
|
+
// 的顶层打开是同类隐患,打开前必须确保目录存在。
|
|
47
|
+
mkdirSync(getAgentDir(), { recursive: true });
|
|
43
48
|
const db = new DatabaseSync(dbPath());
|
|
44
49
|
db.exec("PRAGMA journal_mode = WAL;");
|
|
45
50
|
db.exec("PRAGMA foreign_keys = ON;");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baique/pi-web-sky",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.49",
|
|
4
4
|
"description": "Web UI for the pi coding agent — Sky skin edition (fork of @agegr/pi-web / earendil-labs pi-web, MIT)",
|
|
5
5
|
"homepage": "https://github.com/baique/pi-web-sky#readme",
|
|
6
6
|
"repository": {
|
|
File without changes
|
|
File without changes
|