Загрузка данных


local PREFIX = "[AUDIT LITE]"

local function log(msg)
    local line = PREFIX .. " " .. tostring(msg)
    pcall(function() print(line) end)
    pcall(function()
        if type(rconsoleprint) == "function" then
            rconsoleprint(line .. "\n")
        end
    end)
end

local function warnlog(msg)
    local line = PREFIX .. " " .. tostring(msg)
    pcall(function() warn(line) end)
    pcall(function()
        if type(rconsolewarn) == "function" then
            rconsolewarn(line)
        elseif type(rconsoleprint) == "function" then
            rconsoleprint(line .. "\n")
        end
    end)
end

log("01 script entered")

local Players = game:GetService("Players")
local CollectionService = game:GetService("CollectionService")
local RunService = game:GetService("RunService")
log("02 services resolved")

local player = Players.LocalPlayer
if not player then
    warnlog("FATAL LocalPlayer is nil")
    return
end
log("03 LocalPlayer=" .. player.Name)

local playerGui = player:WaitForChild("PlayerGui", 10)
if not playerGui then
    warnlog("FATAL PlayerGui timeout")
    return
end
log("04 PlayerGui ready")

local old = playerGui:FindFirstChild("audit_lite_gui")
if old then
    old:Destroy()
end

local gui = Instance.new("ScreenGui")
gui.Name = "audit_lite_gui"
gui.ResetOnSpawn = false
gui.IgnoreGuiInset = true
gui.DisplayOrder = 999999
gui.Parent = playerGui

local button = Instance.new("TextButton")
button.Name = "audit_button"
button.Size = UDim2.fromOffset(160, 54)
button.Position = UDim2.fromOffset(20, 100)
button.BackgroundColor3 = Color3.fromRGB(60, 115, 235)
button.BorderSizePixel = 0
button.Text = "AUDIT"
button.TextColor3 = Color3.new(1, 1, 1)
button.TextSize = 18
button.Font = Enum.Font.GothamBold
button.ZIndex = 100
button.Parent = gui

log("05 AUDIT button created")

local busy = false

local function fullname(obj)
    local ok, value = pcall(function()
        return obj:GetFullName()
    end)
    if ok then
        return value
    end
    return obj.Name
end

local function read(obj, property)
    local ok, value = pcall(function()
        return obj[property]
    end)
    if ok then
        return value
    end
    return nil
end

local function isa(obj, className)
    local ok, value = pcall(function()
        return obj:IsA(className)
    end)
    return ok and value == true
end

local function containsRiskWord(text)
    local s = string.lower(tostring(text))
    local words = {
        "admin", "owner", "moderator", "money", "cash", "coin", "currency",
        "reward", "price", "damage", "inventory", "weapon", "purchase", "buy",
        "sell", "give", "grant", "teleport", "trade", "loot", "pickup", "claim",
        "redeem", "save", "ban", "kick", "anticheat", "anti_cheat", "token",
        "secret", "password", "apikey", "api_key"
    }
    for _, word in ipairs(words) do
        if string.find(s, word, 1, true) then
            return true, word
        end
    end
    return false, nil
end

local function valueText(value)
    local t = typeof(value)
    if t == "Instance" then
        return fullname(value)
    end
    if t == "string" then
        if #value > 160 then
            return value:sub(1, 160) .. "..."
        end
        return value
    end
    return tostring(value)
end

