Skip to content

Commit aaf3c1b

Browse files
committed
fix: address security and resilience review findings
- Fix reflected XSS: replace innerHTML with DOM APIs in ARM64 link swap - Verify GitHub asset exists (HEAD probe) before caching npm-resolved version, preventing broken redirects during npm/GitHub sync windows - Make KV cache writes best-effort so transient KV errors don't fail otherwise valid requests - Use github.paginate for staging deploy PR comment lookup to handle PRs with many comments
1 parent 8183f1b commit aaf3c1b

2 files changed

Lines changed: 24 additions & 6 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
script: |
4747
const marker = '<!-- staging-deploy -->';
4848
const body = `${marker}\n✅ Staging deployment successful!\n\nPreview: https://vp-setup-staging.void.app/\nCommit: ${context.sha}`;
49-
const { data: comments } = await github.rest.issues.listComments({
49+
const comments = await github.paginate(github.rest.issues.listComments, {
5050
owner: context.repo.owner,
5151
repo: context.repo.repo,
5252
issue_number: context.issue.number,

routes/index.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,23 @@ async function getRelease(tag: string | undefined): Promise<CachedRelease | null
7676
const version = await fetchNpmDistTagVersion(DEFAULT_DIST_TAG);
7777
if (version) {
7878
const release = buildReleaseFromTag(`v${version}`);
79-
await Promise.all([
80-
kv.put(LATEST_CACHE_KEY, release, { ttl: LATEST_CACHE_TTL }),
81-
kv.put(LATEST_STALE_KEY, release, { ttl: LATEST_CACHE_TTL + 3600 }),
82-
]);
79+
// Verify asset exists before caching — npm may publish before GitHub release assets are ready
80+
try {
81+
const probe = await fetch(release.assets.x64!, { method: "HEAD", redirect: "manual" });
82+
if (probe.status === 404) {
83+
return kv.get<CachedRelease>(LATEST_STALE_KEY);
84+
}
85+
} catch {
86+
// Network error checking asset — still serve the release, cache writes are best-effort below
87+
}
88+
try {
89+
await Promise.all([
90+
kv.put(LATEST_CACHE_KEY, release, { ttl: LATEST_CACHE_TTL }),
91+
kv.put(LATEST_STALE_KEY, release, { ttl: LATEST_CACHE_TTL + 3600 }),
92+
]);
93+
} catch (err) {
94+
console.error("KV write failed:", err);
95+
}
8396
return release;
8497
}
8598

@@ -207,7 +220,12 @@ async function setupDownloadLink() {
207220
mainBtn.textContent = "Download for Windows (ARM64)";
208221
209222
if (altEl && x64Url) {
210-
altEl.innerHTML = 'Also available: <a href="' + x64Url + '" download>Windows x64</a>';
223+
var link = document.createElement("a");
224+
link.href = x64Url;
225+
link.download = "";
226+
link.textContent = "Windows x64";
227+
altEl.textContent = "Also available: ";
228+
altEl.appendChild(link);
211229
}
212230
}
213231

0 commit comments

Comments
 (0)