Skip to content

Commit e144f3f

Browse files
committed
fix: security value problem
1 parent 2956932 commit e144f3f

7 files changed

Lines changed: 71 additions & 18 deletions

File tree

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v22

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
## Build, Test, and Development Commands
1717

18+
- **Node version requirement**: Before running `pnpm` commands (especially checks/tests), run `nvm use v22` in this repository.
19+
1820
- `pnpm dev` – start the Next.js development server with hot reload.
1921
- `pnpm build` – create a production build.
2022
- `pnpm start` – run the production bundle locally.

components/access-keys/new-item.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,14 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
4747

4848
React.useEffect(() => {
4949
if (visible) {
50-
setAccessKey(makeRandomString(20))
51-
setSecretKey(makeRandomString(40))
50+
try {
51+
setAccessKey(makeRandomString(20))
52+
setSecretKey(makeRandomString(40))
53+
} catch {
54+
setAccessKey("")
55+
setSecretKey("")
56+
message.error(t("Failed to generate secure random keys"))
57+
}
5258
setName("")
5359
setDescription("")
5460
setExpiry(null)
@@ -63,7 +69,7 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
6369
setPolicy("{}")
6470
})
6571
}
66-
}, [visible, api])
72+
}, [visible, api, message, t])
6773

6874
const closeModal = () => {
6975
onVisibleChange(false)

lib/delete-task.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { DeleteObjectCommand, DeleteObjectsCommand, ListObjectsV2Command, ListObjectVersionsCommand, S3Client } from "@aws-sdk/client-s3"
22
import type { ManagedTask, TaskHandler, TaskLifecycleStatus } from "./task-manager"
3+
import { createTaskId } from "./task-id"
34

45
export type DeleteStatus = "pending" | "running" | "completed" | "failed" | "canceled"
56

@@ -203,7 +204,7 @@ export function createDeleteTaskHelpers(s3Client: S3Client, config: DeleteTaskCo
203204
prefix?: string,
204205
): DeleteTask[] =>
205206
items.map((item) => ({
206-
id: `${Date.now()}-${Math.random().toString(36).slice(2, 11)}-${item.key}-${item.versionId ?? "latest"}`,
207+
id: createTaskId(`${item.key}-${item.versionId ?? "latest"}`),
207208
kind: "delete" as const,
208209
key: item.key,
209210
versionId: item.versionId,
@@ -244,7 +245,7 @@ export function createDeleteTaskHelpers(s3Client: S3Client, config: DeleteTaskCo
244245
bucketName: string,
245246
options?: { forceDelete?: boolean },
246247
): FolderDeleteTask => ({
247-
id: `${Date.now()}-${Math.random().toString(36).slice(2, 11)}-${prefix}`,
248+
id: createTaskId(prefix),
248249
kind: "delete-folder" as const,
249250
bucketName,
250251
prefix,

lib/functions.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,26 @@ function convertToBytes(value: string, unit: string, fromK8s = false): number {
3333
}
3434

3535
export function makeRandomString(length = 20): string {
36-
let initstr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
37-
const arr = initstr.split("")
38-
39-
for (let i = arr.length - 1; i > 0; i--) {
40-
const j = Math.floor(Math.random() * (i + 1))
41-
const current = arr[i]
42-
const swapWith = arr[j]
43-
if (current !== undefined && swapWith !== undefined) {
44-
arr[i] = swapWith
45-
arr[j] = current
36+
const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
37+
if (length <= 0) return ""
38+
39+
const cryptoApi = globalThis.crypto
40+
if (!cryptoApi?.getRandomValues) {
41+
throw new Error("Secure random generator is not available")
42+
}
43+
44+
const result: string[] = []
45+
const alphabetLength = alphabet.length
46+
const maxByte = 256 - (256 % alphabetLength)
47+
48+
while (result.length < length) {
49+
const bytes = cryptoApi.getRandomValues(new Uint8Array(length))
50+
for (const byte of bytes) {
51+
if (byte >= maxByte) continue
52+
result.push(alphabet[byte % alphabetLength] ?? "")
53+
if (result.length === length) break
4654
}
4755
}
4856

49-
initstr = arr.join("")
50-
return initstr.slice(0, length)
57+
return result.join("")
5158
}

lib/task-id.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const FALLBACK_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"
2+
3+
function createSecureSuffix(length = 12): string {
4+
const cryptoApi = globalThis.crypto
5+
if (!cryptoApi?.getRandomValues) {
6+
throw new Error("Secure random generator is not available")
7+
}
8+
9+
const chars: string[] = []
10+
const alphabetLength = FALLBACK_ALPHABET.length
11+
const maxByte = 256 - (256 % alphabetLength)
12+
13+
while (chars.length < length) {
14+
const bytes = cryptoApi.getRandomValues(new Uint8Array(length))
15+
for (const byte of bytes) {
16+
if (byte >= maxByte) continue
17+
chars.push(FALLBACK_ALPHABET[byte % alphabetLength] ?? "")
18+
if (chars.length === length) break
19+
}
20+
}
21+
22+
return chars.join("")
23+
}
24+
25+
export function createTaskId(seed: string): string {
26+
const prefix = Date.now()
27+
const cryptoApi = globalThis.crypto
28+
29+
if (cryptoApi?.randomUUID) {
30+
return `${prefix}-${cryptoApi.randomUUID()}-${seed}`
31+
}
32+
33+
return `${prefix}-${createSecureSuffix()}-${seed}`
34+
}
35+

lib/upload-task.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from "@aws-sdk/client-s3"
88
import type { ManagedTask, TaskHandler, TaskLifecycleStatus } from "./task-manager"
99
import { formatBytes } from "./functions"
10+
import { createTaskId } from "./task-id"
1011

1112
export type UploadStatus = "pending" | "running" | "completed" | "failed" | "canceled" | "paused"
1213

@@ -105,7 +106,7 @@ export function createUploadTaskHelpers(s3Client: S3Client, config: UploadTaskCo
105106
existKeys.add(`${bucketName}/${item.key}`)
106107
const displayName = item.key.split("/").pop() ?? item.key
107108
return {
108-
id: `${Date.now()}-${Math.random().toString(36).slice(2, 11)}-${item.key}`,
109+
id: createTaskId(item.key),
109110
kind: "upload" as const,
110111
file: item.file,
111112
key: item.key,

0 commit comments

Comments
 (0)