永井@知能.九工大です.

以下の仕様でのパッチです.

重要な点は,ENV のアクセス拒否の場合に例外を返すのではなく,
環境変数が存在しない場合と同様に nil を返すようにしたことです.
その他 ENV.keys 等で,アクセス拒否の環境変数については
その環境変数が存在しない場合と同様になるようにしました.

# 定数については例外があがります.

これは,例外をあげてしまうとその環境変数の存在が知られるため,
その環境変数に関連するアプリケーションの存在が察知されて
攻撃の糸口になってしまうことを回避するためです.

----< $LOAD_PATH ($:, $-I) >------------------------
   $SAFE >= 4 でのアクセスが禁止される.
----------------------------------------------------

----< ENV >-----------------------------------------
   ENV.allow_insecure_ref(name)
     : $SAFE >= 4 での環境変数 name の読み出しを許可する.
     : $SAFE >= 4 でこのメソッドが呼ばれた場合は例外を発生する.
     : すべての環境変数のデフォルト設定は「読み出し可」である.
     : ( $SAFE >= 4 での書き込みは常に例外を発生することに注意.)

   ENV.deny_insecure_ref(name)
     : $SAFE >= 4 での環境変数 name の読み出しを禁止する.
     : $SAFE >= 4 でこのメソッドが呼ばれた場合は例外を発生する.
     : 読み出し禁止に設定した環境変数を $SAFE >= 4 で読み出そうとすると, 
     : その環境変数が存在しない場合と同様に nil が返る.
     : また,読み出し禁止の環境変数は ENV.keys 等でも表示されない.

   ENV.insecure_ref_allowed?(name)
     : 環境変数 name (文字列指定) への $SAFE >= 4 でのアクセスが
     : 許可されているならば true を,そうでなければ false を返す.
     : $SAFE >= 4 でこのメソッドが呼ばれた場合は例外を発生する.

   ENV.insecure_ref_denied?(name)
     : ENV.insecure_ref_allowed?(name) の否定
     : $SAFE >= 4 でこのメソッドが呼ばれた場合は例外を発生する.

   ENV.insecure_ref_allowings
     : $SAFE >= 4 でのアクセスが許可されている環境変数のリストを得る.
     : $SAFE >= 4 でこのメソッドが呼ばれた場合は例外を発生する.

   ENV.insecure_ref_denyings
     : $SAFE >= 4 でのアクセスが禁止されている環境変数のリストを得る.
     : $SAFE >= 4 でこのメソッドが呼ばれた場合は例外を発生する.
----------------------------------------------------

----< Module >--------------------------------------
   Module#allow_insecure_ref(const)
     : module/class において,定数 const (文字列またはシンボル) への
     : $SAFE >= 4 での参照を許可する.(戻り値は const のシンボル)
     : $SAFE >= 4 でこのメソッドが呼ばれた場合は例外を発生する.
     : すべての定数のデフォルト設定は「参照禁止」である.

   Module#deny_insecure_ref(const)
     : module/class において,定数 const (文字列またはシンボル) への
     : $SAFE >= 4 での参照を禁止する.(戻り値は const のシンボル)
     : $SAFE >= 4 でこのメソッドが呼ばれた場合は例外を発生する.
     : 参照禁止に設定した定数を $SAFE >= 4 で参照しようとすると
     : 例外を発生する.

   Module#insecure_ref_allowed?(const)
     : module/class において,定数 const (文字列またはシンボル) への
     : $SAFE >= 4 での参照が許可されているならば true を,
     : 禁止されているならば false を返す.
     : $SAFE >= 4 でこのメソッドが呼ばれた場合は例外を発生する.

   Module#insecure_ref_denied?(const)
     : Module#insecure_ref_allowed?(const) の否定
     : $SAFE >= 4 でこのメソッドが呼ばれた場合は例外を発生する.

   Module#insecure_ref_allowings
     : module/class において,$SAFE >= 4 での参照が許可されている定数の
     : 一覧を返す.
     : $SAFE >= 4 でこのメソッドが呼ばれた場合は例外を発生する.

   Module#insecure_ref_denyings
     : module/class において,$SAFE >= 4 での参照が禁止されている定数の
     : 一覧を返す.
     : $SAFE >= 4 でこのメソッドが呼ばれた場合は例外を発生する.
