GF Code Scanner [Local + Ai]

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

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

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