sl@0: # safe.test -- sl@0: # sl@0: # This file contains a collection of tests for safe Tcl, packages loading, sl@0: # and using safe interpreters. Sourcing this file into tcl runs the tests sl@0: # and generates output for errors. No output means no errors were found. sl@0: # sl@0: # Copyright (c) 1995-1996 Sun Microsystems, Inc. sl@0: # Copyright (c) 1998-1999 by Scriptics Corporation. sl@0: # sl@0: # See the file "license.terms" for information on usage and redistribution sl@0: # of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: # sl@0: # RCS: @(#) $Id: safe.test,v 1.13.2.3 2006/11/28 22:20:03 andreas_kupries Exp $ sl@0: sl@0: if {[lsearch [namespace children] ::tcltest] == -1} { sl@0: package require tcltest sl@0: namespace import -force ::tcltest::* sl@0: } sl@0: sl@0: foreach i [interp slaves] { sl@0: interp delete $i sl@0: } sl@0: sl@0: set saveAutoPath $::auto_path sl@0: set ::auto_path [info library] sl@0: sl@0: # Force actual loading of the safe package sl@0: # because we use un exported (and thus un-autoindexed) APIs sl@0: # in this test result arguments: sl@0: catch {safe::interpConfigure} sl@0: sl@0: proc equiv {x} {return $x} sl@0: sl@0: test safe-1.1 {safe::interpConfigure syntax} { sl@0: list [catch {safe::interpConfigure} msg] $msg; sl@0: } {1 {no value given for parameter "slave" (use -help for full usage) : sl@0: slave name () name of the slave}} sl@0: sl@0: test safe-1.2 {safe::interpCreate syntax} { sl@0: list [catch {safe::interpCreate -help} msg] $msg; sl@0: } {1 {Usage information: sl@0: Var/FlagName Type Value Help sl@0: ------------ ---- ----- ---- sl@0: ( -help gives this help ) sl@0: ?slave? name () name of the slave (optional) sl@0: -accessPath list () access path for the slave sl@0: -noStatics boolflag (false) prevent loading of statically linked pkgs sl@0: -statics boolean (true) loading of statically linked pkgs sl@0: -nestedLoadOk boolflag (false) allow nested loading sl@0: -nested boolean (false) nested loading sl@0: -deleteHook script () delete hook}} sl@0: sl@0: test safe-1.3 {safe::interpInit syntax} { sl@0: list [catch {safe::interpInit -noStatics} msg] $msg; sl@0: } {1 {bad value "-noStatics" for parameter sl@0: slave name () name of the slave}} sl@0: sl@0: sl@0: test safe-2.1 {creating interpreters, should have no aliases} { sl@0: interp aliases sl@0: } "" sl@0: test safe-2.2 {creating interpreters, should have no aliases} { sl@0: catch {safe::interpDelete a} sl@0: interp create a sl@0: set l [a aliases] sl@0: safe::interpDelete a sl@0: set l sl@0: } "" sl@0: test safe-2.3 {creating safe interpreters, should have no aliases} { sl@0: catch {safe::interpDelete a} sl@0: interp create a -safe sl@0: set l [a aliases] sl@0: interp delete a sl@0: set l sl@0: } "" sl@0: sl@0: test safe-3.1 {calling safe::interpInit is safe} { sl@0: catch {safe::interpDelete a} sl@0: interp create a -safe sl@0: safe::interpInit a sl@0: catch {interp eval a exec ls} msg sl@0: safe::interpDelete a sl@0: set msg sl@0: } {invalid command name "exec"} sl@0: test safe-3.2 {calling safe::interpCreate on trusted interp} { sl@0: catch {safe::interpDelete a} sl@0: safe::interpCreate a sl@0: set l [lsort [a aliases]] sl@0: safe::interpDelete a sl@0: set l sl@0: } {encoding exit file load source} sl@0: test safe-3.3 {calling safe::interpCreate on trusted interp} { sl@0: catch {safe::interpDelete a} sl@0: safe::interpCreate a sl@0: set x [interp eval a {source [file join $tcl_library init.tcl]}] sl@0: safe::interpDelete a sl@0: set x sl@0: } "" sl@0: test safe-3.4 {calling safe::interpCreate on trusted interp} { sl@0: catch {safe::interpDelete a} sl@0: safe::interpCreate a sl@0: catch {set x \ sl@0: [interp eval a {source [file join $tcl_library init.tcl]}]} msg sl@0: safe::interpDelete a sl@0: list $x $msg sl@0: } {{} {}} sl@0: sl@0: test safe-4.1 {safe::interpDelete} { sl@0: catch {safe::interpDelete a} sl@0: interp create a sl@0: safe::interpDelete a sl@0: } "" sl@0: test safe-4.2 {safe::interpDelete, indirectly} { sl@0: catch {safe::interpDelete a} sl@0: interp create a sl@0: a alias exit safe::interpDelete a sl@0: a eval exit sl@0: } "" sl@0: test safe-4.3 {safe::interpDelete, state array (not a public api)} { sl@0: catch {safe::interpDelete a} sl@0: namespace eval safe {set [InterpStateName a](foo) 33} sl@0: # not an error anymore to call it if interp is already sl@0: # deleted, to make trhings smooth if it's called twice... sl@0: catch {safe::interpDelete a} m1 sl@0: catch {namespace eval safe {set [InterpStateName a](foo)}} m2 sl@0: list $m1 $m2 sl@0: } "{}\ sl@0: {can't read \"[safe::InterpStateName a](foo)\": no such variable}" sl@0: sl@0: sl@0: test safe-4.4 {safe::interpDelete, state array, indirectly (not a public api)} { sl@0: catch {safe::interpDelete a} sl@0: safe::interpCreate a sl@0: namespace eval safe {set [InterpStateName a](foo) 33} sl@0: a eval exit sl@0: catch {namespace eval safe {set [InterpStateName a](foo)}} msg sl@0: } 1 sl@0: sl@0: test safe-4.5 {safe::interpDelete} { sl@0: catch {safe::interpDelete a} sl@0: safe::interpCreate a sl@0: catch {safe::interpCreate a} msg sl@0: set msg sl@0: } {interpreter named "a" already exists, cannot create} sl@0: test safe-4.6 {safe::interpDelete, indirectly} { sl@0: catch {safe::interpDelete a} sl@0: safe::interpCreate a sl@0: a eval exit sl@0: } "" sl@0: sl@0: # The following test checks whether the definition of tcl_endOfWord can be sl@0: # obtained from auto_loading. sl@0: sl@0: test safe-5.1 {test auto-loading in safe interpreters} { sl@0: catch {safe::interpDelete a} sl@0: safe::interpCreate a sl@0: set r [catch {interp eval a {tcl_endOfWord "" 0}} msg] sl@0: safe::interpDelete a sl@0: list $r $msg sl@0: } {0 -1} sl@0: sl@0: # test safe interps 'information leak' sl@0: proc SI {} { sl@0: global I sl@0: set I [interp create -safe]; sl@0: } sl@0: proc DI {} { sl@0: global I; sl@0: interp delete $I; sl@0: } sl@0: test safe-6.1 {test safe interpreters knowledge of the world} { sl@0: SI; set r [lsort [$I eval {info globals}]]; DI; set r sl@0: } {tcl_interactive tcl_patchLevel tcl_platform tcl_version} sl@0: test safe-6.2 {test safe interpreters knowledge of the world} { sl@0: SI; set r [$I eval {info script}]; DI; set r sl@0: } {} sl@0: test safe-6.3 {test safe interpreters knowledge of the world} { sl@0: SI sl@0: set r [lsort [$I eval {array names tcl_platform}]] sl@0: DI sl@0: # If running a windows-debug shell, remove the "debug" element from r. sl@0: if {$tcl_platform(platform) == "windows" && \ sl@0: [lsearch $r "debug"] != -1} { sl@0: set r [lreplace $r 1 1] sl@0: } sl@0: set threaded [lsearch $r "threaded"] sl@0: if {$threaded != -1} { sl@0: set r [lreplace $r $threaded $threaded] sl@0: } sl@0: set tip [lsearch $r "tip,268"] sl@0: if {$tip != -1} { sl@0: set r [lreplace $r $tip $tip] sl@0: } sl@0: set tip [lsearch $r "tip,280"] sl@0: if {$tip != -1} { sl@0: set r [lreplace $r $tip $tip] sl@0: } sl@0: set r sl@0: } {byteOrder platform wordSize} sl@0: sl@0: # more test should be added to check that hostname, nameofexecutable, sl@0: # aren't leaking infos, but they still do... sl@0: sl@0: # high level general test sl@0: test safe-7.1 {tests that everything works at high level} { sl@0: set i [safe::interpCreate]; sl@0: # no error shall occur: sl@0: # (because the default access_path shall include 1st level sub dirs sl@0: # so package require in a slave works like in the master) sl@0: set v [interp eval $i {package require http 1}] sl@0: # no error shall occur: sl@0: interp eval $i {http_config}; sl@0: safe::interpDelete $i sl@0: set v sl@0: } 1.0 sl@0: sl@0: test safe-7.2 {tests specific path and interpFind/AddToAccessPath} { sl@0: set i [safe::interpCreate -nostat -nested 1 -accessPath [list [info library]]]; sl@0: # should not add anything (p0) sl@0: set token1 [safe::interpAddToAccessPath $i [info library]] sl@0: # should add as p1 sl@0: set token2 [safe::interpAddToAccessPath $i "/dummy/unixlike/test/path"]; sl@0: # an error shall occur (http is not anymore in the secure 0-level sl@0: # provided deep path) sl@0: list $token1 $token2 \ sl@0: [catch {interp eval $i {package require http 1}} msg] $msg \ sl@0: [safe::interpConfigure $i]\ sl@0: [safe::interpDelete $i] sl@0: } "{\$p(:0:)} {\$p(:1:)} 1 {can't find package http 1} {-accessPath {[list $tcl_library /dummy/unixlike/test/path]} -statics 0 -nested 1 -deleteHook {}} {}" sl@0: sl@0: sl@0: # test source control on file name sl@0: test safe-8.1 {safe source control on file} { sl@0: set i "a"; sl@0: catch {safe::interpDelete $i} sl@0: safe::interpCreate $i; sl@0: list [catch {$i eval {source}} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i] ; sl@0: } {1 {wrong # args: should be "source fileName"} {}} sl@0: sl@0: # test source control on file name sl@0: test safe-8.2 {safe source control on file} { sl@0: set i "a"; sl@0: catch {safe::interpDelete $i} sl@0: safe::interpCreate $i; sl@0: list [catch {$i eval {source}} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i] ; sl@0: } {1 {wrong # args: should be "source fileName"} {}} sl@0: sl@0: test safe-8.3 {safe source control on file} { sl@0: set i "a"; sl@0: catch {safe::interpDelete $i} sl@0: safe::interpCreate $i; sl@0: set log {}; sl@0: proc safe-test-log {str} {global log; lappend log $str} sl@0: set prevlog [safe::setLogCmd]; sl@0: safe::setLogCmd safe-test-log; sl@0: list [catch {$i eval {source .}} msg] \ sl@0: $msg \ sl@0: $log \ sl@0: [safe::setLogCmd $prevlog; unset log] \ sl@0: [safe::interpDelete $i] ; sl@0: } {1 {permission denied} {{ERROR for slave a : ".": is a directory}} {} {}} sl@0: sl@0: sl@0: test safe-8.4 {safe source control on file} { sl@0: set i "a"; sl@0: catch {safe::interpDelete $i} sl@0: safe::interpCreate $i; sl@0: set log {}; sl@0: proc safe-test-log {str} {global log; lappend log $str} sl@0: set prevlog [safe::setLogCmd]; sl@0: safe::setLogCmd safe-test-log; sl@0: list [catch {$i eval {source /abc/def}} msg] \ sl@0: $msg \ sl@0: $log \ sl@0: [safe::setLogCmd $prevlog; unset log] \ sl@0: [safe::interpDelete $i] ; sl@0: } {1 {permission denied} {{ERROR for slave a : "/abc/def": not in access_path}} {} {}} sl@0: sl@0: sl@0: test safe-8.5 {safe source control on file} { sl@0: # This tested filename == *.tcl or tclIndex, but that restriction sl@0: # was removed in 8.4a4 - hobbs sl@0: set i "a"; sl@0: catch {safe::interpDelete $i} sl@0: safe::interpCreate $i; sl@0: set log {}; sl@0: proc safe-test-log {str} {global log; lappend log $str} sl@0: set prevlog [safe::setLogCmd]; sl@0: safe::setLogCmd safe-test-log; sl@0: list [catch {$i eval {source [file join [info lib] blah]}} msg] \ sl@0: $msg \ sl@0: $log \ sl@0: [safe::setLogCmd $prevlog; unset log] \ sl@0: [safe::interpDelete $i] ; sl@0: } [list 1 {no such file or directory} [list "ERROR for slave a : [file join [info library] blah]:no such file or directory"] {} {}] sl@0: sl@0: sl@0: test safe-8.6 {safe source control on file} { sl@0: set i "a"; sl@0: catch {safe::interpDelete $i} sl@0: safe::interpCreate $i; sl@0: set log {}; sl@0: proc safe-test-log {str} {global log; lappend log $str} sl@0: set prevlog [safe::setLogCmd]; sl@0: safe::setLogCmd safe-test-log; sl@0: list [catch {$i eval {source [file join [info lib] blah.tcl]}} msg] \ sl@0: $msg \ sl@0: $log \ sl@0: [safe::setLogCmd $prevlog; unset log] \ sl@0: [safe::interpDelete $i] ; sl@0: } [list 1 {no such file or directory} [list "ERROR for slave a : [file join [info library] blah.tcl]:no such file or directory"] {} {}] sl@0: sl@0: sl@0: test safe-8.7 {safe source control on file} { sl@0: # This tested length of filename, but that restriction sl@0: # was removed in 8.4a4 - hobbs sl@0: set i "a"; sl@0: catch {safe::interpDelete $i} sl@0: safe::interpCreate $i; sl@0: set log {}; sl@0: proc safe-test-log {str} {global log; lappend log $str} sl@0: set prevlog [safe::setLogCmd]; sl@0: safe::setLogCmd safe-test-log; sl@0: list [catch {$i eval {source [file join [info lib] xxxxxxxxxxx.tcl]}}\ sl@0: msg] \ sl@0: $msg \ sl@0: $log \ sl@0: [safe::setLogCmd $prevlog; unset log] \ sl@0: [safe::interpDelete $i] ; sl@0: } [list 1 {no such file or directory} [list "ERROR for slave a : [file join [info library] xxxxxxxxxxx.tcl]:no such file or directory"] {} {}] sl@0: sl@0: test safe-8.8 {safe source forbids -rsrc} { sl@0: set i "a"; sl@0: catch {safe::interpDelete $i} sl@0: safe::interpCreate $i; sl@0: list [catch {$i eval {source -rsrc Init}} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i] ; sl@0: } {1 {wrong # args: should be "source fileName"} {}} sl@0: sl@0: sl@0: test safe-9.1 {safe interps' deleteHook} { sl@0: set i "a"; sl@0: catch {safe::interpDelete $i} sl@0: set res {} sl@0: proc testDelHook {args} { sl@0: global res; sl@0: # the interp still exists at that point sl@0: interp eval a {set delete 1} sl@0: # mark that we've been here (successfully) sl@0: set res $args; sl@0: } sl@0: safe::interpCreate $i -deleteHook "testDelHook arg1 arg2"; sl@0: list [interp eval $i exit] $res sl@0: } {{} {arg1 arg2 a}} sl@0: sl@0: test safe-9.2 {safe interps' error in deleteHook} { sl@0: set i "a"; sl@0: catch {safe::interpDelete $i} sl@0: set res {} sl@0: proc testDelHook {args} { sl@0: global res; sl@0: # the interp still exists at that point sl@0: interp eval a {set delete 1} sl@0: # mark that we've been here (successfully) sl@0: set res $args; sl@0: # create an exception sl@0: error "being catched"; sl@0: } sl@0: set log {}; sl@0: proc safe-test-log {str} {global log; lappend log $str} sl@0: safe::interpCreate $i -deleteHook "testDelHook arg1 arg2"; sl@0: set prevlog [safe::setLogCmd]; sl@0: safe::setLogCmd safe-test-log; sl@0: list [safe::interpDelete $i] $res \ sl@0: $log \ sl@0: [safe::setLogCmd $prevlog; unset log]; sl@0: } {{} {arg1 arg2 a} {{NOTICE for slave a : About to delete} {ERROR for slave a : Delete hook error (being catched)} {NOTICE for slave a : Deleted}} {}} sl@0: sl@0: sl@0: test safe-9.3 {dual specification of statics} { sl@0: list [catch {safe::interpCreate -stat true -nostat} msg] $msg sl@0: } {1 {conflicting values given for -statics and -noStatics}} sl@0: sl@0: test safe-9.4 {dual specification of statics} { sl@0: # no error shall occur sl@0: safe::interpDelete [safe::interpCreate -stat false -nostat] sl@0: } {} sl@0: sl@0: test safe-9.5 {dual specification of nested} { sl@0: list [catch {safe::interpCreate -nested 0 -nestedload} msg] $msg sl@0: } {1 {conflicting values given for -nested and -nestedLoadOk}} sl@0: sl@0: test safe-9.6 {interpConfigure widget like behaviour} { sl@0: # this test shall work, don't try to "fix it" unless sl@0: # you *really* know what you are doing (ie you are me :p) -- dl sl@0: list [set i [safe::interpCreate \ sl@0: -noStatics \ sl@0: -nestedLoadOk \ sl@0: -deleteHook {foo bar}]; sl@0: safe::interpConfigure $i -accessPath /foo/bar ; sl@0: safe::interpConfigure $i]\ sl@0: [safe::interpConfigure $i -aCCess]\ sl@0: [safe::interpConfigure $i -nested]\ sl@0: [safe::interpConfigure $i -statics]\ sl@0: [safe::interpConfigure $i -DEL]\ sl@0: [safe::interpConfigure $i -accessPath /blah -statics 1; sl@0: safe::interpConfigure $i]\ sl@0: [safe::interpConfigure $i -deleteHook toto -nosta -nested 0; sl@0: safe::interpConfigure $i] sl@0: } {{-accessPath /foo/bar -statics 0 -nested 1 -deleteHook {foo bar}} {-accessPath /foo/bar} {-nested 1} {-statics 0} {-deleteHook {foo bar}} {-accessPath /blah -statics 1 -nested 1 -deleteHook {foo bar}} {-accessPath /blah -statics 0 -nested 0 -deleteHook toto}} sl@0: sl@0: sl@0: # testing that nested and statics do what is advertised sl@0: # (we use a static package : Tcltest) sl@0: sl@0: if {[catch {package require Tcltest} msg]} { sl@0: puts "This application hasn't been compiled with Tcltest" sl@0: puts "skipping remining safe test that relies on it." sl@0: } else { sl@0: sl@0: # we use the Tcltest package , which has no Safe_Init sl@0: sl@0: test safe-10.1 {testing statics loading} { sl@0: set i [safe::interpCreate] sl@0: list \ sl@0: [catch {interp eval $i {load {} Tcltest}} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i]; sl@0: } {1 {can't use package in a safe interpreter: no Tcltest_SafeInit procedure} {}} sl@0: sl@0: test safe-10.2 {testing statics loading / -nostatics} { sl@0: set i [safe::interpCreate -nostatics] sl@0: list \ sl@0: [catch {interp eval $i {load {} Tcltest}} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i]; sl@0: } {1 {permission denied (static package)} {}} sl@0: sl@0: sl@0: sl@0: test safe-10.3 {testing nested statics loading / no nested by default} { sl@0: set i [safe::interpCreate] sl@0: list \ sl@0: [catch {interp eval $i {interp create x; load {} Tcltest x}} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i]; sl@0: } {1 {permission denied (nested load)} {}} sl@0: sl@0: sl@0: test safe-10.4 {testing nested statics loading / -nestedloadok} { sl@0: set i [safe::interpCreate -nestedloadok] sl@0: list \ sl@0: [catch {interp eval $i {interp create x; load {} Tcltest x}} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i]; sl@0: } {1 {can't use package in a safe interpreter: no Tcltest_SafeInit procedure} {}} sl@0: sl@0: sl@0: } sl@0: sl@0: test safe-11.1 {testing safe encoding} { sl@0: set i [safe::interpCreate] sl@0: list \ sl@0: [catch {interp eval $i encoding} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i]; sl@0: } {1 {wrong # args: should be "encoding option ?arg ...?"} {}} sl@0: sl@0: test safe-11.2 {testing safe encoding} { sl@0: set i [safe::interpCreate] sl@0: list \ sl@0: [catch {interp eval $i encoding system cp775} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i]; sl@0: } {1 {wrong # args: should be "encoding system"} {}} sl@0: sl@0: test safe-11.3 {testing safe encoding} { sl@0: set i [safe::interpCreate] sl@0: set result [catch { sl@0: string match [encoding system] [interp eval $i encoding system] sl@0: } msg] sl@0: list $result $msg [safe::interpDelete $i] sl@0: } {0 1 {}} sl@0: sl@0: test safe-11.4 {testing safe encoding} { sl@0: set i [safe::interpCreate] sl@0: set result [catch { sl@0: string match [encoding names] [interp eval $i encoding names] sl@0: } msg] sl@0: list $result $msg [safe::interpDelete $i] sl@0: } {0 1 {}} sl@0: sl@0: test safe-11.5 {testing safe encoding} { sl@0: set i [safe::interpCreate] sl@0: list \ sl@0: [catch {interp eval $i encoding convertfrom cp1258 foobar} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i]; sl@0: } {0 foobar {}} sl@0: sl@0: sl@0: test safe-11.6 {testing safe encoding} { sl@0: set i [safe::interpCreate] sl@0: list \ sl@0: [catch {interp eval $i encoding convertto cp1258 foobar} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i]; sl@0: } {0 foobar {}} sl@0: sl@0: test safe-11.7 {testing safe encoding} { sl@0: set i [safe::interpCreate] sl@0: list \ sl@0: [catch {interp eval $i encoding convertfrom} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i]; sl@0: } {1 {wrong # args: should be "encoding convertfrom ?encoding? data"} {}} sl@0: sl@0: sl@0: test safe-11.8 {testing safe encoding} { sl@0: set i [safe::interpCreate] sl@0: list \ sl@0: [catch {interp eval $i encoding convertto} msg] \ sl@0: $msg \ sl@0: [safe::interpDelete $i]; sl@0: } {1 {wrong # args: should be "encoding convertto ?encoding? data"} {}} sl@0: sl@0: sl@0: set ::auto_path $saveAutoPath sl@0: # cleanup sl@0: ::tcltest::cleanupTests sl@0: return