local function runScan()
    if busy then
        warnlog("scan already running")
        return
    end
    busy = true
    button.Text = "SCANNING..."
    log("============================================================")
    log("SCAN START")

    local ok, err = pcall(function()
        log("PlaceId=" .. tostring(game.PlaceId))
        log("GameId=" .. tostring(game.GameId))
        log("JobId=" .. tostring(game.JobId))
        log("StreamingEnabled=" .. tostring(read(workspace, "StreamingEnabled")))

        local list = game:GetDescendants()
        log("Visible descendants=" .. tostring(#list))

        local remotes = 0
        local clientCode = 0
        local suspiciousState = 0
        local interactions = 0
        local physics = 0
        local assets = 0
        local objectErrors = 0

        for i, obj in ipairs(list) do
            local objectOk, objectErr = pcall(function()
                local path = fullname(obj)
                local risky, word = containsRiskWord(path)

                if isa(obj, "RemoteEvent") or isa(obj, "RemoteFunction") or isa(obj, "UnreliableRemoteEvent") then
                    remotes = remotes + 1
                    if risky then
                        warnlog("REMOTE REVIEW | " .. path .. " | matched=" .. tostring(word) .. " | " .. obj.ClassName)
                    else
                        log("REMOTE | " .. path .. " | " .. obj.ClassName)
                    end
                end

                if isa(obj, "LocalScript") or isa(obj, "ModuleScript") then
                    clientCode = clientCode + 1
                    log("CLIENT CODE | " .. path .. " | " .. obj.ClassName)
                elseif isa(obj, "Script") then
                    local rc = read(obj, "RunContext")
                    if rc and string.find(tostring(rc), "Client", 1, true) then
                        clientCode = clientCode + 1
                        log("CLIENT CODE | " .. path .. " | Script RunContext=" .. tostring(rc))
                    end
                end

                if isa(obj, "ValueBase") and risky then
                    suspiciousState = suspiciousState + 1
                    local value = read(obj, "Value")
                    warnlog("STATE REVIEW | " .. path .. " | value=" .. valueText(value))
                end

                local attrs = obj:GetAttributes()
                for name, value in pairs(attrs) do
                    local attrRisk, attrWord = containsRiskWord(path .. "." .. tostring(name))
                    if attrRisk then
                        suspiciousState = suspiciousState + 1
                        warnlog("ATTRIBUTE REVIEW | " .. path .. "." .. tostring(name) .. " | matched=" .. tostring(attrWord) .. " | value=" .. valueText(value))
                    end
                end

                local tags = CollectionService:GetTags(obj)
                for _, tag in ipairs(tags) do
                    local tagRisk, tagWord = containsRiskWord(path .. "." .. tag)
                    if tagRisk then
                        suspiciousState = suspiciousState + 1
                        warnlog("TAG REVIEW | " .. path .. " | tag=" .. tag .. " | matched=" .. tostring(tagWord))
                    end
                end

                if isa(obj, "ProximityPrompt") then
                    interactions = interactions + 1
                    log("PROMPT | " .. path .. " | Action=" .. valueText(read(obj, "ActionText")) .. " | Distance=" .. valueText(read(obj, "MaxActivationDistance")) .. " | Hold=" .. valueText(read(obj, "HoldDuration")))
                elseif isa(obj, "ClickDetector") then
                    interactions = interactions + 1
                    log("CLICK | " .. path .. " | Distance=" .. valueText(read(obj, "MaxActivationDistance")))
                elseif isa(obj, "DragDetector") then
                    interactions = interactions + 1
                    log("DRAG | " .. path)
                end

                if isa(obj, "BasePart") then
                    local anchored = read(obj, "Anchored")
                    if anchored == false and risky then
                        physics = physics + 1
                        warnlog("PHYSICS REVIEW | " .. path .. " | unanchored | matched=" .. tostring(word))
                    end
                end

                if isa(obj, "Sound") and read(obj, "SoundId") ~= "" then assets = assets + 1 end
                if isa(obj, "Animation") and read(obj, "AnimationId") ~= "" then assets = assets + 1 end
                if isa(obj, "Decal") and read(obj, "Texture") ~= "" then assets = assets + 1 end
                if isa(obj, "Texture") and read(obj, "Texture") ~= "" then assets = assets + 1 end
                if isa(obj, "MeshPart") then assets = assets + 1 end
                if isa(obj, "ImageLabel") or isa(obj, "ImageButton") then assets = assets + 1 end
            end)

            if not objectOk then
                objectErrors = objectErrors + 1
                if objectErrors <= 10 then
                    warnlog("OBJECT ERROR #" .. tostring(objectErrors) .. " index=" .. tostring(i) .. " | " .. tostring(objectErr))
                end
            end

            if i % 300 == 0 then
                log("progress " .. tostring(i) .. "/" .. tostring(#list))
                RunService.Heartbeat:Wait()
            end
        end

        log("---------------- SUMMARY ----------------")
        log("Remotes=" .. tostring(remotes))
        log("ClientCode=" .. tostring(clientCode))
        log("SuspiciousState=" .. tostring(suspiciousState))
        log("Interactions=" .. tostring(interactions))
        log("SuspiciousPhysics=" .. tostring(physics))
        log("VisibleAssets=" .. tostring(assets))
        log("ObjectErrors=" .. tostring(objectErrors))

        if read(workspace, "StreamingEnabled") == true then
            warnlog("LIMITATION: StreamingEnabled=true, so Workspace results are only what this client currently has streamed")
        end
        warnlog("LIMITATION: client scan cannot inspect server-only code or prove OnServerEvent/OnServerInvoke validation")
        log("SCAN COMPLETE")
        log("============================================================")
    end)

    busy = false
    if ok then
        button.Text = "AUDIT AGAIN"
    else
        button.Text = "AUDIT ERROR"
        warnlog("FATAL SCAN ERROR: " .. tostring(err))
    end
end

button.Activated:Connect(function()
    log("06 AUDIT clicked")
    runScan()
end)

log("READY - press AUDIT")