Script Security Analyzer & Boilerplate Packager

Static security analysis and Tampermonkey boilerplate packager.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

Advertisement:

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

Advertisement:

// ==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();
        }
    });
})();