javascript-solid-server 0.0.170 → 0.0.171
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/package.json +1 -1
- package/src/handlers/git.js +44 -14
package/package.json
CHANGED
package/src/handlers/git.js
CHANGED
|
@@ -24,9 +24,11 @@ export function isGitWriteOperation(urlPath) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Extract the repository path from the URL with path traversal protection
|
|
27
|
+
* Extract the repository path from the URL with path traversal protection.
|
|
28
|
+
* Always returns a non-empty string: '.' for root/empty URL paths, the
|
|
29
|
+
* cleaned relative path otherwise.
|
|
28
30
|
* @param {string} urlPath - The URL path
|
|
29
|
-
* @returns {string
|
|
31
|
+
* @returns {string} The repository relative path ('.' for root)
|
|
30
32
|
*/
|
|
31
33
|
function extractRepoPath(urlPath) {
|
|
32
34
|
// Remove git service suffixes to get the repo path
|
|
@@ -87,6 +89,23 @@ function findGitDir(repoPath) {
|
|
|
87
89
|
return null;
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
// CORS headers for git responses. Single source of truth — used by the
|
|
93
|
+
// success path (Fastify reply on the OPTIONS preflight, raw stream on
|
|
94
|
+
// http-backend output) and by every 4xx early-return. Without these,
|
|
95
|
+
// browser-based git clients (e.g. jss.live/git/) see a generic
|
|
96
|
+
// CORS/network error instead of the actual status, undermining #371.
|
|
97
|
+
const GIT_CORS_HEADERS = {
|
|
98
|
+
'Access-Control-Allow-Origin': '*',
|
|
99
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
100
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization, Git-Protocol',
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function setGitCorsHeaders(reply) {
|
|
104
|
+
for (const [k, v] of Object.entries(GIT_CORS_HEADERS)) {
|
|
105
|
+
reply.header(k, v);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
90
109
|
/**
|
|
91
110
|
* Handle Git HTTP requests using git http-backend
|
|
92
111
|
* @param {FastifyRequest} request
|
|
@@ -95,20 +114,28 @@ function findGitDir(repoPath) {
|
|
|
95
114
|
export async function handleGit(request, reply) {
|
|
96
115
|
// Handle CORS preflight
|
|
97
116
|
if (request.method === 'OPTIONS') {
|
|
98
|
-
reply
|
|
99
|
-
reply.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
100
|
-
reply.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Git-Protocol');
|
|
117
|
+
setGitCorsHeaders(reply);
|
|
101
118
|
return reply.code(200).send();
|
|
102
119
|
}
|
|
103
120
|
|
|
104
|
-
|
|
121
|
+
// Collapse multi-slash sequences before they reach extractRepoPath or
|
|
122
|
+
// get forwarded as PATH_INFO. git http-backend rejects paths like
|
|
123
|
+
// `/foo/test//info/refs` as "aliased" and JSS would otherwise 500.
|
|
124
|
+
// Frontends and bots both produce these (frontend appends `/info/refs`
|
|
125
|
+
// to a URL the user ended with `/`, bots probe `///wp-admin/...`).
|
|
126
|
+
// Same shape as the LDP fix in src/utils/url.js (#131).
|
|
127
|
+
//
|
|
128
|
+
// Note: Fastify's URL parser rejects malformed percent-encoding
|
|
129
|
+
// (`%g1`, truncated `%E0%`, invalid UTF-8 like `%C3%28`) with a 400
|
|
130
|
+
// FST_ERR_BAD_URL before this handler runs — verified empirically — so
|
|
131
|
+
// decodeURIComponent here is safe in practice on current Fastify.
|
|
132
|
+
let urlPath = decodeURIComponent(request.url.split('?')[0]);
|
|
133
|
+
urlPath = urlPath.replace(/\/{2,}/g, '/');
|
|
105
134
|
const queryString = request.url.split('?')[1] || '';
|
|
106
135
|
|
|
107
|
-
//
|
|
136
|
+
// extractRepoPath always returns a non-empty string ('.' for root) —
|
|
137
|
+
// no null check needed.
|
|
108
138
|
const repoRelative = extractRepoPath(urlPath);
|
|
109
|
-
if (!repoRelative) {
|
|
110
|
-
return reply.code(400).send({ error: 'Invalid git request' });
|
|
111
|
-
}
|
|
112
139
|
|
|
113
140
|
// Handle subdomain mode
|
|
114
141
|
let dataRoot = getDataRoot();
|
|
@@ -120,12 +147,14 @@ export async function handleGit(request, reply) {
|
|
|
120
147
|
|
|
121
148
|
// Security: verify resolved path is within data root (path traversal protection)
|
|
122
149
|
if (!isPathWithinDataRoot(repoAbs, getDataRoot())) {
|
|
150
|
+
setGitCorsHeaders(reply);
|
|
123
151
|
return reply.code(403).send({ error: 'Path traversal detected' });
|
|
124
152
|
}
|
|
125
153
|
|
|
126
154
|
// Find git directory
|
|
127
155
|
const gitInfo = findGitDir(repoAbs);
|
|
128
156
|
if (!gitInfo) {
|
|
157
|
+
setGitCorsHeaders(reply);
|
|
129
158
|
return reply.code(404).send({ error: 'Not a git repository' });
|
|
130
159
|
}
|
|
131
160
|
|
|
@@ -214,10 +243,11 @@ export async function handleGit(request, reply) {
|
|
|
214
243
|
}
|
|
215
244
|
}
|
|
216
245
|
|
|
217
|
-
// Add CORS headers for browser git clients
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
246
|
+
// Add CORS headers for browser git clients (same set as the
|
|
247
|
+
// 4xx return paths, kept in sync via GIT_CORS_HEADERS).
|
|
248
|
+
for (const [k, v] of Object.entries(GIT_CORS_HEADERS)) {
|
|
249
|
+
reply.raw.setHeader(k, v);
|
|
250
|
+
}
|
|
221
251
|
|
|
222
252
|
reply.raw.writeHead(statusCode);
|
|
223
253
|
headersSent = true;
|