Tcl Source Code

Artifact [060d8bb2cd]
Login

Artifact 060d8bb2cd7cf461fe3a6722bbc0f5ff8649a41c:

Attachment "safe.tcl.patch3" to ticket [2964715fff] added by kjnash 2010-03-10 23:38:38.
--- original/library/tcl8.5/safe.tcl	2010-03-08 19:36:05.000000000 +0000
+++ patched/library/tcl8.5/safe.tcl	2010-03-10 16:13:18.000000000 +0000
@@ -684,6 +684,21 @@
 #       - It is a good fit to the Safe Base idea that operations are permitted
 #         only in certain directories.
 
+# Further Alterations against {core-8-5-branch v 1.16.4.6 patched by safe.tcl.patch, safe.tcl.patch2}
+#  (7) Reverse (4) - use the same misleading error message as glob itself
+#  (8) -nocomplain protects against error return if there are no matches; it
+#      should not protect against errors in arguments, e.g. no pattern
+#  (9) - therefore we can move the test of -directory back into the
+#        argument-processing loop
+# (10) Return a specific error message for option -path
+# (11) Give a standard error message for invalid arguments other than -path
+# (12) There is no requirement for -join (if present) to be the last argument
+# (13) Remove the test that the directory part of a (compounded) glob pattern
+#      belongs to the access path.  Instead, test whether each result of the
+#      glob belongs to a directory in the access path.  The reason is that the
+#      DirInAccessPath test fails for a directory that has a glob wildcard.
+# (14) - therefore we do not need to do the pattern -join here - let glob do it
+
 
 # ------------------------------------------------------------------------------
 #  Proc ::safe::AliasGlob
@@ -693,6 +708,10 @@
 # This command provides a restricted form of glob that a safe interpreter
 # requires to handle Tcl Modules. See tm.tcl, proc ::tcl::tm::UnknownHandler.
 #
+# The restricted glob must use the -directory option (which must supply a
+# directory in the slave's access path), and returns only files and directories
+# whose parent directory belongs to the access_path.
+#
 # Restrictions cf. standard glob:
 # (1) option -path      is not allowed
 # (2) option -directory must be supplied and must be a directory in the safe
@@ -722,12 +741,6 @@
 	-- 0
     }
 
-    if {$::tcl_platform(platform) eq "windows"} {
-	set dirPartRE {^(.*)[\\/]}
-    } else {
-	set dirPartRE {^(.*)/}
-    }
-
     set dir        {}
     set virtualdir {}
 
@@ -743,77 +756,58 @@
 		incr at
 	    }
 	    -directory {
+		# Use the same misleading error message as glob:
 		if {$got($opt)} {
 		    return -code error \
-			{"-directory" option can be used only once}
+			{"-directory" cannot be used with "-path"}
 		}
 		set got($opt) 1
 		set virtualdir [lindex $args [incr at]]
 		incr at
 
-		# Errors in translation are examined later
-		catch {set dir [TranslatePath $slave $virtualdir]}
+		# Get the real path from the virtual one and check that the path
+		# is in the access path of that slave.
+		if {[catch {
+		    set dir [TranslatePath $slave $virtualdir]
+		    DirInAccessPath $slave $dir
+		} msg]} {
+		    Log $slave $msg
+		    return -code error {directory is not in the access path}
+		}
+
 		lappend cmd -directory $dir
 	    }
+	    -path {
+		Log $slave {Safe Base does not allow glob option "-path"}
+		return -code error {Safe Base does not allow glob option "-path"}
+	    }
 	    -* {
-		Log $slave "Safe base rejecting glob option '$opt'"
-		return -code error "Safe base rejecting glob option '$opt'"
+		Log $slave "bad option \"$opt\": must be -directory, -join, -nocomplain, -tails, -types, or --"
+		return -code error "bad option \"$opt\": must be -directory, -join, -nocomplain, -tails, -types, or --"
 	    }
 	    default {
 		break
 	    }
 	}
