This program allows you to execute programs with protection so that any time a program wants to call a potentially malicious function, you are prompted for your permission the program to execute said call.
For example, if I wasn't sure if a program I just downloaded would wipe my in-game computer clean and didn't want to read through the thousands of lines of code or messy code then I could type the following into my shell:
av programName
Of course, the program doesn't have to be named 'av' nor does the program you're executing need to be named 'programName'. I digress. This command would cause the program to be executed, but if it tried to call 'fs.delete' I would get the following prompt. I could either press 'Y' or 'N' depending on whether or not I wanted to allow the call or not. That's the gist of it, but hopefully it will save a few people's work or perhaps just be an interesting concept to think about.In advance, I apologize for the simple UI, but I really didn't think this program needed anything extravagant in order to serve its purpose.
Here is the code:
Spoiler
--[[
Protected Execute Trystan Cannon
16 May 2013
This program allows for users to run other
programs without the risk of potentially
malicious functions to execute without their
allowing so. Currently, this program only
prevents unauthorized 'fs' and 'os' calls.
Version: 1.0 Updated 16 May 2013
--]]
-- Variables ------------------------------------
local NATIVE_TERMINAL = term.native -- A reference to the native terminal functions.
_G.screenBuffer = {} -- A table containing the visual contents of the screen.
local WARNING_BOX_WIDTH, WARNING_BOX_HEIGHT = 25, 7 -- The width and height of every warning box that is drawn.
local oldFunctions = {}
-- Variables ------------------------------------
-- Screen Buffer Functions ------------------------------------
-- Checks if the given color is a valid color on the computer. Returns true or false depending on the aforementioned condition.
local function isColorValid (color)
for colorIndex, colorValue in pairs (colors) do
if colorValue == color and type (colorValue) == "number" then
return true
end
end
return false
end
-- Initializes the global table 'screenBuffer' table to keep track of the screen's contents. Also, this method redirects the terminal
-- to write to the screen buffer and the screen.
local function initScreenBuffer()
-- Initialize the screen buffer to be an empty canvas of cells whose text color is white and background color is black.
screenBuffer.currentTextColor = colors.white
screenBuffer.currentBackgroundColor = colors.black
local screenWidth, screenHeight = term.getSize()
for height = 1, screenHeight do
screenBuffer[height] = {}
for width = 1, screenWidth do
screenBuffer[height][width] = {text = ' ', textColor = colors.white, backgroundColor = colors.black}
end
end
-- Create a new terminal table which will have all of the native functions with some edited functionality.
local terminal = {}
for functionName, functionObject in pairs (NATIVE_TERMINAL) do
terminal[functionName] = functionObject
end
-- Overrides the term.write method to write to the screen buffer and the screen.
terminal.write = function (text)
local cursorX, cursorY = term.getCursorPos()
if cursorX < 1 or cursorX > screenWidth or cursorY < 1 or cursorY > screenHeight then
return NATIVE_TERMINAL.write (text)
end
for charIndex = 1, text:len() do
screenBuffer[cursorY][cursorX].text = text:sub (charIndex, charIndex)
screenBuffer[cursorY][cursorX].textColor = screenBuffer.currentTextColor
screenBuffer[cursorY][cursorX].backgroundColor = screenBuffer.currentBackgroundColor
cursorX = cursorX + 1
if cursorX > screenWidth then
break
end
end
return NATIVE_TERMINAL.write (text)
end
-- Overrides the term.setTextColor method to set the current text color of the screen buffer and the terminal.
terminal.setTextColor = function (textColor)
if isColorValid (textColor) then
screenBuffer.currentTextColor = textColor
end
return NATIVE_TERMINAL.setTextColor (textColor)
end
-- Overrides the term.setBackgroundColor method to set the current background color of the screen buffer and the terminal.
terminal.setBackgroundColor = function (backgroundColor)
if isColorValid (backgroundColor) then
screenBuffer.currentBackgroundColor = backgroundColor
end
return NATIVE_TERMINAL.setBackgroundColor (backgroundColor)
end
-- Overrides the term.clear method to clear out the entire buffer and the terminal.
terminal.clear = function()
for line = 1, screenHeight do
for width = 1, screenWidth do
local cell = screenBuffer[line][width]
cell.text = ' '
cell.textColor = screenBuffer.currentTextColor
cell.backgroundColor = screenBuffer.currentBackgroundColor
end
end
return NATIVE_TERMINAL.clear()
end
-- Overrides the term.clearLine method to clear out only the current line in the buffer and terminal.
terminal.cearLine = function()
local _, currentLine = term.getCursorPos()
if currentLine > 0 and currentLine <= screenHeight then
for width = 1, screenWidth do
local cell = screenBuffer[currentLine][width]
cell.text = ' '
cell.textColor = screenBuffer.currentTextColor
cell.backgroundColor = screenBuffer.currentBackgroundColor
end
end
return NATIVE_TERMINAL.clearLine()
end
term.redirect (terminal)
end
-- Kills the screen buffer by emptying the buffer and restoring the terminal to its native functionality.
local function killScreenBuffer()
screenBuffer = {}
term.redirect (NATIVE_TERMINAL)
end
-- Screen Buffer Functions ------------------------------------
-- Anti Virus Functions ------------------------------------
-- Prompts the user if a program being executed is trying to execute a malicious function. The user
-- will be asked if they want the program to be allowed to execute the function. However, if the user says
-- no, then the program might not work properly. Returns true or false depending on the Y/N value of the
-- user's input.
local function promptUserOfMaliciousFunctionCall (functionName)
local cursorX, cursorY = term.getCursorPos()
local screenWidth, screenHeight = term.getSize()
local MAX_MESSAGE_WIDTH = WARNING_BOX_WIDTH - 4
-- Draws a string in the center of the screen.
local function drawCentered (myString, line)
term.setCursorPos (screenWidth / 2 - myString:len() / 2, line)
NATIVE_TERMINAL.write (myString)
end
-- Draw a simple black and white box in the middle of the screen which describes the situation.
local currentTextColor, currentBackgroundColor = screenBuffer.currentTextColor, screenBuffer.currentBackgroundColor
term.setTextColor (colors.white)
term.setBackgroundColor (colors.black)
drawCentered ('+' .. string.rep ('-', WARNING_BOX_WIDTH - 2) .. '+', screenHeight / 2 - WARNING_BOX_HEIGHT / 2)
for line = 1, WARNING_BOX_HEIGHT do
drawCentered ('|' .. string.rep (' ', WARNING_BOX_WIDTH - 2) .. '|', line + screenHeight / 2 - WARNING_BOX_HEIGHT / 2)
end
drawCentered ('+' .. string.rep ('-', WARNING_BOX_WIDTH - 2) .. '+', screenHeight / 2 + WARNING_BOX_HEIGHT / 2)
drawCentered ("Malicious call:", screenHeight / 2 - WARNING_BOX_HEIGHT / 2 + 1)
drawCentered (functionName:sub (1, MAX_MESSAGE_WIDTH), screenHeight / 2 - WARNING_BOX_HEIGHT / 2 + 3)
drawCentered ("Allow call? Y \\ N?", screenHeight / 2 - WARNING_BOX_HEIGHT / 2 + 5)
-- Wait for the user to press 'Y' or 'N' signaling their answer.
local char = nil
while not (char == 'y' or char == 'n') do
_, char = oldFunctions.os.pullEvent ("char")
end
-- Restore the screen to the way it was.
for lineNumber = 1, screenHeight do
for cellNumber = 1, screenWidth do
local cell = screenBuffer[lineNumber][cellNumber]
NATIVE_TERMINAL.setTextColor (cell.textColor)
NATIVE_TERMINAL.setBackgroundColor (cell.backgroundColor)
NATIVE_TERMINAL.setCursorPos (cellNumber, lineNumber)
NATIVE_TERMINAL.write (cell.text)
end
end
NATIVE_TERMINAL.setCursorPos (cursorX, cursorY)
NATIVE_TERMINAL.setTextColor (screenBuffer.currentTextColor)
NATIVE_TERMINAL.setBackgroundColor (screenBuffer.currentBackgroundColor)
return char == 'y'
end
-- Secures all of the functions in the APIs specified in 'maliciousAPIs' to prompt the user if they want to allow some program to execute
-- a potentially malicious function.
-- Returns all of the old functions in a table.
local function secureMaliciousAPIs()
local oldFunctions = {}
-- Secure the os API.
oldFunctions.os = {}
for functionName, functionObject in pairs (os) do
oldFunctions.os[functionName] = functionObject
os[functionName] = function (...)
if promptUserOfMaliciousFunctionCall ("os." .. functionName) then
return oldFunctions.os[functionName] (...)
else
return nil
end
end
end
-- Clean up the exceptions for the os API.
os.startTimer = oldFunctions.os.startTimer
os.pullEvent = oldFunctions.os.pullEvent
os.pullEventRaw = oldFunctions.os.pullEventRaw
-- Secure the fs API.
oldFunctions.fs = {}
for functionName, functionObject in pairs (fs) do
oldFunctions.fs[functionName] = functionObject
fs[functionName] = function (...)
if promptUserOfMaliciousFunctionCall ("fs." .. functionName) then
return oldFunctions.fs[functionName] (...)
else
return nil
end
end
end
-- Clean up the exceptions for the fs API.
fs.isDir = oldFunctions.fs.isDir
fs.exists = oldFunctions.fs.exists
fs.combine = oldFunctions.fs.combine
fs.getName = oldFunctions.fs.getName
fs.list = oldFunctions.fs.list
fs.isReadOnly = oldFunctions.fs.isReadOnly
return oldFunctions
end
-- Restores all of the secured APIs to their previous states given a table containing the states of all of the malicious APIs.
local function restoreMaliciousAPIs (oldFunctions)
for apiName, api in pairs (oldFunctions) do
for functionName, functionObject in pairs (api) do
local currentEnvironment = getfenv (1)
if _G[apiName] ~= nil then
_G[apiName][functionName] = functionObject
elseif currentEnvironment[apiName] ~= nil then
currentEnvironment[apiName][functionName] = functionObject
end
end
end
end
-- Anti Virus Functions ------------------------------------
-- Get the potential file path that we need to check for malicious calls. If there wasn't a valid file path then print the program usage instructions.
local tArgs = { ... }
local path = (tArgs[1] ~= nil) and shell.resolve (tArgs[1]) or ""
local currentPath = shell.resolve (shell.getRunningProgram())
if path ~= currentPath and fs.exists (path) and not fs.isDir (path) then
initScreenBuffer()
oldFunctions = secureMaliciousAPIs()
local success, errorMessage = pcall (dofile, path)
restoreMaliciousAPIs (oldFunctions)
killScreenBuffer()
else
print ("Usage: " .. fs.getName (currentPath) .. " <filePath>.")
end
Thanks for reading, Grim.