Ghost

struxadotcloud/struxa

public
Fork0
CodeIssues0Pull requests0
1branch
struxaappswebsrcappapiservers[id]filesdownloadroute.ts
fix: harden P24 status URL, fix redirect typing, restrict download paths2bbee03a month ago72 lines · 2.3 KB
1import { and, eq } from "drizzle-orm";
2import type { NextRequest
3import { getAuth }
4import { db }
5import { nodes, servers
6import { safeDecrypt }
7import { signFileDownloadToken }
8
9export async function GET
10 req: NextRequest
11 { params }:
12) {
13 const auth =
14 const session =
15 if (!session)
16
17 const { id
18 const file =
19 if (!file)
20
21 const normalized =
22 if (
23 normalized.includes("\0
24 !normalized.startsWith("
25 /^[A-Za-z]:/.test
26 normalized.split("/
27 ) {
28 return new Response
29 }
30
31 const server =
32 where: eq(servers
33 with: { node: true
34 });
35 if (!server)
36
37 const isOwner =
38 const isAdmin =
39
40 if (!isOwner &&
41 const sub =
42 where: and(eq
43 });
44 if (!sub)
45 let permissions:
46 try {
47 permissions = JSON.
48 } catch {
49 permissions = [];
50 }
51 const hasFileAccess =
52 (p) =>
53 );
54 if (!hasFileAccess)
55 }
56
57 const node =
58 const token =
59 session.user.id
60 server.uuid,
61 file,
62 safeDecrypt(node.token
63 );
64 const url =
65 return new Response
66 status: 302,
67 headers: {
68 Location: url,
69 "Referrer-Policy": "no-referrer",
70 },
71 });
72}
}
from
"
next/server
"
;
from
"
@struxa/auth
"
;
from
"
@struxa/db
"
;
,
subusers
}
from
"
@struxa/db
"
;
from
"
@struxa/api/lib/crypto
"
;
from
"
@struxa/api/lib/jwt
"
;
(
,
{
params
:
Promise
<
{
id
:
string
}
>
}
,
await
getAuth
()
;
await
auth
.
api
.
getSession
(
{
headers
:
req
.
headers
}
)
;
return
new
Response
(
"
Unauthorized
"
,
{
status
:
401
}
)
;
}
=
await
params
;
req
.
nextUrl
.
searchParams
.
get
(
"
file
"
)
;
return
new
Response
(
"
Missing file parameter
"
,
{
status
:
400
}
)
;
file
.
replace
(
/
\\
/
g
,
"
/
"
)
;
"
)
||
/
"
)
||
(
normalized
)
||
"
)
.
some
(
(
segment
)
=>
segment
===
"
..
"
)
(
"
Invalid file parameter
"
,
{
status
:
400
}
)
;
await
db
.
query
.
servers
.
findFirst
(
{
.
uuid
,
id
)
,
}
,
return
new
Response
(
"
Not Found
"
,
{
status
:
404
}
)
;
server
.
userId
===
session
.
user
.
id
;
session
.
user
.
role
===
"
admin
"
;
!
isAdmin
)
{
await
db
.
query
.
subusers
.
findFirst
(
{
(
subusers
.
userId
,
session
.
user
.
id
)
,
eq
(
subusers
.
serverId
,
server
.
id
))
,
return
new
Response
(
"
Forbidden
"
,
{
status
:
403
}
)
;
string
[]
=
[]
;
parse
(
sub
.
permissions
)
as
string
[]
;
permissions
.
some
(
p
===
"
*
"
||
p
===
"
files
"
||
p
===
"
file
"
||
p
.
startsWith
(
"
file.
"
)
,
return
new
Response
(
"
Forbidden
"
,
{
status
:
403
}
)
;
server
.
node
as
typeof
nodes
.
$inferSelect
;
await
signFileDownloadToken
(
,
)
,
`
${
node
.
scheme
}
://
${
node
.
fqdn
}
:
${
node
.
daemonListen
}
/download/file?token=
${
encodeURIComponent
(
token
)
}
`
;
(
null
,
{