Merge pull request #1184 from yamadashy/feat/website-zip-security-check

feat(website): Enable security check for zip file uploads and display alerts
This commit is contained in:
Kazuki Yamada
2026-02-26 23:23:46 +09:00
committed by GitHub
5 changed files with 85 additions and 13 deletions
@@ -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(() => {
</ol>
</div>
<div class="metadata-section security-warning" v-if="hasSuspiciousFiles">
<h3><AlertTriangle :size="16" class="section-icon warning-icon" /> Security Alert</h3>
<p class="warning-description">The following files were excluded because they may contain sensitive information:</p>
<ul class="suspicious-files-list">
<li v-for="file in result.metadata.suspiciousFiles" :key="file.filePath">
<div class="file-path">{{ file.filePath }}</div>
<div class="suspicious-messages">
<span v-for="(message, index) in file.messages" :key="`${message}-${index}`" class="suspicious-message">{{ message }}</span>
</div>
</li>
</ul>
</div>
</div>
<div class="output-panel">
@@ -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;
+6
View File
@@ -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[];
};
}
-11
View File
@@ -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"
},
@@ -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,
@@ -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((suspiciousResult) => ({
filePath: suspiciousResult.filePath,
messages: suspiciousResult.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,
},
};
+6
View File
@@ -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[];
};
}