Static security analysis and Tampermonkey boilerplate packager.
// ==UserScript==
// @name Script Security Analyzer & Boilerplate Packager
// @namespace http://tampermonkey.net/
// @version 3.2
// @description Static security analysis and Tampermonkey boilerplate packager.
// @match *://*/*
// @grant none
// @icon https://cdn-icons-png.flaticon.com/512/1006/1006771.png
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
let mainHost = null;
// 4 Dil Desteği - Varsayılan İngilizce
const translations = {
en: {
titleLeft: "Source Code Input",
btnClose: "Close Panel",
placeholderInput: "Paste your code here...",
tabAudit: "Security Audit",
tabPackage: "Script Packager",
secTitleAudit: "Static Code Security Audit Report",
infoNotice: "Paste your code on the left to trigger real-time analysis.",
secTitleMeta: "Greasemonkey / Tampermonkey Meta Settings",
lblScriptName: "Script Name:",
lblVersion: "Version:",
lblMatch: "Match Sites (@match):",
lblDesc: "Description:",
btnBuild: "Build Installation Package",
placeholderOutput: "Your packaged meta code will appear here...",
btnCopy: "Copy Package Code",
btnClearInput: "Clear Input Code",
cleanScript: "Clean Script",
cleanScriptDesc: "No obvious static security risks (eval, unsafeWindow, etc.) detected."
},
tr: {
titleLeft: "Kaynak Kod Girisi",
btnClose: "Paneli Kapat",
placeholderInput: "Kodunuzu buraya yapistirin...",
tabAudit: "Guvenlik Taramasi",
tabPackage: "Script Paketleyici",
secTitleAudit: "Statik Kod Guvenlik Analiz Raporu",
infoNotice: "Gercek zamanli analizi tetiklemek icin sol tarafa kod yapistirin.",
secTitleMeta: "Greasemonkey / Tampermonkey Meta Ayarlari",
lblScriptName: "Betik Adi:",
lblVersion: "Versiyon:",
lblMatch: "Calisacagi Siteler (@match):",
lblDesc: "Aciklama:",
btnBuild: "Kurulum Paketini Olustur",
placeholderOutput: "Olusturulan meta paket kodu burada belirecektir...",
btnCopy: "Paket Kodunu Kopyala",
btnClearInput: "Giris Kodunu Temizle",
cleanScript: "Temiz Script",
cleanScriptDesc: "Belirgin bir statik guvenlik riski (eval, unsafeWindow vb.) tespit edilmedi."
},
de: {
titleLeft: "Quellcode-Eingabe",
btnClose: "Panel Schliessen",
placeholderInput: "Fuegen Sie Ihren Code hier ein...",
tabAudit: "Sicherheitsaudit",
tabPackage: "Skript-Packer",
secTitleAudit: "Sicherheitsbericht fuer statischen Code",
infoNotice: "Fuegen Sie links Code ein, um die Echtzeitanalyse zu starten.",
secTitleMeta: "Greasemonkey / Tampermonkey Meta-Einstellungen",
lblScriptName: "Skriptname:",
lblVersion: "Version:",
lblMatch: "Passende Seiten (@match):",
lblDesc: "Beschreibung:",
btnBuild: "Installationspaket Packen",
placeholderOutput: "Ihr gepackter Meta-Code wird hier angezeigt...",
btnCopy: "Paketcode Kopieren",
btnClearInput: "Eingabecode Loeschen",
cleanScript: "Sauberes Skript",
cleanScriptDesc: "Keine offensichtlichen statischen Sicherheitsrisiken (eval, unsafeWindow usw.) erkannt."
},
fr: {
titleLeft: "Saisie du Code Source",
btnClose: "Fermer le Panneau",
placeholderInput: "Collez votre code ici...",
tabAudit: "Audit de Securite",
tabPackage: "Package de Script",
secTitleAudit: "Rapport d'Audit de Securite du Code Statique",
infoNotice: "Collez le code a gauche pour lancer l'analyse en temps reel.",
secTitleMeta: "Parametres Meta Greasemonkey / Tampermonkey",
lblScriptName: "Nom du Script:",
lblVersion: "Version:",
lblMatch: "Sites de Match (@match):",
lblDesc: "Description:",
btnBuild: "Creer le Package d'Installation",
placeholderOutput: "Votre code meta package apparaitra ici...",
btnCopy: "Copier le Code du Package",
btnClearInput: "Effacer le Code Source",
cleanScript: "Script Propre",
cleanScriptDesc: "Aucun risque de securite statique evident (eval, unsafeWindow, etc.) detecte."
}
};
let currentLang = 'en'; // Varsayılan İngilizce
let t = translations[currentLang];
function toggleAnalyzerPanel() {
if (mainHost) { mainHost.remove(); mainHost = null; }
else { createAnalyzerPanel(); }
}
function runSecurityAudit(code) {
const findings = [];
if (!code.trim()) return [{ type: 'info', name: "Notice", desc: t.infoNotice }];
const rules = [
{ rx: /\beval\s*\(/g, type: 'danger', name: 'Risk (eval)', desc: 'Dynamic code execution detected.' },
{ rx: /\bunsafeWindow\b/g, type: 'warning', name: 'Zafiyet (unsafeWindow)', desc: 'Direct page window access.' },
{ rx: /\b(fetch|XMLHttpRequest)\b/g, type: 'info', name: 'Network', desc: 'External data transfer detected.' },
{ rx: /document\.write\s*\(/g, type: 'danger', name: 'DOM (document.write)', desc: 'Direct DOM writing.' },
{ rx: /location\.href\s*=/g, type: 'warning', name: 'Redirect', desc: 'Script contains redirection commands.' },
{ rx: /GM_(xmlhttpRequest|setValue|getValue|download)/g, type: 'info', name: 'API Usage', desc: 'Extension privileged API functions triggered.' }
];
rules.forEach(rule => {
const matches = code.match(rule.rx);
if (matches) {
findings.push({
type: rule.type,
name: rule.name,
desc: rule.desc,
count: matches.length
});
}
});
if (findings.length === 0) {
findings.push({ type: 'success', name: t.cleanScript, desc: t.cleanScriptDesc });
}
return findings;
}
function generateBoilerplate(code, meta) {
return [
`// ==UserScript==`,
`// @name ${meta.name || 'Production Script'}`,
`// @namespace http://tampermonkey.net/`,
`// @version ${meta.version || '1.0.0'}`,
`// @description ${meta.desc || 'Packaged using premium builder.'}`,
`// @match ${meta.match || '*://*/*'}`,
`// @grant ${meta.grant || 'none'}`,
`// @run-at ${meta.runAt || 'document-end'}`,
`// ==/UserScript==\n`,
`(function() {`,
` 'use strict';\n`,
code.split('\n').map(line => ' ' + line).join('\n'),
`\n})();`
].join('\n');
}
function createAnalyzerPanel() {
if (mainHost) return;
mainHost = document.createElement('div');
mainHost.id = "script-analyzer-host";
mainHost.style.position = "fixed";
mainHost.style.zIndex = "999999999";
mainHost.style.top = "0";
mainHost.style.left = "0";
const shadow = mainHost.attachShadow({ mode: 'open' });
document.body.appendChild(mainHost);
const overlay = document.createElement('div');
overlay.className = "sandbox-overlay";
overlay.innerHTML = `
<div class="sandbox-container">
<div class="panel editor-panel">
<div class="panel-header">
<span id="txtTitleLeft">${t.titleLeft}</span>
<div style="display:flex; gap:8px; align-items:center;">
<select id="langSelect" class="crayon-select">
<option value="en" ${currentLang==='en'?'selected':''}>EN</option>
<option value="tr" ${currentLang==='tr'?'selected':''}>TR</option>
<option value="de" ${currentLang==='de'?'selected':''}>DE</option>
<option value="fr" ${currentLang==='fr'?'selected':''}>FR</option>
</select>
<button id="clearInputBtn" class="crayon-btn yellow-crayon" style="padding: 4px 10px; font-size: 0.75rem;">${t.btnClearInput}</button>
<button id="closeBtn" class="close-btn">${t.btnClose}</button>
</div>
</div>
<div class="code-area">
<pre class="line-numbers" id="leftLines">1</pre>
<textarea id="inputCode" spellcheck="false" wrap="off" placeholder="${t.placeholderInput}"></textarea>
</div>
</div>
<div class="panel tools-panel">
<div class="tabs-header">
<button id="tabAudit" class="tab-btn active">${t.tabAudit}</button>
<button id="tabPackage" class="tab-btn">${t.tabPackage}</button>
</div>
<div class="tab-content" id="contentArea">
<div id="auditView" class="view-block">
<div class="section-title" id="txtSecTitleAudit">${t.secTitleAudit}</div>
<div id="auditReportList" class="report-list">
<div class="info-notice">${t.infoNotice}</div>
</div>
</div>
<div id="packageView" class="view-block" style="display: none;">
<div class="section-title" id="txtSecTitleMeta">${t.secTitleMeta}</div>
<div class="meta-form">
<div class="form-group"><label id="lblScriptName">${t.lblScriptName}</label><input type="text" id="metaName" value="My New Script"></div>
<div class="form-group"><label id="lblVersion">${t.lblVersion}</label><input type="text" id="metaVersion" value="1.0.0"></div>
<div class="form-group"><label id="lblMatch">${t.lblMatch}</label><input type="text" id="metaMatch" value="*://*/*"></div>
<div class="form-group"><label id="lblDesc">${t.lblDesc}</label><input type="text" id="metaDesc" value="Packaged production script."></div>
</div>
<div style="display:flex; gap:10px;">
<button id="btnBuildPackage" class="crayon-btn green-crayon" style="flex:1;">${t.btnBuild}</button>
<button id="btnCopyPackage" class="crayon-btn yellow-crayon" style="padding:0 15px;">${t.btnCopy}</button>
</div>
<div class="code-area output-area" style="margin-top: 15px; height: 180px;">
<textarea id="outputPackageCode" readonly placeholder="${t.placeholderOutput}"></textarea>
</div>
</div>
</div>
</div>
</div>
`;
const style = document.createElement('style');
style.textContent = `
/* Çocuksu ve Pastel Boya Sanat Tarzı Tasarım Kuralları */
@import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@400;700&display=swap');
.sandbox-overlay * {
box-sizing: border-box; margin: 0; padding: 0;
font-family: 'Comic Neue', 'Comic Sans MS', cursive, sans-serif;
font-weight: 700;
}
.sandbox-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(20, 25, 35, 0.85); display: flex; align-items: center; justify-content: center; }
/* Konteyner: Pastel Mavi çocuk boyaması arka planı */
.sandbox-container {
width: 96vw; max-width: 1550px; height: 88vh; max-height: 88vh;
background: #E0F2FE;
border: 6px dashed #38BDF8;
border-radius: 30px; padding: 20px;
display: grid; grid-template-columns: 1fr 1fr; gap: 20px; overflow: hidden;
box-shadow: inset 0 0 40px rgba(56, 189, 248, 0.4);
}
/* Paneller: Sanki kağıda pastel tebeşirle çizilmiş gibi */
.panel {
background: #FFFDFA;
border: 5px solid #F43F5E; /* Pastel Kırmızı tebeşir çizgisi */
border-radius: 24px; padding: 16px;
display: flex; flex-direction: column; gap: 14px; height: 100%; overflow: hidden;
box-shadow: 5px 5px 0px #FB7185;
}
.panel-header { display: flex; justify-content: space-between; align-items: center; height: 35px; }
.panel-header span { font-size: 1.1rem; color: #475569; letter-spacing: -0.5px; }
/* Pastel Boya Buton Tipleri */
.crayon-btn {
border: 4px solid #1E293B; border-radius: 14px; padding: 10px; font-size: 0.9rem; cursor: pointer; color: #1E293B;
box-shadow: 3px 3px 0px #1E293B; transition: transform 0.1s;
}
.crayon-btn:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0px #1E293B; }
.green-crayon { background: #4ADE80; border-color: #166534; box-shadow: 3px 3px 0px #166534; color: #166534; }
.yellow-crayon { background: #FDE047; border-color: #854D0E; box-shadow: 3px 3px 0px #854D0E; color: #854D0E; }
.close-btn { background: #FB7185; border: 4px solid #9F1239; color: #9F1239; padding: 6px 12px; border-radius: 12px; font-size: 0.8rem; cursor: pointer; box-shadow: 3px 3px 0px #9F1239; }
.crayon-select { background: #F3F4F6; border: 3px solid #475569; border-radius: 10px; padding: 4px 8px; font-size: 0.8rem; cursor: pointer; outline: none; }
/* Sekme Menü Alanı */
.tabs-header { display: flex; background: #FED7AA; border: 4px solid #EA580C; border-radius: 16px; padding: 4px; gap: 4px; }
.tab-btn { flex: 1; border: none; background: transparent; color: #C2410C; padding: 10px; border-radius: 12px; font-size: 0.9rem; cursor: pointer; }
.tab-btn.active { background: #EA580C; color: white; }
.tab-content { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.view-block { display: flex; flex-direction: column; height: 100%; overflow: hidden; }
.section-title { font-size: 1.1rem; color: #1E293B; margin-bottom: 12px; border-left: 5px solid #FDE047; padding-left: 8px; }
/* Kod Giriş Kağıdı */
.code-area { flex: 1; display: flex; background: #1E293B; border-radius: 16px; border: 4px solid #475569; overflow: hidden; position: relative; }
.line-numbers { background: #0F172A; padding: 16px 8px; text-align: right; font-family: monospace; font-size: 0.85rem; color: #64748B; min-width: 55px; max-width: 55px; line-height: 1.6; overflow: hidden; border-right: 2px solid #334155; white-space: pre; }
textarea { flex: 1; padding: 16px; resize: none; border: none; background: transparent; outline: none; color: #F1F5F9; font-family: monospace; font-size: 0.85rem; line-height: 1.6; overflow: auto; }
.output-area { background: #0F172A; }
/* Rapor Kartları: Pastel Renk Paletleri */
.report-list { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; padding-right: 4px; }
.info-notice { color: #64748B; font-size: 0.9rem; text-align: center; margin-top: 40px; }
.audit-card { border-radius: 14px; padding: 12px; font-size: 0.9rem; border: 3px solid #1E293B; box-shadow: 3px 3px 0px #1E293B; }
.audit-card.danger { background: #FCA5A5; color: #7F1D1D; border-color: #7F1D1D; box-shadow: 3px 3px 0px #7F1D1D; }
.audit-card.warning { background: #FDE047; color: #713F12; border-color: #713F12; box-shadow: 3px 3px 0px #713F12; }
.audit-card.info { background: #93C5FD; color: #1E3A8A; border-color: #1E3A8A; box-shadow: 3px 3px 0px #1E3A8A; }
.audit-card.success { background: #86EFAC; color: #14532D; border-color: #14532D; box-shadow: 3px 3px 0px #14532D; }
.card-title { font-weight: bold; margin-bottom: 4px; display: flex; justify-content: space-between; }
.card-desc { font-size: 0.8rem; opacity: 0.9; }
/* Form Elemanları */
.meta-form { display: flex; flex-direction: column; gap: 10px; background: #FEF3C7; border: 4px solid #D97706; padding: 14px; border-radius: 16px; margin-bottom: 12px; box-shadow: 3px 3px 0px #D97706; }
.form-group { display: flex; flex-direction: column; gap: 4px; }
.form-group label { font-size: 0.8rem; color: #92400E; }
.form-group input { background: #FFF; border: 3px solid #D97706; border-radius: 10px; padding: 8px; color: #1E293B; font-size: 0.85rem; outline: none; }
`;
shadow.appendChild(style);
shadow.appendChild(overlay);
const inputCode = shadow.getElementById('inputCode');
const leftLines = shadow.getElementById('leftLines');
const closeBtn = shadow.getElementById('closeBtn');
const clearInputBtn = shadow.getElementById('clearInputBtn');
const langSelect = shadow.getElementById('langSelect');
const tabAudit = shadow.getElementById('tabAudit');
const tabPackage = shadow.getElementById('tabPackage');
const auditView = shadow.getElementById('auditView');
const packageView = shadow.getElementById('packageView');
const auditReportList = shadow.getElementById('auditReportList');
const btnBuildPackage = shadow.getElementById('btnBuildPackage');
const btnCopyPackage = shadow.getElementById('btnCopyPackage');
const outputPackageCode = shadow.getElementById('outputPackageCode');
const metaName = shadow.getElementById('metaName');
const metaVersion = shadow.getElementById('metaVersion');
const metaMatch = shadow.getElementById('metaMatch');
const metaDesc = shadow.getElementById('metaDesc');
// Statik Dil Güncelleme Motoru
const updateLanguageUI = (langKey) => {
currentLang = langKey;
t = translations[currentLang];
shadow.getElementById('txtTitleLeft').textContent = t.titleLeft;
closeBtn.textContent = t.btnClose;
clearInputBtn.textContent = t.btnClearInput;
inputCode.placeholder = t.placeholderInput;
tabAudit.textContent = t.tabAudit;
tabPackage.textContent = t.tabPackage;
shadow.getElementById('txtSecTitleAudit').textContent = t.secTitleAudit;
shadow.getElementById('txtSecTitleMeta').textContent = t.secTitleMeta;
shadow.getElementById('lblScriptName').textContent = t.lblScriptName;
shadow.getElementById('lblVersion').textContent = t.lblVersion;
shadow.getElementById('lblMatch').textContent = t.lblMatch;
shadow.getElementById('lblDesc').textContent = t.lblDesc;
btnBuildPackage.textContent = t.btnBuild;
btnCopyPackage.textContent = t.btnCopy;
outputPackageCode.placeholder = t.placeholderOutput;
triggerAuditRun();
};
const updateLineNumbers = () => {
const lines = inputCode.value.split('\n');
let nums = '';
for (let i = 1; i <= lines.length; i++) nums += i + '\n';
leftLines.textContent = nums;
};
const triggerAuditRun = () => {
const reports = runSecurityAudit(inputCode.value);
auditReportList.innerHTML = '';
reports.forEach(item => {
const card = document.createElement('div');
card.className = `audit-card ${item.type}`;
if (item.type === 'success' || (item.type === 'info' && !inputCode.value.trim())) {
card.innerHTML = `
<div class="card-title"><span>${item.name}</span></div>
<div class="card-desc">${item.desc}</div>
`;
} else {
card.innerHTML = `
<div class="card-title"><span>${item.name}</span> <span style="background:rgba(0,0,0,0.1); padding:2px 6px; border-radius:8px;">${item.count}</span></div>
<div class="card-desc">${item.desc}</div>
`;
}
auditReportList.appendChild(card);
});
};
// Dil Değişimi Dinleyicisi
langSelect.addEventListener('change', (e) => {
updateLanguageUI(e.target.value);
});
// Giriş Alanı Temizleme Kolaylığı
clearInputBtn.addEventListener('click', () => {
inputCode.value = '';
updateLineNumbers();
triggerAuditRun();
inputCode.focus();
});
inputCode.addEventListener('input', () => {
updateLineNumbers();
triggerAuditRun();
});
inputCode.addEventListener('scroll', () => { leftLines.scrollTop = inputCode.scrollTop; });
tabAudit.addEventListener('click', () => {
tabAudit.className = "tab-btn active";
tabPackage.className = "tab-btn";
auditView.style.display = "flex";
packageView.setAttribute('style', 'display:none;');
triggerAuditRun();
});
tabPackage.addEventListener('click', () => {
tabPackage.className = "tab-btn active";
tabAudit.className = "tab-btn";
auditView.style.display = "none";
packageView.style.display = "flex";
});
btnBuildPackage.addEventListener('click', () => {
const metaData = {
name: metaName.value,
version: metaVersion.value,
match: metaMatch.value,
desc: metaDesc.value
};
outputPackageCode.value = generateBoilerplate(inputCode.value, metaData);
});
// Tek Tıkla Kopyalama Kolaylığı
btnCopyPackage.addEventListener('click', () => {
if (outputPackageCode.value.trim()) {
navigator.clipboard.writeText(outputPackageCode.value);
const oldText = btnCopyPackage.textContent;
btnCopyPackage.textContent = "Copied!";
setTimeout(() => { btnCopyPackage.textContent = oldText; }, 1500);
}
});
const closePanel = () => { if (mainHost) { mainHost.remove(); mainHost = null; } };
closeBtn.addEventListener('click', closePanel);
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closePanel(); });
updateLanguageUI(currentLang);
updateLineNumbers();
}
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'a') {
e.preventDefault(); toggleAnalyzerPanel();
}
});
})();