Nava2
2010-06-28 14:56:52
Well, I spent the majority of yesterday, and part of today writing this up.
Its pretty much a suite for a File/Fire wall.
Currently, the underlying mechanisms are in place and run very quickly. The only thing that needs to be done is making the OptionForm look nicer (Its ugly) and a Settings menu for changing and adding white/black listed urls etc.
I've tried to comment the code so its legible as well as easy to follow.
I post this here because I would like someone to make the Option Dialog more aesthetically pleasing (while remaining functional), I think there should be a form created for changing the white/black listed URLs/Files and lastly I would love more ideas to expand this system.
Here is the code:
Cheers,
Nava2
E: Oh, and if you want to try it, it works from just running it as a Script, or you can enable it as an Extension and run another script.
Its pretty much a suite for a File/Fire wall.
Currently, the underlying mechanisms are in place and run very quickly. The only thing that needs to be done is making the OptionForm look nicer (Its ugly) and a Settings menu for changing and adding white/black listed urls etc.
I've tried to comment the code so its legible as well as easy to follow.
I post this here because I would like someone to make the Option Dialog more aesthetically pleasing (while remaining functional), I think there should be a form created for changing the white/black listed URLs/Files and lastly I would love more ideas to expand this system.
Here is the code:
program SecurityExtention;
type
TNamedArray = record
a: TStringArray;
name: String;
end;
{ var SecurityEnabled: Boolean;
Global variable which decides whether or not to pass through any security
measures. It is toggled by the Menu Item (Tools -> Security -> Enabled). }
var
SecurityEnabled: Boolean;
{ var Menu*: TMenuItem;
Different variables for the different parts of the Menu added to Simba's
TMainMenu. }
var
MenuHead, MenuCheck, MenuEdit: TMenuItem;
{ var PathArr: array of TNamedArray;
Array which stores the different lists of URLs and Files/Directories. Each one
is accessed through the consants listed later which are prefixed with `P_'. }
var
PathArr: array [0..7] of TNamedArray;
{ const P_?_*: Integer;
Each constant is associated with a Path. W/B/L depicts whether is a White,
Black, or Local list respectively. These constants are in reference to indexes
in the `PathArr' variable. }
const
P_W_URLS = 0;
P_W_DIRS = 1;
P_W_FILES = 2;
P_B_URLS = 3;
P_B_DIRS = 4;
P_B_FILES = 5;
P_L_URLS = 6;
P_L_FILES = 7;
{ const SEP_CHAR: char;
Char used for a separator when imploding strings into the Settings.xml file
for storage. }
const
SEP_CHAR = ';';
{ var Settings_DEFAULT_(URL|FILE): Integer;
Depict the default option for when the Option dialog times out. These values
must be part of the constants. *MAY CRASH SIMBA IF NOT ONE OF THE CONSTANTS* }
var
Settings_DEFAULT_URL, Settings_DEFAULT_FILE: Integer;
{ var Settings_DEFAULT_TIMEOUT: Integer;
Time to wait in seconds before timing out the Option Dialog. This allows
scripts to progress without needing User Interaction and will choose the
Settings_DEFAULT_(URL|FILE) respectively. }
var
Settings_DEFAULT_TIMEOUT: Integer;
{
Checks if `s' occurs in TStringArray `a'.
}
function InStrArray(s: String; a: TStringArray): Boolean;
var
i, h: Integer;
begin
Result := True;
h := High(a);
for i := 0 to h do
begin
// writeln(Format('s = %s :: a[%d] = %s', [s, i, a[i]]));
if (s = a[i]) then
Exit;
end;
Result := False;
end;
{
Like `pos(substr, s)' but searches from the end of a string backwards.
}
function LastPos(SubStr, Str: string): integer;
var
i, p: integer;
s, ss: string;
begin
for i := Length(str) downto 1 do
S := S + Str[i];
for i := Length(substr) downto 1 do
ss := ss + substr[i];
p := Pos(ss, S);
if P > 0 then
Result := Length(Str) - p
end;
{
Remove the PHP params being passed around.
}
function trimPHPParams(url: String): String;
var
qPos: Integer;
begin
qPos := pos('?', url);
if (qPos > 0) then
Result := Copy(url, 1, qPos-1)
else
Result := url;
end;
{
Private method for adding to the global arrays.
}
procedure _AddStr(s: String; pathConst: Integer);
var
l: Integer;
{$IFDEF EXTENSION}tmp: String;{$ENDIF}
begin
{ Increase the size of the array, adding the new value }
l := Length(PathArr[pathConst].a);
SetLength(PathArr[pathConst].a, l + 1);
PathArr[pathConst].a[l] := s;
{$IFDEF EXTENSION}
if (InIntArray([P_L_FILES, P_L_URLS], pathConst)) then Exit;
{ Write to the Settings.xml }
tmp := Settings.getKeyValueDef(PathArr[pathConst].name,'');
Settings.setKeyValue(PathArr[pathConst].name, tmp + sep_char + s);
{$ENDIF}
end;
{
Changes MS into a format of: HH:MM:SS.
Taken from SRL4/core/Timing.scar
}
function MsToTime(MS: Integer): string;
var
STA: TVariantArray;
Time: array [0..2] of Integer;
i: Integer;
begin
STA := [':', ':', '', True, 2];
ConvertTime(MS, Time[0], Time[1], Time[2]);
for i := 0 to 2 do
if (Time[i] > 0) or (STA[3]) or (i = 2) then
Result := Result + PadZ(IntToStr(Time[i]), STA[4]) + STA[i];
end;
{
Prints a warning message whenever something attempts to break security
measures.
}
procedure SecurityWarn(msg: String);
begin
Writeln(Format('[%s] :: SECURITY WARNING :: %s', [MSToTime(GetTimeRunning), msg]));
end;
{ var Option*: FormComponents
Various Form components associated with the User Input Dialog. }
var
OptionForm: TForm;
Option_RadGrp: TRadioGroup;
Option_Label_Path: TLabel;
Option_Button_Okay: TButton;
Option_Timer: TTimer;
{ var Option_*_Options: TStringArray;
The different options for the option dialog. Consts are associated with these
arrays. }
var
Option_URL_Options, Option_File_Options: TStringArray;
{ const URL_(LOCAL|ALWAYS|NEVER): Integer;
Indexes associated with Option_URL_Options. }
const
URL_LOCAL = 0;
URL_ALWAYS = 1;
URL_NEVER = 2;
{ const FILE_(LOCAL|ALWAYS|NEVER)_(FILE|DIR|): Integer;
Indexes associated with Option_FILE_Options. }
const
FILE_LOCAL = 0;
FILE_ALWAYS_FILE = 1;
FILE_ALWAYS_DIR = 2;
FILE_NEVER_FILE = 3;
FILE_NEVER_DIR = 4;
{
Method to close the form, disables `Option_Timer' as well.
}
procedure OptionCloseForm(Sender: TObject);
begin
OptionForm.MODALRESULT := mrOk;
Option_Timer.ENABLED := False;
end;
{
Method which will reset the `Option_Timer' when User Input is detected. It is
currently called when the User Click on a radio button of `Option_RadGrp'.
}
procedure OptionResetTimer(Sender: TObject);
begin
Option_Timer.ENABLED := False;
Option_Timer.ENABLED := True;
end;
{
Loads and Shows the form. `isURL' depicts whether or not its a file path vs. a
URL. `path' is the actual path which is displayed on the form.
This method needs to be called in a SafeThread when testing. This is covered
later on using the `TESTING' define.
}
procedure LoadOptionDialog(const isURL: Boolean; const path: String);
var
i: Integer;
begin
{
Load the form.
}
OptionForm := TForm.Create(nil);
OptionForm.BringToFront;
OptionForm.Visible := False;
OptionForm.SetBounds({L} 100, {T} 100, {W} 200, {H} 400);
if (isURL) then
OptionForm.CAPTION := 'Fire Wall :: Input Required'
else
OptionForm.CAPTION := 'File Wall :: Input Required';
Option_RadGrp := TRadioGroup.create(OptionForm);
Option_RadGrp.Parent := OptionForm;
Option_RadGrp.SetBounds({L} 10, {T} 50, {W} 180, {H} 300);
Option_RadGrp.Visible := True;
Option_RadGrp.ITEMS.Clear;
Option_RadGrp.ONCLICK := @OptionResetTimer;
Option_Label_Path := TLabel.Create(OptionForm);
Option_Label_Path.Parent := OptionForm;
Option_Label_Path.Left := 10;
Option_Label_Path.Top := 5;
Option_Label_Path.Visible := True;
Option_Button_Okay := TButton.Create(OptionForm);
Option_Button_Okay.Parent := OptionForm;
Option_Button_Okay.CAPTION := 'Okay';
Option_Button_Okay.Left := 10;
Option_Button_Okay.Top := 360;
Option_Button_Okay.Visible := True;
Option_Button_Okay.ONCLICK := @OptionCloseForm;
Option_Timer := TTimer.Create(OptionForm);
Option_Timer.INTERVAL := Settings_DEFAULT_TIMEOUT * 1000;
Option_Timer.ONTIMER := @OptionCloseForm;
Option_Timer.ENABLED := True;
{
Use the passed parameters to change how the form looks depending on whether
its a URL or a File path.
}
Option_Label_Path.CAPTION := Path;
if (isURL) then
begin
for i := 0 to high(Option_URL_Options) do
Option_RadGrp.ITEMS.Add(Option_URL_Options[i]);
{ Default Option: }
Option_RadGrp.ITEMINDEX := Settings_DEFAULT_URL;
end else
begin
for i := 0 to high(Option_File_Options) do
Option_RadGrp.ITEMS.Add(Option_File_Options[i]);
{ Default Option: }
Option_RadGrp.ITEMINDEX := Settings_DEFAULT_FILE;
end;
OptionForm.ShowModal;
end;
function ShowOptionDialog(const isURL: Boolean; const path: String): Integer;
{$IFNDEF EXTENSION}
var
V: TVariantArray;
{$ENDIF}
begin
{$IFNDEF EXTENSION}
V := [isURL, path];
ThreadSafeCall('LoadOptionDialog', V);
{$ELSE}
LoadOptionDialog(isURL, path);
{$ENDIF}
Result := Option_RadGrp.ITEMINDEX;
OptionForm.Free;
end;
procedure onOpenConnection(var url : string; var Cont : boolean);
var
tmp_url: String;
begin
if (not SecurityEnabled) then
Exit;
tmp_url := trimPHPParams(lowercase(url));
{ Check the Loaded URLs }
if InStrArray(tmp_url, PathArr[P_W_URLS].a) then
begin
Cont := True;
Exit;
end;
if InStrArray(tmp_url, PathArr[P_B_URLS].a) then
begin
SecurityWarn(Format('URL: %s attempted to be opened.', [tmp_url]));
Cont := False;
Exit;
end;
if InStrArray(tmp_url, PathArr[P_L_URLS].a) then
begin
Cont := True;
Exit;
end;
{
Ask what to do with URL using the Dialog.
}
case ShowOptionDialog(True, tmp_url) of
URL_LOCAL:
begin
_AddStr(tmp_url, P_L_URLS);
Cont := True;
end;
URL_ALWAYS:
begin
_AddStr(tmp_url, P_W_URLS);
Cont := True;
end;
URL_NEVER:
begin
_AddStr(tmp_url, P_B_URLS);
Cont := False;
end;
end;
Writeln(Format('Cont [%s] URL [%s]', [BoolToStr(Cont), tmp_url]));
end;
procedure onOpenFile(var FileName : string; var Cont : boolean);
var
dir: String;
begin
if (not SecurityEnabled) then
Exit;
{
check if in either Allowed_URLs, or Local_Allowed_URL
}
dir := Copy(FileName, 1, LastPos('\', FileName));
Writeln(Format('Dir [%s] File [%s]', [dir, FileName]));
{ Check the Loaded URLs }
if InStrArray(dir, PathArr[P_W_DIRS].a) then
begin
Cont := True;
Exit;
end;
if InStrArray(dir, PathArr[P_B_DIRS].a) then
begin
SecurityWarn(Format('File in dir: %s was attempted to be used.', [dir]));
Cont := False;
Exit;
end;
if InStrArray(FileName, PathArr[P_L_FILES].a) then
begin
Cont := True;
Exit;
end;
if InStrArray(FileName, PathArr[P_W_FILES].a) then
begin
Cont := True;
Exit;
end;
if InStrArray(FileName, PathArr[P_B_FILES].a) then
begin
SecurityWarn(Format('File: %s was attempted to be used.', [FileName]));
Cont := False;
Exit;
end;
{
Ask what to do with File Path using the Dialog.
}
case ShowOptionDialog(False, FileName) of
FILE_LOCAL:
begin
_AddStr(FileName, P_L_FILES);
Cont := True;
end;
FILE_ALWAYS_FILE:
begin
_AddStr(FileName, P_W_FILES);
Cont := True;
end;
FILE_ALWAYS_DIR:
begin
_AddStr(dir, P_W_DIRS);
Cont := True;
end;
FILE_NEVER_FILE:
begin
_AddStr(FileName, P_B_FILES);
Cont := False;
end;
FILE_NEVER_DIR:
begin
_AddStr(dir, P_B_DIRS);
Cont := False;
end;
end;
Writeln(Format('Cont [%s] File [%s]', [BoolToStr(Cont), FileName]));
end;
procedure onWriteFile(var FileName : string; var Cont : boolean);
begin
onOpenFile(FileName, Cont);
end;
{$IFDEF EXTENSION}
procedure OnMenuCheckClick(Sender: TObject);
begin
MenuCheck.checked := not MenuCheck.checked;
Settings.setKeyValue('SecurityEnabled', toStr(MenuCheck.checked));
SecurityEnabled := MenuCheck.CHECKED;
end;
{$ENDIF}
procedure Init;
var
i: Integer;
ToolsMenu : TMenuItem;
tmp_Names: TStringArray;
begin;
{
Load consts for the User Input Form.
}
Option_URL_Options := ['Allow This Session', 'Always Allow', 'Never Allow'];
Option_FILE_Options := ['Allow This Session',
'Always Allow for File',
'Always allow for Directory',
'Never Allow for File',
'Never allow for Directory'];
tmp_Names := ['White_URL', 'White_Dirs', 'White_Files',
'Black_URL', 'Black_Dirs', 'Black_Files',
'Local_URL', 'Local_Dirs'];
for i := 0 to 7 do
begin
PathArr[i].name := tmp_Names[i];
{$IFDEF EXTENSION}
{
Load the previously saved settings.
}
if (not InIntArray([P_L_FILES, P_L_URLS], i)) then
PathArr[i].a := Explode(sep_char, Settings.getKeyValueDef(PathArr[i].name,
''));
{$ENDIF}
end;
{$IFDEF EXTENSION}
{
Load Default Settings from Settings.xml
}
Settings_DEFAULT_URL := StrToInt(Settings.getKeyValueDef('Default_URL',
'2'));
Settings_DEFAULT_FILE := StrToInt(Settings.getKeyValueDef('Default_File',
'4'));
Settings_DEFAULT_TIMEOUT := StrToInt(Settings.getKeyValueDef('Settings_TimeOut',
{
Find the Tools Menu.
} '60'));
for i := 0 to Simba_MainMenu.Items.count - 1 do
begin
if (Simba_MainMenu.Items.items[i].caption = '&Tools') then
begin
ToolsMenu := Simba_MainMenu.Items.items[i];
Break;
end;
end;
if (ToolsMenu = nil) then
begin
Writeln('Could not find tools menu.');
Exit;
end;
{ Load the Menu Items into the Tools menu. }
MenuHead := TMenuItem.Create(ToolsMenu);
MenuHead.caption := 'Se&curity';
ToolsMenu.Add(MenuHead);
MenuCheck := TMenuItem.CREATE(MenuHead);
MenuCheck.caption := 'Security &Enabled';
MenuCheck.checked := LowerCase(Settings.getKeyValueDef('SecurityEnabled',
'true')) = 'true';
MenuCheck.OnClick := @OnMenuCheckClick;
MenuHead.add(MenuCheck);
SecurityEnabled := MenuCheck.CHECKED;
MenuEdit := TMenuItem.CREATE(MenuHead);
MenuEdit.caption := '&Settings';
//MenuEdit.OnClick := @LoadEditForm;
MenuHead.Add(MenuEdit);
{$ELSE}
SecurityEnabled := True;
Settings_DEFAULT_URL := 2;
Settings_DEFAULT_FILE := 4;
Settings_DEFAULT_TIMEOUT := 30;
{$ENDIF}
end;
procedure Free;
begin
//if (started) then
//Timer.Enabled := False;//Freeing the components is not needed, as they will be freed upon the freeing of Simba.
end;
procedure Attach;
begin;
Writeln('Security Extention started.');
MenuHead.Visible := True;
SecurityEnabled := MenuCheck.CHECKED;
end;
procedure Detach;
begin
MenuHead.Visible := False;
SecurityEnabled := False;
end;
function GetName: string;
begin;
result := 'Security Extension';
end;
function GetVersion: string;
begin;
result := '0.1';
end;
{$IFNDEF EXTENSION}
var
cont: Boolean;
URL, FileName: String;
{$ENDIF}
begin
{$IFNDEF EXTENSION}Init;
URL := 'http://www.google.ca/search?sourceid=chrome&ie=UTF-8&q=fpc+format';
onOpenConnection(URL, cont);
onOpenConnection(URL, cont);
FileName := ScriptPath + 'tmp.txt';
onOpenFile(FileName, cont);
{$ENDIF}
end.
Cheers,
Nava2
E: Oh, and if you want to try it, it works from just running it as a Script, or you can enable it as an Extension and run another script.
