├── .gitignore ├── Gemfile ├── README.md ├── Rakefile ├── lib ├── ruby_cop.rb └── ruby_cop │ ├── aliases.rb │ ├── analyzer.rb │ ├── gray_list.rb │ ├── node_builder.rb │ ├── policy.rb │ ├── ruby.rb │ ├── ruby │ ├── args.rb │ ├── array.rb │ ├── assignment.rb │ ├── assoc.rb │ ├── blocks.rb │ ├── call.rb │ ├── case.rb │ ├── constants.rb │ ├── definitions.rb │ ├── for.rb │ ├── hash.rb │ ├── if.rb │ ├── list.rb │ ├── node.rb │ ├── operators.rb │ ├── params.rb │ ├── position.rb │ ├── range.rb │ ├── statements.rb │ ├── string.rb │ ├── tokens.rb │ ├── variables.rb │ └── while.rb │ └── version.rb ├── ruby_cop.gemspec ├── spec ├── analyzer │ ├── node_builder_spec.rb │ └── policy_spec.rb ├── policy_spec.rb └── spec_helper.rb └── tasks ├── rspec.rake └── yard.rake /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | .rvmrc 4 | Gemfile.lock 5 | doc 6 | pkg/* 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # Specify your gem's dependencies in ruby_cop.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RubyCop 2 | 3 | RubyCop scans Ruby code and tells you whether it's safe or not. We use 4 | it at [CodeSchool][1] to check user-submitted code before we eval it on 5 | the server. 6 | 7 | RubyCop is made up of two pieces: NodeBuilder and Policy. NodeBuilder is 8 | responsible for parsing Ruby code (using Ripper) and building an AST 9 | (Abstract Syntax Tree). Policy then scans the AST and tells you whether 10 | the code is safe or not. 11 | 12 | ## Requirements 13 | 14 | RubyCop requires Ruby 1.9, though it should work under 1.8 if you 15 | include the "ripper" gem. 16 | 17 | ## Usage 18 | 19 | Here's a quick example of building the AST, and evaluating it with the 20 | Policy: 21 | 22 | >> require "ruby_cop" 23 | => true 24 | >> policy = RubyCop::Policy.new 25 | >> ast = RubyCop::NodeBuilder.build("x = 1 + 2") 26 | >> ast.accept(policy) 27 | => true 28 | 29 | And if you pass in some unsafe code: 30 | 31 | >> ast = RubyCop::NodeBuilder.build("x = `ls -la`") 32 | >> ast.accept(policy) 33 | => false 34 | 35 | ## Drawbacks 36 | 37 | Ruby is a very dynamic language, so this kind of static analysis will 38 | only get you so far. RubyCop blocks obvious things like backticks and 39 | unsafe Kernel methods (#eval, #exec, #fork, etc.), but has to err on the 40 | side of safety in other places. For instance, #send is considered 41 | unsafe. 42 | 43 | [1]: http://www.codeschool.com/ 44 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | Dir['tasks/*.rake'].each { |file| load(file) } 4 | -------------------------------------------------------------------------------- /lib/ruby_cop.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | # Your code goes here... 3 | end 4 | 5 | require 'ripper' 6 | 7 | require 'ruby_cop/ruby' 8 | require 'ruby_cop/node_builder' 9 | require 'ruby_cop/gray_list' 10 | require 'ruby_cop/policy' 11 | require 'ruby_cop/aliases' 12 | -------------------------------------------------------------------------------- /lib/ruby_cop/aliases.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | # Some aliases to smooth over some issues with the two different forks of the 3 | # library. This is temporary until all apps using the library have been 4 | # updated to use the non-Analyzer version. 5 | module Analyzer 6 | GrayList = ::RubyCop::GrayList 7 | NodeBuilder = ::RubyCop::NodeBuilder 8 | Policy = ::RubyCop::Policy 9 | Ruby = ::RubyCop::Ruby 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/ruby_cop/analyzer.rb: -------------------------------------------------------------------------------- 1 | require 'ripper' 2 | 3 | require 'ruby_cop/analyzer/ruby' 4 | require 'ruby_cop/analyzer/node_builder' 5 | require 'ruby_cop/analyzer/gray_list' 6 | require 'ruby_cop/analyzer/policy' -------------------------------------------------------------------------------- /lib/ruby_cop/gray_list.rb: -------------------------------------------------------------------------------- 1 | require 'set' 2 | 3 | module RubyCop 4 | # Combination blacklist and whitelist. 5 | class GrayList 6 | def initialize 7 | @blacklist = Set.new 8 | @whitelist = Set.new 9 | end 10 | 11 | # An item is allowed if it's whitelisted, or if it's not blacklisted. 12 | def allow?(item) 13 | @whitelist.include?(item) || !@blacklist.include?(item) 14 | end 15 | 16 | def blacklist(item) 17 | @whitelist.delete(item) 18 | @blacklist.add(item) 19 | end 20 | 21 | def whitelist(item) 22 | @blacklist.delete(item) 23 | @whitelist.add(item) 24 | end 25 | end 26 | end -------------------------------------------------------------------------------- /lib/ruby_cop/node_builder.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | class NodeBuilder < Ripper::SexpBuilder 3 | def initialize(src, filename=nil, lineno=nil) 4 | @src = src ||= filename && File.read(filename) || '' 5 | @filename = filename 6 | super 7 | end 8 | 9 | class << self 10 | def build(src, filename=nil) 11 | new(src, filename).parse 12 | end 13 | end 14 | 15 | def on_alias(new_name, old_name) 16 | Ruby::Alias.new(to_ident(new_name), to_ident(old_name)) 17 | end 18 | 19 | def on_aref(target, args) 20 | Ruby::Call.new(target, ident(:[]), args) 21 | end 22 | 23 | def on_aref_field(target, args) 24 | Ruby::Call.new(target, ident(:[]), args) 25 | end 26 | 27 | def on_arg_paren(args) 28 | args 29 | end 30 | 31 | def on_args_add(args, arg) 32 | args.add(arg); args 33 | end 34 | 35 | def on_args_add_block(args, block) 36 | args.add_block(block) if block; args 37 | end 38 | 39 | def on_args_new 40 | Ruby::Args.new 41 | end 42 | 43 | def on_array(args) 44 | args ? args.to_array : Ruby::Array.new 45 | end 46 | 47 | def on_assign(lvalue, rvalue) 48 | lvalue.assignment(rvalue, ident(:'=')) 49 | end 50 | 51 | def on_assoc_new(key, value) 52 | Ruby::Assoc.new(key, value) 53 | end 54 | 55 | def on_assoclist_from_args(args) 56 | args 57 | end 58 | 59 | def on_bare_assoc_hash(assocs) 60 | Ruby::Hash.new(assocs) 61 | end 62 | 63 | def on_BEGIN(statements) 64 | Ruby::Call.new(nil, ident(:BEGIN), nil, statements) 65 | end 66 | 67 | def on_begin(body) 68 | body.is_a?(Ruby::ChainedBlock) ? body : body.to_chained_block 69 | end 70 | 71 | def on_binary(lvalue, operator, rvalue) 72 | Ruby::Binary.new(lvalue, rvalue, operator) 73 | end 74 | 75 | def on_blockarg(arg) 76 | arg 77 | end 78 | 79 | def on_block_var(params, something) 80 | params 81 | end 82 | 83 | def on_bodystmt(body, rescue_block, else_block, ensure_block) 84 | statements = [rescue_block, else_block, ensure_block].compact 85 | statements.empty? ? body : body.to_chained_block(statements) 86 | end 87 | 88 | def on_brace_block(params, statements) 89 | statements.to_block(params) 90 | end 91 | 92 | def on_break(args) 93 | Ruby::Call.new(nil, ident(:break), args) 94 | end 95 | 96 | def on_call(target, separator, identifier) 97 | Ruby::Call.new(target, identifier) 98 | end 99 | 100 | def on_case(args, when_block) 101 | Ruby::Case.new(args, when_block) 102 | end 103 | 104 | def on_CHAR(token) 105 | Ruby::Char.new(token, position) 106 | end 107 | 108 | def on_class(const, superclass, body) 109 | Ruby::Class.new(const, superclass, body) 110 | end 111 | 112 | def on_class_name_error(ident) 113 | raise SyntaxError, 'class/module name must be CONSTANT' 114 | end 115 | 116 | def on_command(identifier, args) 117 | Ruby::Call.new(nil, identifier, args) 118 | end 119 | 120 | def on_command_call(target, separator, identifier, args) 121 | Ruby::Call.new(target, identifier, args) 122 | end 123 | 124 | def on_const(token) 125 | Ruby::Constant.new(token, position) 126 | end 127 | 128 | def on_const_path_field(namespace, const) 129 | const.namespace = namespace; const 130 | end 131 | 132 | def on_const_path_ref(namespace, const) 133 | const.namespace = namespace; const 134 | end 135 | 136 | def on_const_ref(const) 137 | const 138 | end 139 | 140 | def on_cvar(token) 141 | Ruby::ClassVariable.new(token, position) 142 | end 143 | 144 | def on_def(identifier, params, body) 145 | Ruby::Method.new(nil, identifier, params, body) 146 | end 147 | 148 | def on_defs(target, separator, identifier, params, body) 149 | Ruby::Method.new(target, identifier, params, body) 150 | end 151 | 152 | def on_defined(ref) 153 | Ruby::Defined.new(ref) 154 | end 155 | 156 | def on_do_block(params, statements) 157 | statements.to_block(params) 158 | end 159 | 160 | def on_dot2(min, max) 161 | Ruby::Range.new(min, max, false) 162 | end 163 | 164 | def on_dot3(min, max) 165 | Ruby::Range.new(min, max, true) 166 | end 167 | 168 | def on_dyna_symbol(symbol) 169 | symbol.to_dyna_symbol 170 | end 171 | 172 | def on_else(statements) 173 | Ruby::Else.new(statements) 174 | end 175 | 176 | def on_END(statements) 177 | Ruby::Call.new(nil, ident(:END), nil, statements) 178 | end 179 | 180 | def on_ensure(statements) 181 | statements 182 | end 183 | 184 | def on_if(expression, statements, else_block) 185 | Ruby::If.new(expression, statements, else_block) 186 | end 187 | alias_method :on_elsif, :on_if 188 | 189 | def on_ifop(condition, then_part, else_part) 190 | Ruby::IfOp.new(condition, then_part, else_part) 191 | end 192 | 193 | def on_if_mod(expression, statement) 194 | Ruby::IfMod.new(expression, statement) 195 | end 196 | 197 | def on_fcall(identifier) 198 | Ruby::Call.new(nil, identifier) 199 | end 200 | 201 | def on_field(target, separator, identifier) 202 | Ruby::Call.new(target, identifier) 203 | end 204 | 205 | def on_float(token) 206 | Ruby::Float.new(token, position) 207 | end 208 | 209 | def on_for(variable, range, statements) 210 | Ruby::For.new(variable, range, statements) 211 | end 212 | 213 | def on_gvar(token) 214 | Ruby::GlobalVariable.new(token, position) 215 | end 216 | 217 | def on_hash(assocs) 218 | Ruby::Hash.new(assocs) 219 | end 220 | 221 | def on_ident(token) 222 | ident(token) 223 | end 224 | 225 | def on_int(token) 226 | Ruby::Integer.new(token, position) 227 | end 228 | 229 | def on_ivar(token) 230 | Ruby::InstanceVariable.new(token, position) 231 | end 232 | 233 | def on_kw(token) 234 | Ruby::Keyword.new(token, position) 235 | end 236 | 237 | def on_label(token) 238 | Ruby::Label.new(token, position) 239 | end 240 | 241 | def on_lambda(params, statements) 242 | Ruby::Block.new(statements, params) 243 | end 244 | 245 | def on_massign(lvalue, rvalue) 246 | lvalue.assignment(rvalue, ident(:'=')) 247 | end 248 | 249 | def on_method_add_arg(call, args) 250 | call.arguments = args; call 251 | end 252 | 253 | def on_method_add_block(call, block) 254 | call.block = block; call 255 | end 256 | 257 | def on_mlhs_add(assignment, ref) 258 | assignment.add(ref); assignment 259 | end 260 | 261 | def on_mlhs_add_star(assignment, ref) 262 | assignment.add(Ruby::SplatArg.new(ref)); assignment 263 | end 264 | 265 | def on_mlhs_new 266 | Ruby::MultiAssignmentList.new 267 | end 268 | 269 | def on_module(const, body) 270 | Ruby::Module.new(const, body) 271 | end 272 | 273 | def on_mrhs_add(assignment, ref) 274 | assignment.add(ref); assignment 275 | end 276 | 277 | def on_mrhs_new_from_args(args) 278 | Ruby::MultiAssignmentList.new(args.elements) 279 | end 280 | 281 | def on_next(args) 282 | Ruby::Call.new(nil, ident(:next), args) 283 | end 284 | 285 | def on_op(operator) 286 | operator.intern 287 | end 288 | 289 | def on_opassign(lvalue, operator, rvalue) 290 | lvalue.assignment(rvalue, operator) 291 | end 292 | 293 | def on_params(params, optionals, rest, something, block) 294 | Ruby::Params.new(params, optionals, rest, block) 295 | end 296 | 297 | def on_paren(node) 298 | node 299 | end 300 | 301 | def on_parse_error(message) 302 | raise SyntaxError, message 303 | end 304 | 305 | def on_program(statements) 306 | statements.to_program(@src, @filename) 307 | end 308 | 309 | def on_qwords_add(array, word) 310 | array.add(Ruby::String.new(word)); array 311 | end 312 | 313 | def on_qwords_new 314 | Ruby::Array.new 315 | end 316 | 317 | def on_redo 318 | Ruby::Call.new(nil, ident(:redo)) 319 | end 320 | 321 | def on_regexp_add(regexp, content) 322 | regexp.add(content); regexp 323 | end 324 | 325 | def on_regexp_literal(regexp, rdelim) 326 | regexp 327 | end 328 | 329 | def on_regexp_new 330 | Ruby::Regexp.new 331 | end 332 | 333 | def on_rescue(types, var, statements, block) 334 | statements.to_chained_block(block, Ruby::RescueParams.new(types, var)) 335 | end 336 | 337 | def on_rescue_mod(expression, statements) 338 | Ruby::RescueMod.new(expression, statements) 339 | end 340 | 341 | def on_rest_param(param) 342 | param 343 | end 344 | 345 | def on_retry 346 | Ruby::Call.new(nil, ident(:retry)) 347 | end 348 | 349 | def on_return(args) 350 | Ruby::Call.new(nil, ident(:return), args) 351 | end 352 | 353 | def on_sclass(superclass, body) 354 | Ruby::SingletonClass.new(superclass, body) 355 | end 356 | 357 | def on_stmts_add(target, statement) 358 | target.add(statement) if statement; target 359 | end 360 | 361 | def on_stmts_new 362 | Ruby::Statements.new 363 | end 364 | 365 | def on_string_add(string, content) 366 | string.add(content); string 367 | end 368 | 369 | def on_string_concat(*strings) 370 | Ruby::StringConcat.new(strings) 371 | end 372 | 373 | def on_string_content 374 | Ruby::String.new 375 | end 376 | 377 | # weird string syntax that I didn't know existed until writing this lib. 378 | # ex. "safe level is #$SAFE" => "safe level is 0" 379 | def on_string_dvar(variable) 380 | variable 381 | end 382 | 383 | def on_string_embexpr(expression) 384 | expression 385 | end 386 | 387 | def on_string_literal(string) 388 | string 389 | end 390 | 391 | def on_super(args) 392 | Ruby::Call.new(nil, ident(:super), args) 393 | end 394 | 395 | def on_symbol(token) 396 | Ruby::Symbol.new(token, position) 397 | end 398 | 399 | def on_symbol_literal(symbol) 400 | symbol 401 | end 402 | 403 | def on_top_const_field(field) 404 | field 405 | end 406 | 407 | def on_top_const_ref(const) 408 | const 409 | end 410 | 411 | def on_tstring_content(token) 412 | token 413 | end 414 | 415 | def on_unary(operator, operand) 416 | Ruby::Unary.new(operator, operand) 417 | end 418 | 419 | def on_undef(args) 420 | Ruby::Call.new(nil, ident(:undef), Ruby::Args.new(args.collect { |e| to_ident(e) })) 421 | end 422 | 423 | def on_unless(expression, statements, else_block) 424 | Ruby::Unless.new(expression, statements, else_block) 425 | end 426 | 427 | def on_unless_mod(expression, statement) 428 | Ruby::UnlessMod.new(expression, statement) 429 | end 430 | 431 | def on_until(expression, statements) 432 | Ruby::Until.new(expression, statements) 433 | end 434 | 435 | def on_until_mod(expression, statement) 436 | Ruby::UntilMod.new(expression, statement) 437 | end 438 | 439 | def on_var_alias(new_name, old_name) 440 | Ruby::Alias.new(to_ident(new_name), to_ident(old_name)) 441 | end 442 | 443 | def on_var_field(field) 444 | field 445 | end 446 | 447 | def on_var_ref(ref) 448 | ref 449 | end 450 | 451 | alias on_vcall on_var_ref 452 | 453 | def on_void_stmt 454 | nil 455 | end 456 | 457 | def on_when(expression, statements, next_block) 458 | Ruby::When.new(expression, statements, next_block) 459 | end 460 | 461 | def on_while(expression, statements) 462 | Ruby::While.new(expression, statements) 463 | end 464 | 465 | def on_while_mod(expression, statement) 466 | Ruby::WhileMod.new(expression, statement) 467 | end 468 | 469 | def on_word_add(string, word) 470 | string.add(word); string 471 | end 472 | 473 | def on_words_add(array, word) 474 | array.add(word); array 475 | end 476 | 477 | def on_word_new 478 | Ruby::String.new 479 | end 480 | 481 | def on_words_new 482 | Ruby::Array.new 483 | end 484 | 485 | def on_xstring_add(string, content) 486 | on_string_add(string, content) 487 | end 488 | 489 | def on_xstring_new 490 | Ruby::ExecutableString.new 491 | end 492 | 493 | def on_xstring_literal(string) 494 | string 495 | end 496 | 497 | def on_yield(args) 498 | Ruby::Call.new(nil, ident(:yield), args) 499 | end 500 | 501 | def on_yield0 502 | Ruby::Call.new(nil, ident(:yield)) 503 | end 504 | 505 | def on_zsuper(*) 506 | Ruby::Call.new(nil, ident(:super)) 507 | end 508 | 509 | private 510 | 511 | def ident(ident) 512 | Ruby::Identifier.new(ident, position) 513 | end 514 | 515 | def to_ident(ident_or_sym) 516 | ident_or_sym.is_a?(Ruby::Identifier) ? ident_or_sym : ident(ident_or_sym) 517 | end 518 | 519 | def position 520 | Ruby::Position.new(lineno, column) 521 | end 522 | end 523 | end 524 | -------------------------------------------------------------------------------- /lib/ruby_cop/policy.rb: -------------------------------------------------------------------------------- 1 | require 'set' 2 | 3 | module RubyCop 4 | # Visitor class for Ruby::Node subclasses. Determines whether the node is 5 | # safe according to our rules. 6 | class Policy 7 | def initialize 8 | @const_list = GrayList.new 9 | initialize_const_blacklist 10 | end 11 | 12 | def inspect 13 | '#<%s:0x%x>' % [self.class.name, object_id] 14 | end 15 | 16 | def blacklist_const(const) 17 | @const_list.blacklist(const) 18 | end 19 | 20 | def const_allowed?(const) 21 | @const_list.allow?(const) 22 | end 23 | 24 | def whitelist_const(const) 25 | @const_list.whitelist(const) 26 | end 27 | 28 | def visit(node) 29 | klass = node.class.ancestors.detect do |ancestor| 30 | respond_to?("visit_#{ancestor.name.split('::').last}") 31 | end 32 | if klass 33 | send("visit_#{klass.name.split('::').last}", node) 34 | else 35 | warn "unhandled node type: #{node.inspect}:#{node.class.name}" 36 | true 37 | end 38 | end 39 | 40 | def visit_Alias(node) 41 | false # never allowed 42 | end 43 | 44 | def visit_Args(node) 45 | node.elements.all? { |e| visit(e) } 46 | end 47 | 48 | def visit_Array(node) 49 | node.elements.all? { |e| visit(e) } 50 | end 51 | 52 | def visit_Assoc(node) 53 | visit(node.key) && visit(node.value) 54 | end 55 | 56 | def visit_Binary(node) 57 | visit(node.lvalue) && visit(node.rvalue) 58 | end 59 | 60 | def visit_Block(node) 61 | (node.params.nil? || visit(node.params)) && node.elements.all? { |e| visit(e) } 62 | end 63 | 64 | CALL_BLACKLIST = %w[ 65 | __send__ 66 | abort 67 | alias_method 68 | at_exit 69 | autoload 70 | binding 71 | callcc 72 | caller 73 | class_eval 74 | const_get 75 | const_set 76 | dup 77 | eval 78 | exec 79 | exit 80 | fail 81 | fork 82 | gets 83 | global_variables 84 | instance_eval 85 | load 86 | loop 87 | method 88 | module_eval 89 | open 90 | public_send 91 | readline 92 | readlines 93 | redo 94 | remove_const 95 | require 96 | retry 97 | send 98 | set_trace_func 99 | sleep 100 | spawn 101 | srand 102 | syscall 103 | system 104 | trap 105 | undef 106 | __callee__ 107 | __method__ 108 | ].to_set.freeze 109 | 110 | def visit_Call(node) 111 | !CALL_BLACKLIST.include?(node.identifier.token.to_s) && [node.target, node.arguments, node.block].compact.all? { |e| visit(e) } 112 | end 113 | 114 | def visit_Case(node) 115 | visit(node.expression) && visit(node.block) 116 | end 117 | 118 | def visit_ChainedBlock(node) 119 | node.elements.all? { |e| visit(e) } && node.blocks.all? { |e| visit(e) } && (node.params.nil? || visit(node.params)) 120 | end 121 | 122 | def visit_Class(node) 123 | visit(node.const) && (node.superclass.nil? || visit(node.superclass)) && visit(node.body) 124 | end 125 | 126 | def visit_ClassVariable(node) 127 | false # never allowed 128 | end 129 | 130 | def visit_ClassVariableAssignment(node) 131 | false # never allowed 132 | end 133 | 134 | def visit_Char(node) 135 | true 136 | end 137 | 138 | def visit_Constant(node) 139 | const_allowed?(node.token) 140 | end 141 | 142 | def visit_ConstantAssignment(node) 143 | visit(node.lvalue) && visit(node.rvalue) 144 | end 145 | 146 | def visit_Defined(node) 147 | false # never allowed (though it's probably safe) 148 | end 149 | 150 | def visit_Else(node) 151 | node.elements.all? { |e| visit(e) } 152 | end 153 | 154 | def visit_ExecutableString(node) 155 | false # never allowed 156 | end 157 | 158 | def visit_Float(node) 159 | true 160 | end 161 | 162 | def visit_For(node) 163 | visit(node.variable) && visit(node.range) && visit(node.statements) 164 | end 165 | 166 | def visit_GlobalVariable(node) 167 | false # never allowed 168 | end 169 | 170 | def visit_GlobalVariableAssignment(node) 171 | false # never allowed 172 | end 173 | 174 | def visit_Hash(node) 175 | node.assocs.nil? || node.assocs.all? { |e| visit(e) } 176 | end 177 | 178 | def visit_Identifier(node) 179 | !CALL_BLACKLIST.include?(node.token) 180 | end 181 | 182 | def visit_If(node) 183 | visit(node.expression) && node.elements.all? { |e| visit(e) } && node.blocks.all? { |e| visit(e) } 184 | end 185 | alias_method :visit_Unless, :visit_If 186 | 187 | def visit_IfMod(node) 188 | visit(node.expression) && node.elements.all? { |e| visit(e) } 189 | end 190 | alias_method :visit_UnlessMod, :visit_IfMod 191 | 192 | def visit_IfOp(node) 193 | visit(node.condition) && visit(node.then_part) && visit(node.else_part) 194 | end 195 | 196 | def visit_InstanceVariable(node) 197 | true 198 | end 199 | 200 | def visit_InstanceVariableAssignment(node) 201 | visit(node.rvalue) 202 | end 203 | 204 | def visit_Integer(node) 205 | true 206 | end 207 | 208 | KEYWORD_WHITELIST = %w[ 209 | false 210 | nil 211 | self 212 | true 213 | ].to_set.freeze 214 | 215 | def visit_Keyword(node) 216 | KEYWORD_WHITELIST.include?(node.token) 217 | end 218 | 219 | def visit_Label(node) 220 | true 221 | end 222 | 223 | def visit_LocalVariableAssignment(node) 224 | visit(node.rvalue) 225 | end 226 | 227 | def visit_Method(node) 228 | [node.target, node.params, node.body].compact.all? { |e| visit(e) } 229 | end 230 | 231 | def visit_Module(node) 232 | visit(node.const) && visit(node.body) 233 | end 234 | 235 | def visit_MultiAssignment(node) 236 | visit(node.lvalue) && visit(node.rvalue) 237 | end 238 | 239 | def visit_MultiAssignmentList(node) 240 | node.elements.all? { |e| visit(e) } 241 | end 242 | 243 | def visit_Params(node) 244 | node.elements.all? { |e| visit(e) } 245 | end 246 | 247 | def visit_Program(node) 248 | node.elements.all? { |e| visit(e) } 249 | end 250 | 251 | def visit_Range(node) 252 | visit(node.min) && visit(node.max) 253 | end 254 | 255 | def visit_RescueMod(node) 256 | node.elements.all? { |e| visit(e) } && visit(node.expression) 257 | end 258 | 259 | def visit_RescueParams(node) 260 | node.elements.all? { |e| visit(e) } 261 | end 262 | 263 | def visit_SingletonClass(node) 264 | visit(node.superclass) && visit(node.body) 265 | end 266 | 267 | def visit_SplatArg(node) 268 | visit(node.arg) 269 | end 270 | 271 | def visit_Statements(node) 272 | node.elements.all? { |e| visit(e) } 273 | end 274 | 275 | def visit_String(node) 276 | # embedded strings can have statements in them, so check those 277 | node.elements.reject { |e| e.is_a?(::String) }.all? { |e| visit(e) } 278 | end 279 | 280 | def visit_StringConcat(node) 281 | node.elements.all? { |e| visit(e) } 282 | end 283 | 284 | def visit_Symbol(node) 285 | true 286 | end 287 | 288 | def visit_Unary(node) 289 | visit(node.operand) 290 | end 291 | 292 | def visit_Until(node) 293 | false # never allowed 294 | end 295 | alias_method :visit_UntilMod, :visit_Until 296 | 297 | def visit_When(node) 298 | visit(node.expression) && node.elements.all? { |e| visit(e) } 299 | end 300 | 301 | def visit_While(node) 302 | false # never allowed 303 | end 304 | alias_method :visit_WhileMod, :visit_While 305 | 306 | private 307 | 308 | CONST_BLACKLIST = %w[ 309 | ARGF 310 | ARGV 311 | Array 312 | Base64 313 | Class 314 | Dir 315 | ENV 316 | Enumerable 317 | Error 318 | Exception 319 | Fiber 320 | File 321 | FileUtils 322 | GC 323 | Gem 324 | Hash 325 | IO 326 | IRB 327 | Kernel 328 | Module 329 | Net 330 | Object 331 | ObjectSpace 332 | OpenSSL 333 | OpenURI 334 | PLATFORM 335 | Proc 336 | Process 337 | RUBY_COPYRIGHT 338 | RUBY_DESCRIPTION 339 | RUBY_ENGINE 340 | RUBY_PATCHLEVEL 341 | RUBY_PLATFORM 342 | RUBY_RELEASE_DATE 343 | RUBY_VERSION 344 | Rails 345 | STDERR 346 | STDIN 347 | STDOUT 348 | String 349 | TOPLEVEL_BINDING 350 | Thread 351 | VERSION 352 | ].freeze 353 | 354 | def initialize_const_blacklist 355 | CONST_BLACKLIST.each { |const| blacklist_const(const) } 356 | end 357 | end 358 | end 359 | -------------------------------------------------------------------------------- /lib/ruby_cop/ruby.rb: -------------------------------------------------------------------------------- 1 | require 'ruby_cop/ruby/node' 2 | require 'ruby_cop/ruby/list' 3 | require 'ruby_cop/ruby/array' 4 | require 'ruby_cop/ruby/args' 5 | require 'ruby_cop/ruby/assignment' 6 | require 'ruby_cop/ruby/assoc' 7 | require 'ruby_cop/ruby/statements' 8 | require 'ruby_cop/ruby/blocks' 9 | require 'ruby_cop/ruby/call' 10 | require 'ruby_cop/ruby/case' 11 | require 'ruby_cop/ruby/tokens' 12 | require 'ruby_cop/ruby/constants' 13 | require 'ruby_cop/ruby/definitions' 14 | require 'ruby_cop/ruby/for' 15 | require 'ruby_cop/ruby/hash' 16 | require 'ruby_cop/ruby/if' 17 | require 'ruby_cop/ruby/operators' 18 | require 'ruby_cop/ruby/params' 19 | require 'ruby_cop/ruby/position' 20 | require 'ruby_cop/ruby/range' 21 | require 'ruby_cop/ruby/string' 22 | require 'ruby_cop/ruby/variables' 23 | require 'ruby_cop/ruby/while' 24 | -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/args.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Args < List 4 | attr_reader :block 5 | 6 | def add_block(block) 7 | @block = block 8 | end 9 | 10 | def to_array 11 | Array.new(@elements) 12 | end 13 | end 14 | 15 | class Arg < Node 16 | def initialize(arg) 17 | @arg = arg 18 | end 19 | 20 | attr_reader :arg 21 | end 22 | 23 | class SplatArg < Arg 24 | end 25 | end 26 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/array.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Array < List 4 | # def inspect 5 | # '[%s]' % @elements.collect { |e| e.inspect }.join(', ') 6 | # end 7 | 8 | def to_array 9 | self 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/assignment.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Assignment < Node 4 | def initialize(lvalue, rvalue, operator) 5 | @lvalue = lvalue 6 | @rvalue = rvalue 7 | @operator = operator 8 | end 9 | 10 | attr_reader :lvalue 11 | attr_reader :rvalue 12 | attr_reader :operator 13 | 14 | # def inspect 15 | # "#{@lvalue.inspect} #{@operator} #{@rvalue.inspect}" 16 | # end 17 | end 18 | 19 | class ClassVariableAssignment < Assignment 20 | end 21 | 22 | class ConstantAssignment < Assignment 23 | end 24 | 25 | class GlobalVariableAssignment < Assignment 26 | end 27 | 28 | class InstanceVariableAssignment < Assignment 29 | end 30 | 31 | class LocalVariableAssignment < Assignment 32 | end 33 | 34 | class MultiAssignment < Assignment 35 | end 36 | 37 | class MultiAssignmentList < List 38 | def assignment(rvalue, operator) 39 | MultiAssignment.new(self, rvalue, operator) 40 | end 41 | end 42 | end 43 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/assoc.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Assoc < Node 4 | def initialize(key, value) 5 | @key = key 6 | @value = value 7 | end 8 | 9 | attr_reader :key 10 | attr_reader :value 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/blocks.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Block < Statements 4 | def initialize(statements, params=nil) 5 | @params = params 6 | super(statements) 7 | end 8 | 9 | attr_reader :params 10 | end 11 | 12 | class ChainedBlock < Block 13 | def initialize(blocks, statements, params=nil) 14 | @blocks = Array(blocks).compact 15 | super(statements, params) 16 | end 17 | 18 | attr_reader :blocks 19 | end 20 | end 21 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/call.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Call < Node 4 | def initialize(target, identifier, arguments=nil, block=nil) 5 | @target = target 6 | @identifier = identifier 7 | @arguments = arguments 8 | @block = block 9 | end 10 | 11 | attr_reader :target 12 | attr_reader :identifier 13 | attr_accessor :arguments 14 | attr_accessor :block 15 | 16 | def assignment(rvalue, operator) 17 | self.class.new(@target, Identifier.new("#{@identifier.token}=", @identifier.position), @arguments, @block) 18 | end 19 | end 20 | 21 | class Alias < Node 22 | def initialize(new_name, old_name) 23 | @new_name = new_name 24 | @old_name = old_name 25 | end 26 | 27 | attr_reader :new_name 28 | attr_reader :old_name 29 | end 30 | end 31 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/case.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Case < Node 4 | def initialize(expression, block) 5 | @expression = expression 6 | @block = block 7 | end 8 | 9 | attr_reader :expression 10 | attr_reader :block 11 | end 12 | 13 | class When < ChainedBlock 14 | def initialize(expression, statements, block=nil) 15 | @expression = expression 16 | super([block], statements) 17 | end 18 | 19 | attr_reader :expression 20 | end 21 | end 22 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/constants.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Constant < Identifier 4 | attr_accessor :namespace 5 | 6 | def assignment(rvalue, operator) 7 | ConstantAssignment.new(self, rvalue, operator) 8 | end 9 | 10 | # def inspect 11 | # @namespace ? "#{@namespace.inspect}::#{@token}" : @token 12 | # end 13 | end 14 | 15 | class Module < Node 16 | def initialize(const, body) 17 | @const = const 18 | @body = body 19 | end 20 | 21 | attr_reader :const 22 | attr_reader :body 23 | end 24 | 25 | class Class < Node 26 | def initialize(const, superclass, body) 27 | @const = const 28 | @superclass = superclass 29 | @body = body 30 | end 31 | 32 | attr_reader :const 33 | attr_reader :superclass 34 | attr_reader :body 35 | end 36 | 37 | class SingletonClass < Node 38 | def initialize(superclass, body) 39 | @superclass = superclass 40 | @body = body 41 | end 42 | 43 | attr_reader :superclass 44 | attr_reader :body 45 | end 46 | end 47 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/definitions.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Method < Node 4 | def initialize(target, identifier, params, body) 5 | @target = target 6 | @identifier = identifier 7 | @params = params 8 | @body = body 9 | end 10 | 11 | attr_reader :target 12 | attr_reader :identifier 13 | attr_reader :params 14 | attr_reader :body 15 | end 16 | 17 | class Defined < Node 18 | def initialize(expression) 19 | @expression = expression 20 | end 21 | 22 | attr_reader :expression 23 | end 24 | end 25 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/for.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class For < Block 4 | def initialize(variable, range, statements) 5 | @variable = variable 6 | @range = range 7 | @statements = statements 8 | end 9 | 10 | attr_reader :variable 11 | attr_reader :range 12 | attr_reader :statements 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/hash.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Hash < Node 4 | def initialize(assocs) 5 | @assocs = assocs 6 | end 7 | 8 | attr_reader :assocs 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/if.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class If < ChainedBlock 4 | def initialize(expression, statements=nil, else_block=nil) 5 | @expression = expression 6 | super([else_block], statements, nil) 7 | end 8 | attr_reader :expression 9 | end 10 | 11 | class Unless < If 12 | end 13 | 14 | class Else < Block 15 | end 16 | 17 | class IfMod < Block 18 | def initialize(expression, statements) 19 | @expression = expression 20 | super(statements) 21 | end 22 | attr_reader :expression 23 | end 24 | 25 | class UnlessMod < IfMod 26 | end 27 | 28 | class RescueMod < IfMod 29 | end 30 | end 31 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/list.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class List < Node 4 | def initialize(elements=nil) 5 | @elements = Array(elements).compact # TODO: compact might cause problems here 6 | end 7 | 8 | attr_reader :elements 9 | 10 | def add(element) 11 | @elements.push(element) 12 | end 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/node.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Node 4 | def accept(visitor) 5 | visitor.visit(self) 6 | end 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/operators.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Operator < Node 4 | end 5 | 6 | class Unary < Operator 7 | def initialize(operator, operand) 8 | @operator = operator 9 | @operand = operand 10 | end 11 | 12 | attr_reader :operator 13 | attr_reader :operand 14 | 15 | # def inspect 16 | # "#{@operator}(#{@operand.inspect})" 17 | # end 18 | end 19 | 20 | class Binary < Operator 21 | def initialize(lvalue, rvalue, operator) 22 | @lvalue = lvalue 23 | @rvalue = rvalue 24 | @operator = operator 25 | end 26 | 27 | attr_reader :lvalue 28 | attr_reader :rvalue 29 | attr_reader :operator 30 | 31 | # def inspect 32 | # "#{@lvalue.inspect} #{@operator} #{@rvalue.inspect}" 33 | # end 34 | end 35 | 36 | class IfOp < Operator 37 | def initialize(condition, then_part, else_part) 38 | @condition = condition 39 | @then_part = then_part 40 | @else_part = else_part 41 | end 42 | 43 | attr_reader :condition 44 | attr_reader :then_part 45 | attr_reader :else_part 46 | 47 | # def inspect 48 | # "#{@condition.inspect} ? #{@then_part.inspect} : #{@else_part.inspect}" 49 | # end 50 | end 51 | end 52 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/params.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Params < List 4 | def initialize(params, optionals, rest, block) 5 | super((Array(params) + Array(optionals) << rest << block).flatten.compact) 6 | end 7 | end 8 | 9 | class RescueParams < List 10 | def initialize(types, var) 11 | if types 12 | errors = Ruby::Array.new(types) 13 | errors = Ruby::Assoc.new(errors, var) if var 14 | super(errors) 15 | else 16 | super() 17 | end 18 | end 19 | end 20 | end 21 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/position.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Position 4 | def initialize(lineno, column) 5 | @lineno = lineno 6 | @column = column 7 | end 8 | 9 | attr_reader :lineno 10 | attr_reader :column 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/range.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Range < Node 4 | def initialize(min, max, exclude_end) 5 | @min = min 6 | @max = max 7 | @exclude_end = exclude_end 8 | end 9 | 10 | attr_reader :min 11 | attr_reader :max 12 | attr_reader :exclude_end 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/statements.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Statements < List 4 | # def inspect 5 | # @elements.collect { |e| e.inspect }.join 6 | # end 7 | 8 | def to_block(params) 9 | Block.new(@elements, params) 10 | end 11 | 12 | def to_chained_block(blocks=nil, params=nil) 13 | ChainedBlock.new(blocks, @elements, params) 14 | end 15 | 16 | def to_program(src, filename) 17 | Program.new(src, filename, @elements) 18 | end 19 | end 20 | 21 | class Program < Statements 22 | def initialize(src, filename, statements) 23 | @src = src 24 | @filename = filename 25 | super(statements) 26 | end 27 | 28 | attr_reader :src 29 | attr_reader :filename 30 | end 31 | end 32 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/string.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class StringConcat < List 4 | end 5 | 6 | class String < List 7 | # def inspect 8 | # @elements.join.inspect 9 | # end 10 | end 11 | 12 | class DynaSymbol < String 13 | end 14 | 15 | class ExecutableString < String 16 | def to_dyna_symbol 17 | DynaSymbol.new(@elements) 18 | end 19 | end 20 | 21 | class Regexp < String 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/tokens.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Token < Node 4 | def initialize(token, position) 5 | @token = token 6 | @position = position 7 | end 8 | 9 | attr_reader :token 10 | attr_reader :position 11 | 12 | # def inspect 13 | # "#{@token}" 14 | # end 15 | end 16 | 17 | class Integer < Token 18 | end 19 | 20 | class Float < Token 21 | end 22 | 23 | class Char < Token 24 | end 25 | 26 | class Label < Token 27 | end 28 | 29 | class Symbol < Token 30 | # def inspect 31 | # ":#{@token.inspect}" 32 | # end 33 | end 34 | 35 | class Keyword < Token 36 | end 37 | 38 | class Identifier < Token 39 | def assignment(rvalue, operator) 40 | LocalVariableAssignment.new(self, rvalue, operator) 41 | end 42 | end 43 | end 44 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/variables.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class Variable < Identifier 4 | end 5 | 6 | class ClassVariable < Variable 7 | def assignment(rvalue, operator) 8 | ClassVariableAssignment.new(self, rvalue, operator) 9 | end 10 | end 11 | 12 | class GlobalVariable < Variable 13 | def assignment(rvalue, operator) 14 | GlobalVariableAssignment.new(self, rvalue, operator) 15 | end 16 | end 17 | 18 | class InstanceVariable < Variable 19 | def assignment(rvalue, operator) 20 | InstanceVariableAssignment.new(self, rvalue, operator) 21 | end 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /lib/ruby_cop/ruby/while.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | module Ruby 3 | class While < Block 4 | def initialize(expression, statements) 5 | @expression = expression 6 | super(statements) 7 | end 8 | 9 | attr_reader :expression 10 | end 11 | 12 | class WhileMod < Block 13 | def initialize(expression, statements) 14 | @expression = expression 15 | super(statements) 16 | end 17 | 18 | attr_reader :expression 19 | end 20 | 21 | class Until < While 22 | end 23 | 24 | class UntilMod < WhileMod 25 | end 26 | end 27 | end -------------------------------------------------------------------------------- /lib/ruby_cop/version.rb: -------------------------------------------------------------------------------- 1 | module RubyCop 2 | VERSION = '1.0.5' 3 | end 4 | -------------------------------------------------------------------------------- /ruby_cop.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "ruby_cop/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "ruby_cop" 7 | s.version = RubyCop::VERSION 8 | s.platform = Gem::Platform::RUBY 9 | s.authors = ["Dray Lacy", "Eric Allam"] 10 | s.email = ["dray@envylabs.com", "eric@envylabs.com"] 11 | s.homepage = "https://github.com/envylabs/RubyCop" 12 | s.summary = %q{Statically analyze Ruby and neutralize nefarious code} 13 | s.description = %q{Statically analyze Ruby and neutralize nefarious code} 14 | 15 | s.rubyforge_project = "ruby_cop" 16 | 17 | s.files = `git ls-files`.split("\n") 18 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 19 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 20 | s.require_paths = ["lib"] 21 | 22 | s.add_development_dependency 'geminabox' 23 | s.add_development_dependency 'rake' 24 | s.add_development_dependency 'rspec', '~> 2.0' 25 | s.add_development_dependency 'yard' 26 | end 27 | -------------------------------------------------------------------------------- /spec/analyzer/node_builder_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RubyCop::NodeBuilder do 4 | subject { described_class } 5 | 6 | RSpec::Matchers.define(:parse) do |ruby| 7 | match { |nb| nb.build(ruby).is_a?(RubyCop::Ruby::Node) } 8 | end 9 | 10 | context "arrays" do 11 | it { should parse('[:foo, :bar]') } 12 | it { should parse('%w(foo bar)') } 13 | it { should parse('%(foo)') } 14 | it { should parse("%(\nfoo bar\n)") } 15 | it { should parse("%( \nfoo bar\n )") } 16 | it { should parse('%W[foo bar]') } 17 | it { should parse('%w()') } 18 | it { should parse('%W()') } 19 | it { should parse('%w[]') } 20 | it { should parse('%W[]') } 21 | it { should parse('%W[#{foo}]') } 22 | it { should parse('foo[1]') } 23 | it { should parse('foo[]') } 24 | it { should parse('foo.bar[]') } 25 | it { should parse('[ foo[bar] ]') } 26 | it { should parse('result[0] = :value') } 27 | it { should parse("\n [type, [row]]\n") } 28 | end 29 | 30 | context "assignment" do 31 | it { should parse('a = b') } 32 | it { should parse('a ||= b') } 33 | it { should parse('a, b = c') } 34 | it { should parse('a, b = c, d') } 35 | it { should parse('a, *b = c') } 36 | it { should parse('A::B = 1') } 37 | end 38 | 39 | context "blocks" do 40 | it { should parse("t do\nfoo\nbar\nend") } 41 | it { should parse('t do ; end') } 42 | it { should parse("t do ||\nfoo\nend") } 43 | it { should parse('t do ;; ;foo ; ;;bar; ; end') } 44 | it { should parse("t do |(a, b), *c|\n foo\n bar\nend") } 45 | it { should parse('t { |(a, b), *c| }') } 46 | it { should parse('t do |(a, b), *c| end') } 47 | it { should parse('t do |*a, &c| end') } 48 | it { should parse('t { |a, (b)| }') } 49 | it { should parse("begin\nend") } 50 | it { should parse('begin ; end') } 51 | it { should parse('begin foo; end') } 52 | it { should parse("begin\nfoo\nelse\nbar\nend") } 53 | it { should parse("begin\nfoo\nrescue\nbar\nend") } 54 | it { should parse("begin \n rescue A \n end") } 55 | it { should parse("begin foo\n rescue A => e\n bar\n end") } 56 | it { should parse("begin foo\n rescue A, B => e\n bar\n end") } 57 | it { should parse("begin foo\n rescue A, B => e\n bar\nrescue C => e\n bam\nensure\nbaz\n end") } 58 | it { should parse('begin a; rescue NameError => e then e else :foo end') } 59 | it { should parse("begin\nrescue A => e\nrescue B\nend") } 60 | it { should parse("begin\nrescue A\nelse\nend\n") } 61 | it { should parse('foo rescue bar') } 62 | it { should parse('foo rescue 0') } 63 | end 64 | 65 | context "calls" do 66 | it { should parse('t') } 67 | it { should parse('I18n.t') } 68 | it { should parse('a.b(:foo)') } 69 | it { should parse('I18n.t()') } 70 | it { should parse("I18n.t('foo')") } 71 | it { should parse("I18n.t 'foo'") } 72 | it { should parse('I18n::t') } 73 | it { should parse('I18n::t()') } 74 | it { should parse("I18n::t('foo')") } 75 | it { should parse('foo.<=>(bar)') } 76 | it { should parse('foo.<< bar') } 77 | it { should parse('foo("#{bar}")') } 78 | it { should parse("t('foo', 'bar')") } 79 | it { should parse("t('foo', 'bar'); t 'baz'") } 80 | it { should parse("t 'foo'") } 81 | it { should parse('t %(foo #{bar}), %(baz)') } 82 | it { should parse('t()') } 83 | it { should parse('t(:a, b(:b))') } 84 | it { should parse('t(:`)') } 85 | it { should parse("t do |a, b, *c|\nfoo\nend") } 86 | it { should parse("t do |(a, b), *c|\nfoo\nend") } 87 | it { should parse('t(:foo, &block)') } 88 | it { should parse('foo(bar do |a| ; end)') } 89 | it { should parse('self.b') } 90 | it { should parse('super') } 91 | it { should parse('super(:foo)') } 92 | it { should parse('yield') } 93 | it { should parse('yield(:foo)') } 94 | it { should parse('return :foo') } 95 | it { should parse('next') } 96 | it { should parse('redo') } 97 | it { should parse('break') } 98 | it { should parse('retry') } 99 | it { should parse('a.b = :c') } 100 | it { should parse('defined?(A)') } 101 | it { should parse('BEGIN { foo }') } 102 | it { should parse('END { foo }') } 103 | it { should parse('alias eql? ==') } 104 | it { should parse('alias :foo :bar') } 105 | it { should parse('alias $ERROR_INFO $!') } 106 | it { should parse('undef :foo') } 107 | it { should parse('undef foo, bar, baz') } 108 | it { should parse('undef =~') } 109 | end 110 | 111 | context "case" do 112 | it { should parse('case a; when 1; 2 end') } 113 | it { should parse('case (a) when 1; 2 end') } 114 | it { should parse('case (a;) when 1; 2 end') } 115 | it { should parse('case (a); when 1; 2 end') } 116 | it { should parse('case (a;;); when 1; 2 end') } 117 | it { should parse('case (a;b); when 1; 2 end') } 118 | it { should parse('case a; when 1; 2 end') } 119 | it { should parse('case a; when 1, 2; 2 end') } 120 | it { should parse('case a; when (1; 2), (3; 4;); 2 end') } 121 | it { should parse('case (true); when 1; false; when 2, 3, 4; nil; else; nil; end') } 122 | it { should parse("case true\n when 1\n false\n when 2\n nil\n else\n nil\n end") } 123 | it { should parse("case true\n when 1 then false\n when 2 then nil\n else\n nil\n end") } 124 | end 125 | 126 | context "constants" do 127 | it { should parse('A') } 128 | it { should parse('module A::B ; end') } 129 | it { should parse('class A::B < C ; end') } 130 | it { should parse('self.class::A') } 131 | it { should parse('class << self; self; end') } 132 | it { should parse('foo (class << @bar; self; end)') } 133 | it { should parse("class A ; end\n::B = 3") } 134 | end 135 | 136 | context "for" do 137 | it { should parse('for i in [1]; a; end') } 138 | it { should parse("for i in [1]\n a\n end") } 139 | it { should parse("for i in [1] do a\n end") } 140 | it { should parse("lambda do\n for a in b\n end\nend") } 141 | it { should parse("\nfor i in 0...1 do\n for j in 0...2 do\n end\nend\n") } 142 | end 143 | 144 | context "hash" do 145 | it { should parse('{ :foo => :bar }') } 146 | it { should parse('{ :a => { :b => { :b => :c, }, }, }') } 147 | it { should parse('t(:a => { :b => :b, :c => { :d => :d } })') } 148 | it { should parse('{ :if => :foo }') } 149 | it { should parse('{ :a => :a, :b => "#{b 1}" }') } 150 | it { should parse('{ foo: bar }') } 151 | it { should parse('t(:a => :a, :b => :b)') } 152 | it { should parse('foo[:bar] = :baz') } 153 | end 154 | 155 | context "heredoc" do 156 | it { should parse("\n<<-eos\neos\n") } 157 | it { should parse("<<-eos\nfoo\neos") } 158 | it { should parse("'string'\n<<-eos\nheredoc\neos\n'string'") } 159 | it { should parse(":'symbol'\n<<-eos\nheredoc\neos\n:'symbol'") } 160 | it { should parse("%w(words)\n<<-eoc\n heredoc\neoc\n%w(words)") } 161 | it { should parse("foo(%w(words))\n<<-eoc\n\neoc") } 162 | it { should parse("\n<<-end;\nfoo\nend\n") } 163 | it { should parse("\n<<-'end' ;\n foo\nend\nfoo;") } 164 | it { should parse("<<-eos\n foo \#{bar} baz\neos") } 165 | it { should parse("<<-end\n\#{a['b']}\nend") } 166 | it { should parse("<<-end\n\#{'a'}\nend") } 167 | it { should parse("foo(<<-eos)\n foo\neos") } 168 | it { should parse("foo(<<-eos\n foo\neos\n)") } 169 | it { should parse("foo(<<-eos, __FILE__, __LINE__ + 1)\n foo\neos") } 170 | it { should parse("foo(<<-eos, __FILE__, line)\n foo\neos") } 171 | it { should parse("foo(<<-eos, \"\#{bar}\")\n foo\neos") } 172 | it { should parse("begin <<-src \n\n\nfoo\nsrc\n end") } 173 | it { should parse("each do\n <<-src \n\nfoo\n\#{bar}\nsrc\n end\n") } 174 | it { should parse("<<-eos\n \#{\"\n \#{sym}\n \"}\neos") } 175 | it { should parse("<<-eos\n \t\#{ a\n }\neos") } 176 | it { should parse("<<-eos\n\#{:'dyna_symbol'}\neos") } 177 | it { should parse("<<-eos # comment\neos\nfoo\nfoo\n") } 178 | it { should parse("<<-eos # comment\nstring\neos\n'index'") } 179 | it { should parse("foo <<-eos if bar\na\neos\nbaz") } 180 | it { should parse("<<-eos.foo\neos\nbar\n") } 181 | it { should parse("<<-end\nend\n<<-end\n\nend\n") } 182 | it { should parse("heredocs = <<-\"foo\", <<-\"bar\"\n I said foo.\nfoo\n I said bar.\nbar\n") } 183 | it { should parse("a = %w[]\n<<-eos\nfoo\neos") } 184 | it { should parse("<<-eos\nfoo\n__END__\neos") } 185 | it { should parse("foo = <<-`foo`\nfoo") } 186 | end 187 | 188 | context "identifier" do 189 | it { should parse('foo') } 190 | it { should parse('@foo') } 191 | it { should parse('@@foo') } 192 | it { should parse('$foo') } 193 | it { should parse('__FILE__') } 194 | it { should parse('__LINE__') } 195 | it { should parse('__ENCODING__') } 196 | end 197 | 198 | context "if" do 199 | it { should parse('if true; false end') } 200 | it { should parse("if true\n false\n end") } 201 | it { should parse("if true\n false\n end") } 202 | it { should parse('if true then false end') } 203 | it { should parse('if true; false; else; true end') } 204 | it { should parse("if true\n false\n else\n true end") } 205 | it { should parse("if true\n false\n elsif false\n true end") } 206 | it { should parse("if true then false; elsif false then true; else nil end") } 207 | it { should parse("if a == 1 then b\nelsif b == c then d\ne\nf\nelse e end") } 208 | it { should parse('foo if true') } 209 | it { should parse('return if true') } 210 | it { should parse('foo.bar += bar if bar') } 211 | it { should parse('foo, bar = *baz if bum') } 212 | it { should parse('foo *args if bar?') } 213 | it { should parse('pos[1] if pos') } 214 | it { should parse('a if (defined? a)') } 215 | it { should parse('rescued rescue rescuing') } 216 | it { should parse('rescued = assigned rescue rescuing') } 217 | end 218 | 219 | context "literals" do 220 | it { should parse('1') } 221 | it { should parse('1.1') } 222 | it { should parse('nil') } 223 | it { should parse('true') } 224 | it { should parse('false') } 225 | it { should parse('1..2') } 226 | it { should parse('1...2') } 227 | it { should parse('?a') } 228 | end 229 | 230 | context "methods" do 231 | it { should parse("def foo(a, b = nil, c = :foo, *d, &block)\n bar\n baz\nend") } 232 | it { should parse("def foo(a, b = {})\nend") } 233 | it { should parse("def foo a, b = nil, c = :foo, *d, &block\n bar\n baz\nend") } 234 | it { should parse("def self.for(options)\nend") } 235 | it { should parse('def foo(a = 1, b=2); end') } 236 | it { should parse('def t(a = []) end') } 237 | it { should parse('def t(*) end') } 238 | it { should parse('def <<(arg) end') } 239 | it { should parse('def | ; end') } 240 | it { should parse('class A < B; def |(foo); end; end') } 241 | it { should parse('def <<(arg) foo; bar; end') } 242 | it { should parse("def t\nrescue => e\nend") } 243 | it { should parse("def a(b, c)\n d\nrescue A\n e\nensure\nb\nend") } 244 | it { should parse('def foo ; bar { |k, v| k } end') } 245 | it { should parse("class A\n def class\n end\nend") } 246 | it { should parse("def end\nend") } 247 | it { should parse('def def; 234; end') } 248 | end 249 | 250 | context "operators" do 251 | context "unary" do 252 | it { should parse('+1') } 253 | it { should parse('-1') } 254 | it { should parse('!1') } 255 | it { should parse('not 1') } 256 | it { should parse('not(1)') } 257 | it { should parse('~1') } 258 | it { should parse('(~1)') } 259 | it { should parse('not (@main or @sub)') } 260 | end 261 | 262 | context "binary" do 263 | context "mathematical" do 264 | it { should parse('1 + 2') } 265 | it { should parse('1 - 2') } 266 | it { should parse('1 * 2') } 267 | it { should parse('1 / 2') } 268 | it { should parse('1 ** 2') } 269 | it { should parse('1 % 2') } 270 | it { should parse('(1 + 2)') } 271 | end 272 | context "logical" do 273 | it { should parse('1 && 2') } 274 | it { should parse('1 || 2') } 275 | it { should parse('1 and 2') } 276 | it { should parse('1 or 2') } 277 | it { should parse('(1 and 2)') } 278 | end 279 | context "bitwise" do 280 | it { should parse('1 << 2') } 281 | it { should parse('1 >> 2') } 282 | it { should parse('1 & 2') } 283 | it { should parse('1 | 2') } 284 | it { should parse('1 ^ 2') } 285 | end 286 | context "comparison, equality, matching" do 287 | it { should parse('1 < 2') } 288 | it { should parse('1 <= 2') } 289 | it { should parse('1 > 2') } 290 | it { should parse('1 >= 2') } 291 | it { should parse('1 <=> 2') } 292 | it { should parse('1 == 2') } 293 | it { should parse('1 != 2') } 294 | it { should parse('1 === 2') } 295 | it { should parse('1 =~ 2') } 296 | it { should parse('1 !~ 2') } 297 | end 298 | end 299 | 300 | context "ternary" do 301 | it { should parse('1 == 1 ? 2 : 3') } 302 | it { should parse('((1) ? 2 : (3))') } 303 | end 304 | end 305 | 306 | context "statements" do 307 | it { should parse('foo') } 308 | it { should parse(';foo') } 309 | it { should parse('foo;') } 310 | it { should parse(';foo;') } 311 | it { should parse(';foo;;bar;baz;') } 312 | it { should parse(';foo;;bar;;;;baz;') } 313 | it { should parse(';;;foo;;bar;baz;;;;') } 314 | it { should parse('(foo)') } 315 | it { should parse('(((foo)); (bar))') } 316 | it { should parse('(((foo)); ((bar); (baz)));') } 317 | it { should parse("\n foo \n \n") } 318 | it { should parse("foo\n__END__\nbar") } 319 | end 320 | 321 | context "string" do 322 | it { should parse('""') } 323 | it { should parse('"foo"') } 324 | it { should parse("'foo'") } 325 | it { should parse('%(foo)') } 326 | it { should parse('%.foo.') } 327 | it { should parse('%|foo|') } 328 | it { should parse('"foo#{bar}"') } 329 | it { should parse('%(foo #{bar})') } 330 | it { should parse("%w(a)\n%(b)") } 331 | it { should parse('/foo/') } 332 | it { should parse('%r(foo)') } 333 | it { should parse('"#{$1}"') } 334 | it { should parse('"#$0"') } 335 | it { should parse("'a' 'b'") } 336 | it { should parse('`foo`') } 337 | it { should parse('%x(foo)') } 338 | end 339 | 340 | context "symbol" do 341 | it { should parse(':foo') } 342 | it { should parse(':!') } 343 | it { should parse(':-@') } 344 | it { should parse(':if') } 345 | it { should parse(':[]') } 346 | it { should parse(':[]=') } 347 | it { should parse(':"foo.bar"') } 348 | it { should parse(":'foo.bar'") } 349 | it { should parse(':"@#{token}"') } 350 | end 351 | 352 | context "unless" do 353 | it { should parse('unless true; false end') } 354 | it { should parse("unless true\n false end") } 355 | it { should parse('unless true then false end') } 356 | it { should parse('unless true; false; else; true end') } 357 | it { should parse("unless true\n false\n else\n true end") } 358 | it { should parse('foo unless true') } 359 | it { should parse('1 unless false if true') } 360 | end 361 | 362 | context "until" do 363 | it { should parse('until true; false end') } 364 | it { should parse('until (true); false end') } 365 | it { should parse('until (true;); false end') } 366 | it { should parse("until true\n false end") } 367 | it { should parse("until (true)\n false end") } 368 | it { should parse('until foo do ; end') } 369 | it { should parse('begin; false; end until true') } 370 | it { should parse("begin\n false\n end until true") } 371 | it { should parse('foo until true') } 372 | it { should parse('foo until (true)') } 373 | end 374 | end -------------------------------------------------------------------------------- /spec/analyzer/policy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RubyCop::Policy do 4 | let(:policy) { described_class.new } 5 | subject { policy } 6 | 7 | RSpec::Matchers.define(:allow) do |ruby| 8 | match { |policy| RubyCop::NodeBuilder.build(ruby).accept(policy) } 9 | end 10 | 11 | context "assignment" do 12 | context "class variables" do 13 | it { should_not allow('@@x = 1') } 14 | it { should_not allow('@@x ||= 1') } 15 | it { should_not allow('@@x += 1') } 16 | end 17 | 18 | context "constants" do 19 | it { should allow('Foo = 1') } 20 | it { should allow('Foo::Bar = 1') } 21 | it { should allow('::Bar = 1') } 22 | 23 | it { should_not allow('Foo = Kernel') } 24 | it { should_not allow('Foo = ::Kernel') } 25 | it { should_not allow('Foo = Object::Kernel') } 26 | end 27 | 28 | context "globals" do 29 | it { should_not allow('$x = 1') } 30 | it { should_not allow('$x ||= 1') } 31 | it { should_not allow('$x += 1') } 32 | end 33 | 34 | context "instance variables" do 35 | it { should allow('@x = 1') } 36 | it { should allow('@x += 1') } 37 | it { should_not allow('@x = $x') } 38 | it { should_not allow('@x = @@x') } 39 | end 40 | 41 | context "locals" do 42 | it { should allow('x = 1') } 43 | it { should allow('x ||= 1') } 44 | it { should allow('x += 1') } 45 | it { should_not allow('x = $x') } 46 | it { should_not allow('x = @@x') } 47 | end 48 | end 49 | 50 | context "begin/rescue/ensure" do 51 | it { should allow('begin; x; rescue; end') } 52 | it { should allow('x rescue 1') } 53 | 54 | it { should_not allow('begin; `ls`; rescue; x; end') } 55 | it { should_not allow('begin; x; rescue; `ls`; end') } 56 | it { should_not allow('begin; x; rescue; 1; ensure `ls`; end') } 57 | it { should_not allow('`ls` rescue 1') } 58 | it { should_not allow('x rescue `ls`') } 59 | it { should_not allow('begin; x; rescue (`ls`; RuntimeError) => err; end') } 60 | it { should_not allow(%{begin ; begin ; ":D" ; rescue ; retry ; ensure ; raise ":D" ; end ; rescue ; retry ; end})} 61 | it { should_not allow(%{begin ; while(true) ; 'x' ; end ; rescue Exception ; retry ; end}) } 62 | end 63 | 64 | context "blocks" do 65 | it { should_not allow('->(a = $x) { }') } 66 | it { should_not allow('->(a) { $x }') } 67 | it { should_not allow('lambda { $x }') } 68 | it { should_not allow('proc { $x }') } 69 | end 70 | 71 | context "calls" do 72 | it { should allow('foo { 1 }') } 73 | it { should_not allow('foo { $x }') } 74 | 75 | context "blacklist" do 76 | # This is a tricky case where we want to allow methods like 77 | # Enumerable#select, but not Kernel#select / IO#select. 78 | it { should allow('[1, 2, 3].select { |x| x.odd? }') } 79 | it { should_not allow('select([$stdin], nil, nil, 1.5)') } 80 | 81 | # TODO: these are a possible concern because symbols are not GC'ed and 82 | # an attacker could create a large number of them to eat up memory. If 83 | # these methods are blacklisted, then dyna-symbols (:"foo#{x}") need to 84 | # be restricted as well. 85 | it { should allow('"abc".intern') } 86 | it { should allow('"abc".to_sym') } 87 | 88 | it { should_not allow('abort("fail")') } 89 | it { should_not allow('alias :foo :bar') } 90 | it { should_not allow('alias foo bar') } 91 | it { should_not allow('alias_method(:foo, :bar)') } 92 | it { should_not allow('at_exit { puts "Bye!" }')} 93 | it { should_not allow('autoload(:Foo, "foo")') } 94 | it { should_not allow('binding') } 95 | it { should_not allow('binding()') } 96 | it { should_not allow('callcc { |cont| }') } 97 | it { should_not allow('caller') } 98 | it { should_not allow('caller()') } 99 | it { should_not allow('caller(1)') } 100 | it { should_not allow('class_eval("$x = 1")') } 101 | it { should_not allow('const_get(:Kernel)') } 102 | it { should_not allow('const_set(:Foo, ::Kernel)') } 103 | it { should_not allow('eval("`ls`")') } 104 | it { should_not allow('exec("ls")') } 105 | it { should_not allow('exit') } 106 | it { should_not allow('exit()') } 107 | it { should_not allow('fail') } 108 | it { should_not allow('fail("failed")') } 109 | it { should_not allow('fail()') } 110 | it { should_not allow('fork { }') } 111 | it { should_not allow('fork') } 112 | it { should_not allow('fork()') } 113 | it { should_not allow('gets') } 114 | it { should_not allow('gets()') } 115 | it { should_not allow('global_variables') } 116 | it { should_not allow('global_variables()') } 117 | it { should_not allow('load("foo")') } 118 | it { should_not allow('loop { }') } 119 | it { should_not allow('method(:eval)') } 120 | it { should_not allow('module_eval("`ls`")') } 121 | it { should_not allow('open("/etc/passwd")') } 122 | it { should_not allow('readline') } 123 | it { should_not allow('readline()') } 124 | it { should_not allow('readlines') } 125 | it { should_not allow('readlines()') } 126 | it { should_not allow('redo') } 127 | it { should_not allow('remove_const(:Kernel)') } 128 | it { should_not allow('require("digest/md5")') } 129 | it { should_not allow('send(:eval, "`ls`")') } 130 | it { should_not allow('set_trace_func(proc { |event,file,line,id,binding,classname| })') } 131 | it { should_not allow('sleep(100**100)') } 132 | it { should_not allow('spawn("ls", :chdir => "/")') } 133 | it { should_not allow('srand') } 134 | it { should_not allow('srand()') } 135 | it { should_not allow('srand(1)') } 136 | it { should_not allow('syscall(4, 1, "hello\n", 6)') } 137 | it { should_not allow('system("ls")') } 138 | it { should_not allow('trap("EXIT") { }') } 139 | it { should_not allow('undef :raise') } 140 | it { should_not allow('undef raise') } 141 | it { should_not allow(%{''.dup}) } 142 | end 143 | end 144 | 145 | context "case" do 146 | it { should allow('case x; when 1; 2; end') } 147 | 148 | it { should_not allow('case $x; when 1; 2; end') } 149 | it { should_not allow('case $x = 1; when 1; 2; end') } 150 | it { should_not allow('case x; when $x; 2; end') } 151 | it { should_not allow('case x; when 1; $x; end') } 152 | end 153 | 154 | context "class / module definition" do 155 | it { should allow("class Foo\nend") } 156 | it { should allow("class Foo::Bar\nend") } 157 | 158 | it { should allow("module Foo\nend") } 159 | it { should allow("module Foo::Bar\nend") } 160 | it { should_not allow("module Kernel\nend") } 161 | it { should_not allow("module ::Kernel\nend") } 162 | end 163 | 164 | context "defined?" do 165 | it { should_not allow('defined?(Kernel)') } 166 | end 167 | 168 | context "dynamic strings" do 169 | it { should_not allow('"abc#{`ls`}"') } 170 | it { should_not allow('"#{`ls`}abc"') } 171 | it { should_not allow('"#$0"') } 172 | end 173 | 174 | context "dynamic symbols" do 175 | it { should_not allow(':"abc#{`ls`}"') } 176 | it { should_not allow(':"#{`ls`}abc"') } 177 | end 178 | 179 | context "for" do 180 | it { should_not allow('for i in ENV; puts i; end') } 181 | it { should_not allow('for $x in [1, 2, 3]; puts $x; end') } 182 | end 183 | 184 | context "if/elsif/else" do 185 | it { should allow('x if true') } 186 | 187 | it { should_not allow('$x ? 1 : 2') } 188 | it { should_not allow('true ? $x : 2') } 189 | it { should_not allow('true ? 1 : $x') } 190 | it { should_not allow('if $x; 1; end') } 191 | it { should_not allow('if true; $x; end') } 192 | it { should_not allow('$x if true') } 193 | it { should_not allow('true if $x') } 194 | it { should_not allow('if $x; 1; else 2; end') } 195 | it { should_not allow('if 1; $x; else 2; end') } 196 | it { should_not allow('if 1; 1; else $x; end') } 197 | it { should_not allow('if 1; 1; elsif 2; 2; else $x; end') } 198 | end 199 | 200 | context "literals" do 201 | it { should allow('"abc"') } 202 | it { should allow('/abc/') } 203 | it { should allow('1') } 204 | it { should allow('1..2') } 205 | it { should allow('1.2') } 206 | it { should allow('false') } 207 | it { should allow('nil') } 208 | it { should allow('true') } 209 | it { should allow('[]') } 210 | it { should allow('[1,2,3]') } 211 | it { should allow('%w[ a b c ]') } 212 | it { should allow('{}') } 213 | it { should allow('{1 => 2}') } 214 | end 215 | 216 | context "magic variables" do 217 | it { should_not allow('__callee__') } 218 | it { should_not allow('__FILE__') } 219 | it { should_not allow('__method__') } 220 | end 221 | 222 | context "methods" do 223 | it { should allow('def initialize(attributes={}); end') } 224 | end 225 | 226 | context "singleton class" do 227 | it { should_not allow('class << Kernel; end') } 228 | it { should_not allow('class << Kernel; `ls`; end') } 229 | end 230 | 231 | context "super" do 232 | it { should allow('super') } 233 | it { should allow('super()') } 234 | it { should allow('super(1)') } 235 | it { should_not allow('super($x)') } 236 | end 237 | 238 | context "system" do 239 | it { should_not allow('`ls`') } 240 | it { should_not allow('%x[ls]') } 241 | it { should_not allow('system("ls")') } 242 | end 243 | 244 | context "unless" do 245 | it { should_not allow('unless $x; 1; end') } 246 | it { should_not allow('unless true; $x; end') } 247 | it { should_not allow('$x unless true') } 248 | it { should_not allow('true unless $x') } 249 | it { should_not allow('unless $x; 1; else 2; end') } 250 | it { should_not allow('unless 1; $x; else 2; end') } 251 | it { should_not allow('unless 1; 1; else $x; end') } 252 | end 253 | 254 | context "until" do 255 | it { should_not allow('true until false') } 256 | end 257 | 258 | context "while" do 259 | it { should_not allow('true while true') } 260 | end 261 | 262 | context "yield" do 263 | it { should allow('def foo; yield; end') } 264 | end 265 | 266 | context "Rails for Zombies" do 267 | before(:each) do 268 | policy.whitelist_const('GenericController') 269 | policy.whitelist_const('Tweet') 270 | policy.whitelist_const('Weapon') 271 | policy.whitelist_const('Zombie') 272 | policy.whitelist_const('ZombiesController') 273 | end 274 | 275 | [ 276 | "1 = Ash\nAsh = Glen Haven Memorial Cemetary", 277 | "<% zombies = Zombie.all %>\n\n\n", 278 | "class HelloRils", 279 | "Class NAme\n\nend", 280 | "class tweet < ActiveRecord::Base\n belongs_to :zombie \n z = zombie.find(2)\nend", 281 | "class zombie < ActiveRecord :: Base\n\nend\n", 282 | "Class Zombie < ActiveRecord::Base\n validates_presence_of :name\nend", 283 | "Class Zombie < ActiveRecord::Base\nend", 284 | "Class Zombie < ActiveRecord::Base\nvalidates_presence_of :status\nvalidates_presence_of :ww\nend", 285 | "Class Zombie < ActiveRecord::Base{\ndef name\ndef graveyard\n\n}\n", 286 | "class zombie < ActiveRecord\nend class", 287 | "Class Zombie \nvalidates_presence_of\nend", 289 | "class.load(Zombie)", 290 | "Poop = Zombie.find(:id=1)", 291 | "SELECT * WHERE ID = 1;", 292 | "String myNewZombie = select name from Zombies where id=1", 293 | "w = Weapon.find(1)\nZombie.create( :Weapon => \"Hammer\", Zombie => 1)\nend\n", 294 | "Zodfsdsfdsdfsz=Zombies.find()1\n" 295 | ].each do |error| 296 | it "raises SyntaxError on #{error.inspect}" do 297 | expect { RubyCop::NodeBuilder.build(error) }.to raise_error(SyntaxError) 298 | end 299 | end 300 | 301 | [ 302 | "1\nZombie = 1\n", 303 | "A = t.find(1)\n\n\n\n", 304 | "Ash = 1\n", 305 | "Ash = 1\n\n", 306 | "Ash = Weapons.find.zombie_id(1)", 307 | "Ash = Zombie.find(1)\nAsh.weapons.count", 308 | "class Com\n\nhasmany dog\n\nend", 309 | "class Finder < Tweet\n z = Tweet.find(1)\nend", 310 | "class Post < ActiveRecord::Base\nend", 311 | "class Weapons < ActiveRecord::Base\n belongs_to :Zombies\nend\n\nclass Zombies < ActiveRecord::Base\n has_many :Weapons\nend", 312 | "Class Zombie < ActiveRecord::Base\n\nEnd", 313 | "class Zombie < Rails::ActiveModel\n \nend", 314 | "Class Zombie {\n validates :name, :presence => true\n}", 315 | "Class Zombies < ActiveRecord::Base\nEnd", 316 | "class ZombiesController < ApplicationController\n before_filter :find_zombie, :only => [:show]\n\n def show\n render :action => :show\n end\n\n def find_zombie\n @zombie = Zombie.find params[:id]\n @numTweets = Tweet.where(:zombie_id => @zombie).count\n if @numTweets < 1 \n redirect_to(zombies_path)\n end\n end\nend\n", 317 | "class Zomvie \"Jim\", :graveyard=> \"My Fathers Basement\")", 320 | "cuntZombie=Zombies[1];", 321 | "def create\n @newZombie = Zombie.create( :name => params[:name], :graveyard => params[:graveyard] )\n \n render action => :create\nend\n", 322 | "Destroy Zombie where ID = 3", 323 | "Find.Tweet.id = (1)\nZombie = Tweet.id", 324 | "firstZombie = Zombies[id '1']\n", 325 | "First_user = initialuser\n", 326 | "Hash tag + lik", 327 | "Hold = Tweets.find 1", 328 | "jh = new Zombie()\njh.name = \"JHDI\"\njh.graveYard = \"JHDI cemetary\"\njh.save", 329 | "Location = puts graveyard.Ash", 330 | "newZombie = Zombie.new\nnewZombie.name = \"Craig\"\nnewZombie.graveyard = \"my cube\"\nnewZombie.save", 331 | "newZombie = Zombie.new\nnewZombie['name'] = \"Renan\"\nnewZombie['graveyard'] = \"Lavras Cemetary\"\nnewZombie.save\n", 332 | "newZombie = Zombies.new\nnewZombie.id = 4\nnewZombie.name = \"Arek\"\nnewZombie.graveyard = \"Centralny cmentarz komunalny\"\nnewZombie.save", 333 | "newZombie=Zombie.new {}\nnewZombie.name = \"Manish\"\nnewZombie.graveyard = \"Shillong Bastards Cemetary\"", 334 | "numeroUno = Zombie(1).name;\n", 335 | "splatid = id.find(1)\nsplatName = splatid[:name]", 336 | "t = new Tweet();\nminTweet == t.find(3);", 337 | "t = Tweet.find(1)\nZombie = t.id", 338 | "T = Zombie.find(3)\nT.graveyard = 'Benny Hills Memorial'\nT.save", 339 | "t = Zombie.find(3)\nt.Zombie = \"Benny Hills Memorial\"\nt.save\n", 340 | "T = Zombie.where(1)\nputs t.name\n", 341 | "t= \nt.Name=\"Hello\"\nt.Graveyard=\"yes\"\nt.save", 342 | "t=Zombie.find(3)\nt.Zombie = \"pucho\"", 343 | "T=Zombie[1]\n", 344 | "Ticket = Tweet.find(1)", 345 | "Tweet = new Tweet;\na = Tweet.find(1);\n", 346 | "Tweet = new Tweet\nt = Tweet.where(:id => 1)\n", 347 | "Tweet = t\nt.zombie = 1", 348 | "Tweet.find(1)\nZombie1 = tweet(1)", 349 | "Tweet=id1\n", 350 | "UPDATE Zombies\nSET name='vijay',graveyard='Ahmedabad'\nWhere Id='2';\n", 351 | "w = Weapon.create(:name => \"Punto\", :Zombie => z)\nash = Zombie.find(1)", 352 | "z = ID=1", 353 | "Z = Zombie.find(1)\n", 354 | "z = Zombie.find(1)\nWeapon.where( :Zombie => z )", 355 | "z = Zombie.find(1)\nZombie1 = z.name", 356 | "Z = Zombie.find(1)\n\n\n\n\n", 357 | "Z = Zombie.find(3)", 358 | "Z = zombie.id(1)", 359 | "z = Zombie.new\nz.name = \"Charly\"\nz.Graveyard = \"EL carlos\"", 360 | "Z=Zombie.new\nz.find(1)", 361 | "Zombie = new Zombie", 362 | "Zombie = Tweet.find(1)", 363 | "Zombie = Zombie.find(Weapons.find(:zombie_id))", 364 | "Zombie = Zombie.find[1]", 365 | "Zombie = Zombies.find(1)", 366 | "Zombie3=Zombie.find(3)\nZombie3.graveyard = \"Benny Hills Memorial\"\nZombie3.save", 367 | "Zombies = '123456'", 368 | "Zombies = id \nZombies.create( :name=>\"roger\" )", 369 | "Zombies = Zombies.find(1)\nput Zombies", 370 | "Zombies = {:Ash => \"Glen Haven mernorial Cemetary\"}\nvar = Zombies.find(1)\nvar.save\n\n", 371 | "Zombies = {:name => [\"Ash\", \"Bob\", \"Jim\"], :graveyard => [\"Glen Haven Memorial Cemetary\",\"Chapel Hill Cemetary\",\"My Fathers Basement\"] }\na = z.find(1)", 372 | "Zombies = {\n :id => 1 }\nt = zombie.find(1)", 373 | "Zombies.find(1)\nputs Zombies.find(1)\nZ=Zombies.find(1)\nZ.lat=[:id]\nz.save\nz\n", 374 | "zoombieID = table.find(1)\n", 375 | 'class << Zombie; self; end', 376 | 'myZombie = Tweet.find(1)', 377 | 'Zombie.create(:name => "Whoa. A Green String")', 378 | 'Zombie.create(name: "Fal", graveyard: "fail")', 379 | ].each do |good| 380 | it "allows #{good.inspect}" do 381 | should allow(good) 382 | end 383 | end 384 | 385 | [ 386 | "Class", 387 | "def show\n @zombie = Zombie.find(params[:id])\n\n respond_to do |format|\n `ls`\n end\nend\n", 388 | "Module.delete(3)", 389 | "Module.find(\"Ash\")", 390 | "require 'tweet'\nt = Tweet.find(1)", 391 | "require 'Tweet'\nTweet.find(2).name\n", 392 | "require \"tempfile\"\nt = Zombies.new('Zombies')\nZombies.where(:id => 1)\nt.save", 393 | "system('ls')", 394 | "t = Zombies.open()", 395 | "Tweet.find(1)\nDim var as String\nvar=Tweet.name", 396 | "Zombie.load(1)\n\n", 397 | "`echo 1`", 398 | "`ls -l`", 399 | "`ps ax`\n", 400 | "`uname -a`", 401 | 'const_get', 402 | 'const_get()' 403 | ].each do |bad| 404 | it "does not allow #{bad.inspect}" do 405 | should_not allow(bad) 406 | end 407 | end 408 | end 409 | end 410 | -------------------------------------------------------------------------------- /spec/policy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'ruby_cop' 2 | 3 | describe RubyCop::Policy do 4 | let(:policy) { described_class.new } 5 | subject { policy } 6 | 7 | RSpec::Matchers.define(:allow) do |ruby| 8 | match { |policy| RubyCop::NodeBuilder.build(ruby).accept(policy) } 9 | end 10 | 11 | context "assignment" do 12 | context "class variables" do 13 | it { should_not allow('@@x = 1') } 14 | it { should_not allow('@@x ||= 1') } 15 | it { should_not allow('@@x += 1') } 16 | end 17 | 18 | context "constants" do 19 | it { should allow('Foo = 1') } 20 | it { should allow('Foo::Bar = 1') } 21 | it { should allow('::Bar = 1') } 22 | 23 | it { should_not allow('Foo = Kernel') } 24 | it { should_not allow('Foo = ::Kernel') } 25 | it { should_not allow('Foo = Object::Kernel') } 26 | end 27 | 28 | context "globals" do 29 | it { should_not allow('$x = 1') } 30 | it { should_not allow('$x ||= 1') } 31 | it { should_not allow('$x += 1') } 32 | end 33 | 34 | context "instance variables" do 35 | it { should allow('@x = 1') } 36 | it { should allow('@x += 1') } 37 | it { should_not allow('@x = $x') } 38 | it { should_not allow('@x = @@x') } 39 | end 40 | 41 | context "locals" do 42 | it { should allow('x = 1') } 43 | it { should allow('x ||= 1') } 44 | it { should allow('x += 1') } 45 | it { should_not allow('x = $x') } 46 | it { should_not allow('x = @@x') } 47 | end 48 | end 49 | 50 | context "begin/rescue/ensure" do 51 | it { should allow('begin; x; rescue; end') } 52 | it { should allow('x rescue 1') } 53 | 54 | it { should_not allow('begin; `ls`; rescue; x; end') } 55 | it { should_not allow('begin; x; rescue; `ls`; end') } 56 | it { should_not allow('begin; x; rescue; 1; ensure `ls`; end') } 57 | it { should_not allow('`ls` rescue 1') } 58 | it { should_not allow('x rescue `ls`') } 59 | it { should_not allow('begin; x; rescue (`ls`; RuntimeError) => err; end') } 60 | it { should_not allow(%{begin ; begin ; ":D" ; rescue ; retry ; ensure ; raise ":D" ; end ; rescue ; retry ; end})} 61 | it { should_not allow(%{begin ; while(true) ; 'x' ; end ; rescue Exception ; retry ; end}) } 62 | end 63 | 64 | context "blocks" do 65 | it { should_not allow('->(a = $x) { }') } 66 | it { should_not allow('->(a) { $x }') } 67 | it { should_not allow('lambda { $x }') } 68 | it { should_not allow('proc { $x }') } 69 | end 70 | 71 | context "calls" do 72 | it { should allow('foo { 1 }') } 73 | it { should_not allow('foo { $x }') } 74 | 75 | context "blacklist" do 76 | # This is a tricky case where we want to allow methods like 77 | # Enumerable#select, but not Kernel#select / IO#select. 78 | it { should allow('[1, 2, 3].select { |x| x.odd? }') } 79 | it { should_not allow('select([$stdin], nil, nil, 1.5)') } 80 | 81 | # TODO: these are a possible concern because symbols are not GC'ed and 82 | # an attacker could create a large number of them to eat up memory. If 83 | # these methods are blacklisted, then dyna-symbols (:"foo#{x}") need to 84 | # be restricted as well. 85 | it { should allow('"abc".intern') } 86 | it { should allow('"abc".to_sym') } 87 | 88 | it { should_not allow('__send__(:eval, "`ls`")') } 89 | it { should_not allow('abort("fail")') } 90 | it { should_not allow('alias :foo :bar') } 91 | it { should_not allow('alias foo bar') } 92 | it { should_not allow('alias_method(:foo, :bar)') } 93 | it { should_not allow('at_exit { puts "Bye!" }')} 94 | it { should_not allow('autoload(:Foo, "foo")') } 95 | it { should_not allow('binding') } 96 | it { should_not allow('binding()') } 97 | it { should_not allow('callcc { |cont| }') } 98 | it { should_not allow('caller') } 99 | it { should_not allow('caller()') } 100 | it { should_not allow('caller(1)') } 101 | it { should_not allow('class_eval("$x = 1")') } 102 | it { should_not allow('const_get(:Kernel)') } 103 | it { should_not allow('const_set(:Foo, ::Kernel)') } 104 | it { should_not allow('eval("`ls`")') } 105 | it { should_not allow('exec("ls")') } 106 | it { should_not allow('exit') } 107 | it { should_not allow('exit()') } 108 | it { should_not allow('fail') } 109 | it { should_not allow('fail("failed")') } 110 | it { should_not allow('fail()') } 111 | it { should_not allow('fork { }') } 112 | it { should_not allow('fork') } 113 | it { should_not allow('fork()') } 114 | it { should_not allow('gets') } 115 | it { should_not allow('gets()') } 116 | it { should_not allow('global_variables') } 117 | it { should_not allow('global_variables()') } 118 | it { should_not allow('load("foo")') } 119 | it { should_not allow('loop { }') } 120 | it { should_not allow('method(:eval)') } 121 | it { should_not allow('module_eval("`ls`")') } 122 | it { should_not allow('open("/etc/passwd")') } 123 | it { should_not allow('public_send(:eval, "`ls`")') } 124 | it { should_not allow('readline') } 125 | it { should_not allow('readline()') } 126 | it { should_not allow('readlines') } 127 | it { should_not allow('readlines()') } 128 | it { should_not allow('redo') } 129 | it { should_not allow('remove_const(:Kernel)') } 130 | it { should_not allow('require("digest/md5")') } 131 | it { should_not allow('send(:eval, "`ls`")') } 132 | it { should_not allow('set_trace_func(proc { |event,file,line,id,binding,classname| })') } 133 | it { should_not allow('sleep(100**100)') } 134 | it { should_not allow('spawn("ls", :chdir => "/")') } 135 | it { should_not allow('srand') } 136 | it { should_not allow('srand()') } 137 | it { should_not allow('srand(1)') } 138 | it { should_not allow('syscall(4, 1, "hello\n", 6)') } 139 | it { should_not allow('system("ls")') } 140 | it { should_not allow('trap("EXIT") { }') } 141 | it { should_not allow('undef :raise') } 142 | it { should_not allow('undef raise') } 143 | it { should_not allow(%{''.dup})} 144 | end 145 | end 146 | 147 | context "case" do 148 | it { should allow('case x; when 1; 2; end') } 149 | 150 | it { should_not allow('case $x; when 1; 2; end') } 151 | it { should_not allow('case $x = 1; when 1; 2; end') } 152 | it { should_not allow('case x; when $x; 2; end') } 153 | it { should_not allow('case x; when 1; $x; end') } 154 | end 155 | 156 | context "class / module definition" do 157 | it { should allow("class Foo\nend") } 158 | it { should allow("class Foo::Bar\nend") } 159 | 160 | it { should allow("module Foo\nend") } 161 | it { should allow("module Foo::Bar\nend") } 162 | it { should_not allow("module Kernel\nend") } 163 | it { should_not allow("module ::Kernel\nend") } 164 | end 165 | 166 | context "defined?" do 167 | it { should_not allow('defined?(Kernel)') } 168 | end 169 | 170 | context "dynamic strings" do 171 | it { should_not allow('"abc#{`ls`}"') } 172 | it { should_not allow('"#{`ls`}abc"') } 173 | it { should_not allow('"#$0"') } 174 | end 175 | 176 | context "dynamic symbols" do 177 | it { should_not allow(':"abc#{`ls`}"') } 178 | it { should_not allow(':"#{`ls`}abc"') } 179 | end 180 | 181 | context "for" do 182 | it { should_not allow('for i in ENV; puts i; end') } 183 | it { should_not allow('for $x in [1, 2, 3]; puts $x; end') } 184 | end 185 | 186 | context "if/elsif/else" do 187 | it { should allow('x if true') } 188 | 189 | it { should_not allow('$x ? 1 : 2') } 190 | it { should_not allow('true ? $x : 2') } 191 | it { should_not allow('true ? 1 : $x') } 192 | it { should_not allow('if $x; 1; end') } 193 | it { should_not allow('if true; $x; end') } 194 | it { should_not allow('$x if true') } 195 | it { should_not allow('true if $x') } 196 | it { should_not allow('if $x; 1; else 2; end') } 197 | it { should_not allow('if 1; $x; else 2; end') } 198 | it { should_not allow('if 1; 1; else $x; end') } 199 | it { should_not allow('if 1; 1; elsif 2; 2; else $x; end') } 200 | end 201 | 202 | context "literals" do 203 | it { should allow('"abc"') } 204 | it { should allow('/abc/') } 205 | it { should allow('1') } 206 | it { should allow('1..2') } 207 | it { should allow('1.2') } 208 | it { should allow('false') } 209 | it { should allow('nil') } 210 | it { should allow('true') } 211 | it { should allow('[]') } 212 | it { should allow('[1,2,3]') } 213 | it { should allow('{}') } 214 | it { should allow('{1 => 2}') } 215 | end 216 | 217 | context "magic variables" do 218 | it { should_not allow('__callee__') } 219 | it { should_not allow('__FILE__') } 220 | it { should_not allow('__method__') } 221 | end 222 | 223 | context "methods" do 224 | it { should allow('def initialize(attributes={}); end') } 225 | end 226 | 227 | context "singleton class" do 228 | it { should_not allow('class << Kernel; end') } 229 | it { should_not allow('class << Kernel; `ls`; end') } 230 | end 231 | 232 | context "super" do 233 | it { should allow('super') } 234 | it { should allow('super()') } 235 | it { should allow('super(1)') } 236 | it { should_not allow('super($x)') } 237 | end 238 | 239 | context "system" do 240 | it { should_not allow('`ls`') } 241 | it { should_not allow('%x[ls]') } 242 | it { should_not allow('system("ls")') } 243 | end 244 | 245 | context "unless" do 246 | it { should_not allow('unless $x; 1; end') } 247 | it { should_not allow('unless true; $x; end') } 248 | it { should_not allow('$x unless true') } 249 | it { should_not allow('true unless $x') } 250 | it { should_not allow('unless $x; 1; else 2; end') } 251 | it { should_not allow('unless 1; $x; else 2; end') } 252 | it { should_not allow('unless 1; 1; else $x; end') } 253 | end 254 | 255 | context "until" do 256 | it { should_not allow('true until false') } 257 | end 258 | 259 | context "while" do 260 | it { should_not allow('true while true') } 261 | end 262 | 263 | context "yield" do 264 | it { should allow('def foo; yield; end') } 265 | end 266 | 267 | context "Rails for Zombies" do 268 | before(:each) do 269 | policy.whitelist_const('GenericController') 270 | policy.whitelist_const('Tweet') 271 | policy.whitelist_const('Weapon') 272 | policy.whitelist_const('Zombie') 273 | policy.whitelist_const('ZombiesController') 274 | end 275 | 276 | [ 277 | "1 = Ash\nAsh = Glen Haven Memorial Cemetary", 278 | "<% zombies = Zombie.all %>\n\n
    \n <% zombies.each do |zombie| %>\n
  • \n <%= zombie.name %>\n <% if zombie.Tweet >= 1 %>\n

    <%= SMART ZOMBIE =%>

    \n <% end %>\n
  • \n <% end %>\n
\n", 279 | "class HelloRils", 280 | "Class NAme\n\nend", 281 | "class tweet < ActiveRecord::Base\n belongs_to :zombie \n z = zombie.find(2)\nend", 282 | "class zombie < ActiveRecord :: Base\n\nend\n", 283 | "Class Zombie < ActiveRecord::Base\n validates_presence_of :name\nend", 284 | "Class Zombie < ActiveRecord::Base\nend", 285 | "Class Zombie < ActiveRecord::Base\nvalidates_presence_of :status\nvalidates_presence_of :ww\nend", 286 | "Class Zombie < ActiveRecord::Base{\ndef name\ndef graveyard\n\n}\n", 287 | "class zombie < ActiveRecord\nend class", 288 | "Class Zombie \nvalidates_presence_of\nend", 290 | "class.load(Zombie)", 291 | "Poop = Zombie.find(:id=1)", 292 | "SELECT * WHERE ID = 1;", 293 | "String myNewZombie = select name from Zombies where id=1", 294 | "w = Weapon.find(1)\nZombie.create( :Weapon => \"Hammer\", Zombie => 1)\nend\n", 295 | "Zodfsdsfdsdfsz=Zombies.find()1\n" 296 | ].each do |error| 297 | it "raises SyntaxError on #{error.inspect}" do 298 | expect { RubyCop::NodeBuilder.build(error) }.to raise_error(SyntaxError) 299 | end 300 | end 301 | 302 | [ 303 | "1\nZombie = 1\n", 304 | "A = t.find(1)\n\n\n\n", 305 | "Ash = 1\n", 306 | "Ash = 1\n\n", 307 | "Ash = Weapons.find.zombie_id(1)", 308 | "Ash = Zombie.find(1)\nAsh.weapons.count", 309 | "class Com\n\nhasmany dog\n\nend", 310 | "class Finder < Tweet\n z = Tweet.find(1)\nend", 311 | "class Post < ActiveRecord::Base\nend", 312 | "class Weapons < ActiveRecord::Base\n belongs_to :Zombies\nend\n\nclass Zombies < ActiveRecord::Base\n has_many :Weapons\nend", 313 | "Class Zombie < ActiveRecord::Base\n\nEnd", 314 | "class Zombie < Rails::ActiveModel\n \nend", 315 | "Class Zombie {\n validates :name, :presence => true\n}", 316 | "Class Zombies < ActiveRecord::Base\nEnd", 317 | "class ZombiesController < ApplicationController\n before_filter :find_zombie, :only => [:show]\n\n def show\n render :action => :show\n end\n\n def find_zombie\n @zombie = Zombie.find params[:id]\n @numTweets = Tweet.where(:zombie_id => @zombie).count\n if @numTweets < 1 \n redirect_to(zombies_path)\n end\n end\nend\n", 318 | "class Zomvie \"Jim\", :graveyard=> \"My Fathers Basement\")", 321 | "cuntZombie=Zombies[1];", 322 | "def create\n @newZombie = Zombie.create( :name => params[:name], :graveyard => params[:graveyard] )\n \n render action => :create\nend\n", 323 | "Destroy Zombie where ID = 3", 324 | "Find.Tweet.id = (1)\nZombie = Tweet.id", 325 | "firstZombie = Zombies[id '1']\n", 326 | "First_user = initialuser\n", 327 | "Hash tag + lik", 328 | "Hold = Tweets.find 1", 329 | "jh = new Zombie()\njh.name = \"JHDI\"\njh.graveYard = \"JHDI cemetary\"\njh.save", 330 | "Location = puts graveyard.Ash", 331 | "newZombie = Zombie.new\nnewZombie.name = \"Craig\"\nnewZombie.graveyard = \"my cube\"\nnewZombie.save", 332 | "newZombie = Zombie.new\nnewZombie['name'] = \"Renan\"\nnewZombie['graveyard'] = \"Lavras Cemetary\"\nnewZombie.save\n", 333 | "newZombie = Zombies.new\nnewZombie.id = 4\nnewZombie.name = \"Arek\"\nnewZombie.graveyard = \"Centralny cmentarz komunalny\"\nnewZombie.save", 334 | "newZombie=Zombie.new {}\nnewZombie.name = \"Manish\"\nnewZombie.graveyard = \"Shillong Bastards Cemetary\"", 335 | "numeroUno = Zombie(1).name;\n", 336 | "splatid = id.find(1)\nsplatName = splatid[:name]", 337 | "t = new Tweet();\nminTweet == t.find(3);", 338 | "t = Tweet.find(1)\nZombie = t.id", 339 | "T = Zombie.find(3)\nT.graveyard = 'Benny Hills Memorial'\nT.save", 340 | "t = Zombie.find(3)\nt.Zombie = \"Benny Hills Memorial\"\nt.save\n", 341 | "T = Zombie.where(1)\nputs t.name\n", 342 | "t= \nt.Name=\"Hello\"\nt.Graveyard=\"yes\"\nt.save", 343 | "t=Zombie.find(3)\nt.Zombie = \"pucho\"", 344 | "T=Zombie[1]\n", 345 | "Ticket = Tweet.find(1)", 346 | "Tweet = new Tweet;\na = Tweet.find(1);\n", 347 | "Tweet = new Tweet\nt = Tweet.where(:id => 1)\n", 348 | "Tweet = t\nt.zombie = 1", 349 | "Tweet.find(1)\nZombie1 = tweet(1)", 350 | "Tweet=id1\n", 351 | "UPDATE Zombies\nSET name='vijay',graveyard='Ahmedabad'\nWhere Id='2';\n", 352 | "w = Weapon.create(:name => \"Punto\", :Zombie => z)\nash = Zombie.find(1)", 353 | "z = ID=1", 354 | "Z = Zombie.find(1)\n", 355 | "z = Zombie.find(1)\nWeapon.where( :Zombie => z )", 356 | "z = Zombie.find(1)\nZombie1 = z.name", 357 | "Z = Zombie.find(1)\n\n\n\n\n", 358 | "Z = Zombie.find(3)", 359 | "Z = zombie.id(1)", 360 | "z = Zombie.new\nz.name = \"Charly\"\nz.Graveyard = \"EL carlos\"", 361 | "Z=Zombie.new\nz.find(1)", 362 | "Zombie = new Zombie", 363 | "Zombie = Tweet.find(1)", 364 | "Zombie = Zombie.find(Weapons.find(:zombie_id))", 365 | "Zombie = Zombie.find[1]", 366 | "Zombie = Zombies.find(1)", 367 | "Zombie3=Zombie.find(3)\nZombie3.graveyard = \"Benny Hills Memorial\"\nZombie3.save", 368 | "Zombies = '123456'", 369 | "Zombies = id \nZombies.create( :name=>\"roger\" )", 370 | "Zombies = Zombies.find(1)\nput Zombies", 371 | "Zombies = {:Ash => \"Glen Haven mernorial Cemetary\"}\nvar = Zombies.find(1)\nvar.save\n\n", 372 | "Zombies = {:name => [\"Ash\", \"Bob\", \"Jim\"], :graveyard => [\"Glen Haven Memorial Cemetary\",\"Chapel Hill Cemetary\",\"My Fathers Basement\"] }\na = z.find(1)", 373 | "Zombies = {\n :id => 1 }\nt = zombie.find(1)", 374 | "Zombies.find(1)\nputs Zombies.find(1)\nZ=Zombies.find(1)\nZ.lat=[:id]\nz.save\nz\n", 375 | "zoombieID = table.find(1)\n", 376 | 'class << Zombie; self; end', 377 | 'myZombie = Tweet.find(1)', 378 | 'Zombie.create(:name => "Whoa. A Green String")', 379 | 'Zombie.create(name: "Fal", graveyard: "fail")', 380 | ].each do |good| 381 | it "allows #{good.inspect}" do 382 | should allow(good) 383 | end 384 | end 385 | 386 | [ 387 | "Class", 388 | "def show\n @zombie = Zombie.find(params[:id])\n\n respond_to do |format|\n `ls`\n end\nend\n", 389 | "Module.delete(3)", 390 | "Module.find(\"Ash\")", 391 | "require 'tweet'\nt = Tweet.find(1)", 392 | "require 'Tweet'\nTweet.find(2).name\n", 393 | "require \"tempfile\"\nt = Zombies.new('Zombies')\nZombies.where(:id => 1)\nt.save", 394 | "system('ls')", 395 | "t = Zombies.open()", 396 | "Tweet.find(1)\nDim var as String\nvar=Tweet.name", 397 | "Zombie.load(1)\n\n", 398 | "`echo 1`", 399 | "`ls -l`", 400 | "`ps ax`\n", 401 | "`uname -a`", 402 | 'const_get', 403 | 'const_get()' 404 | ].each do |bad| 405 | it "does not allow #{bad.inspect}" do 406 | should_not allow(bad) 407 | end 408 | end 409 | end 410 | end 411 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 2 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 3 | require "bundler/setup" 4 | Bundler.require(:default, :test) 5 | 6 | require 'ruby_cop' 7 | 8 | # Requires supporting files with custom matchers and macros, etc, 9 | # in ./support/ and its subdirectories. 10 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} 11 | 12 | RSpec.configure do |config| 13 | end 14 | -------------------------------------------------------------------------------- /tasks/rspec.rake: -------------------------------------------------------------------------------- 1 | require 'rspec/core/rake_task' 2 | 3 | RSpec::Core::RakeTask.new(:spec) do |t| 4 | t.pattern = 'spec/**/*_spec.rb' 5 | t.rspec_opts = ['--backtrace'] 6 | end 7 | 8 | task :default => :spec 9 | -------------------------------------------------------------------------------- /tasks/yard.rake: -------------------------------------------------------------------------------- 1 | require 'yard' 2 | YARD::Rake::YardocTask.new 3 | --------------------------------------------------------------------------------