Skip to content

Feat/stage#100

Merged
AdriGeorge merged 13 commits into
mainfrom
feat/stage
Mar 25, 2026
Merged

Feat/stage#100
AdriGeorge merged 13 commits into
mainfrom
feat/stage

Conversation

@AdriGeorge
Copy link
Copy Markdown

@AdriGeorge AdriGeorge commented Mar 23, 2026

v2.0.2

Key: key,
Body: stream,
ContentType: 'application/octet-stream',
ContentDisposition: `attachment; filename="${filename.replace(/"/g, '\\"')}"`

Check failure

Code scanning / CodeQL

Incomplete string escaping or encoding High

This does not escape backslash characters in the input.

Copilot Autofix

AI about 2 months ago

In general, to fix incomplete escaping you must escape all characters that are significant in the target context, and ensure that backslashes themselves are escaped before escaping other characters. For HTTP header parameter values like filename="...", that means first escaping backslashes, then double quotes, so that sequences like \" in the original filename cannot be misinterpreted when the header is parsed.

The best minimal change here is to adjust the filename.replace(/"/g, '\\"') call on line 114 so that it also escapes backslashes. We can do this with a single replace using a character class and a replacement function that prefixes any backslash or double quote with a backslash: filename.replace(/["\\]/g, '\\$&'). This ensures that any existing backslash is turned into \\, and any " is turned into \", covering the case CodeQL complains about without changing the surrounding logic or behavior beyond making header values safer.

Concretely, in src/components/storage/S3Storage.ts, at the ContentDisposition property inside the upload method (around lines 107–115), replace the current interpolation expression filename.replace(/"/g, '\\"') with filename.replace(/["\\]/g, '\\$&'). No additional imports or helper functions are required.

Suggested changeset 1
src/components/storage/S3Storage.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/components/storage/S3Storage.ts b/src/components/storage/S3Storage.ts
--- a/src/components/storage/S3Storage.ts
+++ b/src/components/storage/S3Storage.ts
@@ -111,7 +111,7 @@
         Key: key,
         Body: stream,
         ContentType: 'application/octet-stream',
-        ContentDisposition: `attachment; filename="${filename.replace(/"/g, '\\"')}"`
+        ContentDisposition: `attachment; filename="${filename.replace(/["\\]/g, '\\$&')}"`
       },
       queueSize: 4,
       partSize: 5 * 1024 * 1024, // 5MB minimum for S3
EOF
@@ -111,7 +111,7 @@
Key: key,
Body: stream,
ContentType: 'application/octet-stream',
ContentDisposition: `attachment; filename="${filename.replace(/"/g, '\\"')}"`
ContentDisposition: `attachment; filename="${filename.replace(/["\\]/g, '\\$&')}"`
},
queueSize: 4,
partSize: 5 * 1024 * 1024, // 5MB minimum for S3
Copilot is powered by AI and may make mistakes. Always verify output.
return {
httpStatus: response.status,
headers: response.headers as Record<string, string | string[]>
}

Check failure

Code scanning / CodeQL

Incomplete string escaping or encoding High

This does not escape backslash characters in the input.

Copilot Autofix

AI about 2 months ago

In general, to fix incomplete escaping you should ensure that all characters that act as escape/meta characters in the target context are handled consistently. When you already escape a quote character using backslashes, you must also escape any existing backslashes first, otherwise an input containing backslashes followed by quotes can defeat or confuse the escaping.

Here, the header is built as:

'Content-Disposition': `attachment; filename="${filename.replace(/"/g, '\\"')}"`

This replaces only " with \". To avoid inconsistent escaping, we should first escape backslashes, then escape quotes, e.g.:

const safeFilename = filename.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
'Content-Disposition': `attachment; filename="${safeFilename}"`

This change is confined to src/components/storage/UrlStorage.ts, inside the upload method, around line 58. No new methods or imports are needed; we can implement the escaping inline using standard String.prototype.replace with appropriate regular expressions. Functionality remains the same for filenames without backslashes; for filenames with backslashes, the header becomes more robust and correctly escaped.

