Files
core/ui/static/builder.js

198 lines
7.6 KiB
JavaScript
Raw Normal View History

2026-04-02 10:57:36 -04:00
const fileInput = document.getElementById('local-file');
const pathInput = document.getElementById('findings_path');
let currentRawData = null;
let isJson = false;
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
isJson = file.name.toLowerCase().endsWith('.json');
const reader = new FileReader();
reader.onload = (event) => {
currentRawData = event.target.result;
document.getElementById('preview-placeholder').style.display = 'none';
if (isJson) {
try {
const parsed = JSON.parse(currentRawData);
const guessedPath = autoDetectArrayPath(parsed);
if (guessedPath) {
pathInput.value = guessedPath;
}
} catch (e) {
console.error("Auto-detect failed:", e);
}
}
processPreview();
};
reader.readAsText(file);
});
pathInput.addEventListener('input', () => {
if (currentRawData && isJson) processPreview();
});
function autoDetectArrayPath(obj) {
if (Array.isArray(obj)) return ".";
let bestPath = "";
let maxLen = -1;
function search(currentObj, currentPath) {
if (Array.isArray(currentObj)) {
if (currentObj.length > 0 && typeof currentObj[0] === 'object') {
if (currentObj.length > maxLen) {
maxLen = currentObj.length;
bestPath = currentPath;
}
}
return;
}
if (currentObj !== null && typeof currentObj === 'object') {
for (let key in currentObj) {
let nextPath = currentPath ? currentPath + "." + key : key;
search(currentObj[key], nextPath);
}
}
}
search(obj, "");
return bestPath || ".";
}
function processPreview() {
let headers = [];
let rows = [];
if (isJson) {
try {
const parsed = JSON.parse(currentRawData);
const findings = getNestedValue(parsed, pathInput.value);
if (!Array.isArray(findings) || findings.length === 0) {
const rawPreview = JSON.stringify(parsed, null, 2).substring(0, 1500) + "\n\n... (file truncated for preview)";
document.getElementById('preview-table-container').innerHTML =
`<div style="padding: 15px; background: #1e293b; border-radius: 6px; font-family: monospace; overflow-x: auto; text-align: left; font-size: 0.85rem;">
<p style="color: #fca5a5; margin-top: 0; font-weight: bold;"> Path "${pathInput.value}" is not an array.</p>
<p style="color: #cbd5e1; margin-bottom: 10px;">Here is the structure of your file to help you find the correct path:</p>
<pre style="margin: 0; color: #a6e22e;">${rawPreview}</pre>
</div>`;
document.getElementById('save-btn').classList.add('disabled');
return;
}
document.getElementById('save-btn').classList.remove('disabled');
headers = Object.keys(findings[0]);
rows = findings.slice(0, 5).map(obj => headers.map(h => formatCell(obj[h])));
} catch(e) {
document.getElementById('preview-table-container').innerHTML = `<div style="color: var(--critical); padding: 20px; font-weight: bold;">JSON Parse Error: ${e.message}</div>`;
return;
}
} else {
const lines = currentRawData.split('\n').filter(l => l.trim() !== '');
headers = lines[0].split(',').map(h => h.trim());
rows = lines.slice(1, 6).map(line => line.split(',').map(c => c.trim()));
document.getElementById('save-btn').classList.remove('disabled');
}
renderTable(headers, rows);
populateDropdowns(headers);
}
function getNestedValue(obj, path) {
if (path === '' || path === '.') return obj;
return path.split('.').reduce((acc, part) => acc && acc[part], obj);
}
function formatCell(val) {
if (typeof val === 'object') return JSON.stringify(val);
if (val === undefined || val === null) return "";
const str = String(val);
return str.length > 50 ? str.substring(0, 47) + "..." : str;
}
function renderTable(headers, rows) {
let html = '<table style="width: 100%; border-collapse: collapse; font-size: 0.85rem;">';
html += '<thead style="background: #f8fafc; text-transform: uppercase; color: #64748b;"><tr>' + headers.map(h => `<th style="padding: 10px; border-bottom: 2px solid #e2e8f0; text-align: left;">${h}</th>`).join('') + '</tr></thead><tbody>';
rows.forEach(row => {
html += '<tr>' + row.map(cell => `<td style="padding: 10px; border-bottom: 1px solid #e2e8f0;">${cell}</td>`).join('') + '</tr>';
});
html += '</tbody></table>';
document.getElementById('preview-table-container').innerHTML = html;
}
function populateDropdowns(headers) {
const selects = document.querySelectorAll('.source-header');
selects.forEach(select => {
select.innerHTML = '<option value="">-- Select Column --</option>';
headers.forEach(h => {
const opt = document.createElement('option');
opt.value = h;
opt.textContent = h;
select.appendChild(opt);
});
});
}
document.getElementById('adapter-form').onsubmit = async (e) => {
e.preventDefault();
const data = {
name: document.getElementById('name').value,
source_name: document.getElementById('source_name').value,
findings_path: document.getElementById('findings_path').value,
mapping_title: document.getElementById('mapping_title').value,
mapping_asset: document.getElementById('mapping_asset').value,
mapping_severity: document.getElementById('mapping_severity').value,
mapping_description: document.getElementById('mapping_description').value,
mapping_remediation: document.getElementById('mapping_remediation').value
};
const resp = await fetch('/api/adapters', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (resp.ok) {
alert("Adapter Saved! Taking you back to the Landing Zone.");
window.location.href = "/ingest";
} else {
alert("Failed to save adapter: " + await resp.text());
}
};
window.exportAdapterJSON = function() {
const name = document.getElementById("adapterName").value.trim();
const sourceName = document.getElementById("sourceName").value.trim();
const rootPath = document.getElementById("rootPath").value.trim();
if (!name || !sourceName) {
return alert("Adapter Name and Source Name are required to export.");
}
const payload = {
name: name,
source_name: sourceName,
findings_path: rootPath,
mapping_title: document.getElementById("mapTitle").value.trim(),
mapping_asset: document.getElementById("mapAsset").value.trim(),
mapping_severity: document.getElementById("mapSeverity").value.trim(),
mapping_description: document.getElementById("mapDesc").value.trim(),
mapping_remediation: document.getElementById("mapRem").value.trim()
};
// Create a downloadable JSON blob
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(payload, null, 4));
const downloadAnchorNode = document.createElement('a');
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", `${sourceName.toLowerCase().replace(/\s+/g, '_')}_adapter.json`);
document.body.appendChild(downloadAnchorNode);
downloadAnchorNode.click();
downloadAnchorNode.remove();
};