artifacty 0.10.3 → 0.10.4
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/docs/network-sharing.md +2 -2
- package/docs/threat-model.md +3 -2
- package/package.json +1 -1
- package/src/server.js +17 -3
package/docs/network-sharing.md
CHANGED
|
@@ -44,9 +44,9 @@ When Artifacty binds outside loopback, startup output includes a warning that th
|
|
|
44
44
|
|
|
45
45
|
## Browser Write Behavior
|
|
46
46
|
|
|
47
|
-
Remote browsers can read shared pages,
|
|
47
|
+
Remote browsers can read shared pages and can submit browser forms when the request is same-origin with the Artifacty host, for example `Origin: http://10.0.0.50:8787` with `Host: 10.0.0.50:8787`. Mutating browser routes reject cross-origin requests to reduce CSRF risk. For scripts and agents, prefer API or MCP writes with an explicit token header.
|
|
48
48
|
|
|
49
|
-
Do not
|
|
49
|
+
Do not disable the origin check just to make remote browser writes easier. Same-origin central dashboard writes should pass; writes initiated from another website should fail with `NON_LOCAL_ORIGIN`.
|
|
50
50
|
|
|
51
51
|
## Renderer Guidance
|
|
52
52
|
|
package/docs/threat-model.md
CHANGED
|
@@ -68,9 +68,10 @@ reachable from the user's browser.
|
|
|
68
68
|
|
|
69
69
|
Controls:
|
|
70
70
|
|
|
71
|
-
- Mutating browser routes
|
|
71
|
+
- Mutating browser routes allow loopback or same-origin requests and reject
|
|
72
|
+
cross-origin writes.
|
|
72
73
|
- API routes require a token when configured.
|
|
73
|
-
- LAN/team mode does not
|
|
74
|
+
- LAN/team mode does not allow arbitrary browser origins.
|
|
74
75
|
|
|
75
76
|
### Untrusted Artifact Rendering
|
|
76
77
|
|
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -816,11 +816,25 @@ export function assertLocalOrigin(request) {
|
|
|
816
816
|
return;
|
|
817
817
|
}
|
|
818
818
|
|
|
819
|
-
|
|
819
|
+
let parsed;
|
|
820
|
+
try {
|
|
821
|
+
parsed = new URL(origin);
|
|
822
|
+
} catch {
|
|
823
|
+
throw Object.assign(new Error(`Rejected invalid origin: ${origin}`), {
|
|
824
|
+
statusCode: 403,
|
|
825
|
+
code: "NON_LOCAL_ORIGIN"
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
|
|
820
829
|
const hostname = parsed.hostname.toLowerCase();
|
|
821
|
-
const
|
|
830
|
+
const host = String(request.headers.host || "").toLowerCase();
|
|
831
|
+
const allowed =
|
|
832
|
+
hostname === "localhost" ||
|
|
833
|
+
hostname === "127.0.0.1" ||
|
|
834
|
+
hostname === "::1" ||
|
|
835
|
+
(host && parsed.host.toLowerCase() === host && (parsed.protocol === "http:" || parsed.protocol === "https:"));
|
|
822
836
|
if (!allowed) {
|
|
823
|
-
throw Object.assign(new Error(`Rejected
|
|
837
|
+
throw Object.assign(new Error(`Rejected untrusted origin: ${origin}`), {
|
|
824
838
|
statusCode: 403,
|
|
825
839
|
code: "NON_LOCAL_ORIGIN"
|
|
826
840
|
});
|