Suggested changeset 1
src/components/storage/UrlStorage.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/components/storage/UrlStorage.ts b/src/components/storage/UrlStorage.ts
--- a/src/components/storage/UrlStorage.ts
+++ b/src/components/storage/UrlStorage.ts
@@ -53,9 +53,10 @@
     if (url.endsWith('/')) {
       url = `${url.replace(/\/+$/, '')}/${encodeURIComponent(filename)}`
     }
+    const escapedFilename = filename.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
     const headers: Record<string, string> = {
       ...(fileHeaders ?? {}),
-      'Content-Disposition': `attachment; filename="${filename.replace(/"/g, '\\"')}"`
+      'Content-Disposition': `attachment; filename="${escapedFilename}"`
     }
     const response = await axios({
       method: 'put',
EOF
@@ -53,9 +53,10 @@
if (url.endsWith('/')) {
url = `${url.replace(/\/+$/, '')}/${encodeURIComponent(filename)}`
}
const escapedFilename = filename.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
const headers: Record<string, string> = {
...(fileHeaders ?? {}),
'Content-Disposition': `attachment; filename="${filename.replace(/"/g, '\\"')}"`
'Content-Disposition': `attachment; filename="${escapedFilename}"`
}
const response = await axios({
method: 'put',
Copilot is powered by AI and may make mistakes. Always verify output.
res.status(500).send(err.message)
closedResponse = true
if (!res.headersSent) {
res.status(500).send(err.message)

Check warning

Code scanning / CodeQL

Exception text reinterpreted as HTML Medium

Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.
Exception text
is reinterpreted as HTML without escaping meta-characters.

Copilot Autofix

AI about 2 months ago

In general terms, the problem is that detailed exception messages, which can include user-influenced content, are being returned directly to the client and interpreted as HTML. The best fix is to avoid sending raw exception messages in HTTP responses; instead, send a generic, safe error message to the client and log the detailed error message server-side. This removes any user-controlled content from the response and avoids XSS, while preserving debuggability through logs.

For this specific code, the key change is in src/components/httpRoutes/commands.ts in the catch block of the /directCommand route. Instead of:

137:       HTTP_LOGGER.error(err.message)
138:       closedResponse = true
139:       if (!res.headersSent) {
140:         res.status(500).send(err.message)
141:       }

we should:

  1. Log the full error (ideally the whole err object) via HTTP_LOGGER.error.
  2. Return a generic error string or JSON that does not expose err.message to the client, such as 'Internal server error' or a similar safe message.

To preserve existing behavior shape (string response, 500 status) while making it safe, we can replace err.message in the send call with a constant like 'Internal server error'. We should also guard for non-Error types safely when logging, but we can do that without changing imports or external behavior: e.g., log err as-is or fall back to String(err).

No other locations in these snippets send err.message to clients; the other flows (e.g., via validateCommandParameters and buildInvalidParametersResponse) already use structured validation messages we control. The single change in the catch block addresses all alert variants, because they all target this same res.status(500).send(err.message) sink.

Suggested changeset 1
src/components/httpRoutes/commands.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/components/httpRoutes/commands.ts b/src/components/httpRoutes/commands.ts
--- a/src/components/httpRoutes/commands.ts
+++ b/src/components/httpRoutes/commands.ts
@@ -134,10 +134,10 @@
         res.end()
       }
     } catch (err) {
-      HTTP_LOGGER.error(err.message)
+      HTTP_LOGGER.error(err)
       closedResponse = true
       if (!res.headersSent) {
-        res.status(500).send(err.message)
+        res.status(500).send('Internal server error')
       }
     }
   }
EOF
@@ -134,10 +134,10 @@
res.end()
}
} catch (err) {
HTTP_LOGGER.error(err.message)
HTTP_LOGGER.error(err)
closedResponse = true
if (!res.headersSent) {
res.status(500).send(err.message)
res.status(500).send('Internal server error')
}
}
}
Copilot is powered by AI and may make mistakes. Always verify output.
@AdriGeorge AdriGeorge merged commit aae2eaa into main Mar 25, 2026
16 of 18 checks passed
@AdriGeorge AdriGeorge deleted the feat/stage branch March 25, 2026 08:52
@AdriGeorge AdriGeorge restored the feat/stage branch March 25, 2026 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants