Monday, November 08, 2010 at 12:06 AM.
system.verbs.builtins.webBrowser.protocols.safetyCheck
<<Version 2.0 - 9/5/95 Mason Hale
<<10/29/95 Added alert parameter. If true client is alerted of errors. If false not alert is made.
on safetyCheck (textToBeChecked, alert = true, adrSafetyScriptsInit = @webBrowser.protocols.safeScripts) {
local {
tempWordCounter;
workingCopyOfScript = "";
foundVerbList = {};
checkedVerbList = {};
loopCounterTwo;
loopCounterOne;
escapedCharList = {"\r", "\t", "(", ")", "[", "]", "{", "}", ",", ";", "+"}};
on debugReport (msg) {
if not defined (scratchpad.results) {
new (wpTextType, @scratchpad.results)};
edit (@scratchpad.results);
wp.setText (msg + "\r\rBefore:\r" + texttoBeChecked + "\r\rAfter:\r" + workingCopyOfScript + "\r\rVerbs to be checked:\r" + string (foundVerbList) + "\r\rVerbs checked (and passed):\r" + string (checkedVerbList))};
on reportError (msg) {
if alert {
dialog.alert ("Script was not run!\r" + msg)}};
<<debugReport (msg)
<<workingCopyOfScript = textToBeChecked
string.setWordChar (" ");
if textToBeChecked contains "<<" {
reportError ("Illegal instruction: Comments not allowed"); <<don't allow comments, it throws off identification of strings
return (false)};
if (textToBeChecked contains "“") or (textToBeChecked contains "”") {
reportError ("Illegal instruction: Curly quotes not allowed"); <<don't allow comments, it throws off identification of strings
return (false)};
textToBeChecked = string.replaceAll (textToBeChecked, "\\\"", "Æ"); <<replace escaped quotes
for loopCounterTwo = 1 to string.countFields (textToBeChecked, "\"") { <<build macrostring, ignoring things between quotes
local (testString = string.nthField (textToBeChecked, "\"", loopCounterTwo));
if (mod (loopCounterTwo, 2)) > 0 { << test even/odd
testString = string.lower (testString); << lowercase all verbs for replacement by string.replaceAll later
if loopCounterTwo == string.countFields (textToBeChecked, "\"") {
workingCopyOfScript = workingCopyOfScript + testString}
else {
workingCopyOfScript = workingCopyOfScript + testString + "\""};
for loopCounterOne = 1 to sizeOf (escapedCharList) {
testString = string.replaceAll (testString, escapedCharList[loopCounterOne], " ")}; <<replace escaped chars with spaces
for loopCounterOne = 1 to string.countWords (testString) {
bundle { <<catch illegal verb combinations
if (string.nthWord (testString, loopCounterOne) beginsWith "@") and (string.nthWord (testString, loopCounterOne - 1) == "=") {
reportError ("Illegal Instruction: = followed by @");
return (false)};
if (string.nthWord (testString, loopCounterOne) contains "=@") {
reportError ("Illegal Instruction: = followed by @");
return (false)};
if (string.nthWord (testString, loopCounterOne) == "=") and (defined (string.nthWord (testString, loopCounterOne - 1)^)) {
reportError ("Illegal Instruction: " + string.nthWord (testString, loopCounterOne - 1) + " " + string.nthWord (testString, loopCounterOne) + "\rCannot assign new values to database objects");
return (false)}};
if not (foundVerbList contains string.nthWord (teststring, loopCounterOne)) {
foundVerbList = foundVerbList + string.nthWord (teststring, loopCounterOne)}}}
else {
if loopCounterTwo == string.countFields (textToBeChecked, "\"") {
workingCopyOfScript = workingCopyOfScript + testString}
else {
workingCopyOfScript = workingCopyOfScript + testString + "\""}}};
workingCopyOfScript = string.replaceAll (workingCopyOfScript, "Æ", "\\\""); <<put back escaped quotes
<<At this point I have a list of verbs to check. To speed things up that list could be generated by a UCMD.
for loopCounterTwo = 1 to sizeOf (foundVerbList) {
bundle { <<catch special exceptions/restrict keywords
if foundVerbList [loopCounterTwo] beginsWith "kernel" {
reportError ("Illegal Instruction: " + foundVerbList [loopCounterTwo]); <<don't allow calls the kernel directly
return (false)};
if foundVerbList [loopCounterTwo] == "with" {
reportError ("Illegal Instruction: " + foundVerbList [loopCounterTwo]);
return (false)};
if foundVerbList [loopCounterTwo] contains "^" {
reportError ("Illegal Instruction: " + foundVerbList [loopCounterTwo] + "\rExpansion of variables not allowed.");
return (false)};
if (foundVerbList [loopCounterTwo] endsWith ".") or (foundVerbList [loopCounterTwo] beginsWith ".") {
reportError ("Illegal Instruction: " + foundVerbList [loopCounterTwo] + "\rVerb cannot begin or end with a period.");
return (false)};
if foundVerbList [loopCounterTwo] == "string" {
checkedVerbList = checkedVerbList + foundVerbList [loopCounterTwo];
continue}}; <<explicitly allow the string verb
local (adrSafetyScripts = adrSafetyScriptsInit); << define address variable before with statement
if defined (foundVerbList [loopCounterTwo]^) { <<only dangerous if defined
bundle { << check address in table
local (subTableCounter = 1, nextFieldHolder);
loop {
if not defined (adrSafetyScripts^) {
reportError ("Illegal Instruction: " + foundVerbList [loopCounterTwo]);
return (false)};
nextFieldHolder = string.nthField (foundVerbList [loopCounterTwo], '.', subTableCounter++);
if nextFieldHolder == "" { <<ran out of fields
break};
adrSafetyScripts = @adrSafetyScripts^.[nextFieldHolder]}};
<<made it out of the loop, replace verb with object at address given
case typeOf (adrSafetyScripts^) { <<addresses and strings only are supported
addressType { << replace with address
workingCopyOfScript = string.replaceAll (workingCopyOfScript, foundVerbList [loopCounterTwo], adrSafetyScripts^);
checkedVerbList = checkedVerbList + foundVerbList [loopCounterTwo]};
stringType { << make sure string value is coming from safeScripts table not elsewhere
workingCopyOfScript = string.replaceAll (workingCopyOfScript, foundVerbList [loopCounterTwo], string (adrSafetyScripts));
checkedVerbList = checkedVerbList + foundVerbList [loopCounterTwo]}}
else {
reportError ("Illegal Instruction: " + foundVerbList [loopCounterTwo]);
return (false)}}
else {
<<Check for things like user.name = "King of the Dorks" but still allow edit (@user) to open objects remotely.
<<also allow for subtables in variables created in the embedded script
if (foundVerbList [loopCounterTwo] contains ".") and (!foundVerbList [loopCounterTwo] beginsWith "@") {
if defined (string.nthField (foundVerbList [loopCounterTwo], '.', 1)^) {
reportError ("Illegal Instruction: " + foundVerbList [loopCounterTwo]);
return (false)}};
checkedVerbList = checkedVerbList + foundVerbList [loopCounterTwo]}};
<<debugReport ("Success")
return (workingCopyOfScript)};
<<bundle <<test code
<<local (x)
<<bundle <<should pass
<<x = "local (duration = 10, AMplitude = 200, frequency = 1000, ixxx);speaker.sound (duration, amplitude, frequency);"
<<x = "edit%20(@webserver.utilities.readme);frontier.bringTofront (); "
<<x = "dialog.notify%20(%22Hello%20World!%22);%0Dlocal%20(i);%0Dfor%20i%20%3D%201%20to%2010%20{%0D%09speaker.sound%20(10,250,%204000%20%2B%20(i%20*%201000))}"
<<x = "edit (@root.user); Frontier.bringToFront (); dialog.notify (\"Hello \\\" \" %2B user.name %2B \"\\\"!\");"
<<x = "local (s = string (clock.now ())); string (clock.ticks ());s = string.delete (s, 3, 2); dialog.notify (s);"
<<x = "DIALOG.notify (\"Hello \" %2B user.name %2B \"!\");dialog.NOTIFY (\"It is \" %2B string.delete (clock.now (), 3,2));dialog.notify (\"Are we having fun yet?\");"
<<
<<bundle <<should fail
<<x = "local (rd = sizeOf (user), i); dialog.notify (rd)\r; for i = 1 to rd {;dialog.notify (nameOf (user[i]))};"
<<x = "[\"file\"] . [\"delete\"] (\"Mac HD:Test\")"
<<x = "[\"file\"]. [\"delete\"] (\"Mac HD:Test\")"
<<x = "file.[\"delete\"] (\"Mac HD:Test\")"
<<x = "[\"file\"] .[\"delete\"] (\"Mac HD:Test\")"
<<x = "[\"file\"].[\"delete\"] (\"Mac HD:Test\")"
<<x = "scratchpad.test1 = “I wrote data to the OD”"
<<x = "local (test) <<enclose \";file.delete (\"HD:Sacrificial\");<<\""
<<x = "local (f = address (\"file.delete\"); f^ (\"Macintosh HD:Sacrificial File\");"
<<x = "evaluate (-300);"
<<x = "file.delete (\"Macintosh HD:Sacrificial File\")"
<<x = "local (i =@file.delete); i^ (\"Macintosh HD:Sacrificial File\")"
<<x = "dialog.notify (file.delete (\"Macintosh HD:Sacrificial File\"))"
<<x = "new (tableType, @scratchpad.zzz);zzz.a = \"I'm back...\";"
<<x = "new (addressType, @webBrowser.protocols.safeScripts.user); webBrowser.protocols.safeScripts.user = @file.delete;"
<<x = "local (f = \"file\"); f = f %2B \".delete\"; f^ (\"Macintosh HD:Sacrificial File\");"
<<x = "user.name = \"Dork King\"; dialog.notify (user.name);"
<<x = toys.urlDecode (x) << "+" = %2B
<<evaluate (safetyCheck (x))
<<I'm using if defined (verb) to identify verbs that might be dangerous. By disallowing with statements
<<that should allow variables and flag verbs that need to be checked.
<<Can't assume tableType is safe. Allows hacker to step through an array to get to the verb, or call it by index.
<<Embedded Script Rules
<<No comments
<<No curly quotes
<<No with statements
<<No ^
<<No calls to kernel
<<No = followed by @ (diallows assignment of scripts to variable)
<<Verbs must match address entry in SafeScripts table exactly
<<Cannot assign new values to database objects (i.e. scratchpad.x = "foo")
<<Old version
<<on safetyCheck (textToBeChecked, adrSafetyScriptsInit = @webBrowser.protocols.safeScripts) <<8/1/95 MAH
<<local (tempChunkCounter, tempParamCounter, tempLineCounter, tempWordCounter, workingCopyOfScript, checkedVerbList = {})
<<workingCopyOfScript = textToBeChecked
<<for tempLineCounter = 1 to string.countFields (textToBeChecked, ";") << break down into lines
<<local (lineStringVarHolder = string.nthField (textToBeChecked, ";", tempLineCounter))
<<for tempChunkCounter = 1 to string.countFields (lineStringVarHolder, "(") << break down into chunks
<<local (chunkStringVarHolder = string.nthField (lineStringVarHolder, "(", tempChunkCounter))
<<for tempParamCounter = 1 to string.countFields (chunkStringVarHolder, ",") <<break down into parameters
<<local (paramStringVarHolder = string.nthField (chunkStringVarHolder, ",", tempParamCounter))
<<for tempWordCounter = 1 to string.countWords (paramStringVarHolder) << break down into words
<<local (verbStringVarHolder = string.nthWord (paramStringVarHolder, tempWordCounter))
<<bundle << clean up verb string
<<verbStringVarHolder = string.popTrailing (verbStringVarHolder, ")") << pop trailing closed parens
<<verbStringVarHolder = string.popLeading (verbStringVarHolder, " ") << pop leading spaces
<<verbStringVarHolder = string.popTrailing (verbStringVarHolder, " ") << pop trailing spaces
<<bundle <<catch special exceptions/restrict keywords
<<if verbStringVarHolder beginsWith "kernel"
<<bundle <<debugging code
<<if not defined (scratchpad.results)
<<new (wpTextType, @scratchpad.results)
<<edit (@scratchpad.results)
<<wp.setText ("Illegal Instruction: Kernel verbs are not allowed\r\rBefore:\r" + texttoBeChecked + "\r\rAfter:\r" + workingCopyOfScript + "\r\rVerbs checked (and passed):\r" + string (checkedVerbList))
<<return (false) <<don't allow calls the kernel directly
<<if verbStringVarHolder == "with"
<<bundle <<debugging code
<<if not defined (scratchpad.results)
<<new (wpTextType, @scratchpad.results)
<<edit (@scratchpad.results)
<<wp.setText ("Illegal Instruction: With statements are not allowed\r\rBefore:\r" + texttoBeChecked + "\r\rAfter:\r" + workingCopyOfScript + "\r\rVerbs checked (and passed):\r" + string (checkedVerbList))
<<return (false) <<don't allow "with" statements
<<if verbStringVarHolder contains "^"
<<bundle <<debugging code
<<if not defined (scratchpad.results)
<<new (wpTextType, @scratchpad.results)
<<edit (@scratchpad.results)
<<wp.setText ("Illegal Instruction: Expansion of addresses not allowed\r\rBefore:\r" + texttoBeChecked + "\r\rAfter:\r" + workingCopyOfScript + "\r\rVerbs checked (and passed):\r" + string (checkedVerbList))
<<return (false) <<don't allow expansion of addresses statements
<<if string.nthChar (verbStringVarHolder, 1) == "@" and string.nthWord (paramStringVarHolder, tempWordCounter -1) == "="
<<bundle <<debugging code
<<if not defined (scratchpad.results)
<<new (wpTextType, @scratchpad.results)
<<edit (@scratchpad.results)
<<wp.setText ("Illegal Instruction: Assigning address to variable\r\rBefore:\r" + texttoBeChecked + "\r\rAfter:\r" + workingCopyOfScript + "\r\rVerbs checked (and passed):\r" + string (checkedVerbList))
<<return (false) <<Cannot copy database objects into variables (plugs a big security hole)
<<if checkedVerbList contains verbStringVarHolder
<<continue <<don't check the same word twice
<<else
<<local (adrSafetyScripts = adrSafetyScriptsInit) << define address variable before with statement
<<with webBrowser.protocols.safeScripts << include custom safe scripts
<<if defined (verbStringVarHolder^) <<only dangerous if defined
<<bundle << check address in table
<<local (subTableCounter =1, nextFieldHolder)
<<loop
<<if not defined (adrSafetyScripts^)
<<bundle <<debugging code
<<if not defined (scratchpad.results)
<<new (wpTextType, @scratchpad.results)
<<system.verbs.globals.edit (@scratchpad.results)
<<system.verbs.builtins.wp.setText ("Illegal Verb: " + verbStringVarHolder + "\r\rBefore:\r" + texttoBeChecked + "\r\rAfter:\r" + workingCopyOfScript + "\r\rVerbs checked (and passed):\r" + system.verbs.globals.string (checkedVerbList))
<<return (false) <<don't do anything
<<nextFieldHolder = system.verbs.builtins.string.nthField (verbStringVarHolder, '.', subTableCounter++)
<<if nextFieldHolder == "" <<ran out of fields
<<break
<<adrSafetyScripts = @adrSafetyScripts^.[nextFieldHolder]
<<made it out of the loop, replace verb with object at address given
<<case typeOf (adrSafetyScripts^) << scripts, addresses and string only are supported
<<scriptType << run script
<<need to be careful not to replace "string.replace" when replacing "string"
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, "." + verbStringVarHolder, "£°¹")
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, verbStringVarHolder + ".", "Æ°¢")
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, verbStringVarHolder, string (adrSafetyScripts) + " ")
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, "£°¹", "." + verbStringVarHolder)
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, "Æ°¢", verbStringVarHolder + ".")
<<checkedVerbList = checkedVerbList + verbStringVarHolder
<<addressType << replace with address
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, "." + verbStringVarHolder, "£°¹")
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, verbStringVarHolder + ".", "Æ°¢")
<<workingCopyOfScript = root.system.verbs.builtins.string.replaceAll (workingCopyOfScript, verbStringVarHolder, adrSafetyScripts^)
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, "£°¹", "." + verbStringVarHolder)
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, "Æ°¢", verbStringVarHolder + ".")
<<checkedVerbList = checkedVerbList + verbStringVarHolder
<<stringType << make sure string value is coming from safeScripts table not elsewhere
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, "." + verbStringVarHolder, "£°¹")
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, verbStringVarHolder + ".", "Æ°¢")
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, verbStringVarHolder, string (adrSafetyScripts))
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, "£°¹", "." + verbStringVarHolder)
<<workingCopyOfScript = system.verbs.builtins.string.replaceAll (workingCopyOfScript, "Æ°¢", verbStringVarHolder + ".")
<<checkedVerbList = checkedVerbList + verbStringVarHolder
<<else
<<return (false)
<<tableType << this allow string coercions & string.xxx verbs is it a hole?
<<checkedVerbList = checkedVerbList + verbStringVarHolder
<<else
<<checkedVerbList = checkedVerbList + verbStringVarHolder
<<bundle <<debugging code
<<if not defined (scratchpad.results)
<<new (wpTextType, @scratchpad.results)
<<edit (@scratchpad.results)
<<wp.setText ("Before:\r" + texttoBeChecked + "\r\rAfter:\r" + workingCopyOfScript + "\r\rVerbs checked (and passed):\r" + string (checkedVerbList))
<<return (workingCopyOfScript)
This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.