----------------------------------------------------

----< RUBY_PLATFORM (PLATFORM) >--------------------
   Object.deny_insecure_ref(:RUBY_PLATFORM) および 
   Object.deny_insecure_ref(:PLATFORM) として設定されている.
----------------------------------------------------

Index: eval.c
===================================================================
RCS file: /src/ruby/eval.c,v
retrieving revision 1.582
diff -u -r1.582 eval.c
--- eval.c	30 Oct 2003 16:33:31 -0000	1.582
+++ eval.c	1 Nov 2003 04:24:01 -0000
@@ -6426,6 +6426,16 @@
 }
 
 static VALUE
+loadpath_getter(id)
+    ID id;
+{
+    if (rb_safe_level() >= 4) {
+	return rb_ary_new();
+    }
+    return rb_load_path;
+}
+
+static VALUE
 rb_f_local_variables()
 {
     ID *tbl;
@@ -6725,9 +6735,9 @@
 Init_load()
 {
     rb_load_path = rb_ary_new();
-    rb_define_readonly_variable("$:", &rb_load_path);
-    rb_define_readonly_variable("$-I", &rb_load_path);
-    rb_define_readonly_variable("$LOAD_PATH", &rb_load_path);
+    rb_define_hooked_variable("$:", &rb_load_path, loadpath_getter, 0);
+    rb_define_hooked_variable("$-I", &rb_load_path, loadpath_getter, 0);
+    rb_define_hooked_variable("$LOAD_PATH", &rb_load_path, loadpath_getter, 0);
 
     rb_features = rb_ary_new();
     rb_define_readonly_variable("$\"", &rb_features);
Index: hash.c
===================================================================
RCS file: /src/ruby/hash.c,v
retrieving revision 1.117
diff -u -r1.117 hash.c
--- hash.c	24 Oct 2003 14:31:10 -0000	1.117
+++ hash.c	1 Nov 2003 04:24:02 -0000
@@ -44,7 +44,6 @@
 
 VALUE rb_cHash;
 
-static VALUE envtbl;
 static ID id_hash, id_call, id_default;
 
 VALUE
@@ -1017,6 +1016,60 @@
     return env_str_new(ptr, strlen(ptr));
 }
 
+#define ENV_INSECURE_REF_ALLOW  1
+#define ENV_INSECURE_REF_DENY   0
+
+#ifndef DEFAULT_ENVKEY_ON_TABLE
+#define DEFAULT_ENVKEY_ON_TABLE ENV_INSECURE_REF_DENY
+#endif
+static int envkey_on_table = DEFAULT_ENVKEY_ON_TABLE;
+
+static VALUE envtbl, envkey_table;
+static ID id_case_conv;
+
+static int
+env_secure_i(name)
+    VALUE name;
+{
+    VALUE key;
+
+    StringValue(name);
+#ifdef ENV_IGNORECASE
+    key = rb_funcall(name, id_case_conv, 0);
+#else
+    key = name;
+#endif
+    if (RTEST(rb_ary_includes(envkey_table, key))) {
+	return envkey_on_table;
+    } else {
+	return !envkey_on_table;
+    }
+}
+
+static VALUE
+env_secure_p(obj, name)
+    VALUE obj, name;
+{
+    rb_secure(4);
+    if (env_secure_i(name)) {
+	return Qtrue;
+    } else {
+	return Qfalse;
+    }
+}
+
+static VALUE
+env_insecure_p(obj, name)
+    VALUE obj, name;
+{
+    rb_secure(4);
+    if (env_secure_i(name)) {
+	return Qfalse;
+    } else {
+	return Qtrue;
+    }
+}
+
 static VALUE
 env_delete(obj, name)
     VALUE obj, name;
@@ -1069,6 +1122,14 @@
     if (strlen(nam) != RSTRING(name)->len) {
 	rb_raise(rb_eArgError, "bad environment variable name");
     }
+    if (rb_safe_level() >= 4) {
+	/* allowed key? */
+	if (!env_secure_i(name)) {
+	    /* not allowed */
+	    /* rb_raise(rb_eSecurityError, "Insecure: can't refer ENV[\"%s\"]", name); */
+	    return Qnil;
+	}
+    }
     env = getenv(nam);
     if (env) {
 #ifdef ENV_IGNORECASE
@@ -1101,7 +1162,12 @@
     if (strlen(nam) != RSTRING(key)->len) {
 	rb_raise(rb_eArgError, "bad environment variable name");
     }
-    env = getenv(nam);
+    if (rb_safe_level() >= 4 && !env_secure_i(key)) {
+	/* not allowed */
+	env = (char*)NULL;
+    } else {
+	env = getenv(nam);
+    }
     if (!env) {
 	if (rb_block_given_p()) {
 	    if (argc > 1) {
@@ -1304,7 +1370,10 @@
     while (*env) {
 	char *s = strchr(*env, '=');
 	if (s) {
-	    rb_ary_push(ary, env_str_new(*env, s-*env));
+	    VALUE k = env_str_new(*env, s-*env);
+	    if (rb_safe_level() < 4 || env_secure_i(k)) {
+		rb_ary_push(ary, k);
+	    }
 	}
 	env++;
     }
@@ -1335,7 +1404,9 @@
     while (*env) {
 	char *s = strchr(*env, '=');
 	if (s) {
-	    rb_ary_push(ary, env_str_new2(s+1));
+	    if (rb_safe_level() < 4 || env_secure_i(env_str_new(*env,s-*env))) {
+		rb_ary_push(ary, env_str_new2(s+1));
+	    }
 	}
 	env++;
     }
@@ -1369,8 +1440,11 @@
     while (*env) {
 	char *s = strchr(*env, '=');
 	if (s) {
-	    rb_ary_push(ary, env_str_new(*env, s-*env));
-	    rb_ary_push(ary, env_str_new2(s+1));
+	    VALUE k = env_str_new(*env, s-*env);
+	    if (rb_safe_level() < 4 || env_secure_i(k)) {
+		rb_ary_push(ary, k);
+		rb_ary_push(ary, env_str_new2(s+1));
+	    }
 	}
 	env++;
     }
@@ -1468,8 +1542,10 @@
 	if (s) {
 	    VALUE k = env_str_new(*env, s-*env);
 	    VALUE v = env_str_new2(s+1);
-	    if (RTEST(rb_yield_values(2, k, v))) {
-		rb_ary_push(result, rb_assoc_new(k, v));
+	    if (rb_safe_level() < 4 || env_secure_i(k)) {
+		if (RTEST(rb_yield_values(2, k, v))) {
+		    rb_ary_push(result, rb_assoc_new(k, v));
+		}
 	    }
 	}
 	env++;
@@ -1514,15 +1590,17 @@
     while (*env) {
 	char *s = strchr(*env, '=');
 
-	if (env != environ) {
-	    rb_str_buf_cat2(str, ", ");
-	}
 	if (s) {
-	    rb_str_buf_cat2(str, "\"");
-	    rb_str_buf_cat(str, *env, s-*env);
-	    rb_str_buf_cat2(str, "\"=>");
-	    i = rb_inspect(rb_str_new2(s+1));
-	    rb_str_buf_append(str, i);
+	    if (rb_safe_level() < 4 || env_secure_i(env_str_new(*env,s-*env))) {
+		if (env != environ) {
+		    rb_str_buf_cat2(str, ", ");
+		}
+		rb_str_buf_cat2(str, "\"");
+		rb_str_buf_cat(str, *env, s-*env);
+		rb_str_buf_cat2(str, "\"=>");
+		i = rb_inspect(rb_str_new2(s+1));
+		rb_str_buf_append(str, i);
+	    }
 	}
 	env++;
     }
@@ -1543,8 +1621,10 @@
     while (*env) {
 	char *s = strchr(*env, '=');
 	if (s) {
-	    rb_ary_push(ary, rb_assoc_new(env_str_new(*env, s-*env),
-					  env_str_new2(s+1)));
+	    VALUE k = env_str_new(*env, s-*env);
+	    if (rb_safe_level() < 4 || env_secure_i(k)) {
+		rb_ary_push(ary, rb_assoc_new(k, env_str_new2(s+1)));
+	    }
 	}
 	env++;
     }
@@ -1561,14 +1641,21 @@
 static VALUE
 env_size()
 {
-    int i;
+    int size = 0;
     char **env;
 
     env = GET_ENVIRON(environ);
-    for(i=0; env[i]; i++)
-	;
+    while (*env) {
+	char *s = strchr(*env, '=');
+	if (s) {
+	    if (rb_safe_level() < 4 || env_secure_i(env_str_new(*env,s-*env))) {
+		size++;
+	    }
+	}
+	env++;
+    }
     FREE_ENVIRON(environ);
-    return INT2FIX(i);
+    return INT2FIX(size);
 }
 
 static VALUE
@@ -1576,6 +1663,13 @@
 {
     char **env;
 
+    if (rb_safe_level() >= 4) {
+	if (FIX2INT(env_size()) > 0) {
+	    return Qtrue;
+	} else {
+	    return Qfalse;
+	}
+    }
     env = GET_ENVIRON(environ);
     if (env[0] == 0) {
 	FREE_ENVIRON(environ);
@@ -1591,6 +1685,9 @@
 {
     char *s;
 
+    if (rb_safe_level() >= 4 && !env_secure_i(key)) {
+	return Qfalse;
+    }
     s = StringValuePtr(key);
     if (strlen(s) != RSTRING(key)->len)
 	rb_raise(rb_eArgError, "bad environment variable name");
@@ -1609,13 +1706,16 @@
     while (*env) {
 	char *s = strchr(*env, '=');
 	if (s++) {
+	    VALUE k = env_str_new(*env, s-*env);
+	    if (rb_safe_level() < 4 || env_secure_i(k)) {
 #ifdef ENV_IGNORECASE
-	    if (strncasecmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
+		if (strncasecmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
 #else
-	    if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
+		if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
 #endif
-		FREE_ENVIRON(environ);
-		return Qtrue;
+		    FREE_ENVIRON(environ);
+		    return Qtrue;
+		}
 	    }
 	}
 	env++;
@@ -1629,21 +1729,22 @@
     VALUE dmy, value;
 {
     char **env;
-    VALUE str;
 
     StringValue(value);
     env = GET_ENVIRON(environ);
     while (*env) {
 	char *s = strchr(*env, '=');
 	if (s++) {
+	    VALUE k = env_str_new(*env, s-*env);
+	    if (rb_safe_level() < 4 || env_secure_i(k)) {
 #ifdef ENV_IGNORECASE
-	    if (strncasecmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
+		if (strncasecmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
 #else
-	    if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
+		if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
 #endif
-		str = env_str_new(*env, s-*env-1);
-		FREE_ENVIRON(environ);
-		return str;
+		    FREE_ENVIRON(environ);
+		    return k;
+		}
 	    }
 	}
 	env++;
@@ -1668,7 +1769,12 @@
 	    RARRAY(indexes)->ptr[i] = Qnil;
 	}
 	else {
-	    RARRAY(indexes)->ptr[i] = env_str_new2(getenv(RSTRING(tmp)->ptr));
+	    if (rb_safe_level() >= 4 && !env_secure_i(tmp)) {
+		RARRAY(indexes)->ptr[i] = Qnil;
+	    }
+	    else {
+		RARRAY(indexes)->ptr[i] = env_str_new2(getenv(RSTRING(tmp)->ptr));
+	    }
 	}
 	RARRAY(indexes)->len = i+1;
     }
@@ -1686,8 +1792,10 @@
     while (*env) {
 	char *s = strchr(*env, '=');
 	if (s) {
-	    rb_hash_aset(hash, env_str_new(*env, s-*env),
-			       env_str_new2(s+1));
+	    VALUE key = env_str_new(*env, s-*env);
+	    if (rb_safe_level() < 4 || env_secure_i(key)) {
+		rb_hash_aset(hash, key, env_str_new2(s+1));
+	    }
 	}
 	env++;
     }
@@ -1779,6 +1887,82 @@
     return env;
 }
 
+static VALUE
+env_secure_keys(obj)
+    VALUE obj;
+{
+    VALUE keys, secure;
+    long i;
+
+    rb_secure(4);
+    secure = rb_ary_new();
+    keys = env_keys();
+    for(i=0; i<RARRAY(keys)->len; i++) {
+	if (env_secure_i(RARRAY(keys)->ptr[i])) {
+	    rb_ary_push(secure, RARRAY(keys)->ptr[i]);
+	}
+    }
+    return rb_obj_freeze(secure);
+}
+
+static VALUE
+env_insecure_keys(obj)
+    VALUE obj;
+{
+    VALUE keys, insecure;
+    long i;
+
+    rb_secure(4);
+    insecure = rb_ary_new();
+    keys = env_keys();
+    for(i=0; i<RARRAY(keys)->len; i++) {
+	if (!env_secure_i(RARRAY(keys)->ptr[i])) {
+	    rb_ary_push(insecure, RARRAY(keys)->ptr[i]);
+	}
+    }
+    return rb_obj_freeze(insecure);
+}
+
+static VALUE
+env_set_secure_mode(name, mode)
+    VALUE name;
+    int mode;
+{
+    VALUE key;
+
+    rb_secure(4);
+
+    StringValue(name);
+#ifdef ENV_IGNORECASE
+    key = rb_funcall(name, id_case_conv, 0);
+#else
+    key = name;
+#endif
+
+    if (envkey_on_table == mode) {
+	if (!RTEST(rb_ary_includes(envkey_table, key))) {
+	    rb_ary_push(envkey_table, rb_obj_freeze(rb_obj_dup(key)));
+	}
+    } else {
+	rb_ary_delete(envkey_table, key);
+    }
+    return key;
+}
+
+static VALUE
+env_secure(obj, name)
+    VALUE obj, name;
+{
+    return env_set_secure_mode(name, ENV_INSECURE_REF_ALLOW);
+}
+
+static VALUE
+env_insecure(obj, name)
+    VALUE obj, name;
+{
+    return env_set_secure_mode(name, ENV_INSECURE_REF_DENY);
+}
+
 void
 Init_Hash()
 {
@@ -1846,11 +2030,25 @@
     rb_define_method(rb_cHash,"key?", rb_hash_has_key, 1);
     rb_define_method(rb_cHash,"value?", rb_hash_has_value, 1);
 
+#if defined(__human68k__)
+    id_case_conv = rb_intern("downcase");
+#else
+    id_case_conv = rb_intern("upcase");
+#endif
+
 #ifndef __MACOS__ /* environment variables nothing on MacOS. */
     origenviron = environ;
     envtbl = rb_obj_alloc(rb_cObject);
     rb_extend_object(envtbl, rb_mEnumerable);
+    envkey_table = rb_ary_new();
+    rb_global_variable(&envkey_table);
 
+    rb_define_singleton_method(envtbl,"allow_insecure_ref", env_secure, 1);
+    rb_define_singleton_method(envtbl,"deny_insecure_ref", env_insecure, 1);
+    rb_define_singleton_method(envtbl,"insecure_ref_allowed?", env_secure_p, 1);
+    rb_define_singleton_method(envtbl,"insecure_ref_denied?", env_insecure_p, 1);
+    rb_define_singleton_method(envtbl,"insecure_ref_allowings", env_secure_keys, 0);
+    rb_define_singleton_method(envtbl,"insecure_ref_denyings", env_insecure_keys, 0);
     rb_define_singleton_method(envtbl,"[]", rb_f_getenv, 1);
     rb_define_singleton_method(envtbl,"fetch", env_fetch, -1);
     rb_define_singleton_method(envtbl,"[]=", env_aset, 2);
Index: intern.h
===================================================================
RCS file: /src/ruby/intern.h,v
retrieving revision 1.137
diff -u -r1.137 intern.h
--- intern.h	14 Oct 2003 02:53:53 -0000	1.137
+++ intern.h	1 Nov 2003 04:24:02 -0000
@@ -476,6 +476,11 @@
 void rb_define_class_variable _((VALUE, const char*, VALUE));
 VALUE rb_mod_class_variables _((VALUE));
 VALUE rb_mod_remove_cvar _((VALUE, VALUE));
+int rb_insecure_const_defined _((VALUE, ID));
+void rb_insecure_const_set _((VALUE, ID));
+VALUE rb_insecure_const_remove _((VALUE, ID));
+VALUE rb_mod_insecure_constants _((VALUE));
+VALUE rb_mod_secure_constants _((VALUE));
 /* version.c */
 void ruby_show_version _((void));
 void ruby_show_copyright _((void));
Index: object.c
===================================================================
RCS file: /src/ruby/object.c,v
retrieving revision 1.129
diff -u -r1.129 object.c
--- object.c	13 Aug 2003 07:13:45 -0000	1.129
+++ object.c	1 Nov 2003 04:24:02 -0000
@@ -908,6 +908,62 @@
 }
 
 static VALUE
+rb_mod_insecure_const(klass, name)
+    VALUE klass;
+    VALUE name;
+{
+    ID id;
+
+    id = rb_to_id(name);
+    if (!rb_is_const_id(id)) {
+	rb_name_error(id, "wrong constant name %s", rb_id2name(id));
+    }
+
+    rb_insecure_const_set(klass, id);
+    return ID2SYM(id);
+}
+
+static VALUE
+rb_mod_secure_const(klass, name)
+    VALUE klass;
+    VALUE name;
+{
+    ID id;
+
+    rb_secure(4);
+    id = rb_to_id(name);
+    if (!rb_is_const_id(id)) {
+	rb_name_error(id, "wrong constant name %s", rb_id2name(id));
+    }
+
+    return rb_insecure_const_remove(klass, id);
+}
+
+VALUE
+rb_mod_insecure_const_p(mod, name)
+    VALUE mod;
+    VALUE name;
+{
+    rb_secure(4);
+    if (rb_insecure_const_defined(mod, rb_to_id(name))) {
+	return Qtrue;
+    }
+    return Qfalse;
+}
+
+VALUE
+rb_mod_secure_const_p(mod, name)
+    VALUE mod;
+    VALUE name;
+{
+    rb_secure(4);
+    if (RTEST(rb_mod_insecure_const_p(mod, name))) {
+	return Qfalse;
+    }
+    return Qtrue;
+}
+
+static VALUE
 rb_mod_const_defined(mod, name)
     VALUE mod, name;
 {
@@ -1527,6 +1583,13 @@
     rb_define_method(rb_cModule, "const_missing", rb_mod_const_missing, 1);
     rb_define_method(rb_cModule, "class_variables", rb_mod_class_variables, 0);
     rb_define_private_method(rb_cModule, "remove_class_variable", rb_mod_remove_cvar, 1);
+
+    rb_define_method(rb_cModule, "allow_insecure_ref", rb_mod_secure_const, 1);
+    rb_define_method(rb_cModule, "deny_insecure_ref", rb_mod_insecure_const, 1);
+    rb_define_method(rb_cModule, "insecure_ref_allowed?", rb_mod_secure_const_p, 1);
+    rb_define_method(rb_cModule, "insecure_ref_denied?", rb_mod_insecure_const_p, 1);
+    rb_define_method(rb_cModule, "insecure_ref_allowings", rb_mod_secure_constants, 0);
+    rb_define_method(rb_cModule, "insecure_ref_denyings", rb_mod_insecure_constants, 0);
 
     rb_define_method(rb_cClass, "allocate", rb_obj_alloc, 0);
     rb_define_method(rb_cClass, "new", rb_class_new_instance, -1);
Index: variable.c
===================================================================
RCS file: /src/ruby/variable.c,v
retrieving revision 1.107
diff -u -r1.107 variable.c
--- variable.c	13 Oct 2003 14:57:36 -0000	1.107
+++ variable.c	1 Nov 2003 04:24:02 -0000
@@ -21,6 +21,7 @@
 static st_table *rb_global_tbl;
 st_table *rb_class_tbl;
 static ID autoload, classpath, tmp_classpath;
+static ID insecure_consts;
 
 void
 Init_var_tables()
@@ -30,6 +31,7 @@
     autoload = rb_intern("__autoload__");
     classpath = rb_intern("__classpath__");
     tmp_classpath = rb_intern("__tmp_classpath__");
+    insecure_consts = rb_intern("__insecure_contants__");
 }
 
 struct fc_result {
@@ -1275,6 +1277,12 @@
     VALUE value, tmp;
     int mod_retry = 0;
 
+    if (rb_insecure_const_defined(klass, id) && rb_safe_level() >= 4) {
+	rb_raise(rb_eSecurityError, 
+		 "Insecure: can't access insecure constant %s", 
+		 rb_id2name(id));
+    }
+
     tmp = klass;
   retry:
     while (tmp) {
@@ -1430,6 +1438,141 @@
     return rb_const_list(rb_mod_const_of(mod, 0));
 }
 
+static int 
+rb_insecure_const_defined_0(klass, id, exclude, recurse)
+    VALUE klass;
+    ID id;
+    int exclude, recurse;
+{
+    VALUE tmp, table, val;
+    int mod_retry = 0;
+
+    tmp = klass;
+  retry:
+    while (tmp) {
+	table = ivar_get(tmp, insecure_consts, 0);
+	if (TYPE(table) == T_ARRAY && rb_ary_includes(table, ID2SYM(id))) {
+	    if (exclude && tmp == rb_cObject && klass != rb_cObject) {
+		rb_warn("toplevel constant %s referenced by %s::%s",
+			rb_id2name(id), rb_class2name(klass), rb_id2name(id));
+	    }
+	    return 1;
+	}
+	if (!recurse && klass != rb_cObject) break;
+	tmp = RCLASS(tmp)->super;
+    }
+    if (!exclude && !mod_retry && BUILTIN_TYPE(klass) == T_MODULE) {
+	mod_retry = 1;
+	tmp = rb_cObject;
+	goto retry;
+    }
+
+    return 0;
+}
+
+int
+rb_insecure_const_defined_from(klass, id)
+    VALUE klass;
+    ID id;
+{
+    return rb_insecure_const_defined_0(klass, id, Qtrue, Qtrue);
+}
+
+int
+rb_insecure_const_defined(klass, id)
+    VALUE klass;
+    ID id;
+{
+    return rb_insecure_const_defined_0(klass, id, Qfalse, Qtrue);
+}
+
+int
+rb_insecure_const_defined_at(klass, id)
+    VALUE klass;
+    ID id;
+{
+    return rb_insecure_const_defined_0(klass, id, Qtrue, Qfalse);
+}
+
+void
+rb_insecure_const_set(klass, id)
+    VALUE klass;
+    ID id;
+{
+    VALUE table, sym;
+    long i;
+
+    table = ivar_get(klass, insecure_consts, 0);
+    if (TYPE(table) != T_ARRAY) {
+	table = rb_ary_new();
+	rb_ivar_set(klass, insecure_consts, table);
+    }
+
+    sym = ID2SYM(id);
+    for (i=0; i<RARRAY(table)->len; i++) {
+	if (rb_equal(RARRAY(table)->ptr[i], sym)) {
+	    return;
+	}
+    }
+    rb_ary_push(table, sym);
+    return;
+}
+
+VALUE 
+rb_insecure_const_remove(klass, id)
+    VALUE klass;
+    ID id;
+{
+    VALUE table;
+
+    table = ivar_get(klass, insecure_consts, 0);
+    if (TYPE(table) == T_ARRAY) {
+	return rb_ary_delete(table, ID2SYM(id));
+    }
+    return Qnil;
+}
+
+VALUE
+rb_mod_insecure_constants(mod)
+    VALUE mod;
+{
+    VALUE table, ary;
+    long i;
+
+    rb_secure(4);
+
+    table = ivar_get(mod, insecure_consts, 0);
+
+    if (TYPE(table) != T_ARRAY) {
+	/* no table */
+	return rb_obj_freeze(rb_ary_new2(0));
+    }
+
+    ary = rb_ary_new();
+    for(i=0; i<RARRAY(table)->len; i++) {
+	rb_ary_push(ary, rb_obj_freeze(rb_str_new2(rb_id2name(SYM2ID(RARRAY(table)->ptr[i])))));
+    }
+    return rb_obj_freeze(ary);
+}
+
+VALUE
+rb_mod_secure_constants(mod)
+    VALUE mod;
+{
+    VALUE list, secure;
+    long i;
+
+    rb_secure(4);
+    secure = rb_ary_new();
+    list = rb_mod_constants(mod);
+    for(i=0; i<RARRAY(list)->len; i++) {
+	if (!rb_insecure_const_defined(mod, rb_to_id(RARRAY(list)->ptr[i]))) {
+	    rb_ary_push(secure, RARRAY(list)->ptr[i]);
+	}
+    }
+    return rb_obj_freeze(secure);
+}
+
 static int
 rb_const_defined_0(klass, id, exclude, recurse)
     VALUE klass;
@@ -1517,6 +1660,11 @@
     ID id;
     VALUE val;
 {
+    if (rb_insecure_const_defined(klass, id) && rb_safe_level() >= 4) {
+	rb_raise(rb_eSecurityError, 
+		 "Insecure: can't change insecure constant %s", 
+		 rb_id2name(id));
+    }
     mod_av_set(klass, id, val, Qtrue);
 }
 
Index: version.c
===================================================================
RCS file: /src/ruby/version.c,v
retrieving revision 1.8
diff -u -r1.8 version.c
--- version.c	16 Jan 2003 07:34:03 -0000	1.8
+++ version.c	1 Nov 2003 04:24:02 -0000
@@ -24,11 +24,13 @@
     rb_define_global_const("RUBY_VERSION", v);
     rb_define_global_const("RUBY_RELEASE_DATE", d);
     rb_define_global_const("RUBY_PLATFORM", p);
+    rb_insecure_const_set(rb_cObject, rb_intern("RUBY_PLATFORM"));
 
     /* obsolete constants */
     rb_define_global_const("VERSION", v);
     rb_define_global_const("RELEASE_DATE", d);
     rb_define_global_const("PLATFORM", p);
+    rb_insecure_const_set(rb_cObject, rb_intern("PLATFORM"));
 }
 
 void

-- 
                                         永井 秀利 (九工大 知能情報)
                                             nagai / ai.kyutech.ac.jp