allow single files in base64 scanner

This commit is contained in:
2026-05-08 15:09:54 +08:00
parent e42002f5de
commit aa9b9846ca
3 changed files with 64 additions and 10 deletions

Binary file not shown.

View File

@@ -29,19 +29,39 @@ export function openBase64Scanner(_context: vscode.ExtensionContext) {
if (picked && picked[0]) { if (picked && picked[0]) {
panel.webview.postMessage({ type: 'folderPicked', path: picked[0].fsPath }); panel.webview.postMessage({ type: 'folderPicked', path: picked[0].fsPath });
} }
} else if (msg.type === 'pickFiles') {
const picked = await vscode.window.showOpenDialog({
canSelectFolders: false,
canSelectFiles: true,
canSelectMany: true,
openLabel: 'Select Files',
filters: { 'HTML': ['html', 'htm'] },
});
if (picked && picked.length) {
panel.webview.postMessage({ type: 'filesPicked', paths: picked.map(u => u.fsPath) });
}
} else if (msg.type === 'scan') { } else if (msg.type === 'scan') {
const folder: string = msg.folder; const folder: string | undefined = msg.folder;
if (!folder || !fs.existsSync(folder)) { const files: string[] | undefined = msg.files;
panel.webview.postMessage({ type: 'results', results: [], error: 'Invalid folder' }); let targets: string[] = [];
let baseDir = '';
if (files && files.length) {
targets = files.filter(f => fs.existsSync(f));
baseDir = targets.length ? commonBaseDir(targets) : '';
} else if (folder && fs.existsSync(folder)) {
targets = await collectHtmlFiles(folder);
baseDir = folder;
} else {
panel.webview.postMessage({ type: 'results', results: [], error: 'Pick a folder or files first' });
return; return;
} }
const htmlFiles = await collectHtmlFiles(folder);
const results: { file: string; ok: boolean; assets: string[] }[] = []; const results: { file: string; ok: boolean; assets: string[] }[] = [];
for (const file of htmlFiles) { for (const file of targets) {
try { try {
const content = fs.readFileSync(file, 'utf8'); const content = fs.readFileSync(file, 'utf8');
const offenders = findNonBase64Assets(content); const offenders = findNonBase64Assets(content);
results.push({ file: path.relative(folder, file), ok: offenders.length === 0, assets: offenders }); const display = baseDir ? path.relative(baseDir, file) || path.basename(file) : file;
results.push({ file: display, ok: offenders.length === 0, assets: offenders });
} catch { } catch {
// skip unreadable // skip unreadable
} }
@@ -51,6 +71,16 @@ export function openBase64Scanner(_context: vscode.ExtensionContext) {
}); });
} }
function commonBaseDir(files: string[]): string {
if (files.length === 0) return '';
if (files.length === 1) return path.dirname(files[0]);
const split = files.map(f => path.dirname(f).split(path.sep));
const first = split[0];
let i = 0;
while (i < first.length && split.every(parts => parts[i] === first[i])) i++;
return first.slice(0, i).join(path.sep);
}
async function collectHtmlFiles(root: string): Promise<string[]> { async function collectHtmlFiles(root: string): Promise<string[]> {
const out: string[] = []; const out: string[] = [];
const stack: string[] = [root]; const stack: string[] = [root];
@@ -132,9 +162,11 @@ function getHtml(): string {
<h2>Base64 Asset Scanner</h2> <h2>Base64 Asset Scanner</h2>
<div class="row"> <div class="row">
<input id="folder" type="text" placeholder="Folder to scan recursively..." /> <input id="folder" type="text" placeholder="Folder to scan recursively..." />
<button id="pick">Browse...</button> <button id="pick">Browse Folder...</button>
<button id="pickFiles">Pick Files...</button>
<button id="scan">Scan</button> <button id="scan">Scan</button>
</div> </div>
<div id="selection" class="summary"></div>
<div id="status"></div> <div id="status"></div>
<div id="results"></div> <div id="results"></div>
<script> <script>
@@ -143,22 +175,43 @@ function getHtml(): string {
const folderEl = document.getElementById('folder'); const folderEl = document.getElementById('folder');
const statusEl = document.getElementById('status'); const statusEl = document.getElementById('status');
const resultsEl = document.getElementById('results'); const resultsEl = document.getElementById('results');
const selectionEl = document.getElementById('selection');
let pickedFiles = [];
function clearFiles() {
pickedFiles = [];
selectionEl.textContent = '';
}
folderEl.addEventListener('input', clearFiles);
document.getElementById('pick').addEventListener('click', () => { document.getElementById('pick').addEventListener('click', () => {
vscode.postMessage({ type: 'pickFolder' }); vscode.postMessage({ type: 'pickFolder' });
}); });
document.getElementById('pickFiles').addEventListener('click', () => {
vscode.postMessage({ type: 'pickFiles' });
});
document.getElementById('scan').addEventListener('click', () => { document.getElementById('scan').addEventListener('click', () => {
const folder = folderEl.value.trim();
if (!folder) { statusEl.textContent = 'Pick a folder first.'; return; }
statusEl.textContent = 'Scanning...'; statusEl.textContent = 'Scanning...';
resultsEl.innerHTML = ''; resultsEl.innerHTML = '';
vscode.postMessage({ type: 'scan', folder }); if (pickedFiles.length) {
vscode.postMessage({ type: 'scan', files: pickedFiles });
} else {
const folder = folderEl.value.trim();
if (!folder) { statusEl.textContent = 'Pick a folder or files first.'; return; }
vscode.postMessage({ type: 'scan', folder });
}
}); });
window.addEventListener('message', (event) => { window.addEventListener('message', (event) => {
const msg = event.data; const msg = event.data;
if (msg.type === 'folderPicked') { if (msg.type === 'folderPicked') {
folderEl.value = msg.path; folderEl.value = msg.path;
clearFiles();
} else if (msg.type === 'filesPicked') {
pickedFiles = msg.paths;
folderEl.value = '';
selectionEl.textContent = pickedFiles.length + ' file(s) selected';
} else if (msg.type === 'results') { } else if (msg.type === 'results') {
if (msg.error) { statusEl.textContent = 'Error: ' + msg.error; return; } if (msg.error) { statusEl.textContent = 'Error: ' + msg.error; return; }
const total = msg.results.length; const total = msg.results.length;

View File

@@ -91,6 +91,7 @@ function getHtml(lastProject: string): string {
<option>Started</option> <option>Started</option>
<option>Progress</option> <option>Progress</option>
<option>Finished</option> <option>Finished</option>
<option>Client Feedback</option>
<option>For OT</option> <option>For OT</option>
</select> </select>