-	if {$got(--) || $got(-join)} break
-    }
-
-    # Get the real path from the virtual one and check that the path is in the
-    # access path of that slave. Done after basic argument processing so that
-    # we know if -nocomplain is set.
-    if {$got(-directory)} {
-	if {[catch {
-	    set dir [TranslatePath $slave $virtualdir]
-	    DirInAccessPath $slave $dir
-	} msg]} {
-	    Log $slave $msg
-	    if {!$got(-nocomplain)} {
-		return -code error "permission denied"
-	    } else {
-		return
-	    }
-	}
-    } else {
-        return -code error {when glob is used in the Safe base, option "-directory" must be specified}
+	if {$got(--)} break
     }
 
-    # Apply the -join semantics ourselves
-    if {$got(-join)} {
-	set args [lreplace $args $at end [join [lrange $args $at end] "/"]]
+    if {!$got(-directory)} {
+	return -code error {when glob is used in the Safe Base, option "-directory" must be specified}
     }
 
     # Process remaining pattern arguments
-    set firstPattern [llength $cmd]
-    while {$at < [llength $args]} {
-	set opt [lindex $args $at]
-	incr at
-	if {[regexp $dirPartRE $opt -> thedir] && [catch {
-	    set thedir [file join $virtualdir $thedir]
-	    DirInAccessPath $slave [TranslatePath $slave $thedir]
-	} msg]} {
-	    Log $slave $msg
-	    if {$got(-nocomplain)} {
-		continue
-	    } else {
-		return -code error "permission denied"
-	    }
-	}
-	lappend cmd $opt
+    set pattern [lrange $args $at end]
+    set cmd [concat $cmd $pattern]
+
+    # We could let glob raise this error, but this message is more helpful
+    # than our rewritten message "script error".
+    if {[llength $pattern] == 0} {
+	return -code error {wrong # args: should be "glob ?switches? name ?name ...?"}
     }
 
     Log $slave "GLOB = $cmd" NOTICE
 
-    if {$got(-nocomplain) && [llength $cmd] eq $firstPattern} {
-	return
-    }
     if {[catch {
 	::interp invokehidden $slave glob {*}$cmd
     } msg]} {
@@ -827,16 +821,62 @@
     set res {}
     set l [string length $dir]
     foreach p $msg {
+	# Return only results that are in the access path.
+	# Normalize because [dirname foo/..] is "foo".
+	if {[catch {
+	    DirInAccessPath $slave [file dirname [file normalize $p]]
+	}]} {
+	    continue
+	}
 	if {[string equal -length $l $dir $p]} {
 	    set p [string replace $p 0 [expr {$l-1}] $virtualdir]
+	    lappend res $p
+	} else {
+	    # Something's gone wrong: $p has not been normalized and so should
+	    # always begin with $dir.
+	    return -code error "error handling glob results"
 	}
-	lappend res $p
     }
 
+    # A non-empty result list $msg might have had all its members removed.
+    if {($res eq {}) && (!$got(-nocomplain))} {
+	# E.g. {no files matched glob pattern "foo"}
+	return -code error [GlobErrorText $got(-join) $pattern]
+    }
     Log $slave "GLOB @ $res" NOTICE
     return $res
 }
 
+
+# ------------------------------------------------------------------------------
+#  Proc ::safe::GlobErrorText
+# ------------------------------------------------------------------------------
+# Command to return a suitable error message for use by AliasGlob, for the case
+# when there are no matches in the access path.
+#
+# Arguments:
+# gotJoin     - (boolean) whether glob was called with option -join
+# pattern     - list of pattern arguments supplied to glob
+#
+# Return Value: an error message
+# ------------------------------------------------------------------------------
+
+proc ::safe::GlobErrorText {gotJoin pattern} {
+    if {$gotJoin} {
+	set errorPattern [join $pattern /]
+	set numPat 1
+    } else {
+	set errorPattern $pattern
+	set numPat [llength $pattern]
+    }
+    if {$numPat > 1} {
+	set errorWord patterns
+    } else {
+	set errorWord pattern
+    }
+    return "no files matched glob $errorWord \"$errorPattern\""
+}
+
 # AliasSource is the target of the "source" alias in safe interpreters.
 
 proc ::safe::AliasSource {slave args} {