GF Code Scanner [Local + Ai]

Проверка кода скриптов на GreasyFork на безопасность (Локальная + ИИ на русском )

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         GF Code Scanner [Local + Ai]
// @name:en      GF Code Scanner [Local + Ai]
// @namespace    http://tampermonkey.net/
// @version      1.0.3
// @description  Проверка кода скриптов на GreasyFork на безопасность (Локальная  + ИИ на русском )
// @description:en Security check for GreasyFork scripts (Local scan + AI in Russian)
// @match        https://greasyfork.org/*/scripts/*/code*
// @match        https://greasyfork.org/*/scripts/*
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @connect      api.groq.com
// @connect      api.deepseek.com
// @connect      dashscope.aliyuncs.com
// @connect      api.openai.com
// @connect      api.x.ai
// @connect      generativelanguage.googleapis.com
// @require      https://update.greasyfork.org/scripts/34138/223779/markedjs.js
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function() {
    'use strict';
    console.log('[GF-Scanner-v1.0.3] Start');

    // 1. КОНФИГ МОДЕЛЕЙ
    const MODELS = {
        groq: { n:'Groq', u:'https://api.groq.com/openai/v1/chat/completions', m:'llama-3.3-70b-versatile' },
        deepseek: { n:'DeepSeek', u:'https://api.deepseek.com/chat/completions', m:'deepseek-chat' },
        gemini: { n:'Gemini', u:'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent', m:'gemini-2.5-flash', g:true },
        grok: { n:'Grok', u:'https://api.x.ai/v1/chat/completions', m:'grok-beta' },
        qwen: { n:'Qwen', u:'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions', m:'qwen-turbo' },
        openai: { n:'ChatGPT', u:'https://api.openai.com/v1/chat/completions', m:'gpt-3.5-turbo' }
    };

    let K = GM_getValue('k', {});
    let S = GM_getValue('s', 'groq');

    // 2. ПОЛНЫЙ СПИСОК ПРАВИЛ (ВОЗВРАЩЕНО ВСЁ)
    const R = [
        { p:/\beval\s*\(/g, v:4, t:'eval()', d:'Выполнение кода' },
        { p:/\bnew\s+Function\s*\(/g, v:4, t:'new F()', d:'Динамика' },
        { p:/coinhive|cryptonight|monero|minero\.js/i, v:4, t:'Miner', d:'Криптомайнинг' },
        { p:/\batob\s*\(/g, v:3, t:'atob()', d:'Скрытый Base64' },
        { p:/_\x04|_0x[a-f0-9]{4,}/i, v:3, t:'Obfus', d:'Запутанный код' },
        { p:/String\.fromCharCode/i, v:3, t:'CharCode', d:'Обфускация' },
        { p:/\bnavigator\.geolocation\b/g, v:3, t:'Geo', d:'GPS' },
        { p:/\bnavigator\.mediaDevices\b/g, v:3, t:'Cam/Mic', d:'Камера/Микрофон' },
        { p:/\bGM_xmlhttpRequest\b/g, v:3, t:'GM_XHR', d:'Скрытые запросы' },
        { p:/\bdocument\.cookie\b/g, v:2, t:'Cookies', d:'Доступ к куки' },
        { p:/\blocalStorage\b/g, v:2, t:'Local', d:'Хранение данных' },
        { p:/\bsessionStorage\b/g, v:2, t:'Session', d:'Врем. хранение' },
        { p:/\bnavigator\.clipboard\b/g, v:2, t:'Clipboard', d:'Буфер обмена' },
        { p:/\bGM_getValue\b|\bGM_setValue\b/g, v:2, t:'GM_Store', d:'Хранилище TM' },
        { p:/\bfetch\s*\(/g, v:1, t:'Fetch', d:'Сетевой запрос' },
        { p:/\bXMLHttpRequest\b/g, v:1, t:'XHR', d:'Сетевой запрос' },
        { p:/\bdocument\.write\s*\(/g, v:2, t:'DocWrite', d:'Перезапись страницы' }
    ];

    const U = /https?:\/\/[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)/gi;
    const getL = v => ({0:0,1:1,2:2,3:3,4:4}[v]||0);
    const E = t => t ? t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;") : '';

    // Поиск строк
    const FL = (c, p) => {
        let r=[], x=new RegExp(p.source,p.flags);
        c.split('\n').forEach((l,i)=>{ if(x.test(l)) r.push('Str '+(i+1)+': '+l.trim().substr(0,70)); x.lastIndex=0; });
        return r.slice(0,5);
    };

    // Поиск URL
    const GU = c => {
        let u=[...new Set(c.match(U)||[])], d=new Set();
        u.forEach(x=>{ try{d.add(new URL(x).hostname)}catch(e){} });
        return {u,d:[...d]};
    };

    const scan = () => {
        let old=document.getElementById('gfs'); if(old) old.remove();
        let blk=document.querySelector('pre code')||document.querySelector('pre');
        if(!blk) return;

        let code=blk.innerText, f=[], max=0;
        
        // Запуск полного сканирования
        R.forEach(r=>{
            let m=code.match(new RegExp(r.p.source,r.p.flags));
            if(m){ f.push({...r,c:m.length,l:FL(code,r.p)}); if(r.v>max) max=r.v; }
        });
        let net=GU(code);

        let box=document.createElement('div'); box.id='gfs';
        box.style.cssText='background:#222;color:#fff;border:2px solid #0f0;padding:15px;margin:10px 0;font-family:sans-serif;border-radius:8px;';

        let col='#0f0', txt='Safe';
        if(max===1){col='#ff0';txt='Low Risk';}
        if(max===2){col='#fa0';txt='Medium Risk';}
        if(max>=3){col='#f00';txt='DANGER';}
        box.style.borderColor=col;

        // --- ПОЛНЫЙ ОТЧЕТ ---
        let h='<h3 style="margin:0;color:'+col+'">Report: '+txt+'</h3>';
        h+='<p>Issues: '+f.length+' | Domains: '+net.d.length+'</p>';

        // Домены
        if(net.d.length) {
            h+='<div style="background:#333;padding:8px;margin:5px 0;border-radius:4px;">';
            h+='<b style="color:#4fc3f7">Domains:</b> '+net.d.join(', ')+'</div>';
            if(net.u.length<=15) {
                h+='<details style="font-size:11px;color:#aaa;"><summary>Show URLs</summary><div style="word-break:break-all;">'+net.u.map(E).join('<br>')+'</div></details>';
            }
        }

        // Угрозы (ПОДРОБНО)
        if(f.length) {
            f.forEach(x=>{
                let c=x.v===4?'#f00':'#fa0';
                h+='<div style="border-left:3px solid '+c+';padding:5px;margin:5px 0;background:#333;">';
                h+='<b style="color:'+c+'">'+x.t+'</b>: '+x.d+' ('+x.c+')';
                if(x.l.length) {
                    h+='<details><summary style="cursor:pointer;font-size:10px;color:#aaa;">Show Lines</summary>';
                    h+='<div style="background:#111;padding:4px;font-family:monospace;font-size:9px;color:#aaa;">'+x.l.join('<br>')+'</div></details>';
                }
                h+='</div>';
            });
        } else if (!net.d.length) {
            h+='<p style="color:#aaa">No threats found.</p>';
        }

        // Кнопки и выбор модели
        h+='<div style="margin-top:15px;display:flex;gap:10px;flex-wrap:wrap;">';
        h+='<button id="gfb-local" style="flex:1;padding:8px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;font-weight:bold;">Retry Local</button>';
        h+='<button id="gfb-ai" style="flex:1;padding:8px;background:linear-gradient(45deg,#667eea,#764ba2);color:#fff;border:none;border-radius:4px;cursor:pointer;font-weight:bold;">Check AI</button>';
        h+='</div>';
        
        let opts='';
        Object.keys(MODELS).forEach(k=>{ opts+='<option value="'+k+'" '+(k===S?'selected':'')+'>'+MODELS[k].n+'</option>'; });
        h+='<select id="gfs-sel" style="margin-top:5px;background:#333;color:#fff;border:1px solid #555;padding:4px;border-radius:4px;">'+opts+'</select>';
        h+='<div id="gfr" style="margin-top:10px;display:none;background:#111;padding:10px;border:1px solid #444;"></div>';
        
        box.innerHTML=h;
        let tgt=document.querySelector('pre');
        if(tgt) tgt.parentNode.insertBefore(box,tgt); else document.body.prepend(box);

        // Логика
        document.getElementById('gfs-sel').onchange=e=>{ S=e.target.value; GM_setValue('s',S); };
        document.getElementById('gfb-local').onclick=()=>scan();

        document.getElementById('gfb-ai').onclick=async ()=>{
            let btn=document.getElementById('gfb-ai'), res=document.getElementById('gfr');
            let key=K[S];
            
            // Запрос ключа
            if(!key){
                let g={groq:'https://console.groq.com/keys',deepseek:'https://platform.deepseek.com/api_keys',gemini:'https://aistudio.google.com/app/apikey',grok:'https://console.x.ai/',openai:'https://platform.openai.com/api-keys',qwen:'https://dashscope.console.aliyun.com/apiKey'};
                key=prompt('Enter Key for '+MODELS[S].n+' ('+g[S]+'):', '');
                if(key&&key.length>5){ K[S]=key; GM_setValue('k',K); } else { alert('No key'); return; }
            }

            btn.disabled=true; btn.innerText='Thinking...';
            res.style.display='block'; res.innerHTML='Sending data to '+MODELS[S].n+'...';

            let cfg=MODELS[S];
            
            // Формирование данных для ИИ
            let localData = f.length ? 'LOCAL SCAN FOUND:\n' + f.map(x=>'- '+x.t+': '+x.d+' (Lines: '+x.l.join('; ')+')').join('\n') : 'Local scan found nothing obvious.';
            let domainData = net.d.length ? 'DOMAINS: '+net.d.join(', ') : 'No external domains.';
            
            // ПРОМПТ НА РУССКОМ С ЖЕСТКИМ ТРЕБОВАНИЕМ
            let pr = 'YOU ARE A JS SECURITY EXPERT. ANSWER STRICTLY IN RUSSIAN LANGUAGE.\n\n';
            pr += localData + '\n' + domainData + '\n\nFULL CODE:\n' + code.substr(0,15000);
            pr += '\n\nTASK:\n1. Verify each local threat (Confirm/False Positive).\n2. Find hidden threats.\n3. Verdict (SAFE/DANGEROUS).\n4. Answer in RUSSIAN ONLY.';

            let body, headers={'Content-Type':'application/json'};
            
            // Исправленная логика запросов для разных провайдеров
            if(cfg.g){
                // Gemini
                body = JSON.stringify({contents:[{parts:[{text:pr}]}]});
                if(cfg.u.includes('?')) {
                     // Если ключ уже в URL (редко), но обычно для Gemini ключ в query param
                } else {
                     // Для Gemini ключ часто в URL
                }
            } else {
                // OpenAI, DeepSeek, Groq, Grok - стандартный формат
                body = JSON.stringify({
                    model: cfg.m,
                    messages: [
                        {role:'system', content:'You are a security expert. Answer strictly in Russian.'},
                        {role:'user', content:pr}
                    ],
                    temperature: 0.2
                });
                headers['Authorization'] = 'Bearer '+key;
            }

            let url = cfg.u;
            if(cfg.g && cfg.u.indexOf('?')===-1) url += '?key='+key;

            GM_xmlhttpRequest({
                method:'POST',
                url:url,
                headers:headers,
                data:body,
                onload:r=>{
                    try{
                        let j=JSON.parse(r.responseText);
                        let ans='';
                        
                        if(cfg.g){
                            // Gemini ответ
                            if(j.candidates && j.candidates[0] && j.candidates[0].content && j.candidates[0].content.parts) {
                                ans = j.candidates[0].content.parts[0].text;
                            } else if(j.error) { ans='Error: '+j.error.message; }
                        } else {
                            // OpenAI/DeepSeek/Groq ответ
                            if(j.choices && j.choices[0] && j.choices[0].message) {
                                ans = j.choices[0].message.content;
                            } else if(j.error) { ans='Error: '+j.error.message; }
                        }

                        if(!ans) ans = 'Empty response from AI.';
                        
                        // Рендеринг
                        res.innerHTML = typeof marked!=='undefined' ? marked.parse(ans) : ans.replace(/\n/g,'<br>');
                    }catch(e){ 
                        res.innerHTML='Parse Error: '+e.message+'. Response: '+r.responseText.substr(0,200); 
                    }
                    btn.disabled=false; btn.innerText='Retry AI';
                },
                onerror:()=>{ 
                    res.innerHTML='Network Error. Check console (F12).'; 
                    btn.disabled=false; btn.innerText='Error'; 
                }
            });
        };
    };
    setTimeout(scan, 800);
})();