From 814df377b3a8b4281916be32534e87f7af276403 Mon Sep 17 00:00:00 2001 From: Kazuki Yamada Date: Thu, 26 Feb 2026 22:43:32 +0900 Subject: [PATCH 1/3] feat(website): Enable security check for zip file uploads Zip files uploaded by users may contain sensitive information like API keys or credentials, so security check should be enabled. Remote repos remain unchecked since they are already public. --- website/server/src/domains/pack/processZipFile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/server/src/domains/pack/processZipFile.ts b/website/server/src/domains/pack/processZipFile.ts index a63b6493..b13af707 100644 --- a/website/server/src/domains/pack/processZipFile.ts +++ b/website/server/src/domains/pack/processZipFile.ts @@ -38,7 +38,7 @@ export async function processZipFile(file: File, format: string, options: PackOp fileSummary: options.fileSummary, directoryStructure: options.directoryStructure, compress: options.compress, - securityCheck: false, + securityCheck: true, topFilesLen: 10, include: options.includePatterns, ignore: options.ignorePatterns, From a19518e91689b98e142e09429b154a7cd7fa681b Mon Sep 17 00:00:00 2001 From: Kazuki Yamada Date: Thu, 26 Feb 2026 22:43:43 +0900 Subject: [PATCH 2/3] feat(website): Display security alert for suspicious files in pack results When security check detects files containing potentially sensitive information in zip uploads, show a warning section in the result metadata panel listing the excluded files and detection reasons. --- .../components/Home/TryItResultContent.vue | 63 ++++++++++++++++++- website/client/components/api/client.ts | 6 ++ website/client/package-lock.json | 11 ---- .../server/src/domains/pack/processZipFile.ts | 10 +++ website/server/src/types.ts | 6 ++ 5 files changed, 84 insertions(+), 12 deletions(-) diff --git a/website/client/components/Home/TryItResultContent.vue b/website/client/components/Home/TryItResultContent.vue index f22fd168..6a5afef1 100644 --- a/website/client/components/Home/TryItResultContent.vue +++ b/website/client/components/Home/TryItResultContent.vue @@ -2,7 +2,7 @@ import ace, { type Ace } from 'ace-builds'; import themeTomorrowUrl from 'ace-builds/src-noconflict/theme-tomorrow?url'; import themeTomorrowNightUrl from 'ace-builds/src-noconflict/theme-tomorrow_night?url'; -import { BarChart2, Copy, Download, GitFork, PackageSearch, Share, Terminal } from 'lucide-vue-next'; +import { AlertTriangle, BarChart2, Copy, Download, GitFork, PackageSearch, Share, Terminal } from 'lucide-vue-next'; import { useData } from 'vitepress'; import { computed, onMounted, onUnmounted, ref, watch } from 'vue'; import { VAceEditor } from 'vue3-ace-editor'; @@ -50,6 +50,10 @@ watch(isDark, (newIsDark) => { } }); +const hasSuspiciousFiles = computed(() => { + return props.result.metadata.suspiciousFiles && props.result.metadata.suspiciousFiles.length > 0; +}); + const formattedTimestamp = computed(() => { return formatTimestamp(props.result.metadata.timestamp); }); @@ -205,6 +209,19 @@ onUnmounted(() => { + +
@@ -350,6 +367,50 @@ dd { padding-left: 8px; } +.security-warning { + background: var(--vp-c-warning-soft); + border-radius: 6px; + padding: 12px; +} + +.warning-icon { + color: var(--vp-c-warning-1); +} + +.warning-description { + font-size: 12px; + color: var(--vp-c-text-2); + margin: 0 0 8px; +} + +.suspicious-files-list { + margin: 0; + padding: 0; + list-style: none; + font-size: 13px; +} + +.suspicious-files-list li { + margin-bottom: 6px; + border-left: 2px solid var(--vp-c-warning-1); + padding-left: 8px; +} + +.suspicious-files-list li:last-child { + margin-bottom: 0; +} + +.suspicious-messages { + display: flex; + flex-direction: column; + gap: 2px; +} + +.suspicious-message { + font-size: 12px; + color: var(--vp-c-text-2); +} + .file-path { color: var(--vp-c-text-1); margin-bottom: 2px; diff --git a/website/client/components/api/client.ts b/website/client/components/api/client.ts index f2e3f3fd..f1fa6f53 100644 --- a/website/client/components/api/client.ts +++ b/website/client/components/api/client.ts @@ -25,6 +25,11 @@ export interface PackRequest { file?: File; } +export interface SuspiciousFile { + filePath: string; + messages: string[]; +} + export interface PackResult { content: string; format: string; @@ -42,6 +47,7 @@ export interface PackResult { tokenCount: number; }[]; allFiles?: FileInfo[]; + suspiciousFiles?: SuspiciousFile[]; }; } diff --git a/website/client/package-lock.json b/website/client/package-lock.json index 62dbffbc..d18311d1 100644 --- a/website/client/package-lock.json +++ b/website/client/package-lock.json @@ -164,7 +164,6 @@ "integrity": "sha512-qI3LcFsVgtvpsBGR7aNSJYxhsR+Zl46+958ODzg8aCxIcdxiK7QEVLMJMZAR57jGqW0Lg/vrjtuLFDMfSE53qA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@algolia/client-common": "5.18.0", "@algolia/requester-browser-xhr": "5.18.0", @@ -325,7 +324,6 @@ "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", @@ -2948,7 +2946,6 @@ "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -3387,7 +3384,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -3405,7 +3401,6 @@ "integrity": "sha512-/tfpK2A4FpS0o+S78o3YSdlqXr0MavJIDlFK3XZrlXLy7vaRXJvW5jYg3v5e/wCaF8y0IpMjkYLhoV6QqfpOgw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@algolia/client-abtesting": "5.18.0", "@algolia/client-analytics": "5.18.0", @@ -3623,7 +3618,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -4415,7 +4409,6 @@ "integrity": "sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "tabbable": "^6.2.0" } @@ -6167,7 +6160,6 @@ "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.6" }, @@ -7239,7 +7231,6 @@ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -7372,7 +7363,6 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/compiler-sfc": "3.5.13", @@ -7707,7 +7697,6 @@ "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "rollup": "dist/bin/rollup" }, diff --git a/website/server/src/domains/pack/processZipFile.ts b/website/server/src/domains/pack/processZipFile.ts index b13af707..bd803725 100644 --- a/website/server/src/domains/pack/processZipFile.ts +++ b/website/server/src/domains/pack/processZipFile.ts @@ -68,6 +68,15 @@ export async function processZipFile(file: File, format: string, options: PackOp // Read the generated file const content = await fs.readFile(outputFilePath, 'utf-8'); + // Map suspicious files results + const suspiciousFiles = + packResult.suspiciousFilesResults.length > 0 + ? packResult.suspiciousFilesResults.map((result) => ({ + filePath: result.filePath, + messages: result.messages, + })) + : undefined; + // Create pack result const packResultData: PackResult = { content, @@ -88,6 +97,7 @@ export async function processZipFile(file: File, format: string, options: PackOp })) .sort((a, b) => b.charCount - a.charCount) .slice(0, cliOptions.topFilesLen), + suspiciousFiles, }, }; diff --git a/website/server/src/types.ts b/website/server/src/types.ts index e376e700..917c7781 100644 --- a/website/server/src/types.ts +++ b/website/server/src/types.ts @@ -28,6 +28,11 @@ interface PackSummary { totalTokens: number; } +export interface SuspiciousFile { + filePath: string; + messages: string[]; +} + export interface PackResult { content: string; format: string; @@ -37,6 +42,7 @@ export interface PackResult { summary?: PackSummary; topFiles?: TopFile[]; allFiles?: FileInfo[]; + suspiciousFiles?: SuspiciousFile[]; }; } From 6fec71e12068012d5bc31dec180ea722e19b3e6d Mon Sep 17 00:00:00 2001 From: Kazuki Yamada Date: Thu, 26 Feb 2026 22:58:29 +0900 Subject: [PATCH 3/3] refactor(website): Address PR review feedback Rename shadowed variable `result` to `suspiciousResult` in map callback to avoid confusion with outer scope variable. Use composite key for v-for loop on suspicious messages for better rendering stability. --- website/client/components/Home/TryItResultContent.vue | 2 +- website/server/src/domains/pack/processZipFile.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/website/client/components/Home/TryItResultContent.vue b/website/client/components/Home/TryItResultContent.vue index 6a5afef1..a59fd9f1 100644 --- a/website/client/components/Home/TryItResultContent.vue +++ b/website/client/components/Home/TryItResultContent.vue @@ -216,7 +216,7 @@ onUnmounted(() => {
  • {{ file.filePath }}
    - {{ message }} + {{ message }}
  • diff --git a/website/server/src/domains/pack/processZipFile.ts b/website/server/src/domains/pack/processZipFile.ts index bd803725..2ba5daef 100644 --- a/website/server/src/domains/pack/processZipFile.ts +++ b/website/server/src/domains/pack/processZipFile.ts @@ -71,9 +71,9 @@ export async function processZipFile(file: File, format: string, options: PackOp // Map suspicious files results const suspiciousFiles = packResult.suspiciousFilesResults.length > 0 - ? packResult.suspiciousFilesResults.map((result) => ({ - filePath: result.filePath, - messages: result.messages, + ? packResult.suspiciousFilesResults.map((suspiciousResult) => ({ + filePath: suspiciousResult.filePath, + messages: suspiciousResult.messages, })) : undefined;