1# encoding: ASCII-8BIT 2 3# iExploder - Generates bad HTML files to perform QA for web browsers. 4# 5# Copyright 2010 Thomas Stromberg - All Rights Reserved. 6# 7# Licensed under the Apache License, Version 2.0 (the "License"); 8# you may not use this file except in compliance with the License. 9# You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, software 14# distributed under the License is distributed on an "AS IS" BASIS, 15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16# See the License for the specific language governing permissions and 17# limitations under the License. 18 19require 'cgi' 20require 'yaml' 21 22require './scanner.rb' 23require './version.rb' 24 25# Used to speed up subtest generation 26$TEST_CACHE = {} 27 28# Media extensions to proper mime type map (not that we always listen' 29$MIME_MAP = { 30 'bmp' => 'image/bmp', 31 'gif' => 'image/gif', 32 'jpg' => 'image/jpeg', 33 'png' => 'image/png', 34 'svg' => 'image/svg+xml', 35 'tiff' => 'image/tiff', 36 'xbm' => 'image/xbm', 37 'ico' => 'image/x-icon', 38 'jng' => 'image/x-jng', 39 'xpm' => 'image/x-portable-pixmap', 40 'ogg' => 'audio/ogg', 41 'snd' => 'audio/basic', 42 'wav' => 'audio/wav' 43} 44 45# These tags get src properties more often than others 46$SRC_TAGS = ['img', 'audio', 'video', 'embed'] 47 48class IExploder 49 attr_accessor :test_num, :subtest_data, :lookup_mode, :random_mode, :cgi_url, :browser, :claimed_browser 50 attr_accessor :offset, :lines, :stop_num, :config 51 52 def initialize(config_path) 53 @config = YAML::load(File.open(config_path)) 54 @stop_num = nil 55 @subtest_data = nil 56 @test_num = 0 57 @cgi_url = '/iexploder.cgi' 58 @browser = 'UNKNOWN' 59 @claimed_browser = nil 60 readTagFiles() 61 return nil 62 end 63 64 def setRandomSeed 65 if @test_num > 0 66 srand(@test_num) 67 else 68 srand 69 end 70 end 71 72 73 def readTagFiles 74 # These if statements are so that mod_ruby doesn't have to reload the files 75 # each time 76 data_path = @config['mangle_data_path'] 77 @cssTags = readTagsDir("#{data_path}/css-properties") 78 @cssPseudoTags = readTagsDir("#{data_path}/css-pseudo") 79 @cssAtRules = readTagsDir("#{data_path}/css-atrules") 80 @htmlTags = readTagsDir("#{data_path}/html-tags") 81 @htmlAttr = readTagsDir("#{data_path}/html-attrs") 82 @htmlValues = readTagsDir("#{data_path}/html-values") 83 @cssValues = readTagsDir("#{data_path}/css-values") 84 @headerValues = readTagsDir("#{data_path}/headers") 85 @protocolValues = readTagsDir("#{data_path}/protocols") 86 @mimeTypes = readTagsDir("#{data_path}/mime-types") 87 @media = readMediaDir("#{data_path}/media") 88 end 89 90 def readTagsDir(directory) 91 values = [] 92 Dir.foreach(directory) { |filename| 93 if File.file?(directory + "/" + filename) 94 values = values + readTagFile(directory + "/" + filename) 95 end 96 } 97 return values.uniq 98 end 99 100 def readMediaDir(directory) 101 data = {} 102 Dir.foreach(directory) { |filename| 103 if File.file?(directory + "/" + filename) 104 (base, extension) = filename.split('.') 105 mime_type = $MIME_MAP[extension] 106 data[mime_type] = File.read(directory + "/" + filename) 107 end 108 } 109 return data 110 end 111 112 def readTagFile(filename) 113 list = Array.new 114 File.new(filename).readlines.each { |line| 115 line.chop! 116 117 # Don't include comments. 118 if (line !~ /^# /) && (line.length > 0) 119 list << line 120 end 121 } 122 return list 123 end 124 125 126 def generateHtmlValue(tag) 127 choice = rand(100) 128 tag = tag.sub('EXCLUDED_', '') 129 if tag =~ /^on/ and choice < 90 130 return generateHtmlValue('') + "()" 131 elsif tag == 'src' or tag == 'data' or tag == 'profile' and choice < 90 132 return generateGarbageUrl(tag) 133 end 134 135 case choice 136 when 0..50 then 137 return @htmlValues[rand(@htmlValues.length)] 138 when 51..75 139 return generateGarbageNumber() 140 when 76..85 141 return generateGarbageValue() 142 when 86..90 143 return generateGarbageNumber() + ',' + generateGarbageNumber() 144 when 91..98 145 return generateGarbageUrl(tag) 146 else 147 return generateOverflow() 148 end 149 end 150 151 def generateMediaUrl(tag) 152 mime_type = @media.keys[rand(@media.keys.length)] 153 return generateTestUrl(@test_num, nil, nil, mime_type) 154 end 155 156 def generateGarbageUrl(tag) 157 choice = rand(100) 158 case choice 159 when 0..30 160 return generateMediaUrl(tag) 161 when 31..50 162 return @protocolValues[rand(@protocolValues.length)] + '%' + generateGarbageValue() 163 when 51..60 164 return @protocolValues[rand(@protocolValues.length)] + '//../' + generateGarbageValue() 165 when 60..75 166 return @protocolValues[rand(@protocolValues.length)] + '//' + generateGarbageValue() 167 when 75..85 168 return generateOverflow() + ":" + generateGarbageValue() 169 when 86..97 170 return generateGarbageValue() + ":" + generateOverflow() 171 else 172 return generateOverflow() 173 end 174 end 175 176 def generateCssValue(property) 177 size_types = ['', 'em', 'px', '%', 'pt', 'pc', 'ex', 'in', 'cm', 'mm'] 178 179 choice = rand(100) 180 case choice 181 when 0..50 then 182 # return the most likely scenario 183 case property.sub('EXCLUDED_', '') 184 when /-image|content/ 185 return 'url(' + generateGarbageUrl(property) + ')' 186 when /-width|-radius|-spacing|margin|padding|height/ 187 return generateGarbageValue() + size_types[rand(size_types.length)] 188 when /-color/ 189 return generateGarbageColor() 190 when /-delay|-duration/ 191 return generateGarbageValue() + 'ms' 192 else 193 return @cssValues[rand(@cssValues.length)] 194 end 195 when 51..75 then return generateGarbageNumber() 196 when 76..85 then return 'url(' + generateGarbageUrl(property) + ')' 197 when 85..98 then return generateGarbageValue() 198 else 199 return generateOverflow() 200 end 201 end 202 203 def generateGarbageColor() 204 case rand(100) 205 when 0..50 then return '#' + generateGarbageValue() 206 when 51..70 then return 'rgb(' + generateGarbageNumber() + ',' + generateGarbageNumber() + ',' + generateGarbageNumber() + ')' 207 when 71..98 then return 'rgb(' + generateGarbageNumber() + '%,' + generateGarbageNumber() + '%,' + generateGarbageNumber() + '%)' 208 else 209 return generateOverflow() 210 end 211 end 212 213 def generateGarbageNumber() 214 choice = rand(100) 215 case choice 216 when 0 then return '0' 217 when 1..40 then return '9' * rand(100) 218 when 41..60 then return '999999.' + rand(999999999999999999999).to_s 219 when 61..80 then return '-' + ('9' * rand(100)) 220 when 81..90 then return '-999999.' + rand(999999999999999999999).to_s 221 when 91..98 then return generateGarbageText() 222 else 223 return generateOverflow() 224 end 225 end 226 227 def generateGarbageValue() 228 case rand(100) 229 when 0..30 then return rand(255).chr * rand(@config['buffer_overflow_length']) 230 when 31..50 then return "%n" * 50 231 when 51..65 then return ("&#" + rand(999999).to_s + ";") * rand(@config['max_garbage_text_size']) 232 when 66..70 then 233 junk = [] 234 0.upto(rand(20)+1) do 235 junk << "\\x" + rand(65535).to_s(16) 236 end 237 return junk.join('') * rand(@config['max_garbage_text_size']) 238 when 71..99 then 239 junk = [] 240 chars = '%?!$#^0123456789ABCDEF%#./\&|;' 241 0.upto(rand(20)+1) do 242 junk << chars[rand(chars.length)].chr 243 end 244 return junk.join('') * rand(@config['max_garbage_text_size']) 245 end 246 end 247 248 def generateOverflow() 249 return rand(255).chr * (@config['buffer_overflow_length'] + rand(500)) 250 end 251 252 def generateGarbageText 253 case rand(100) 254 when 0..70 then return 'X' * 129 255 when 71..75 then return "%n" * 15 256 when 76..85 then return ("&#" + rand(9999999999999).to_s + ";") * rand(@config['max_garbage_text_size']) 257 when 86..90 then return generateGarbageValue() 258 when 91..98 then return rand(255).chr * rand(@config['max_garbage_text_size']) 259 else 260 return generateOverflow() 261 end 262 end 263 264 def isPropertyInBlacklist(properties) 265 # Format: [img, src] or [img, style, property] 266 blacklist_entries = [] 267 if @config.has_key?('exclude') and @config['exclude'] 268 blacklist_entries << properties.join('.') 269 wildcard_property = properties.dup 270 wildcard_property[0] = '*' 271 blacklist_entries << wildcard_property.join('.') 272 blacklist_entries.each do |entry| 273 if @config['exclude'].has_key?(entry) and @browser =~ /#{@config['exclude'][entry]}/ 274 return true 275 end 276 end 277 end 278 return false 279 end 280 281 def generateCssStyling(tag) 282 out = ' style="' 283 0.upto(rand(@config['properties_per_style_max'])) { 284 property = @cssTags[rand(@cssTags.length)] 285 if isPropertyInBlacklist([tag, 'style', property]) 286 property = "EXCLUDED_#{property}" 287 end 288 out << property 289 290 # very small chance we let the tag run on. 291 if rand(65) > 1 292 out << ": " 293 end 294 295 values = [] 296 0.upto(rand(@config['attributes_per_style_property_max'])) { 297 values << generateCssValue(property) 298 } 299 out << values.join(' ') 300 # we almost always put the ; there. 301 if rand(65) > 1 302 out << ";\n " 303 end 304 } 305 out << "\"" 306 return out 307 end 308 309 def mangleTag(tag, no_close_chance=false) 310 if not no_close_chance and rand(100) < 15 311 return "</" + tag + ">" 312 end 313 out = "<" + tag 314 if rand(100) > 1 315 out << ' ' 316 else 317 out << generateOverflow() 318 end 319 320 attrNum = rand(@config['attributes_per_html_tag_max']) + 1 321 attrs = [] 322 # The HTML head tag does not have many useful attributes, but is always included in tests. 323 if tag == 'head' and rand(100) < 75 324 case rand(3) 325 when 0 then attrs << 'lang' 326 when 1 then attrs << 'dir' 327 when 2 then attrs << 'profile' 328 end 329 end 330 # 75% of the time, these tags get a src attribute 331 if $SRC_TAGS.include?(tag) and rand(100) < 75 332 if @config.has_key?('exclude') and @config['exclude'] and @config['exclude'].has_key?("#{tag}.src") 333 attrs << 'EXCLUDED_src' 334 else 335 attrs << 'src' 336 end 337 end 338 339 while attrs.length < attrNum 340 attribute = @htmlAttr[rand(@htmlAttr.length)] 341 if isPropertyInBlacklist([tag, attribute]) 342 attribute = "EXCLUDED_#{attribute}" 343 end 344 attrs << attribute 345 end 346 347 # Add a few HTML attributes 348 for attr in attrs 349 out << attr 350 if rand(100) > 1 351 out << '=' 352 end 353 if (rand(100) >= 50) 354 quoted = 1 355 out << "\"" 356 else 357 quoted = nil 358 end 359 out << generateHtmlValue(attr) 360 if quoted 361 if rand(100) >= 10 362 out << "\"" 363 end 364 end 365 if rand(100) >= 1 366 out << "\n " 367 end 368 end 369 370 if rand(100) >= 25 371 out << generateCssStyling(tag) 372 end 373 out << ">\n" 374 return out 375 end 376 377 def nextTestNum() 378 if @subtest_data 379 return @test_num 380 elsif @random_mode 381 return rand(99999999999) 382 else 383 return @test_num + 1 384 end 385 end 386 387 def generateCssPattern() 388 # Generate a CSS selector pattern. 389 choice = rand(100) 390 pattern = '' 391 case choice 392 when 0..84 then pattern = @htmlTags[rand(@htmlTags.length)].dup 393 when 85..89 then pattern = "*" 394 when 90..94 then pattern = @cssAtRules[rand(@cssAtRules.length)].dup 395 when 95..100 then pattern = '' 396 end 397 398 if rand(100) < 25 399 pattern << " " + @htmlTags[rand(@htmlTags.length)] 400 end 401 402 if rand(100) < 25 403 pattern << " > " + @htmlTags[rand(@htmlTags.length)] 404 end 405 406 if rand(100) < 25 407 pattern << " + " + @htmlTags[rand(@htmlTags.length)] 408 end 409 410 if rand(100) < 10 411 pattern << "*" 412 end 413 414 415 if rand(100) < 25 416 pseudo = @cssPseudoTags[rand(@cssPseudoTags.length)].dup 417 # These tags typically have a parenthesis 418 if (pseudo =~ /^lang|^nth|^not/ and rand(100) < 75 and pseudo !~ /\(/) or rand(100) < 20 419 pseudo << '(' 420 end 421 422 if pseudo =~ /\(/ 423 if rand(100) < 75 424 pseudo << generateGarbageValue() 425 end 426 if rand(100) < 75 427 pseudo << ')' 428 end 429 end 430 pattern << ":" + pseudo 431 end 432 433 if rand(100) < 20 434 html_attr = @htmlAttr[rand(@htmlAttr.length)] 435 match = '[' + html_attr 436 choice = rand(100) 437 garbage = generateGarbageValue() 438 case choice 439 when 0..25 then match << ']' 440 when 26..50 then match << "=\"#{garbage}\"]" 441 when 51..75 then match << "=~\"#{garbage}\"]" 442 when 76..99 then match << "|=\"#{garbage}\"]" 443 end 444 pattern << match 445 end 446 447 if rand(100) < 20 448 if rand(100) < 50 449 pattern << '.' + generateGarbageValue() 450 else 451 pattern << '.*' 452 end 453 end 454 455 if rand(100) < 20 456 pattern << '#' + generateGarbageValue() 457 end 458 459 if rand(100) < 5 460 pattern << ' #' + generateGarbageValue() 461 end 462 463 return pattern 464 end 465 466 def buildStyleTag() 467 out = "\n" 468 0.upto(rand(@config['properties_per_style_max'])) { 469 out << generateCssPattern() 470 if rand(100) < 90 471 out << " {\n" 472 end 473 474 0.upto(rand(@config['properties_per_style_max'])) { 475 property = @cssTags[rand(@cssTags.length)].dup 476 if isPropertyInBlacklist(['style', 'style', property]) 477 property = " EXCLUDED_#{property}" 478 end 479 out << " #{property}: " 480 481 values = [] 482 0.upto(rand(@config['attributes_per_style_property_max'])) { 483 values << generateCssValue(property) 484 } 485 out << values.join(' ') 486 if rand(100) < 95 487 out << ";\n" 488 end 489 } 490 if rand(100) < 90 491 out << "\n}\n" 492 end 493 494 } 495 return out 496 end 497 498 499 # Build any malicious javascript here. Fairly naive at the moment. 500 def buildJavaScript 501 target = @htmlTags[rand(@htmlTags.length)] 502 css_property = @cssTags[rand(@cssTags.length)] 503 css_property2 = @cssTags[rand(@cssTags.length)] 504 html_attr = @htmlAttr[rand(@htmlAttr.length)] 505 css_value = generateCssValue(css_property) 506 html_value = generateHtmlValue(html_attr) 507 html_value2 = generateGarbageNumber() 508 mangled = mangleTag(@htmlTags[rand(@htmlTags.length)]); 509 mangled2 = mangleTag(@htmlTags[rand(@htmlTags.length)]); 510 511 js = [] 512 js << "window.onload=function(){" 513 js << " var ietarget = document.createElement('#{target}');" 514 js << " ietarget.style.#{css_property} = '#{css_value}';" 515 js << " ietarget.#{html_attr} = '#{html_value}';" 516 js << " document.body.appendChild(ietarget);" 517 js << " ietarget.style.#{css_property2} = #{html_value2};" 518 519 js << " document.write('#{mangled}');" 520 js << " document.write('#{mangled2}');" 521 js << "}" 522 return js.join("\n") 523 end 524 525 def buildMediaFile(mime_type) 526 if @media.has_key?(mime_type) 527 data = @media[mime_type].dup 528 else 529 puts "No media found for #{mime_type}" 530 data = generateGarbageText() 531 end 532 533 # corrupt it in a subtle way 534 choice = rand(100) 535 if choice > 50 536 garbage = generateGarbageValue() 537 else 538 garbage = rand(255).chr * rand(8) 539 end 540 541 if "1.9".respond_to?(:encoding) 542 garbage.force_encoding('ASCII-8BIT') 543 data.force_encoding('ASCII-8BIT') 544 end 545 546 garbage_start = rand(data.length) 547 garbage_end = garbage_start + garbage.length 548 data[garbage_start..garbage_end] = garbage 549 if rand(100) < 15 550 data << generateGarbageValue() 551 end 552 return data 553 end 554 555 # Parse the subtest data passed in as part of the URL 556 def parseSubTestData(subtest_data) 557 # Initialize with one line at 0 558 if not subtest_data or subtest_data.to_i == 0 559 return [@config['initial_subtest_width'], [0]] 560 end 561 (lines_at_time, offsets_string) = subtest_data.split('_') 562 offsets = offsets_string.split(',').map! {|x| x.to_i } 563 return [lines_at_time.to_i, offsets] 564 end 565 566 def generateTestUrl(test_num, subtest_width=nil, subtest_offsets=nil, mime_type=nil) 567 url = @cgi_url + '?' 568 if subtest_width 569 if subtest_offsets.length > @config['subtest_combinations_max'] 570 url << "t=" << test_num.to_s << "&l=test_redirect&z=THE_END" 571 else 572 url << "t=" << test_num.to_s << "&s=" << subtest_width.to_s << "_" << subtest_offsets.join(',') 573 end 574 else 575 url << "t=" << test_num.to_s 576 end 577 578 if @random_mode 579 url << "&r=1" 580 elsif @stop_num 581 url << "&x=" << @stop_num.to_s 582 end 583 584 if mime_type 585 url << '&m=' + CGI::escape(mime_type) 586 end 587 588 url << "&b=" << CGI::escape(@browser) 589 return url 590 end 591 592 def buildBodyTags(tag_count) 593 tagList = ['body'] 594 # subtract the <body> tag from tag_count. 595 1.upto(tag_count-1) { tagList << @htmlTags[rand(@htmlTags.length)] } 596 597 # Lean ourselves toward lots of img and src tests 598 for tag, percent in @config['favor_html_tags'] 599 if rand(100) < percent.to_f 600 # Don't overwrite the body tag. 601 tagList[rand(tagList.length-1)+1] = tag 602 end 603 end 604 605 # Now we have our hitlist of tags,lets mangle them. 606 mangled_tags = [] 607 tagList.each do |tag| 608 tag_data = mangleTag(tag) 609 if tag == 'script' 610 if rand(100) < 40 611 tag_data = "<script>" 612 end 613 tag_data << buildJavaScript() + "\n" + "</script>\n" 614 elsif tag == 'style' 615 if rand(100) < 40 616 tag_data = "<style>" 617 end 618 tag_data << buildStyleTag() + "\n" + "</style>\n" 619 elsif rand(100) <= 90 620 tag_data << generateGarbageText() << "\n" 621 else 622 tag_data << "\n" 623 end 624 625 if rand(100) <= 33 626 tag_data << "</#{tag}>\n" 627 end 628 mangled_tags << "\n<!-- START #{tag} -->\n" + tag_data + "\n<!-- END #{tag} -->\n" 629 end 630 return mangled_tags 631 end 632 633 def buildHeaderTags(tag_count) 634 valid_head_tags = ['title', 'base', 'link', 'meta'] 635 header_tags = ['html', 'head'] 636 1.upto(tag_count-1) { header_tags << valid_head_tags[rand(valid_head_tags.length)] } 637 header_tags << @htmlTags[rand(@htmlTags.length)] 638 mangled_tags = [] 639 header_tags.each do |tag| 640 mangled_tags << mangleTag(tag, no_close_chance=true) 641 end 642 return mangled_tags 643 end 644 645 def buildSurvivedPage(page_type) 646 page = "<html><head>" 647 page << "<body>Bummer. You survived both redirects. Let me go sulk in the corner.</body>" 648 page << "</html>" 649 return page 650 end 651 652 def buildRedirect(test_num, subtest_data, lookup_mode, stop_num=nil) 653 # no more redirects. 654 if lookup_mode == '1' or stop_num == test_num 655 return '' 656 end 657 658 if subtest_data 659 width, offsets = parseSubTestData(@subtest_data) 660 else 661 width, offsets = nil 662 end 663 664 # We still need a redirect, but don't bother generating new data. 665 if lookup_mode 666 redirect_url = generateTestUrl(test_num, width, offsets) 667 if lookup_mode == 'test_redirect' 668 redirect_url << "&l=test_another_redirect" 669 elsif lookup_mode == 'test_another_redirect' 670 redirect_url << "&l=survived_redirect" 671 else 672 redirect_url << "&l=#{lookup_mode}" 673 end 674 else 675 # This is a normal redirect going on to the next page. If we have subtest, get the next one. 676 if subtest_data 677 width, offsets = combine_combo_creator(@config['html_tags_per_page'], width, offsets)[0..1] 678 end 679 redirect_url = generateTestUrl(nextTestNum(), width, offsets) 680 end 681 682 redirect_code = "\t<META HTTP-EQUIV=\"Refresh\" content=\"0;URL=#{redirect_url}\">\n" 683 # use both techniques, because you never know how you might be corrupting yourself. 684 redirect_code << "\t<script language=\"javascript\">setTimeout('window.location=\"#{redirect_url}\"', 1000);</script>\n" 685 return redirect_code 686 end 687 688 def buildPage() 689 if @lookup_mode == 'survived_redirect' 690 return self.buildSurvivedPage(@lookup_mode) 691 end 692 tag_count = @config['html_tags_per_page'] 693 694 if $TEST_CACHE.has_key?(@test_num) 695 (header_tags, body_tags) = $TEST_CACHE[@test_num] 696 else 697 header_tags = buildHeaderTags(3) 698 body_tags = buildBodyTags(tag_count - header_tags.length) 699 end 700 required_tags = { 701 0 => 'html', 702 1 => 'head', 703 header_tags.length => 'body' 704 } 705 706 if @subtest_data and @subtest_data.length > 0 707 if not $TEST_CACHE.has_key?(@test_num) 708 $TEST_CACHE[@test_num] = [header_tags, body_tags] 709 end 710 (width, offsets) = parseSubTestData(@subtest_data) 711 lines = combine_combo_creator(tag_count, width, offsets)[2] 712 all_tags = header_tags + body_tags 713 body_start = header_tags.length 714 header_tags = [] 715 body_tags = [] 716 # <html> and <body> are required, regardless of their existence in the subtest data. 717 0.upto(tag_count) do |line_number| 718 tag_data = nil 719 if lines.include?(line_number) 720 tag_data = all_tags[line_number] 721 elsif required_tags.key?(line_number) 722 tag_data = "<" + required_tags[line_number] + ">" 723 end 724 if tag_data 725 if line_number < body_start 726 header_tags << tag_data 727 else 728 body_tags << tag_data 729 end 730 end 731 end 732 header_tags << "<!-- subtest mode: #{offsets.length} combinations, width: #{width} -->" 733 end 734 735 htmlText = header_tags[0..1].join("\n\t") 736 htmlText << buildRedirect(@test_num, @subtest_data, @lookup_mode, @stop_num) 737 htmlText << "<title>[#{@test_num}:#{@subtest_data}] iExploder #{$VERSION} - #{generateGarbageText()}</title>\n" 738 if @claimed_browser and @claimed_browser.length > 1 739 show_browser = @claimed_browser 740 else 741 show_browser = @browser 742 end 743 htmlText << "\n<!-- iExploder #{$VERSION} | test #{@test_num}:#{@subtest_data} at #{Time.now} -->\n" 744 htmlText << "<!-- browser: #{show_browser} -->\n" 745 htmlText << header_tags[2..-1].join("\n\t") 746 htmlText << "\n</head>\n\n" 747 htmlText << body_tags.join("\n") 748 htmlText << "</body>\n</html>" 749 return htmlText 750 end 751 752 def buildHeaders(mime_type) 753 use_headers = [] 754 banned_headers = [] 755 response = {'Content-Type' => mime_type} 756 0.upto(rand(@config['headers_per_page_max'])) do 757 try_header = @headerValues[rand(@headerValues.length)] 758 if ! banned_headers.include?(try_header.downcase) 759 use_headers << try_header 760 end 761 end 762 for header in use_headers.uniq 763 if rand(100) > 75 764 response[header] = generateGarbageNumber() 765 else 766 response[header] = generateGarbageUrl(header) 767 end 768 end 769 return response 770 end 771end 772 773 774# for testing 775if $0 == __FILE__ 776 ie = IExploder.new('config.yaml') 777 ie.test_num = ARGV[0].to_i || 1 778 ie.subtest_data = ARGV[1] || nil 779 mime_type = ARGV[2] || nil 780 ie.setRandomSeed() 781 if not mime_type 782 html_output = ie.buildPage() 783 puts html_output 784 else 785 headers = ie.buildHeaders(mime_type) 786 for (key, value) in headers 787 puts "#{key}: #{value}" 788 end 789 puts "Mime-Type: #{mime_type}" 790 puts ie.buildMediaFile(mime_type) 791 end 792end 793