[
  {
    "path": ".circleci/config.yml",
    "content": "version: 2.1\norbs:\n  qlty: qltysh/qlty-orb@0.1\njobs:\n  default: &default\n    docker:\n      - image: cimg/ruby:4.0\n    steps:\n      - checkout\n      - run: ruby --version && bundle check || bundle install\n      - run:\n          command: bundle exec rake\n      - store_test_results:\n          path: test-results\n  test-3-2:\n    <<: *default\n    docker:\n      - image: cimg/ruby:3.2\n    steps:\n      - checkout\n      - attach_workspace:\n          at: ~/repo/tmp\n      - run: bundle check || bundle install\n      - run:\n          name: Run tests and generate coverage\n          command: |\n            # This triggers SimpleCov to generate a coverage.json file\n            export CC_TEST_REPORTER_ID=CC_TEST_REPORTER_ID\n            bundle exec rake RUBYOPT='--enable-frozen-string-literal --debug-frozen-string-literal'\n            mkdir -p tmp/\n            mv coverage/coverage.json tmp/coverage.json\n      - store_test_results:\n          path: test-results\n      - persist_to_workspace:\n          root: tmp\n          paths:\n            - coverage.json\n  test-3-3:\n    <<: *default\n    docker:\n      - image: cimg/ruby:3.3\n  test-3-4:\n    <<: *default\n    docker:\n      - image: cimg/ruby:3.4\n  upload-coverage:\n    <<: *default\n    working_directory: ~/repo\n    steps:\n      - checkout\n      - attach_workspace:\n          at: ~/repo/tmp\n      - qlty/coverage_publish:\n          files: tmp/coverage.json\n          strip_prefix: /home/circleci/project\nworkflows:\n  version: 2\n  tests:\n    jobs:\n      - default\n      - test-3-2\n      - test-3-3\n      - test-3-4\n      - upload-coverage:\n          requires:\n            - test-3-2\n"
  },
  {
    "path": ".dockerignore",
    "content": "# ignore .git and .cache folders\n.git\n.cache\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "content": "---\nname: Bug Report\nabout: Create a report to help us improve\n\n---\n\n### Background\n\nBrakeman version: ?\nRails version: ?\nRuby version: ?\n\nLink to Rails application code: ?\n\n### Issue\n\nWhat problem are you seeing?\n\n#### Other Error\n\nRun Brakeman with `--debug` to see the full stack trace.\n\nStack trace:\n\n```\n?\n```\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "content": "---\nname: Feature Request\nabout: Suggest an idea for this project\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/hanging-or-slow-scans.md",
    "content": "---\nname: Hanging or Slow Scans\nabout: Let us know if Brakeman is too slow\n\n---\n\n### Background\n\nBrakeman version: ?\nRails version: ?\nRuby version: ?\n\nLink to Rails application code: ?\n\n#### Hanging or Slowness\n\n_Consult https://brakemanscanner.org/docs/troubleshooting/hanging/ first_\n\n_Please run Brakeman with `--debug` to see which file may be causing the issue._\n\nCode example:\n\n```ruby\n?\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/parsing-error.md",
    "content": "---\nname: Parsing Error\nabout: Report a parse error\n\n---\n\n### Background\n\nBrakeman version: ?\nRails version: ?\nRuby version: ?\n\nLink to Rails application code: ?\n\n#### Parse Error\n\n(Consult https://brakemanscanner.org/docs/troubleshooting/parse_errors/ first. Note that (most) parsing errors are from the ruby_parser library, not Brakeman itself.)\n\nMinimal example that does not parse:\n\n```ruby\n?\n```\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/report-a-false-positive.md",
    "content": "---\nname: Report a False Positive\nabout: When Brakeman warns about something that may not be a vulnerability\n\n---\n\n### Background\n\nBrakeman version: ?\nRails version: ?\nRuby version: ?\n\nLink to Rails application code: ?\n#### False Positive\n\n*Full* warning from Brakeman: `?`\n\nRelevant code:\n\n```ruby\n?\n```\n\n_Why might this be a false positive?_\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/something-else.md",
    "content": "---\nname: Something Else\nabout: Something not covered by an existing issue type\n\n---\n\n\n"
  },
  {
    "path": ".github/workflows/docker-hub-push.yml",
    "content": "name: docker-hub-push\non:\n  push:\n    tags:\n      - '*'\njobs:\n  docker:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n      - name: Set up QEMU\n        uses: docker/setup-qemu-action@v2\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v2\n      - name: Login to DockerHub\n        uses: docker/login-action@v2\n        with:\n          username: ${{ secrets.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n      - name: Build and push\n        uses: docker/build-push-action@v3\n        with:\n          context: .\n          platforms: linux/amd64, linux/arm64\n          push: true\n          tags: |\n            presidentbeef/brakeman:latest\n            presidentbeef/brakeman:${{ github.ref_name }}\n"
  },
  {
    "path": ".gitignore",
    "content": "Gemfile.lock\ncoverage/\ntest/coverage/\n.bundle\nbundle\n*.gem\n"
  },
  {
    "path": "CHANGES.md",
    "content": "# 8.0.4 - 2026-02-26\n\n* Load 'date' library for `--ensure-latest`\n\n# 8.0.3 - 2026-02-26\n\n* Fix `polymorphic_name` SQLi false positive (Fredrico Franco)\n* Fix logger behavior when loading config files\n* Handle application names with module prefixes\n* Add release age option for `--ensure-latest`\n\n# 8.0.2 - 2026-02-03\n\n* Reline console control should use stderr\n* Fix logger cleanup based method (Imran Iqbal)\n\n# 8.0.1 - 2026-01-29\n\n* Make sure to reset the cursor even when exit code is 0\n\n# 8.0.0 - 2026-01-29\n\n* No longer produce weak dynamic render path warnings\n* `--skip-libs` removed\n* `--index-libs` removed\n* Revamp of scan progress output and logging\n* Faster file globbing for templates (Mikael Henriksson)\n* Fix singleton method prefixes (viralpraxis)\n* Fix qualified constant lookup to respect module/class context (Mike Dalessio)\n* Replace Erubis with Erubi\n\n# 7.1.2 - 2025-12-25\n\n* Update `ruby_parser` to remove version restriction (Chedli Bourguiba)\n* Raise minimum required Ruby to 3.2.0\n* Use Minitest 6.0\n* Reduce SQL injection false positives from `count` calls\n* Ignore more Haml attribute builder methods\n\n# 7.1.1 - 2025-11-03\n\n* Fix false positive when calling `with_content` on ViewComponents (Peer Allan)\n* Word wrap text output in pager\n* Consider Tempfile.create.path as safe input (Ali Ismayilov)\n* Exclude directories before searching for files\n* Check each side of `or` SQL arguments\n* Ignore attribute builder in Haml 6\n* Add `FilePath#to_path` for Ruby 3.5 compatibility (S-H-GAMELINKS)\n* Fix SQL injection check for calculate method (Rohan Sharma)\n* Fix missing `td` in HTML report (John Hawthorn)\n* Check for unsafe SQL when two arguments are passed to AR methods (Patrick Brinich-Langlois)\n\n# 7.1.0 - 2025-07-18\n\n* Add EOL dates for Rails 8.0 and Ruby 3.4\n* Support render model shortcut\n* Use lazy file lists for AppTree\n* Add Haml 6.x support\n* Improve ignored warnings layout in HTML report (Sebastien Savater)\n* Update JUnit report for CircleCI (Philippe Bernery)\n* Only load escape functionality from cgi library (Earlopain)\n* Add `--ensure-no-obsolete-ignore-entries` option (viralpraxis)\n\n# 7.0.2 - 2025-04-04\n\n* Fix error with empty `BUNDLE_GEMFILE` env variable\n\n# 7.0.1 - 2025-04-03\n\n* Avoid warning on evaluation of plain strings\n* Enable use of custom/alternative Gemfiles\n* Fix error on directory with `rb` extension (viralpraxis)\n* Support `terminal-table` 4.0 (Chedli Bourguiba)\n* Better support Prism 1.4.0\n* Only output timing for each file when using `--debug`\n\n# 7.0.0 - 2024-12-30\n\n* Always warn about deserializing from Marshal\n* Output `originalBaseUriIds` for SARIF format report\n* Default to using Prism parser if available (disable with `--no-prism`)\n* Update `terminal-table` version to use latest\n* Update `eval` check to be a little noisier\n* Fix array/hash unknown index handling\n* Disable following symbolic links by default, re-enable with --follow-symlinks\n* Add step (and timing) for finding files\n* Add CSV library as explicit dependency for Ruby 3.4 support\n* Major changes to how rescanning works\n* Raise minimum Ruby version to 3.1\n* Fix hardcoded globally excluded paths\n* Remove updated entry in Brakeman ignore files (Toby Hsieh)\n* Fix recursion when handling multiple assignment expressions\n\n# 6.2.2 - 2024-10-15\n\n* Ignore more native gems when building gem\n* Revamp command injection in `pipeline*` calls\n* New end-of-support dates for Rails\n\n# 6.2.1 - 2024-08-22\n\nJust a packaging fix for brakeman.gem\n\n# 6.2.0 - 2024-08-22\n\n* Add `--show-ignored` option (Gabriel Zayas)\n* Add optional support for Prism parser\n* Warn about unscoped finds with `find_by!`\n* Treat `::X` and `X` the same, for now (Jill Klang)\n* Fix compatibility with default frozen string literals (Jean Boussier)\n* Remediation advice for command injection (Nicholas Barone)\n* Fix Ruby warnings in test suite (Jean Boussier)\n* Support YAML aliases in secret configs (Chedli Bourguiba)\n* Add initial Rails 8 support (Ron Shinall)\n* Handle mass assignment with splats\n* Add support for symbolic links (Lu Zhu)\n\n# 6.1.2 - 2024-02-01\n\n* Update Highline to 3.0\n* Add EOL date for Ruby 3.3.0\n* Avoid copying Sexps that are too large\n* Avoid detecting `ViewComponentContrib::Base` as dynamic render paths (vividmuimui)\n* Remove deprecated use of `Kernel#open(\"|...\")`\n* Remove `safe_yaml` gem dependency\n* Avoid detecting Phlex components as dynamic render paths (Máximo Mussini)\n\n# 6.1.1 - 2023-12-24\n\n* Handle racc as a default gem in Ruby 3.3.0\n\n# 6.1.0 - 2023-12-04\n\n* Add `--timing` to add timing duration for scan steps\n* Fix keyword splats in filter arguments\n* Add check for unfiltered search with Ransack\n* Fix class method lookup in parent classes\n* Handle `class << self`\n* Add `PG::Connection.escape_string` as a SQL sanitization method (Joévin Soulenq)\n\n# 6.0.1 - 2023-07-20\n\n* Accept strings for `load_defaults` version\n\n# 6.0.0 - 2023-05-24\n\n* Add obsolete fingerprints to comparison report\n* Warn about missing CSRF protection when defaults are not loaded (Chris Kruger)\n* Scan directories that include the word `public`\n* Raise minimum Ruby version to 3.0\n* Drop support for Ruby 1.8/1.9 syntax\n* Fix end-of-life dates for Ruby\n* Fix false positive with `content_tag` in newer Rails\n\n# 5.4.1 - 2023-02-21\n\n* Fix file/line location for EOL software warnings\n* Revise checking for request.env to only consider request headers\n* Add `redirect_back` and `redirect_back_or_to` to open redirect check\n* Support Rails 7 redirect options\n* Add Rails 6.1 and 7.0 default configuration values\n* Prevent redirects using `url_from` being marked as unsafe (Lachlan Sylvester)\n* Warn about unscoped find for `find_by(id: ...)`\n* Support `presence`, `presence_in` and `in?`\n* Fix issue with `if` expressions in `when` clauses\n\n# 5.4.0 - 2022-11-17\n\n* Use relative paths for CodeClimate report format (Mike Poage)\n* Add check for weak RSA key sizes and padding modes\n* Handle multiple values and splats in case/when\n* Ignore more model methods in redirects\n* Add check for absolute paths issue with Pathname\n* Fix `load_rails_defaults` overwriting settings in the Rails application (James Gregory-Monk)\n\n# 5.3.1 - 2022-08-09\n\n* Fix version range for CVE-2022-32209\n\n# 5.3.0 - 2022-08-09\n\n* Include explicit engine or lib paths in vendor/ (Joe Rafaniello)\n* Load rexml as a Brakeman dependency\n* Fix \"full call\" information propagating unnecessarily\n* Add check for CVE-2022-32209\n* Add CWE information to warnings (Stephen Aghaulor)\n\n# 5.2.3 - 2022-05-01\n\n* Fix error with hash shorthand syntax\n* Match order of interactive options with help message (Rory O'Kane)\n\n# 5.2.2 - 2022-04-06\n\n* Update `ruby_parser` for Ruby 3.1 support (Merek Skubela)\n* Handle `nil` when joining values (Dan Buettner)\n* Update message for unsafe reflection (Pedro Baracho)\n* Add additional String methods for SQL injection check\n* Respect equality in `if` conditions\n\n# 5.2.1 - 2022-01-30\n\n* Add warning codes for EOL software warnings\n\n# 5.2.0 - 2021-12-15\n\n* Initial Rails 7 support\n* Require Ruby 2.5.0+\n* Fix issue with calls to `foo.root` in routes\n* Ignore `I18n.locale` in SQL queries\n* Do not treat `sanitize_sql_like` as safe\n* Add new checks for unsupported Ruby and Rails versions\n\n# 5.1.2 - 2021-10-28\n\n* Handle cases where enums are not symbols\n* Support newer Haml with ::Haml::AttributeBuilder.build\n* Fix issue where the previous output is still visible (Jason Frey)\n* Fix warning sorting with nil line numbers\n* Update for latest RubyParser (Ryan Davis)\n\n# 5.1.1 - 2021-07-19\n\n* Unrefactor IgnoreConfig's use of `Brakeman::FilePath`\n\n# 5.1.0 - 2021-07-19\n\n* Initial support for ActiveRecord enums\n* Support `Hash#include?`\n* Interprocedural dataflow from very simple class methods\n* Fix SARIF report when checks have no description (Eli Block)\n* Add ignored warnings to SARIF report (Eli Block)\n* Add `--sql-safe-methods` option (Esty Scheiner)\n* Update SQL injection check for Rails 6.0/6.1\n* Fix false positive in command injection with `Open3.capture` (Richard Fitzgerald)\n* Fix infinite loop on mixin self-includes (Andrew Szczepanski)\n* Ignore dates in SQL\n* Refactor `cookie?`/`param?` methods (Keenan Brock)\n* Ignore renderables in dynamic render path check (Brad Parker)\n* Support `Array#push`\n* Better `Array#join` support\n* Adjust copy of `--interactive` menu (Elia Schito)\n* Support `Array#*`\n* Better method definition tracking and lookup\n* Support `Hash#values` and `Hash#values_at`\n* Check for user-controlled evaluation even if it's a call target\n* Support `Array#fetch` and `Hash#fetch`\n* Ignore `sanitize_sql_like` in SQL\n* Ignore method calls on numbers in SQL\n* Add GitHub Actions format (Klaus Badelt)\n* Read and parse files in parallel\n\n# 5.0.4 - 2021-06-08\n\n(brakeman gem release only)\n\n* Update bundled `ruby_parser` to include argument forwarding support\n\n# 5.0.2 - 2021-06-07\n\n* Fix Loofah version check\n\n# 5.0.1 - 2021-04-27\n\n* Detect `::Rails.application.configure` too\n* Set more line numbers on Sexps\n* Support loading `slim/smart`\n* Don't fail if $HOME/$USER are not defined\n* Always ignore slice/only calls for mass assignment\n* Convert splat array arguments to arguments\n\n# 5.0.0 - 2021-01-26\n\n* Ignore `uuid` as a safe attribute\n* Collapse `__send__` calls\n* Ignore `Tempfile#path` in shell commands\n* Ignore development environment\n* Revamp CSV report to a CSV list of warnings\n* Set Rails configuration defaults based on `load_defaults` version\n* Add check for (more) unsafe method reflection\n* Suggest using `--force` if no Rails application is detected\n* Add Sonarqube report format (Adam England)\n* Add check for potential HTTP verb confusion\n* Add `--[no-]skip-vendor` option\n* Scan (almost) all Ruby files in project\n\n# 4.10.1 - 2020-12-24\n\n* Declare REXML as a dependency (Ruby 3.0 compatibility)\n* Use `Sexp#sexp_body` instead of `Sexp#[..]` (Ruby 3.0 compatibility)\n* Prevent render loops when template names are absolute paths\n* Ensure RubyParser is passed file path as a String\n* Support new Haml 5.2.0 escaping method\n\n# 5.0.0.pre1 - 2020-11-17\n\n* Add check for (more) unsafe method reflection\n* Suggest using `--force` if no Rails application is detected\n* Add Sonarqube report format (Adam England)\n* Add check for potential HTTP verb confusion\n* Add `--[no-]skip-vendor` option\n* Scan (almost) all Ruby files in project\n* Add support for Haml 5.2.0\n\n# 4.10.0 - 2020-09-28\n\n* Add SARIF report format (Steve Winton)\n\n# 4.9.1 - 2020-09-04\n\n* Check `chomp`ed strings for SQL injection\n* Use version from `active_record` for non-Rails apps (Ulysse Buonomo)\n* Always set line number for joined arrays\n* Avoid warning about missing `attr_accessible` if `protected_attributes` gem is used\n\n# 4.9.0 - 2020-08-04\n\n* Add check for CVE-2020-8166 (Jamie Finnigan)\n* Avoid warning when `safe_yaml` is used via `YAML.load(..., safe: true)`\n* Add check for user input in `ERB.new` (Matt Hickman)\n* Add `--ensure-ignore-notes` (Eli Block)\n* Remove whitelist/blacklist language, add clarifications\n* Do not warn about mass assignment with `params.permit!.slice`\n* Add \"full call\" information to call index results\n* Ignore `params.permit!` in path helpers\n* Treat `Dir.glob` as safe source of values in guards\n* Always scan `environment.rb`\n\n# 4.8.2 - 2020-05-12\n\n* Add check for CVE-2020-8159\n* Fix `authenticate_or_request_with_http_basic` check for passed blocks (Hugo Corbucci)\n* Add `--text-fields` option\n* Add check for escaping HTML entities in JSON configuration\n\n# 4.8.1 - 2020-04-06\n\n* Check SQL query strings using `String#strip` or `String.squish`\n* Handle non-symbol keys in locals hash for render()\n* Warn about global(!) mass assignment\n* Index calls in render arguments\n\n# 4.8.0 - 2020-02-18 \n\n* Add JUnit-XML report format (Naoki Kimura)\n* Sort ignore files by fingerprint and line (Ngan Pham)\n* Freeze call index results\n* Fix output test when using newer Minitest\n* Properly render confidence in Markdown report\n* Report old warnings as fixed if zero warnings reported\n* Catch dangerous concatenation in `CheckExecute` (Jacob Evelyn)\n* Show user-friendly message when ignore config file has invalid JSON (D. Hicks)\n* Initialize Rails version with `nil` (Carsten Wirth)\n\n# 4.7.2 - 2019-11-25\n\n* Remove version guard for `named_scope` vs. `scope`\n* Find SQL injection in `String#strip_heredoc` target\n* Handle more `permit!` cases\n* Ensure file name is set when processing model\n* Add `request.params` as query parameters\n\n# 4.7.1 - 2019-10-29 \n\n* Check string length against limit before joining\n* Fix errors from frozen `Symbol#to_s` in Ruby 2.7\n* Fix flaky rails4 test (Adam Kiczula)\n* Added release dates to each version in CHANGES (TheSpartan1980)\n* Catch reverse tabnabbing with `:_blank` symbol (Jacob Evelyn)\n* Convert `s(:lambda)` to `s(:call)` in `Sexp#block_call`\n* Sort text report by file and line (Jacob Evelyn)\n\n# 4.7.0 - 2019-10-16\n\n* Refactor `Brakeman::Differ#second_pass` (Benoit Côté-Jodoin)\n* Ignore interpolation in `%W[]`\n* Fix `version_between?` (Andrey Glushkov)\n* Add support for `ruby_parser` 3.14.0\n* Ignore `form_for` for XSS check\n* Update Haml support to Haml 5.x\n* Catch shell injection from `-c` shell commands (Jacob Evelyn)\n* Correctly handle non-symbols in `CheckCookieSerialization` (Phil Turnbull)\n\n# 4.6.1 - 2019-07-24\n\n* Fix Reverse Tabnabbing warning message (Steffen Schildknecht / Jörg Schiller)\n\n# 4.6.0 - 2019-07-23\n\n* Skip calls to `dup`\n* Add reverse tabnabbing check (Linos Giannopoulos)\n* Better handling of gems with no version declared\n* Warn people that Haml 5 is not fully supported (Jared Beck)\n* Avoid warning about file access with `ActiveStorage::Filename#sanitized` (Tejas Bubane)\n* Update loofah version for fixing CVE-2018-8048 (Markus Nölle)\n* Restore `Warning#relative_path`\n* Add check for cookie serialization with Marshal\n* Index calls in initializers\n* Improve template output handling in conditional branches\n* Avoid assigning `nil` line numbers to `Sexp`s\n* Add special warning code for custom checks\n* Add call matching by regular expression\n\n# 4.5.1 - 2019-05-11\n\n* Add `Brakeman::FilePath` to represent file paths\n* Handle trailing comma in block args\n* Properly handle empty partial name\n* Use relative paths for `__FILE__`\n* Convert `!!` calls to boolean value\n* Add optional check for `config.force_ssl`\n* Remove code for Ruby versions prior to 1.9\n* Check `link_to` with block for href XSS\n* Add SQL injection checks for `find_or_create_by` and friends\n* Add deserialization warning for `Oj.load/object_load`\n* Add initial Rails 6 support\n* Add SQL injection checks for `destroy_by`/`delete_by`\n\n# 4.5.0 - 2019-03-16\n\n* Update `ruby_parser`, use `ruby_parser-legacy`\n* More thoroughly handle `Shellwords` escaping\n* Handle non-integer version number comparisons\n* Use `FileParser` in `Scanner` to parse files\n* Add original exception to `Tracker#errors` list\n* Add support for CoffeeScript in Slim templates\n* Improve support for embedded template \"filters\"\n* Remove Sass dependency\n* Set location information in `CheckContentTag`\n* Stop swallowing exceptions in `AliasProcessor`\n* Avoid joining strings with different encodings\n* Handle `**` inside Hash literals\n* Better handling of splat/kwsplat arguments\n* Improve \"user input\" reported for SQL injection\n\n# 4.4.0 - 2019-01-17\n\n* Set default encoding to UTF-8\n* Update to Slim 4.0.1 (Jake Peterson)\n* Update to RubyParser 3.12.0\n* Add rendered template information to render paths\n* Fix trim mode for ERb templates in old Rails versions\n* Fix thread-safety issue in CallIndex\n* Add `--enable` option to enable optional checks\n* Support reading gem versions from gemspecs\n* Support gem versions which are just major.minor (e.g. 3.0)\n* Treat `if not` like `unless`\n* Handle empty `secrets.yml` files (Naoki Kimura)\n* Correctly set `rel=\"noreferrer\"` in HTML reports\n* Avoid warning about command injection when `String#shellescape` and `Shellwords.shelljoin` are used (George Ogata)\n* Add Dockerfile to run Brakeman inside Docker (Ryan Kemper)\n* Trim some unnecessary files from bundled gems\n* Add check for CVE-2018-3760\n* Avoid nils when concatenating arrays\n* Ignore Tempfiles in FileAccess warnings (Christina Koller)\n* Complete overhaul of warning message construction\n* Deadcode and typo fixes found via Coverity\n\n# 4.3.1 - 2018-06-07\n\n* Ignore `Object#freeze`, use the target instead\n* Ignore `foreign_key` calls in SQL\n* Handle `included` calls outside of classes/modules\n* Add `:BRAKEMAN_SAFE_LITERAL` to represent known-safe literals\n* Handle `Array#map` and `Array#each` over literal arrays\n* Use safe literal when accessing literal hash with unknown key\n* Avoid deprecated use of ERB in Ruby 2.6 (Koichi ITO)\n* Allow `symbolize_keys` to be called on `params` in SQL (Jacob Evelyn)\n* Improve handling of conditionals in shell commands (Jacob Evelyn)\n* Fix error when setting line number in implicit renders\n\n# 4.3.0 - 2018-05-11\n\n* Check exec-type calls even if they are targets\n* Convert `Array#join` to string interpolation\n* `BaseCheck#include_interp?` should return first string interpolation\n* Add `--parser-timeout` option\n* Track parent calls in CallIndex\n* Warn about dangerous `link_to` href with `sanitize()`\n* Ignore `params#to_h` and `params#to_hash` in SQL checks\n* Change \"\".freeze to just \"\"\n* Ignore `Process.pid` in system calls\n* Index Kernel#\\` calls even if they are targets\n* Code Climate: omit leading dot from `only_files` (Todd Mazierski)\n* `--color` can be used to force color output\n* Fix reported line numbers for CVE-2018-3741 and CVE-2018-8048\n\n# 4.2.1 - 2018-03-24\n\n* Add warning for CVE-2018-3741\n* Add warning for CVE-2018-8048\n* Scan `app/jobs/` directory\n* Handle `template_exists?` in controllers\n\n# 4.2.0 - 2018-02-22\n\n* Avoid warning about symbol DoS on `Model#attributes`\n* Avoid warning about open redirects with model methods ending with `_path`\n* Avoid warning about command injection with `Shellwords.escape`\n* Use ivars from `initialize` in libraries\n* `Sexp#body=` can accept `:rlist` from `Sexp#body_list`\n* Update RubyParser to 3.11.0\n* Fix multiple assignment of globals\n* Warn about SQL injection in `not`\n* Exclude template folders in `lib/` (kru0096)\n* Handle ERb use of `String#<<` method for Ruby 2.5 (Pocke)\n\n# 4.1.1 - 2017-12-19\n\n* Remove check for use of `permit` with `*_id` keys\n* Avoid duplicate warnings about permitted attributes\n\n# 4.1.0 - 2017-12-14\n\n* Process models as root sexp instead of each sexp\n* Avoid CSRF warning in Rails 5.2 default config\n* Show better location for Sass errors (Andrew Bromwich)\n* Warn about dynamic values in `Arel.sql`\n* Fix `include_paths` for Code Climate engine (Will Fleming)\n* Add check for dangerous keys in `permit`\n* Try to guess options for `less` pager\n* Better processing of op_asgn1 (e.g. x[:y] += 1)\n* Add optional check for divide by zero\n* Remove errors about divide by zero\n* Avoid warning about file access for temp files\n* Do not warn on params.permit with safe values\n* Add Sexp#call_chain\n* Use HTTPS for warning links\n* Handle nested destructuring/multiple assignment\n* Leave results on screen after paging\n* Do not page if results fit on screen\n* Support `app_path` configuration for Code Climate engine (Noah Davis)\n* Refactor Code Climate engine options parsing (Noah Davis)\n* Fix upgrade version for CVE-2016-6316\n\n# 4.0.1 - 2017-09-25\n\n* Disable pager when `CI` environment variable is set\n* Fix output when pager fails\n\n# 4.0.0 - 2017-09-25\n\n* Add simple pager for reports output to terminal\n* Rename \"Cross Site Scripting\" to \"Cross-Site Scripting\" (Paul Tetreau)\n* Rearrange tests a little bit\n* Treat `request.cookies` like `cookies`\n* Treat `fail`/`raise` like early returns\n* Remove reliance on `CONFIDENCE` constant in checks\n* Remove low confidence mass assignment warnings\n* Reduce warnings about XSS in `link_to`\n* \"Plain\" report output is now the default\n* --exit-on-error and --exit-on-warn are now the default\n* Fix --exit-on-error and --exit-on-warn in config files\n\n# 3.7.2 - 2017-08-16\n\n* Fix --ensure-latest (David Guyon)\n\n# 3.7.1 - 2017-08-16\n\n* Handle simple guard with return at end of branch\n* Modularize bin/brakeman\n* Improve multi-value Sexp error message\n* Add more collection methods for iteration detection\n* Update ruby2ruby and ruby_parser\n\n# 3.7.0 - 2017-06-30\n\n* Improve support for rails4/rails5 options in config file\n* Track more information about constant assignments\n* Show progress indicator in interactive mode\n* Handle simple conditional guards that use `return`\n* Fix false positive for redirect_to in Rails 4 (Mário Areias)\n* Avoid interpolating hashes/arrays on failed access\n\n# 3.6.2 - 2017-05-19\n\n* Handle safe call operator in checks\n* Better handling of `if` expressions in HAML rendering\n* Remove `--rake` option\n* Properly handle template names without `.html` or `.js`\n* Set template file names during rendering for better errors\n* Limit Slim dependency to before 3.0.8\n* Catch YAML parsing errors in session settings check\n* Avoid warning about SQLi with `to_s` in `exists?`\n* Update RubyParser to 3.9.0\n* Do not honor additional check paths in config by default\n* Handle empty `if` expressions when finding return values\n* Fix finding return value from empty `if`\n\n# 3.6.1 - 2017-03-24\n\n* Fix error when using `--compare` (Sean Gransee)\n\n# 3.6.0 - 2017-03-23\n\n* Avoid recursive Concerns\n* Branch inside of `case` expressions\n* Print command line option errors without modification\n* Fix issue with nested interpolation inside SQL strings\n* Ignore GraphQL tags inside ERB templates\n* Add `--exit-on-error` (Michael Grosser)\n* Only report CVE-2015-3227 when exact version is known\n* Check targetless SQL calls outside of known models\n\n# 3.5.0 - 2017-02-01\n\n* Allow `-t None`\n* Fail on invalid checks specified by `-x` or `-t`\n* Avoid warning about all, first, or last after Rails 4.0\n* Avoid warning about models in SQLi\n* Lower confidence of SQLi when maybe not on models\n* Warn about SQLi even potentially on non-models\n* Report check name in JSON and plain reports\n* Treat templates without `.html` as HTML anyway\n* Add `--ensure-latest` option (tamgrosser / Michael Grosser)\n* Add `--no-summary` to hide summaries in HTML/text reports\n* Handle `included` block in concerns\n* Process concerns before controllers\n\n# 3.4.1 - 2016-11-02\n\n* Show action help at start of interactive ignore\n* Check CSRF setting in direct subclasses of `ActionController::Base` (Jason Yeo)\n* Configurable engines path (Jason Yeo)\n* Use Ruby version to turn off SymbolDoS check\n* Pull Ruby version from `.ruby-version` or Gemfile\n* Avoid warning about `where_values_hash` in SQLi\n* Fix ignoring link interpolation not at beginning of string\n\n# 3.4.0 - 2016-09-08\n\n* Add new `plain` report format\n* Add option to prune ignore file with `-I`\n* Improved Slim template support\n* Show obsolete ignore entries in reports (Jonathan Cheatham)\n* Support creating reports in non-existent paths\n* Add `--no-exit-warn`\n\n# 3.3.5 - 2016-08-12\n\n* Fix bug in reports when using --debug option\n\n# 3.3.4 - 2016-08-12\n\n* Add generic warning for CVE-2016-6316\n* Warn about dangerous use of `content_tag` with CVE-2016-6316\n* Add warning for CVE-2016-6317\n* Use Minitest\n\n# 3.3.3 - 2016-07-21\n\n* Show path when no Rails app found (Neil Matatall)\n* Index calls in view helpers\n* Process inline template renders\n* Avoid warning about hashes in link_to hrefs\n* Add documentation for authentication category\n* Ignore boolean methods in render paths\n* Reduce open redirect duplicates\n* Fix SymbolDoS error with unknown Rails version\n* Sexp#value returns nil when there is no value\n* Improve return value estimation\n\n# 3.3.2 - 2016-06-10\n\n* Fix serious performance regression with global constant tracking\n\n# 3.3.1 - 2016-06-03\n\n* Delay loading vendored gems and modifying load path\n* Avoid warning about SQL injection with `quoted_primary_key`\n* Support more safe `&.` operations\n* Allow multiple line regex in `validates_format_of` (Dmitrij Fedorenko)\n* Only consider `if` branches in templates\n* Avoid overwriting instance/class methods with same name (Tim Wade)\n* Add `--force-scan` option (Neil Matatall)\n* Improved line number accuracy in ERB templates (Patrick Toomey)\n\n# 3.3.0 - 2016-05-05\n\n* Skip processing obviously false if branches (more broadly)\n* Skip if branches with `Rails.env.test?`\n* Return exit code `4` if no Rails application is detected\n* Avoid warning about mass assignment with `params.slice`\n* Avoid warning about `u` helper (Chad Dollins)\n* Add optional check for secrets in source code\n* Process `Array#first`\n* Allow non-Hash arguments in `protect_from_forgery` (Jason Yeo)\n* Avoid warning on `popen` with array\n* Bundle all dependencies in gem\n* Track constants globally\n* Handle HAML `find_and_preserve` with a block\n* [Code Climate engine] When possible, output to /dev/stdout (Gordon Diggs)\n* [Code Climate engine] Remove nil entries from include_paths (Gordon Diggs)\n* [Code Climate engine] Report end lines for issues (Gordon Diggs)\n\n# 3.2.1 - 2016-02-25\n\n* Remove `multi_json` dependency from `bin/brakeman`\n\n# 3.2.0 - 2016-02-25\n\n* Skip Symbol DoS check on Rails 5\n* Only update ignore config file on changes\n* Sort ignore config file\n* Support calls using `&.` operator\n* Update ruby_parser dependency to 3.8.1\n* Remove `fastercsv` dependency\n* Fix finding calls with `targets: nil`\n* Remove `multi_json` dependency\n* Handle CoffeeScript in HAML\n* Avoid render warnings about params[:action]/params[:controller]\n* Index calls in class bodies but outside methods\n\n# 3.1.5 - 2016-01-28\n\n* Fix CodeClimate construction of --only-files (Will Fleming)\n* Add check for denial of service via routes (CVE-2015-7581)\n* Warn about RCE with `render params` (CVE-2016-0752)\n* Add check for `strip_tags` XSS (CVE-2015-7579)\n* Add check for `sanitize` XSS (CVE-2015-7578/80)\n* Add check for `reject_if` proc bypass (CVE-2015-7577)\n* Add check for mime-type denial of service (CVE-2016-0751)\n* Add check for basic auth timing attack (CVE-2015-7576)\n* Add initial Rails 5 support\n* Check for implicit integer comparison in dynamic finders\n* Support directories better in --only-files and --skip-files (Patrick Toomey)\n* Avoid warning about `permit` in SQL\n* Handle guards using `detect`\n* Avoid warning on user input in comparisons\n* Handle module names with self methods\n* Add session manipulation documentation\n\n# 3.1.4 - 2015-12-22\n\n* Emit brakeman's native fingerprints for Code Climate engine (Noah Davis)\n* Ignore secrets.yml if in .gitignore\n* Clean up Ruby warnings (Andy Waite)\n* Increase test coverage for option parsing (Zander Mackie)\n* Work around safe_yaml error\n\n# 3.1.3 - 2015-12-03\n\n* Check for session secret in secrets.yml\n* Respect `exit_on_warn` in config file\n* Avoid warning on `without_protection: true` with hash literals\n* Make sure before_filter call with block is still a call\n* CallIndex improvements\n* Restore minimum Highline version (Kevin Glowacz)\n* Add Code Climate output format (Ashley Baldwin-Hunter/Devon Blandin/John Pignata/Michael Bernstein)\n* Iteratively replace values\n* Output nil instead of false for user_input in JSON\n* Depend on safe_yaml 1.0 or later\n* Test coverage improvements for Brakema module (Bethany Rentz)\n\n# 3.1.2 - 2015-10-28\n\n* Treat `current_user` like a model\n* Set user input value for inline renders\n* Avoid warning on inline renders with safe content types\n* Handle empty interpolation in HAML filters\n* Ignore filters that are not method names\n* Avoid warning about model find/find_by* in hrefs\n* Use SafeYAML to load configuration files\n* Warn on SQL query keys, not values in hashes\n* Allow inspection of recursive Sexps\n* Add line numbers to class-level warnings\n* Handle `private def ...`\n* Catch divide-by-zero in alias processing\n* Reduce string allocations in Warning#initialize\n* Sortable tables in HTML report (David Lanner)\n* Search for config file relative to application root\n\n# 3.1.1 - 2015-09-23\n\n* Add optional check for use of MD5 and SHA1\n* Avoid warning when linking to decorated models\n* Add check for user input in session keys\n* Fix chained assignment\n* Treat a.try(&:b) like a.b()\n* Consider j/escape_javascript safe inside HAML JavaScript blocks\n* Better HAML processing of find_and_preserve calls\n* Add more Arel methods to be ignored in SQL\n* Fix absolute paths for Windows (Cody Frederick)\n* Support newer terminal-table releases\n* Allow searching call index methods by regex (Alex Ianus)\n\n# 3.1.0 - 2015-08-31\n\n* Add support for gems.rb/gems.locked\n* Update render path information in JSON reports\n* Remove renaming of several Sexp nodes\n* Convert YAML config keys to symbols (Karl Glaser)\n* Use railties version if rails gem is missing (Lucas Mazza)\n* Warn about unverified SSL mode in Net::HTTP.start\n* Add Model, Controller, Template, Config classes internally\n* Report file being parsed in debug output\n* Update dependencies to Ruby 1.8 incompatible versions\n* Treat Array.new and Hash.new as arrays/hashes\n* Fix handling of string concatenation with existing string\n* Treat html_safe like raw()\n* Fix low confidence XSS warning code\n* Avoid warning on path creation methods in link_to\n* Expand safe methods to match methods with targets\n* Avoid duplicate eval() warnings\n\n# 3.0.5 - 2015-06-20\n\n* Fix check for CVE-2015-3227\n\n# 3.0.4 - 2015-06-18\n\n* Add check for CVE-2015-3226 (XSS via JSON keys)\n* Add check for CVE-2015-3227 (XML DoS)\n* Treat `<%==` as unescaped output\n* Update `ruby_parser` dependency to 3.7.0\n\n# 3.0.3 - 2015-04-20\n\n* Ignore more Arel methods in SQL\n* Warn about protect_from_forgery without exceptions (Neil Matatall)\n* Handle lambdas as filters\n* Ignore quoted_table_name in SQL (Gabriel Sobrinho)\n* Warn about RCE and file access with `open`\n* Handle array include? guard conditionals\n* Do not ignore targets of `to_s` in SQL\n* Add Rake task to exit with error code on warnings (masarakki)\n\n# 3.0.2 - 2015-03-09\n\n* Alias process methods called in class scope on models\n* Treat primary_key, table_name_prefix, table_name_suffix as safe in SQL\n* Fix using --compare and --add-checks-path together\n* Avoid warning about mass assignment with string literals\n* Only report original regex DoS locations\n* Improve render path information implementation\n* Report correct file for simple_format usage CVE warning\n* Remove URI.escape from HTML reports with GitHub repos\n* Update ruby_parser to ~> 3.6.2\n* Remove formatting newlines in HAML template output\n* Ignore case value in XSS checks\n* Fix CSV output when there are no warnings\n* Handle processing of explicitly shadowed block arguments\n\n# 3.0.1 - 2015-01-23\n\n* Avoid protect_from_forgery warning unless ApplicationController inherits from ActionController::Base\n* Properly format command interpolation (again)\n* Remove Slim dependency (Casey West)\n* Allow for controllers/models/templates in directories under `app/` (Neal Harris)\n* Add `--add-libs-path` for additional libraries (Patrick Toomey)\n* Properly process libraries (Patrick Toomey)\n\n# 3.0.0 - 2015-01-03\n\n* Add check for CVE-2014-7829\n* Add check for cross-site scripting via inline renders\n* Fix formatting of command interpolation\n* Local variables are no longer formatted as `(local var)`\n* Actually skip skipped before filters\n* `--exit-on-warn --compare` only returns error code on new warnings (Jeff Yip)\n* Fix parsing of `<%==` in ERB\n* Sort warnings by fingerprint in JSON report (Jeff Yip)\n* Handle symmetric multiple assignment\n* Do not branch for self attribute assignment `x = x.y`\n* Fix CVE for CVE-2011-2932\n* Remove \"fake filters\" from warning fingerpints\n* Index calls in `lib/` files\n* Move Symbol DoS to optional checks\n* CVEs report correct line and file name (Gemfile/Gemfile.lock) (Rob Fletcher)\n* Change `--separate-models` to be the default\n\n# 2.6.3 - 2014-10-14\n\n* Whitelist `exists` arel method from SQL injection check\n* Avoid warning about Symbol DoS on safe parameters as method targets\n* Fix stack overflow in ProcessHelper#class_name\n* Add optional check for unscoped find queries (Ben Toews)\n* Add framework for optional checks\n* Fix stack overflow for cycles in class ancestors (Jeff Rafter)\n\n# 2.6.2 - 2014-08-18\n\n* Add check for CVE-2014-3415\n* Avoid warning about symbolizing safe parameters\n* Update ruby2ruby dependency to 2.1.1\n* Expand app path in one place instead of all over (Jeff Rafter)\n* Add `--add-checks-path` option for external checks (Clint Gibler)\n* Fix SQL injection detection in deep nested string building\n* Add `-4` option to force Rails 4 mode\n* Check entire call for `send`\n* Check for .gitignore of secrets in subdirectories\n* Fix block statement endings in Erubis\n* Fix undefined variable in controller processing error (Jason Barnabe) \n\n# 2.6.1 - 2014-07-02\n\n* Add check for CVE-2014-3482 and CVE-2014-3483\n* Add support for keyword arguments in blocks\n* Remove unused warning codes (Bill Fischer)\n\n# 2.6.0 - 2014-06-06\n\n* Fix detection of `:host` setting in redirects with chained calls\n* Add check for CVE-2014-0130\n* Add `find_by`/`find_by!` to SQLi check for Rails 4\n* Parse most files upfront instead of on demand\n* Do not branch values for `+=`\n* Update to use RubyParser 3.5.0 (Patrick Toomey)\n* Improve default route detection in Rails 3/4 (Jeff Jarmoc)\n* Handle controllers and models split across files (Patrick Toomey)\n* Fix handling of `protected_attributes` gem in Rails 4 (Geoffrey Hichborn)\n* Ignore more model methods in redirects\n* Fix CheckRender with nested render calls\n\n# 2.5.0 - 2014-04-30\n\n * Add support for RailsLTS 2.3.18.7 and 2.3.18.8\n * Add support for Rails 4 `before_actions` and friends\n * Move SQLi CVE checks to `CheckSQLCVEs`\n * Check for protected_attributes gem\n * Fix SQLi detection in chain calls in scopes\n * Add GitHub-flavored Markdown output format (Greg Ose)\n * Fix false positives when sanitize() is used in SQL (Jeff Yip)\n * Add String#intern and Hash#symbolize_keys DoS check (Jan Rusnacko)\n * Check all arguments in Model.select for SQLi\n * Fix false positive when :host is specified in redirect\n * Handle more non-literals in routes\n * Add check for regex denial of service (Ben Toews) \n\n# 2.4.3 - 2014-03-23\n\n No changes. 2.4.2 gem release was unsigned, 2.4.3 is signed.\n\n# 2.4.2 - 2014-03-21\n\n * Remove `rescue Exception`\n * Fix duplicate warnings about sanitize CVE\n * Reuse duplicate call location information\n * Only track original template output locations\n * Skip identically rendered templates\n * Fix HAML template processing\n\n# 2.4.1 - 2014-02-19\n\n * Add check for CVE-2014-0082\n * Add check for CVE-2014-0081, replaces CVE-2013-6415\n * Add check for CVE-2014-0080\n\n# 2.4.0 - 2014-02-05\n\n * Detect Rails LTS versions\n * Reduce false positives for SQL injection in string building\n * More accurate user input marking for SQL injection warnings\n * Detect SQL injection in `delete_all`/`destroy_all`\n * Detect SQL injection raw SQL queries using `connection`\n * Parse exact versions from Gemfile.lock for all gems\n * Ignore generators\n * Update to RubyParser 3.4.0\n * Fix false positives when SQL methods are not called on AR models (Aaron Bedra)\n * Add check for uses of OpenSSL::SSL::VERIFY_NONE (Aaron Bedra)\n * No longer raise exceptions if a class name cannot be determined\n * Fingerprint attribute warnings individually (Case Taintor)\n\n# 2.3.1 - 2013-12-13\n\n * Fix check for CVE-2013-4491 (i18n XSS) to detect workaround\n * Fix link for CVE-2013-6415 (number_to_currency)\n\n# 2.3.0 - 2013-12-12\n\n * Add check for Parameters#permit!\n * Add check for CVE-2013-4491 (i18n XSS)\n * Add check for CVE-2013-6414 (header DoS)\n * Add check for CVE-2013-6415 (number_to_currency)\n * Add check for CVE-2013-6416 (simple_format XSS)\n * Add check for CVE-2013-6417 (query generation) \n * Fix typos in reflection and translate bug messages\n * Collapse send/try calls \n * Fix Slim XSS false positives (Noah Davis)\n * Whitelist `Model#create` for redirects\n * Fix scoping issues with instance variables and blocks\n\n# 2.2.0 - 2013-10-28\n\n * Reduce command injection false positives\n * Use Rails version from Gemfile if it is available\n * Only add routes with actual names\n * Ignore redirects to models using friendly_id (AJ Ostrow) \n * Support scanning Rails engines (Geoffrey Hichborn)\n * Add check for detailed exceptions in production\n\n# 2.1.2 - 2013-09-18\n\n * Do not attempt to load custom Haml filters\n * Do not warn about `to_json` XSS in Rails 4\n * Add --table-width option to set width of text reports (ssendev)\n * Remove fuzzy matching on dangerous attr_accessible values\n\n# 2.1.1 - 2013-08-21\n\n * New warning code for dangerous attributes in attr_accessible\n * Do not warn on attr_accessible using roles\n * More accurate results for model attribute warnings\n * Use exit code zero with `-z` if all warnings ignored\n * Respect ignored warnings in rescans\n * Ignore dynamic controller names in routes\n * Fix infinite loop when run as rake task (Matthew Shanley)\n * Respect ignored warnings in tabs format reports\n\n# 2.1.0 - 2013-07-17\n\n * Support non-native line endings in Gemfile.lock (Paul Deardorff)\n * Support for ignoring warnings\n * Check for dangerous model attributes defined in attr_accessible (Paul Deardorff)\n * Update to ruby_parser 3.2.2\n * Add brakeman-min gemspec\n * Load gem dependencies on-demand \n * Output JSON diff to file if -o option is used \n * Add check for authenticate_or_request_with_http_basic\n * Refactor of SQL injection check code (Bart ten Brinke)\n * Fix detection of duplicate XSS warnings\n * Refactor reports into separate classes \n * Allow use of Slim 2.x (Ian Zabel) \n * Return error exit code when application path is not found\n * Add `--branch-limit` option, limit to 5 by default\n * Add more methods to check for command injection\n * Fix output format detection to be more strict again\n * Allow empty Brakeman configuration file\n\n# 2.0.0 - 2013-05-20\n  \n * Add `--only-files` option to specify files/paths to scan (Ian Ehlert)\n * Add Marshal/CSV deserialization check\n * Combine deserialization checks into single check\n * Avoid duplicate \"Dangerous Send\" and \"Unsafe Reflection\" warnings\n * Avoid duplicate results for Symbol DoS check\n * Medium confidence for mass assignment to attr_protected models\n * Remove \"timestamp\" key from JSON reports\n * Remove deprecated config file locations\n * Relative paths are used by default in JSON reports\n * `--absolute-paths` replaces `--relative-paths`\n * Only treat classes with names containing `Controller` like controllers\n * Better handling of classes nested inside controllers\n * Better handling of controller classes nested in classes/modules\n * Handle `->` lambdas with no arguments\n * Handle explicit block argument destructuring\n * Skip Rails config options that are real objects\n * Detect Rails 3 JSON escape config option\n * Much better tracking of warning file names\n * Fix errors when using `--separate-models` (Noah Davis)\n * Fix fingerprint generation to actually use the file path\n * Fix text report console output in JRuby\n * Fix false positives on `Model#id`\n * Fix false positives on `params.to_json`\n * Fix model path guesses to use \"models/\" instead of \"controllers/\"\n * Clean up SQL CVE warning messages\n * Use exceptions instead of abort in brakeman lib\n * Update to Ruby2Ruby 2.0.5\n\n# 1.9.5 - 2013-04-05\n\n * Add check for unsafe symbol creation\n * Do not warn on mass assignment with `slice`/`only`\n * Do not warn on session secret if in `.gitignore`\n * Fix scoping for blocks and block arguments\n * Fix error when modifying blocks in templates\n * Fix session secret check for Rails 4\n * Fix crash on `before_filter` outside controller\n * Fix `Sexp` hash cache invalidation\n * Respect `quiet` option in configuration file\n * Convert assignment to simple `if` expressions to `or`\n * More fixes for assignments inside branches\n * Pin to ruby2ruby version 2.0.3\n\n# 1.9.4 - 2013-03-19\n \n * Add check for CVE-2013-1854\n * Add check for CVE-2013-1855\n * Add check for CVE-2013-1856\n * Add check for CVE-2013-1857\n * Fix `--compare` to work with older versions\n * Add \"no-referrer' to HTML report links\n * Don't warn when invoking `send` on user input\n * Slightly faster cloning of Sexps\n * Detect another way to add `strong_parameters`\n\n# 1.9.3 - 2013-03-01\n \n * Add render path to JSON report\n * Add warning fingerprints\n * Add check for unsafe reflection (Gabriel Quadros)\n * Add check for skipping authentication methods with blacklist\n * Add support for Slim templates\n * Remove empty tables from reports (Owen Ben Davies)\n * Handle `prepend/append_before_filter`\n * Performance improvements when handling branches\n * Fix processing of `production.rb`\n * Fix version check for Ruby 2.0\n * Expand HAML dependency to include 4.0\n * Scroll errors into view when expanding in HTML report\n\n# 1.9.2 - 2013-02-14\n\n * Add check for CVE-2013-0269\n * Add check for CVE-2013-0276\n * Add check for CVE-2013-0277\n * Add check for CVE-2013-0333\n * Check for more send-like methods\n * Check for more SQL injection locations\n * Check for more dangerous YAML methods\n * Support MultiJSON 1.2 for Rails 3.0 and 3.1\n\n# 1.9.1 - 2013-01-19\n\n * Update to RubyParser 3.1.1 (neersighted)\n * Remove ActiveSupport dependency (Neil Matatall)\n * Do not warn on arrays passed to `link_to` (Neil Matatall)\n * Warn on secret tokens\n * Warn on more mass assignment methods\n * Add check for CVE-2012-5664\n * Add check for CVE-2013-0155\n * Add check for CVE-2013-0156\n * Add check for unsafe `YAML.load`\n\n# 1.9.0 - 2012-12-25\n\n * Update to RubyParser 3\n * Ignore route information by default\n * Support `strong_parameters`\n * Support newer `validates :format` call\n * Add scan time to reports\n * Add Brakeman version to reports\n * Fix `CheckExecute` to warn on all string interpolation\n * Fix false positive on `to_sql` calls\n * Don't mangle whitespace in JSON code formatting\n * Add AppTree as facade for filesystem (brynary)\n * Add link for translate vulnerability warning (grosser)\n * Rename LICENSE to MIT-LICENSE, remove from README (grosser)\n * Add Rakefile to run tests (grosser)\n * Better default config file locations (grosser)\n * Reduce Sexp creation\n * Handle empty model files\n * Remove \"find by regex\" feature from `CallIndex`\n\n# 1.8.3 - 2012-11-13\n\n * Use `multi_json` gem for better harmony\n * Performance improvement for call indexing\n * Fix issue with processing HAML files\n * Handle pre-release versions when processing `Gemfile.lock`\n * Only check first argument of `redirect_to`\n * Fix false positives from `Model.arel_table` accesses\n * Fix false positives on redirects to models decorated with Draper gem\n * Fix false positive on redirect to model association\n * Fix false positive on `YAML.load`\n * Fix false positive XSS on any `to_i` output\n * Fix error on Rails 2 name routes with no args\n * Fix error in rescan of mixins with symbols in method name\n * Do not rescan non-Ruby files in config/\n\n# 1.8.2 - 2012-10-17\n\n * Fixed rescanning problems caused by 1.8.0 changes\n * Fix scope calls with single argument\n * Report specific model name in rendered collections\n * Handle overwritten JSON escape settings\n * Much improved test coverage\n * Add CHANGES to gemspec\n\n# 1.8.1 - 2012-09-24\n\n * Recover from errors in output formatting\n * Fix false positive in redirect_to (Neil Matatall)\n * Fix problems with removal of `Sexp#method_missing`\n * Fix array indexing in alias processing\n * Fix old mail_to vulnerability check\n * Fix rescans when only controller action changes\n * Allow comparison of versions with unequal lengths\n * Handle super calls with blocks\n * Respect `-q` flag for \"Rails 3 detected\" message\n\n# 1.8.0 - 2012-09-05\n\n * Support relative paths in reports (fsword)\n * Allow Brakeman to be run without tty (fsword)\n * Fix exit code with `--compare` (fsword)\n * Fix `--rake` option (Deepak Kumar)\n * Add high confidence warnings for `to_json` XSS (Neil Matatall)\n * Fix `redirect_to` false negative\n * Fix duplicate warnings with `raw` calls\n * Fix shadowing of rendered partials\n * Add \"render chain\" to HTML reports\n * Add check for XSS in `content_tag`\n * Add full backtrace for errors in debug mode\n * Treat model attributes in `or` expressions as immediate values\n * Switch to method access for Sexp nodes\n\n# 1.7.1 - 2012-08-13\n\n * Add check for CVE-2012-3463\n * Add check for CVE-2012-3464\n * Add check for CVE-2012-3465\n * Add charset to HTML report (hooopo)\n * Report XSS in select() for Rails 2\n\n# 1.7.0 - 2012-07-31\n\n * Add check for CVE-2012-3424\n * Link report types to descriptions on website\n * Report errors raised while running check\n * Improve processing of Rails 3 routes\n * Fix \"empty char-class\" error\n * Improve file access check\n * Avoid warning on non-ActiveModel models\n * Speed improvements by stripping down SexpProcessor\n * Fix how `params[:x] ||=` is handled\n * Treat user input in `or` expressions as immediate values\n * Fix processing of negative array indexes\n * Add line breaks to truncated table rows\n\n# 1.6.2 - 2012-06-13\n\n * Add checks for CVE-2012-2660, CVE-2012-2661, CVE-2012-2694, CVE-2012-2695 (Dave Worth)\n * Avoid warning when redirecting to a model instance\n * Add `request.parameters` as a parameters hash\n * Raise confidence level for model attributes in redirects\n * Return non-zero exit code when missing dependencies\n * Fix `before_filter :except` logic\n * Only accept symbol literals as before_filter names\n * Cache before_filter lookups\n * Turn off quiet mode by default for `--compare`\n\n# 1.6.1 - 2012-05-23\n\n * Major rewrite of CheckSQL\n * Fix rescanning of deleted templates\n * Process actions mixed into controllers\n * Handle `render :template => ...`\n * Check for inherited attr_accessible (Neil Matatall)\n * Fix highlighting of HTML escaped values in HTML report\n * Report line number of highlighted value, if available\n\n# 1.6.0 - 2012-04-20\n\n * Remove the Ruport dependency (Neil Matatall)\n * Add more informational JSON output (Neil Matatall)\n * Add comparison to previous JSON report (Neil Matatall)\n * Add highlighting of dangerous values in HTML/text reports\n * Model#update_attribute should not raise mass assignment warning (Dave Worth)\n * Don't check `find_by_*` method for SQL injection\n * Fix duplicate reporting of mass assignment and SQL injection\n * Fix rescanning of deleted files \n * Properly check for rails_xss in Gemfile\n\n# 1.5.3 - 2012-04-10\n\n * Add check for user input in Object#send (Neil Matatall)\n * Handle render :layout in views\n * Support output to multiple formats (Nick Green)\n * Prevent infinite loops in mutually recursive templates\n * Only check eval arguments for user input, not targets\n * Search subdirectories for models\n * Set values in request hashes and propagate to views\n * Add rake task file to gemspec (Anton Ageev)\n * Filter rescanning of templates (Neil Matatall)\n * Improve handling of modules and nesting\n * Test for zero errors in test reports\n\n# 1.5.2 - 2012-03-22\n\n * Fix link_to checks for Rails 2.0 and 2.3\n * Fix rescanning of lib files (Neil Matatall)\n * Output stack trace on interrupt when debugging\n * Ignore user input in if statement conditions\n * Fix --skip-files option\n * Only warn on user input in render paths\n * Fix handling of views when using rails_xss\n * Revert to ruby_parser 2.3.1 for Ruby 1.8 parsing\n\n# 1.5.1- 2012-03-06\n\n * Fix detection of global mass assignment setting\n * Fix partial rendering in Rails 3\n * Show backtrace when interrupt received (Ruby 1.9 only)\n * More debug output\n * Remove duplicate method in Brakeman::Rails2XSSErubis\n * Add tracking of module and class to Brakeman::BaseProcessor\n * Report module when using Brakeman::FindCall\n\n# 1.5.0 - 2012-03-02\n\n * Add version check for SafeBuffer vulnerability\n * Add check for select vulnerability in Rails 3\n * select() is no longer considered safe in Rails 2\n * Add check for skipping CSRF protection with a blacklist\n * Add JSON report format\n * Model#id should not be considered XSS\n * Standardize methods to check for SQL injection\n * Fix Rails 2 route parsing issue with nested routes\n\n# 1.4.0 - 2012-02-24\n\n * Add check for user input in link_to href parameter\n * Match ERB processing to rails_xss plugin when plugin used\n * Add Brakeman::Report#to_json, Brakeman::Warning#to_json\n * Warnings below minimum confidence are dropped completely\n * Brakeman.run always returns a Tracker\n\n# 1.3.0 - 2012-02-09\n\n * Add file paths to HTML report\n * Add caching of filters\n * Add --skip-files option\n * Add support for attr_protected\n * Add detection of request.env as user input\n * Descriptions of checks in -k output\n * Improved processing of named scopes\n * Check for mass assignment in ActiveRecord::Associations::AssociationCollection#build\n * Better variable substitution\n * Table output option for rescan reports\n\n# 1.2.2 - 2012-01-26\n\n * --no-progress works again\n * Make CheckLinkTo a separate check\n * Don't fail on unknown options to resource(s)\n * Handle empty resource(s) blocks\n * Add RescanReport#existing_warnings\n\n## 1.2.1 - 2012-01-20\n\n * Remove link_to warning for Rails 3.x or when using rails_xss\n * Don't warn if first argument to link_to is escaped\n * Detect usage of attr_accessible with no arguments\n * Fix error when rendering a partial from a view but not through a controller\n * Fix some issues with rails_xss, CheckCrossSiteScripting, and CheckTranslateBug\n * Simplify Brakeman Rake task\n * Avoid modifying $VERBOSE\n * Add Brakeman::RescanReport#to_s\n * Add Brakeman::Warning#to_s\n\n## 1.2.0 - 2012-01-14\n\n * Speed improvements for CheckExecute and CheckRender\n * Check named_scope() and scope() for SQL injection\n * Add --rake option to create rake task to run Brakeman\n * Add experimental support for rescanning a subset of files\n * Add --summary option to only output summary\n * Fix a problem with Rails 3 routes\n\n## 1.1.0 - 2011-12-22\n\n * Relax required versions for dependencies\n * Performance improvements for source processing\n * Better progress reporting\n * Handle basic operators like << + - * /\n * Rescue more errors to prevent complete crashes\n * Compatibility with newer Haml versions\n * Fix some warnings\n\n## 1.0.0 - 2011-12-08\n\n * Better handling of assignments inside ifs\n * Check more expressions for SQL injection\n * Use latest ruby_parser for better 1.9 syntax support\n * Better behavior for Brakeman as a library\n\n## 1.0.0rc1 - 2011-12-06\n\n * Brakeman can now be used as a library\n * Faster call search\n * Add option to return error code if warnings are found (tw-ngreen)\n * Allow truncated messages to be expanded in HTML\n * Fix summary when using warning thresholds\n * Better support for Rails 3 routes\n * Reduce SQL injection duplicate warnings\n * Lower confidence on mass assignment with no user input\n * Ignore mass assignment using all literal arguments\n * Keep expanded context in view with HTML output\n\n## 0.9.2 - 2011-11-22\n\n * Fix Rails 3 configuration parsing\n * Add t() helper to check for translate XSS bug\n\n## 0.9.1 - 2011-11-18\n\n * Add warning for translator helper XSS vulnerability\n\n## 0.9.0 - 2011-11-17\n\n * Process Rails 3 configuration files\n * Fix CSV output\n * Check for config.active_record.whitelist_attributes = true\n * Always produce a warning for without_protection => true\n\n## 0.8.4 - 2011-11-04\n\n * Option for separate attr_accessible warnings\n * Option to set CSS file for HTML output\n * Add file names for version-specific warnings\n * Add line number for default routes in a controller\n * Fix hash_insert()\n * Remove use of Queue from threaded checks\n\n## 0.8.3 - 2011-10-25\n \n * Respect -w flag in .tabs format (tw-ngreen)\n * Escape HTML output of error messages\n * Add --skip-libs option\n\n## 0.8.2 - 2011-10-01\n\n * Run checks in parallel threads by default\n * Fix compatibility with ruby_parser 2.3.1\n\n## 0.8.1 - 2011-09-28\n\n * Add option to assume all controller methods are actions\n * Recover from errors when parsing routes\n\n## 0.8.0 - 2011-09-15\n\n * Add check for mass assignment using without_protection\n * Add check for password in http_basic_authenticate_with\n * Warn on user input in hash argument with mass assignment\n * auto_link is now considered safe for Rails >= 3.0.6\n * Output detected Rails version in report\n * Keep track of methods called in class definition\n * Add ruby_parser hack for Ruby 1.9 hash syntax\n * Add a few Rails 3.1 tests\n\n## 0.7.2 - 2011-08-27\n\n * Fix handling of params and cookies with nested access\n * Add CVEs for checks added in 0.7.0\n\n## 0.7.1 - 2011-08-18\n\n * Require BaseProcessor for GemProcessor\n\n## 0.7.0 - 2011-08-17\n\n * Allow local variable as a class name\n * Add checks for vulnerabilities fixed in Rails 2.3.14 and 3.0.10\n * Check for default routes in Rails 3 apps\n * Look in Gemfile or Gemfile.lock for Rails version\n\n## 0.6.1 - 2011-07-29\n\n * Fix XSS check for cookies as parameters in output\n * Don't bother calling super in CheckSessionSettings\n * Add escape_once as a safe method\n * Accept '\\Z' or '\\z' in model validations\n\n## 0.6.0 - 2011-07-20\n\n * Tests are in place and fully functional\n * Hide errors by default in HTML output\n * Warn if routes.rb cannot be found\n * Narrow methods assumed to be file access\n * Increase confidence for methods known to not escape output\n * Fixes to output processing for Erubis\n * Fixes for Rails 3 XSS checks\n * Fixes to line numbers with Erubis\n * Fixes to escaped output scanning\n * Update CSRF CVE-2011-0447 message to be less assertive\n\n## 0.5.2 - 2011-06-29\n\n * Output report file name when finished\n * Add initial tests for Rails 2.x\n * Fix ERB line numbers when using Ruby 1.9\n\n## 0.5.1 - 2011-06-17\n\n * Fix issue with 'has_one' => in routes\n\n## 0.5.0 - 2011-06-08\n\n  * Add support for routes like get 'x/y', :to => 'ctrlr#whatever'\n  * Allow empty blocks in Rails 3 routes\n  * Check initializer for session settings\n  * Add line numbers to session setting warnings\n  * Add --checks option to list checks\n\n## 0.4.1 - 2011-05-23\n  \n  * Fix reported line numbers when using new Erubis parser\n    (Mostly affects Rails 3 apps)\n\n## 0.4.0 - 2011-05-19\n\n  * Handle Rails XSS protection properly\n  * More detection options for rails_xss\n  * Add --escape-html option \n\n## 0.3.2 - 2011-05-12  \n\n  * Autodetect Rails 3 applications\n  * Turn on auto-escaping for Rails 3 apps\n  * Check Model.create() for mass assignment\n\n## 0.3.1 - 2011-05-03\n\n  * Always output a line number in tabbed output format\n  * Restrict characters in category name in tabbed output format to\n    word characters and spaces, for Hudson/Jenkins plugin\n\n## 0.3.0 - 2011-03-21\n\n  * Check for SQL injection in calls using constantize()\n  * Check for SQL injection in calls to count_by_sql()\n\n## 0.2.2 - 2011-02-22\n\n  * Fix version_between? when no Rails version is specified\n\n## 0.2.1 - 2011-02-18\n\n  * Add code snippet to tab output messages\n\n## 0.2.0 - 2011-02-16\n\n  * Add check for mail_to vulnerability - CVE-2011-0446\n  * Add check for CSRF weakness - CVE-2011-0447\n\n## 0.1.1 - 2011-01-25\n\n  * Be more permissive with ActiveSupport version\n\n## 0.1.0 - 2011-01-18\n\n  * Check link_to for XSS (because arguments are not escaped)\n  * Process layouts better (although not perfectly yet)\n  * Load custom Haml filters if they are in lib/\n  * Tab separated output via .tabs output extension\n  * Switch to normal versioning scheme\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at conduct@brakeman.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## Copyright Assignment\n\nBy opening a pull request to https://github.com/presidentbeef/brakeman,\nyou agree to assign all rights to the code to Synopsys, Inc. under the [Brakeman Public Use License](LICENSE.md).\n\n## Submitting a Pull Request\n\nPull requests are welcome!\n\nPlease follow the typical GitHub flow:\n\n* Fork Brakeman\n* Clone locally `git clone your_new_fork`\n* Create a new branch `git checkout -b fix_some_broken_stuff`\n* Add new tests\n* Make fixes, follow coding conventions of project\n* Run tests with `ruby test/test.rb` or just `rake` \n* Push your changes `git push origin fix_some_broken_stuff`\n* Go to *your* fork, click \"Submit pull request\"\n* Provide a description of the bug and fix\n* Submit!\n\n### Code Conventions\n\nThese are some code conventions to follow so your code fits into the rest of Brakeman.\n\n* Must use typical Ruby 2 space indentation\n* Must work with Ruby 2.4.0\n* Prefer to wrap lines near 80 characters but it's not a hard rule\n\n### Preparing Tests\n\nTests are very important to ensure fixes actually work, and to make it obvious what your changes are supposed to fix. They also protect against breaking features in the future.\n\n#### Run Tests\n\nTo run Brakeman tests:\n\n    ruby test/test.rb\n\nor\n\n    rake\n\nTo check test coverage, install `simplecov` before running tests. Then open `coverage/index.html` in a browser. For a correct report, run the tests from the root directory.\n\n#### Add a Test Case\n\nBrakeman has several Rails applications in the `test/apps` directory. Choose the one that best matches your situation and modify it to reproduce the issue. It is preferable to modify the application in such a way that the fewest existing tests are broken. In particular, the tests for \"expected number of reported warnings\" will probably change, but no other tests should. Unless the tests or expected behavior are broken.\n\nIn the `test/tests` directory, each application has its own set of tests. Most of these consist of `assert_warning` or `assert_no_warning`, which test for warnings generated by Brakeman.\n\nWhen adding a test for a false positive, use `assert_no_warning` so the expected behavior is clear.\n\n#### Generating Tests\n\nWriting the `assert_warning` tests can be tedious, especially in bulk. There is a tool which will convert Brakeman reports to tests in `tests/to_test.rb`. This file takes exactly the same options as Brakeman. This makes it easy to generate a smaller set of tests (as opposed to tests for every Brakeman warning, which probably already have tests).\n\nExample:\n\n```\nruby to_test.rb apps/rails2 -t Execute\n```\n\nwill generate some boilerplate and then a set of methods:\n\n```ruby\n#...\n \n  def test_command_injection_1\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :line => 34,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n\n  def test_command_injection_2\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :line => 36,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n#...\n```\n\nThe boilerplate is unnecessary unless you are adding a whole new test application.\n\nWhen adding a single test or set of tests, copy the tests from here, change the names to something descriptive, and you are done!\n\nNote that when adding an `assert_no_warning` test for false positives, you can still generate the test with the false positive, then change the assertion.\n"
  },
  {
    "path": "COPYING.md",
    "content": "Code committed on or after June 15, 2018 is licensed under the [Brakeman Public Use License](https://github.com/presidentbeef/brakeman/blob/main/LICENSE.md) and is owned by Synopsys, Inc.\n\nCode committed prior to June 15, 2018 is licensed under the MIT license and is owned by the respective copyright holders.\n\nThe code available on [GitHub](https://github.com/presidentbeef/brakeman/) and as packaged on [RubyGems](https://rubygems.org/gems/brakeman) is distributed under the [Brakeman Public Use License](https://github.com/presidentbeef/brakeman/blob/main/LICENSE.md), sublicensed as necessary under MIT.\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM ruby:3.3-alpine\nLABEL maintainer=\"Justin Collins <gem@brakeman.org>\"\n\nWORKDIR /usr/src/app\n\nRUN apk --update add build-base\n\n# Copy our Gemfile (and related files) *without* copying our actual source code yet\nCOPY Gemfile* *.gemspec gem_common.rb ./\n# Copy lib/brakeman/version.rb so that bundle install works\nCOPY lib/brakeman/version.rb ./lib/brakeman/\n\n# Install the necessary gems\nRUN bundle install --jobs 4 --without \"development test\"\n\n# Copy in the latest Brakeman source code as the final stage\nCOPY . /usr/src/app\n\n# Default to looking for source in /code\nWORKDIR /code\n\nENTRYPOINT [\"/usr/src/app/bin/brakeman\"]\n"
  },
  {
    "path": "Dockerfile.codeclimate",
    "content": "FROM ruby:3.0-alpine\nLABEL maintainer=\"Justin Collins\"\n\nWORKDIR /usr/src/app\n\n# Create user named app with uid=9000, give it ownership of /usr/src/app\nRUN adduser -u 9000 -D app && \\\n    chown -R app:app /usr/src/app\nUSER app\n\n# Copy our Gemfile (and related files) *without* copying our actual source code yet\nCOPY Gemfile* *.gemspec gem_common.rb ./\n# Copy lib/brakeman/version.rb so that bundle install works\nCOPY lib/brakeman/version.rb ./lib/brakeman/\n\n# Install the necessary gems\nRUN bundle install --jobs 4 --without \"development test\"\n\n# Copy in the latest Brakeman source code as the final stage\nCOPY . /usr/src/app\n\n# Default to looking for source in /code\nWORKDIR /code\n\nCMD [\"/usr/src/app/bin/codeclimate-brakeman\"]\n"
  },
  {
    "path": "FEATURES",
    "content": "Can detect:\n-Possibly unescaped model attributes or parameters in views (Cross-Site Scripting)\n-Bad string interpolation in calls to Model.find, Model.last, Model.first, etc., as well as chained calls (SQL Injection)\n-String interpolation in find_by_sql (SQL Injection)\n-String interpolation or params in calls to system, exec, and syscall and `` (Command Injection)\n-Unrestricted mass assignments\n-Global restriction of mass assignment\n-Missing call to protect_from_forgery in ApplicationController (CSRF protection)\n-Default routes, per-controller and globally\n-Redirects based on params (probably too broad currently)\n-Validation regexes not using \\A and \\z\n-Calls to render with dynamic paths\n\nGeneral capabilities:\n-Search for method calls based on target class and/or method name\n-Determine 'output' of templates using ERB, Erubis, or HAML. Can handle automatic escaping\n"
  },
  {
    "path": "Gemfile",
    "content": "source \"https://rubygems.org\"\n\ngemspec :name => \"brakeman\"\n\nunless ENV['BM_PACKAGE']\n  group :test do\n    gem 'rake'\n    gem 'minitest', '>= 6.0'\n  end\nend\n"
  },
  {
    "path": "LICENSE.md",
    "content": "**LICENSE**\n\n# Brakeman Public Use License\n\nSynopsys, Inc. is willing to authorize use of the Software pursuant to the terms and conditions of this License by Licensee only upon the condition that Licensee accepts that the Agreement governs Licensee's use of the Software. By accepting this Agreement or installing or using the Software (directly or through the actions of an authorized representative), Licensee confirms its acceptance of the License and the Agreement and its agreement to comply with the License terms.\n\nThe Brakeman software (the \"***Software***\") is licensed for use by third parties according to the terms and conditions set forth in this license agreement (the \"***Agreement***\"). The copyright to the Software and this license agreement is owned by Synopsys, Inc. and its global affiliates (\"***Synopsys***\").\n\n**Copyright 2019 Synopsys, Inc. All rights not granted in this Agreement are expressly reserved.**\n\nCommercial Uses (as defined below) of the Software for commercial purposes require a commercial, non-free license. Otherwise, the Software may be used by the party that has downloaded the Software and accepted the terms of this Agreement without charge.\n\n## 1. Definitions\n\n1.1 \"***License***\" means this Agreement.\n\n1.2 \"***Licensee***\" means you, the end user of the Software.\n\n1.3 \"***Contributor***\" means each individual or legal entity that creates, contributes to the creation of, or owns the Software.\n\n1.4 \"***Contribution***\" means the creation of and/or contribution to the development of the Software\n\n1.5 \"***Software***\" has the meaning set forth in the recital to this Agreement.\n\n## 2. Commercial Uses\n\nA \"***Commercial Use***\" of the Software is one intended for commercial advantage or monetary compensation.\n\nExamples of Commercial Uses include (but are not limited to):\n\n* Using the Software to provide commercial managed/Software-as-a-Service services.\n* Distributing the Software as a commercial product or as part of one.\n* Using the Software as a component of a value-added service/product.\n\nExample of uses that are not Commercial Uses, and are subject to the terms of this License, include (but are not limited to):\n\n* Using the Software to analyze Licensee's software.\n* Any non-commercial use of the Software.\n\nTo purchase a license to the Software for Commercial Use, or if Licensee is unsure whether it needs to purchase a Commercial Use license, contact Synopsys at \\[sig-sales-ww@synopsys.com\\].\nSynopsys may grant commercial licenses at no monetary cost at its own discretion if the commercial usage is deemed by Synopsys to significantly benefit the development of the Software.\n\n## 3. License Grant\n\nSynopsys grants Licensee a nonexclusive, nontransferable (except as permitted in this Agreement), limited license to use and modify the Software, subject to the terms and conditions stated in this Agreement, only for the purpose of non-Commercial Use and not for any other purpose. Licensee may make copies of the Software to the extent reasonably necessary to exercise the License granted in this Agreement.\n\nAs a condition to the grant of the foregoing License, Licensee agrees not to do or undertake to do the following:\n\n* Use the Software for any Commercial Use;\n\n* Remove or modify any trademarks or any copyright notice in the Software; or\n\n* Assign the License or the Agreement, or distribute, give, or transfer the Software to any third party, except as expressly permitted in this Agreement.\n\nAll rights not expressly granted in this Agreement are reserved by Synopsys. Synopsys or its licensors retain all ownership and intellectual property rights to the Software.\n\n## 4. Redistribution\n\nRedistribution is permitted solely under the following conditions:\n\n* A copy of this License, without modification, is provided with the Software.\n* All Copyright notices to the Software and this Agreement are provided with the Software.\n* Redistribution and subsequent use does not conflict with the Commercial Uses clause above.\n\n## 4. Copying\n\nCopying of the Software is permitted so long as it does not conflict with the Redistribution and Commercial Uses clauses.\n\n## 5. Modification\n\nModification of the Software is permitted so long as it does not conflict with the Redistribution clause.\n\n## 6. Contributions\n\nAll right, title, and interest in any Contributions to the Software are hereby assigned to Synopsys, effective upon the date of creation of any such Contribution. Synopsys shall have the unlimited, exclusive right to reuse, modify and relicense any Contributions.\n\n## 7. Support\n\nThe Software is provided under an AS-IS basis and without any support, updates or maintenance. Updates to the Software may be provided by Synopsys at its the sole discretion.\n\n## 8. Disclaimer of Warranty\n\nThe Software is provided under this License on an \"as is\" basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Software is free of defects, merchantable, fit for a particular purpose or non-infringing.\n\n## 9. Disclaimer of Liability\n\nTo the extent permitted under law, the Software is provided under an AS-IS basis. Synopsys shall never be liable for any damage (including without limitation indirect, incidental, special, punitive or consequential damage, or damage for loss of profits, revenue, data, of data use), cost, expense or any other payment incurred as a result of Licensee's use of the Software for any purpose, even if Synopsys has been advised of the possibility of such damages and regardless of whether the action for such damage arises in contract or tort. The entire liability of Synopsys under the Agreement shall not exceed one hundred dollars (USD $100).\n\n## 10. Trademark\n\n\"Synopsys\" is a registered trademark of Synopsys, Inc. All rights are reserved to Synopsys, Inc. This Agreement does not grant the use of the trademark or the use of the Synopsys logo to you for any purpose.\n\n## 11. Export Controls\n\nExport laws and regulations of the United States and any other relevant local export laws and regulations apply to the Software. Licensee agrees that United States export control laws govern the use of the Software (including any corresponding documentation).  Licensee also agrees to comply with all United States export laws and regulations (including \"deemed export\" and \"deemed re-export\" regulations). Licensee agrees that no results of analysis created or derived from the use of the Software will be exported, directly or indirectly, in violation of these laws, or will be used for any purpose prohibited by these laws including, without limitation, nuclear, chemical, or biological weapons proliferation, or development of missile technologies. \n\nLicensee confirms and agrees:\n\n* Licensee will not download, provide, make available or otherwise export or re-export the Software, directly or indirectly, to countries prohibited by applicable laws and regulations nor to citizens, nationals or residents of those countries.\n* Licensee is not listed on the United States Department of Treasury lists of Specially Designated Nationals and Blocked Persons, Specially Designated Terrorists, and Specially Designated Narcotic Traffickers, nor is Licensee listed on the United States Department of Commerce Table of Denial Orders.\n* Licensee will not export or re-export the Software, directly or indirectly, to persons on the above mentioned lists.\n* Licensee will not use or allow the Software to be used for, any purposes prohibited by applicable law, including, without limitation, for the development, design, manufacture or production of nuclear, chemical or biological weapons of mass destruction.\n\n## 12. Relationship Between the Parties\n\nLicensee is an independent contractor and hereby agrees that no partnership, joint venture, or agency relationship exists between Licensee and Synopsys.\n\n## 13. Entire Agreement; Governing Law\n\nThe Agreement is the complete agreement for the Software. The Agreement supersedes all prior or contemporaneous agreements or representations, including any license agreements for prior versions of the Software.\n\nThis Agreement may not be modified and the rights and restrictions may not be altered or waived except in a writing signed by authorized representatives of the parties.  If any term of the License or the Agreement is found to be invalid or unenforceable, the remaining provisions will remain effective.\n\nThe Agreement is governed by California law.  The parties agree to submit to the exclusive jurisdiction of, and venue in, the courts of Santa Clara county, California with respect to any action arising out of or relating to the License or the Agreement.\n\n## 14. Notices\n\nAny questions concerning the License and/or the Agreement and any notices to Synopsys under this agreement shall be directed to:\n\n  Synopsys, Inc.  \n  800 E. Middlefield Road  \n  Mountain View, CA 94045\n\n\nEnd of Agreement\n"
  },
  {
    "path": "MIT-LICENSE",
    "content": "The MIT License\n\nCopyright (c) 2010-2012, YELLOWPAGES.COM, LLC\nCopyright (c) 2012, Twitter, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "OPTIONS.md",
    "content": "This file may or may not be up-to-date. For best results but less information, run `brakeman --help`.\n\n## Scanning Options\n\nThere are some checks which are not run by default. To run all checks, use:\n\n    brakeman -A\n\nEach check will be run in a separate thread by default. To disable this behavior:\n\n    brakeman -n\n\nBy default, Brakeman scans the current directory. A path can also be specified as a bare argument, like:\n\n    brakeman some/path/to/app\n\nBut to be even more specific, the `-p` or `--path` option may be used:\n\n    brakeman -p path/to/app\n\nTo suppress informational warnings and just output the report:\n\n    brakeman -q\n\nNote all Brakeman output except reports are sent to stderr, making it simple to redirect stdout to a file and just get the report.\n\nBy default, Brakeman will return a non-zero exit code if any security warnings are found or scanning errors are encountered. To disable this:\n\n    brakeman --no-exit-on-warn --no-exit-on-error\n\nTo force Brakeman into Rails 3 mode:\n\n    brakeman -3\n\nOr to force Brakeman into Rails 4 mode:\n\n    brakeman -4\n\nBeware some behavior and checks rely on knowing the exact version name. This shouldn't be a problem with any modern Rails app using a `Gemfile.lock` though.\n\nBrakeman used to parse `routes.rb` and attempt to infer which controller methods are used as actions. However, this is not perfect (especially for Rails 3/4), so now it assumes all controller methods are actions. To disable this behavior:\n\n    brakeman --no-assume-routes\n\nWhile this shouldn't be necessary, it is possible to force Brakeman to assume output is escaped by default:\n\n    brakeman --escape-html\n\nIf Brakeman is running a bit slow, try\n\n    brakeman --faster\n\nThis will disable some features, but will probably be much faster (currently it is the same as `--no-branching`). *WARNING*: This may cause Brakeman to miss some vulnerabilities.\n\nTo disable flow sensitivity in `if` expressions:\n\n    brakeman --no-branching\n\nTo instead limit the number of branches tracked for a given value:\n\n    brakeman --branch-limit LIMIT\n\n`LIMIT` should be an integer value. `0` is almost the same as `--no-branching` but `--no-branching` is preferred. The default value is `5`. Lower values generally make Brakeman go faster. `-1` is the same as unlimited.\n\nTo skip certain files or directories use:\n\n    brakeman --skip-files file1,/path1/,path2/\n\nDirectories are matched relative to the root path of your application and must end in a path separator for your platform (ex. `/`). The above invocation would match and skip the following:\n\n* Any file named `file1`. Any file that has `file1` as a path component would still be scanned.\n* Any file within `/path1`. Because of the leading `/`, only directories from the application's root directory will match. For example, `/lib/path1/some_path1_file.rb` would still be scanned.\n* Any directory named `path2`. Because there is no leading `/`, any directory with `path2` as a path component will be skipped. For example, `/lib/path2/some_lib_for_testing.rb` would not be scanned.\n\nNote Brakeman does \"whole program\" analysis, therefore skipping a file may affect warning results from more than just that one file.\n\nThe inverse but even more dangerous option is to choose specific files or directories to scan:\n\n    brakeman --only-files file1,/path2/,path2/\n\nAgain, since Brakeman looks at the whole program, it is very likely not going to behave as expected when scanning a subset of files. Also, if certain files are excluded Brakeman may not function at all.\n\nTo run a subset of checks:\n\n    brakeman --test Check1,Check2,etc\n\nTo exclude certain checks:\n\n    brakeman --except Check1,Check2,etc\n\nNote it is not necessary to include the `Check` part of the check. For example, these are equivalent:\n\n    brakeman --test CheckSQL\n    brakeman --test SQL\n\n## Output Options\n\nTo see all kinds of debugging information:\n\n    brakeman -d\n\nTo specify an output file for the results:\n\n    brakeman -o output_file\n\nThe output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json`, `junit`, `markdown`, `csv`, `codeclimate`, `github`, `sarif` and `sonar`.\n\nMultiple output files can be specified:\n\n    brakeman -o output.html -o output.json\n\nTo output to both a file and to the console, with color:\n\n    brakeman --color -o /dev/stdout -o output.json\n\nTo specify a CSS stylesheet to use with the HTML report:\n\n    brakeman --css-file my_cool_styling\n\nBy default, Brakeman will only report a single warning of a given type for the same line of code. This can be disabled using\n\n    brakeman --no-combine-locations\n\nTo disable highlighting of \"dangerous\" or \"user input\" values in warnings:\n\n    brakeman --no-highlights\n\nTo report controller and route information:\n\n    brakeman --routes\n\nHowever, if you really want to know what routes an app has, use\n\n    rake routes\n\nTo set the limit on message length in HTML reports, use\n\n    brakeman --message-limit LIMIT\n\nThe default LIMIT is 100.\n\nTo limit width of the tables output in text reports, use\n\n    brakeman --table-width LIMIT\n\nBy default, there is no limit.\n\nBrakeman will warn about each model without `attr_accessible`. In the HTML report it may be nicer to get all models in one warning with\n\n    brakeman --no-separate-models\n\nSometimes you don't need a big report, just the summary:\n\n    brakeman --summary\n\nReports show relative paths by default. To use absolute paths instead:\n\n    brakeman --absolute-paths\n\nThis does not affect HTML or tab-separated reports.\n\nTo output Markdown with nice links to files on GitHub, use\n\n    brakeman --github-repo USER/REPO[/PATH][@REF]\n\nFor example,\n\n    brakeman --github-repo presidentbeef/inject-some-sql\n\nTo compare results of a scan with a previous scan, use the JSON output option and then:\n\n    brakeman --compare old_report.json\n\nThis will output JSON with two lists: one of fixed warnings and one of new warnings.\n\nBy default, brakeman opens output in `less` pager. To have brakeman output directly to terminal, use\n\n    brakeman --no-pager\n\n## Ignoring Stuff\n\nBrakeman will ignore warnings if configured to do so. By default, it looks for a configuration file in `config/brakeman.ignore`.\n\nTo specify a file to use:\n\n    brakeman -i path/to/config.ignore\n\nTo create and manage this file, use:\n\n    brakeman -I\n\nTo ignore possible XSS from model attributes:\n\n    brakeman --ignore-model-output\n\nBrakeman will raise warnings on models that use `attr_protected`. To suppress these warnings:\n\n    brakeman --ignore-protected\n\nTo show all ignored warnings without affecting the exit code (i.e. - Will return `0` if the application shows no warnings when simply running `brakeman`):\n\n    brakeman --show-ignored\n\nBrakeman will assume that unknown methods involving untrusted data are dangerous. For example, this would cause a warning (Rails 2):\n\n    <%= some_method(:option => params[:input]) %>\n\nTo only raise warnings only when untrusted data is being directly used:\n\n    brakeman --report-direct\n\nThis option is not supported very consistently, though.\n\nTo indicate certain methods return properly escaped output and should not be warned about in XSS checks:\n\n    brakeman --safe-methods benign_method_escapes_output,totally_safe_from_xss\n\nTo indicate certain methods return properly escaped output and should not be warned about in SQL checks:\n\n    brakeman --sql-safe-methods benign_method_escapes_output,totally_safe_from_sql\n\nBrakeman warns about use of user input in URLs generated with `link_to`. Since Rails does not provide anyway of making these URLs really safe (e.g. limiting protocols to HTTP(S)), safe methods can be ignored with\n\n    brakeman --url-safe-methods ensure_safe_protocol_or_something\n\n## Confidence Levels\n\nBrakeman assigns a confidence level to each warning. This provides a rough estimate of how certain the tool is that a given warning is actually a problem. Naturally, these ratings should not be taken as absolute truth.\n\nThere are three levels of confidence:\n\n + High - Either this is a simple warning (boolean value) or user input is very likely being used in unsafe ways.\n + Medium - This generally indicates an unsafe use of a variable, but the variable may or may not be user input.\n + Weak - Typically means user input was indirectly used in a potentially unsafe manner.\n\nTo only get warnings above a given confidence level:\n\n    brakeman -w3\n\nThe `-w` switch takes a number from 1 to 3, with 1 being low (all warnings) and 3 being high (only highest confidence warnings).\n\n## Configuration Files\n\nBrakeman options can stored and read from YAML files. To simplify the process of writing a configuration file, the `-C` option will output the currently set options.\n\nOptions passed in on the commandline have priority over configuration files.\n\nThe default config locations are `./config/brakeman.yml`, `~/.brakeman/config.yml`, and `/etc/brakeman/config.yml`\n\nThe `-c` option can be used to specify a configuration file to use.\n\n## Miscellaneous\n\nTo list available checks with short descriptions:\n\n    brakeman --checks\n\nTo show checks which are optional (not run by default):\n\n    brakeman --optional-checks\n\nTo see Brakeman's version:\n\n    brakeman --version\n\nTo see the real list of options:\n\n    brakeman --help\n"
  },
  {
    "path": "README.md",
    "content": "[![Brakeman Logo](http://brakemanscanner.org/images/logo_medium.png)](http://brakemanscanner.org/)\n\n[![Build Status](https://circleci.com/gh/presidentbeef/brakeman.svg?style=svg)](https://circleci.com/gh/presidentbeef/brakeman)\n[![Code Coverage](https://qlty.sh/gh/presidentbeef/projects/brakeman/coverage.svg)](https://qlty.sh/gh/presidentbeef/projects/brakeman)\n\n# Brakeman\n\nBrakeman is a static analysis tool which checks Ruby on Rails applications for security vulnerabilities.\n\n# Installation\n\nUsing RubyGems:\n\n    gem install brakeman\n\nUsing Bundler:\n\n```ruby\ngroup :development do\n  gem 'brakeman', require: false\nend\n```\n\nUsing Docker:\n\n    docker pull presidentbeef/brakeman\n\nUsing Docker to build from source:\n\n    git clone https://github.com/presidentbeef/brakeman.git\n    cd brakeman\n    docker build . -t brakeman\n\n# Usage\n\n#### Running locally\n\nFrom a Rails application's root directory:\n\n    brakeman\n\nOutside of Rails root:\n\n    brakeman /path/to/rails/application\n\n#### Running with Docker\n\nFrom a Rails application's root directory:\n\n    docker run -v \"$(pwd)\":/code presidentbeef/brakeman\n\nWith a little nicer color:\n\n    docker run -v \"$(pwd)\":/code presidentbeef/brakeman --color\n\nFor an HTML report:\n\n    docker run -v \"$(pwd)\":/code presidentbeef/brakeman -o brakeman_results.html\n\nOutside of Rails root (note that the output file is relative to path/to/rails/application):\n\n    docker run -v 'path/to/rails/application':/code presidentbeef/brakeman -o brakeman_results.html\n\n# Compatibility\n\nBrakeman should work with any version of Rails from 2.3.x to 8.x.\n\nBrakeman can analyze code written with Ruby 2.0 syntax and newer, but requires at least Ruby 3.2.0 to run.\n\n# Basic Options\n\nFor a full list of options, use `brakeman --help` or see the [OPTIONS.md](OPTIONS.md) file.\n\nTo specify an output file for the results:\n\n    brakeman -o output_file\n\nThe output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json`, `junit`, `markdown`, `csv`, `codeclimate`, `github`, `sarif`, and `sonar`.\n\nMultiple output files can be specified:\n\n    brakeman -o output.html -o output.json\n\nTo output to both a file and to the console, with color:\n\n    brakeman --color -o /dev/stdout -o output.json\n\nTo suppress informational warnings and just output the report:\n\n    brakeman -q\n\nNote all Brakeman output except reports are sent to stderr, making it simple to redirect stdout to a file and just get the report.\n\nTo see all kinds of debugging information:\n\n    brakeman -d\n\nSpecific checks can be skipped, if desired. The name needs to be the correct case. For example, to skip looking for default routes (`DefaultRoutes`):\n\n    brakeman -x DefaultRoutes\n\nMultiple checks should be separated by a comma:\n\n    brakeman -x DefaultRoutes,Redirect\n\nTo do the opposite and only run a certain set of tests:\n\n    brakeman -t SQL,ValidationRegex\n\nIf Brakeman is running a bit slow, try\n\n    brakeman --faster\n\nThis will disable some features, but will probably be much faster (currently it is the same as `--skip-libs --no-branching`). *WARNING*: This may cause Brakeman to miss some vulnerabilities.\n\nBy default, Brakeman will return a non-zero exit code if any security warnings are found or scanning errors are encountered. To disable this:\n\n    brakeman --no-exit-on-warn --no-exit-on-error\n\nTo skip certain files or directories that Brakeman may have trouble parsing, use:\n\n    brakeman --skip-files file1,/path1/,path2/\n\nTo compare results of a scan with a previous scan, use the JSON output option and then:\n\n    brakeman --compare old_report.json\n\nThis will output JSON with two lists: one of fixed warnings and one of new warnings.\n\nBrakeman will ignore warnings if configured to do so. By default, it looks for a configuration file in `config/brakeman.ignore`.\nTo create and manage this file, use:\n\n    brakeman -I\n\nIf you want to temporarily see the warnings you ignored without affecting the exit code, use:\n\n    brakeman --show-ignored\n\n# Warning information\n\nSee [warning\\_types](docs/warning_types) for more information on the warnings reported by this tool.\n\n# Warning context\n\nThe HTML output format provides an excerpt from the original application source where a warning was triggered. Due to the processing done while looking for vulnerabilities, the source may not resemble the reported warning and reported line numbers may be slightly off. However, the context still provides a quick look into the code which raised the warning.\n\n# Confidence levels\n\nBrakeman assigns a confidence level to each warning. This provides a rough estimate of how certain the tool is that a given warning is actually a problem. Naturally, these ratings should not be taken as absolute truth.\n\nThere are three levels of confidence:\n\n + High - Either this is a simple warning (boolean value) or user input is very likely being used in unsafe ways.\n + Medium - This generally indicates an unsafe use of a variable, but the variable may or may not be user input.\n + Weak - Typically means user input was indirectly used in a potentially unsafe manner.\n\nTo only get warnings above a given confidence level:\n\n    brakeman -w3\n\nThe `-w` switch takes a number from 1 to 3, with 1 being low (all warnings) and 3 being high (only highest confidence warnings).\n\n# Configuration files\n\nBrakeman options can be stored and read from YAML files.\n\nTo simplify the process of writing a configuration file, the `-C` option will output the currently set options:\n\n```sh\n$ brakeman -C --skip-files plugins/\n---\n:skip_files:\n- plugins/\n```\n\nOptions passed in on the commandline have priority over configuration files.\n\nThe default config locations are `./config/brakeman.yml`, `~/.brakeman/config.yml`, and `/etc/brakeman/config.yml`\n\nThe `-c` option can be used to specify a configuration file to use.\n\n# Continuous Integration\n\nThere is a [plugin available](http://brakemanscanner.org/docs/jenkins/) for Jenkins/Hudson.\n\nFor even more continuous testing, try the [Guard plugin](https://github.com/guard/guard-brakeman).\n\nThere are a couple [GitHub Actions](https://github.com/marketplace?type=actions&query=brakeman) available.\n\n# Building\n\n    git clone git://github.com/presidentbeef/brakeman.git\n    cd brakeman\n    gem build brakeman.gemspec\n    gem install brakeman*.gem\n\n# Who is Using Brakeman?\n\n* [Code Climate](https://codeclimate.com/)\n* [GitHub](https://github.com/)\n* [Groupon](http://www.groupon.com/)\n* [New Relic](http://newrelic.com)\n* [Twitter](https://twitter.com/)\n\n[..and more!](http://brakemanscanner.org/brakeman_users)\n\n# Homepage/News\n\nWebsite: http://brakemanscanner.org/\n\nTwitter: https://twitter.com/brakeman\n\nChat: https://gitter.im/presidentbeef/brakeman\n\n# License\n\nBrakeman is free for non-commercial use.\n\nSee [COPYING](COPYING.md) for details.\n"
  },
  {
    "path": "Rakefile",
    "content": "require 'bundler/setup'\nrequire 'rake/testtask'\n\nRake::TestTask.new do |t|\n  t.pattern = 'test/tests/*.rb'\nend\n\ntask default: :test\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nThe following versions are supported for security updates.\n\n| Version | Supported          |\n| ------- | ------------------ |\n| >= 4.4.0 | :white_check_mark:|\n| < 4.4.0 | :x:                |\n\n## Reporting a Vulnerability\n\nTo report a vulnerability, email security@brakeman.org.\n\nWe will work as quickly as possible to investigate and address the issue, if necessary.\n\nWe do not have a vulnerability reward program.\n"
  },
  {
    "path": "bin/brakeman",
    "content": "#!/usr/bin/env ruby\n#Adjust path in case called directly and not through gem\n$:.unshift \"#{File.expand_path(File.dirname(__FILE__))}/../lib\"\n\nEncoding.default_external = 'UTF-8'\n\nrequire 'brakeman'\nrequire 'brakeman/commandline'\n\nBrakeman::Commandline.start\n"
  },
  {
    "path": "bin/codeclimate-brakeman",
    "content": "#!/usr/bin/env ruby\n$:.unshift \"#{File.expand_path(File.dirname(__FILE__))}/../lib\"\n\nrequire \"brakeman\"\nrequire \"json\"\nrequire \"brakeman/codeclimate/engine_configuration\"\n\nengine_options = {}\n\nif File.exist?(\"/config.json\")\n  engine_options = JSON.parse(File.read(\"/config.json\"))\nend\n\nBrakeman.run Brakeman::Codeclimate::EngineConfiguration.new(engine_options).options\n"
  },
  {
    "path": "brakeman-lib.gemspec",
    "content": "require './lib/brakeman/version'\nrequire './gem_common'\n\nGem::Specification.new do |s|\n  s.name = %q{brakeman-lib}\n  s.version = Brakeman::Version\n  s.authors = [\"Justin Collins\"]\n  s.email = \"gem@brakeman.org\"\n  s.summary = \"Security vulnerability scanner for Ruby on Rails.\"\n  s.description = \"Brakeman detects security vulnerabilities in Ruby on Rails applications via static analysis. This package declares gem dependencies instead of bundling them.\"\n  s.homepage = \"http://brakemanscanner.org\"\n  s.files = [\"bin/brakeman\", \"CHANGES.md\", \"FEATURES\", \"README.md\"] + Dir[\"lib/**/*\"]\n  s.executables = [\"brakeman\"]\n  s.license = \"Brakeman Public Use License\"\n  s.required_ruby_version = '>= 3.2.0'\n\n  s.metadata = {\n    \"bug_tracker_uri\"   => \"https://github.com/presidentbeef/brakeman/issues\",\n    \"changelog_uri\"     => \"https://github.com/presidentbeef/brakeman/releases\",\n    \"documentation_uri\" => \"https://brakemanscanner.org/docs/\",\n    \"homepage_uri\"      => \"https://brakemanscanner.org/\",\n    \"mailing_list_uri\"  => \"https://gitter.im/presidentbeef/brakeman\",\n    \"source_code_uri\"   => \"https://github.com/presidentbeef/brakeman\",\n    \"wiki_uri\"          => \"https://github.com/presidentbeef/brakeman/wiki\"\n  }\n\n  Brakeman::GemDependencies.dev_dependencies(s)\n  Brakeman::GemDependencies.base_dependencies(s)\n  Brakeman::GemDependencies.extended_dependencies(s)\nend\n"
  },
  {
    "path": "brakeman-min.gemspec",
    "content": "require './lib/brakeman/version'\nrequire './gem_common'\n\nGem::Specification.new do |s|\n  s.name = %q{brakeman-min}\n  s.version = Brakeman::Version\n  s.authors = [\"Justin Collins\"]\n  s.email = \"gem@brakeman.org\"\n  s.summary = \"Security vulnerability scanner for Ruby on Rails.\"\n  s.description = \"Brakeman detects security vulnerabilities in Ruby on Rails applications via static analysis. This version of the gem only requires the minimum number of dependencies. Use the 'brakeman' gem for a full install.\"\n  s.homepage = \"http://brakemanscanner.org\"\n  s.files = [\"bin/brakeman\", \"CHANGES.md\", \"FEATURES\", \"README.md\"] + Dir[\"lib/**/*\"]\n  s.executables = [\"brakeman\"]\n  s.license = \"Brakeman Public Use License\"\n  s.required_ruby_version = '>= 3.2.0'\n\n  s.metadata = {\n    \"bug_tracker_uri\"   => \"https://github.com/presidentbeef/brakeman/issues\",\n    \"changelog_uri\"     => \"https://github.com/presidentbeef/brakeman/releases\",\n    \"documentation_uri\" => \"https://brakemanscanner.org/docs/\",\n    \"homepage_uri\"      => \"https://brakemanscanner.org/\",\n    \"mailing_list_uri\"  => \"https://gitter.im/presidentbeef/brakeman\",\n    \"source_code_uri\"   => \"https://github.com/presidentbeef/brakeman\",\n    \"wiki_uri\"          => \"https://github.com/presidentbeef/brakeman/wiki\"\n  }\n\n  Brakeman::GemDependencies.dev_dependencies(s)\n  Brakeman::GemDependencies.base_dependencies(s)\nend\n"
  },
  {
    "path": "brakeman-public_cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDijCCAnKgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQ8wDQYDVQQDDAZqdXN0\naW4xHTAbBgoJkiaJk/IsZAEZFg1wcmVzaWRlbnRiZWVmMRMwEQYKCZImiZPyLGQB\nGRYDY29tMB4XDTE1MDEwMzAxMjI0NFoXDTE2MDEwMzAxMjI0NFowRTEPMA0GA1UE\nAwwGanVzdGluMR0wGwYKCZImiZPyLGQBGRYNcHJlc2lkZW50YmVlZjETMBEGCgmS\nJomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMjt\nxjn8ArkEqQNrRjEeyZAOyr0O8+WZ54AcObsKg2osrcAW6iFd7tjnTFclQHmZgje+\ncwxeF/YG4PbA72ElmCvjn8vQJkdgHspKds1otSozvTF2VDnyAEg0nDTMgkQGQy4R\nHX3NHXMJ8UCAJv2IV/FsItzcPzPmhhf6vu/QaNrmAm3/nF52EsMSEJNC9eTPWudC\nkPgt19T9LRKMk5YbXDM6jWGRubusE03bTwY3RThqYM5ra1DwI/HpWKsKdmNrBbse\nf065WyR7RNAxindc2wMyq1EaInmO7Vds+rsOFZ4ZnO90z046ywmTLTadqlfuc9Qo\nCEw/AhYB6f6DLH8ICkMCAwEAAaOBhDCBgTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE\nsDAdBgNVHQ4EFgQUmIuIvxLr7ziB52LOpVgd694EfaEwIwYDVR0RBBwwGoEYanVz\ndGluQHByZXNpZGVudGJlZWYuY29tMCMGA1UdEgQcMBqBGGp1c3RpbkBwcmVzaWRl\nbnRiZWVmLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAbgSKdn/VSDdl5H2ayE+OM662\ngTJWP1CWfbcRVJW/UDjDucEF42t6V/dZTDmwyYTR8Qv+5FsQoPHsDsD3Jr1E62dl\nVYDeUkbmiV5f8fANbvnGUknzrHwp2T0/URxiIY8oFcaCGT+iua9zlNU20+XhB9JN\nfsOSUNBuuE/MYGA37MR1sP7lFHr5e7I1Qk1x3HvjNB/kSv1+Cj26Lde1ehvMqpmi\nbxoxp9KNxkO+709YwLO1rYfmcGghg8WV6MYz3PSHdlgWF4KrjRFc/00hXHqVk0Sf\nmREEv2LPwHH2SgpSSab+iawnX4l6lV8XcIrmp/HSMySsPVFBeOmB0c05LpEN8w==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "brakeman.gemspec",
    "content": "require './lib/brakeman/version'\nrequire './gem_common'\n\nGem::Specification.new do |s|\n  s.name = %q{brakeman}\n  s.version = Brakeman::Version\n  s.authors = [\"Justin Collins\"]\n  s.email = \"gem@brakeman.org\"\n  s.summary = \"Security vulnerability scanner for Ruby on Rails.\"\n  s.description = \"Brakeman detects security vulnerabilities in Ruby on Rails applications via static analysis.\"\n  s.homepage = \"https://brakemanscanner.org\"\n  s.files = [\"bin/brakeman\", \"CHANGES.md\", \"FEATURES\", \"README.md\"] + Dir[\"lib/**/*\"]\n  s.executables = [\"brakeman\"]\n  s.license = \"Brakeman Public Use License\"\n  s.required_ruby_version = '>= 3.2.0'\n\n  s.metadata = {\n    \"bug_tracker_uri\"   => \"https://github.com/presidentbeef/brakeman/issues\",\n    \"changelog_uri\"     => \"https://github.com/presidentbeef/brakeman/releases\",\n    \"documentation_uri\" => \"https://brakemanscanner.org/docs/\",\n    \"homepage_uri\"      => \"https://brakemanscanner.org/\",\n    \"source_code_uri\"   => \"https://github.com/presidentbeef/brakeman\",\n    \"wiki_uri\"          => \"https://github.com/presidentbeef/brakeman/wiki\"\n  }\n\n  if File.exist? 'bundle/load.rb'\n    # Pull in vendored dependencies\n    s.files << 'bundle/load.rb'\n\n    s.files += Dir['bundle/ruby/*/gems/**/*'].reject do |path|\n      # Skip unnecessary files in dependencies\n      path =~ %r{^bundle/ruby/\\d\\.\\d\\.\\d/gems/[^\\/]+/(Rakefile|benchmark|bin|doc|example|man|site|spec|test)} or\n        path =~ %r{/gems/(io-console|prism|racc|strscan)/}\n    end\n\n    # racc is not only a built-in gem, but also has native code which we cannot\n    # bundle with Brakeman, so leaving it as a regular dependency\n    s.add_dependency \"racc\"\n  else\n    Brakeman::GemDependencies.dev_dependencies(s) unless ENV['BM_PACKAGE']\n    Brakeman::GemDependencies.base_dependencies(s)\n    Brakeman::GemDependencies.extended_dependencies(s)\n  end\nend\n"
  },
  {
    "path": "build.rb",
    "content": "#!/usr/bin/env ruby\nrequire 'fileutils'\nbundle_exclude = %w[io-console prism racc strscan thor]\n\nputs 'Packaging Brakeman gem...'\n\nsystem 'rm -rf bundle Gemfile.lock brakeman-*.gem' and\n  system 'BM_PACKAGE=true bundle install --standalone'\n\nabort \"No bundle installed\" unless Dir.exist? 'bundle'\n\nFile.delete \"bundle/bundler/setup.rb\"\nDir.delete \"bundle/bundler\"\n\nFile.open \"bundle/load.rb\", \"w\" do |f|\n  f.puts \"path = File.expand_path('../..', __FILE__)\"\n\n  Dir[\"bundle/ruby/**/lib\"].each do |dir|\n    if bundle_exclude.any? { |gem_name| dir.include? gem_name }\n      FileUtils.rm_rf(File.expand_path('..', dir))\n    else\n      f.puts %Q[$:.unshift \"\\#{path}/#{dir}\"]\n    end\n  end\nend\n\nsystem \"BM_PACKAGE=true gem build brakeman.gemspec\"\n"
  },
  {
    "path": "docs/warning_types/CVE-2010-3933/index.markdown",
    "content": "Rails 2.3.9 and 3.0.0 are vulnerable to an attack on nested attributes wherein a malicious user could alter data in any record in the system.\n\nIt is recommended to upgrade to at least 2.3.10 or 3.0.1.\n\nFor more details see [CVE-2011-0446](http://groups.google.com/group/rubyonrails-security/browse_thread/thread/f9f913d328dafe0c).\n"
  },
  {
    "path": "docs/warning_types/CVE-2011-0446/index.markdown",
    "content": "Certain versions of Rails were vulnerable to a cross-site scripting vulnerability mail\\_to.\n\nVersions of Rails after 2.3.10 or 3.0.3 are not affected. Updating or removing the mail\\_to links is advised.\n\nFor more details see [CVE-2011-0446](http://groups.google.com/group/rubyonrails-security/browse_thread/thread/f02a48ede8315f81).\n"
  },
  {
    "path": "docs/warning_types/CVE-2011-3186/index.markdown",
    "content": "Response splitting is a simple attack that can be used as part or a larger exploit chain. A malicious user sends data that causes the HTTP response header to include unintended newline characters which are interpreted as the end of the header. The attacker may then forge their own response body and an entirely false HTTP response, essentially hijacking the entire page load.\n\nVersions of Rails 2 previous to 2.3.13 were vulnerable to this type of attack. The Rails 3 branch is not affected.\n\nFor more details see [CVE-2011-3186](http://groups.google.com/group/rubyonrails-security/browse_thread/thread/6ffc93bde0298768).\n"
  },
  {
    "path": "docs/warning_types/attribute_restriction/index.markdown",
    "content": "This warning comes up if a model does not limit what attributes can be set through mass assignment.\n\nIn particular, this check looks for `attr_accessible` inside model definitions. If it is not found, this warning will be issued.\n\nBrakeman also warns on use of `attr_protected` - especially since it was found to be [vulnerable to bypass](https://groups.google.com/d/topic/rubyonrails-security/AFBKNY7VSH8/discussion). Warnings for mass assignment on models using `attr_protected` will be reported, but at a lower confidence level.\n\nNote that disabling mass assignment globally will suppress these warnings.\n"
  },
  {
    "path": "docs/warning_types/authentication/index.markdown",
    "content": "\"Authentication\" is the act of verifying that a user or client is who they say they are.\n\nRight now, the only Brakeman warning in the authentication category is regarding hardcoded passwords.\nBrakeman will warn about constants with literal string values that appear to be passwords.\n\nHardcoded passwords are security issues since they imply a single password and that password is stored in the source code.\nTypically source code is available to a wide number of people inside an organization, and there have been many instances of source\ncode leaking to the public. Passwords and secrets should be stored in a separate, secure location to limit access.\n\nAdditionally, it is recommended not to use a single password for accessing sensitive information.\nEach user should have their own password to make it easier to audit and revoke access.\n"
  },
  {
    "path": "docs/warning_types/authentication_whitelist/index.markdown",
    "content": "When skipping `before_filter`s with security implications, a \"whitelist\" approach using `only` should be used instead of `except`. This ensures actions are protected by default, and unprotected only by exception.\n"
  },
  {
    "path": "docs/warning_types/basic_auth/index.markdown",
    "content": "In Rails 3.1, a new feature was added to simplify basic authentication.\n\nThe example provided in the official [Rails Guide](http://guides.rubyonrails.org/getting_started.html) looks like this:\n\n    class PostsController < ApplicationController\n\n      http_basic_authenticate_with :name => \"dhh\", :password => \"secret\", :except => :index\n\n      #...\n\n    end\n\nThis warning will be raised if `http_basic_authenticate_with` is used and the password is found to be a string (i.e., stored somewhere in the code).\n"
  },
  {
    "path": "docs/warning_types/command_injection/index.markdown",
    "content": "Injection is #1 on the 2010 [OWASP Top Ten](https://www.owasp.org/index.php/Top_10_2010-A1) web security risks. Command injection occurs when shell commands unsafely include user-manipulatable values.\n\nThere are many ways to run commands in Ruby:\n\n    `ls #{params[:file]}`\n\n    system(\"ls #{params[:dir]}\")\n\n    exec(\"md5sum #{params[:input]}\")\n\nBrakeman will warn on any method like these that uses user input or unsafely interpolates variables.\n\nYou can use [`shellescape`](https://apidock.com/ruby/Shellwords/shellescape) to render a variable safe:\n\n    `ls #{params[:file].shellescape}`\n\nSee [the Ruby Security Guide](http://guides.rubyonrails.org/security.html#command-line-injection) for details.\n"
  },
  {
    "path": "docs/warning_types/content_tag/index.markdown",
    "content": "Cross-site scripting (or XSS) is #2 on the 2010 [OWASP Top Ten](https://www.owasp.org/index.php/Top_10_2010-A2) web security risks and it pops up nearly everywhere. XSS occurs when a user-manipulatable value is displayed on a web page without escaping it, allowing someone to inject Javascript or HTML into the page.\n\n[content\\_tag](http://apidock.com/rails/ActionView/Helpers/TagHelper/content_tag) is a view helper which generates an HTML tag with some content:\n\n    >> content_tag :p, \"Hi!\"\n    => \"<p>Hi!</p>\"\n\nIn Rails 2, this content is unescaped (although attribute values are escaped):\n\n    >> content_tag :p, \"<script>alert(1)</script>\"\n    => \"<p><script>alert(1)</script></p>\"\n\nIn Rails 3, the content is escaped. However, only the *content* and the tag attribute *values* are escaped. The tag and attribute names are never escaped in Rails 2 or 3.\n\nThis is more dangerous than a typical method call because `content_tag` marks its output as \"HTML safe\", meaning the `rails_xss` plugin and Rails 3 auto-escaping will not escape its output. Due to this, `content_tag` should be used carefully if user input is provided as an argument.\n\nNote that while `content_tag` does have an `escape` parameter, this only applies to tag attribute *values* and is true by default.\n"
  },
  {
    "path": "docs/warning_types/cross-site_request_forgery/index.markdown",
    "content": "Cross-site request forgery is #5 on the [OWASP Top Ten](https://www.owasp.org/index.php/Top_10_2010-A5). CSRF allows an attacker to perform actions on a website as if they are an authenticated user.\n\nThis warning is raised when no call to `protect_from_forgery` is found in `ApplicationController`. This method prevents CSRF.\n\nFor Rails 4 applications, it is recommended that you use `protect_from_forgery :with => :exception`. This code is inserted into newly generated applications. The default is to `nil` out the session object, which has been a source of many CSRF bypasses due to session memoization.\n\nSee [the Ruby Security Guide](http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf) for details.\n"
  },
  {
    "path": "docs/warning_types/cross_site_scripting/index.markdown",
    "content": "Cross-site scripting (or XSS) is #3 on the 2013 [OWASP Top Ten](https://www.owasp.org/index.php/Top_10_2013-A3-Cross-Site_Scripting_(XSS\\)) web security risks and it pops up nearly everywhere.\n\nXSS occurs when a user-controlled value is displayed on a web page without properly escaping it, allowing someone to inject Javascript or HTML into the page which will be interpreted and executed by the browser..\n\nIn Rails 2.x, values need to be explicitly escaped (e.g., by using the `h` method). Since Rails 3.x, auto-escaping in views is enabled by default. However, one can still use the `raw` or `html_safe` methods to output a value directly.\n\nSee [the Ruby Security Guide](http://guides.rubyonrails.org/security.html#cross-site-scripting-xss) for more details.\n\n### Query Parameters and Cookies\n\nERB example:\n\n    <%= params[:query].html_safe %>\n\nBrakeman looks for several situations that can allow XSS. The simplest is like the example above: a value from the `params` or `cookies` is being directly output to a view. In such cases, it will issue a warning like:\n\n    Unescaped parameter value near line 3: params[:query]\n\nBy default, Brakeman will also warn when a parameter or cookie value is used as an argument to a method, the result of which is output unescaped to a view.\n\nFor example:\n\n    <%= raw some_method(cookie[:name]) %>\n\nThis raises a warning like:\n\n    Unescaped cookie value near line 5: some_method(cookies[:oreo])\n\nHowever, the confidence level for this warning will be weak, because it is not directly outputting the cookie value.\n\nSome methods are known to Brakeman to either be dangerous (`link_to` is one) or safe (`escape_once`). Users can specify safe methods using the `--safe-methods` option. Alternatively, Brakeman can be set to _only_ warn when values are used directly with the `--report-direct` option.\n\n### Model Attributes\n\nBecause (many) models come from database values, Brakeman mistrusts them by default.\n\nFor example, if `@user` is an instance of a model set in an action like\n\n    def set_user\n      @user = User.first\n    end\n\nand there is a view with\n\n    <%= @user.name.html_safe %>\n\nBrakeman will raise a warning like\n\n    Unescaped model attribute near line 3: User.first.name\n\nIf you trust all your data (although you probably shouldn't), this can be disabled with `--ignore-model-output`.\n"
  },
  {
    "path": "docs/warning_types/cross_site_scripting_to_json/index.markdown",
    "content": "Cross-site scripting (or XSS) is #2 on the 2010 [OWASP Top Ten](https://www.owasp.org/index.php/Top_10_2010-A2) web security risks and it pops up nearly everywhere.\n\nXSS occurs when a user-manipulatable value is displayed on a web page without escaping it, allowing someone to inject Javascript or HTML into the page.  Calls to `Hash#to_json` can be used to trigger XSS.  Brakeman will check to see if there are any calls to `Hash#to_json` with `ActiveSupport#escape_html_entities_in_json` set to false (or if you are running Rails < 2.1.0 which did not have this functionality).\n\n`ActiveSupport#escape_html_entities_in_json` was introduced in the \"new\\_rails\\_defaults\" initializer in Rails 2.1.0 which is set to `false` by default.  In Rails 3.0.0, `true` became the default setting.  Setting this value to `true` will automatically escape '<', '>', '&' which are commonly used to break out of code generated by a to\\_json call.\n\nSee [ActiveSupport#escape\\_html\\_entities\\_in\\_json](http://rubydoc.info/docs/rails/ActiveSupport/JSON/Encoding.escape_html_entities_in_json=) for more details.\n\n### Exploiting to\\_json\n\nConsider the following snippet of Rails 2.x ERB:\n\n    # controller\n    @attrs = {:email => 'some@email.com</script><script>alert(document.domain)//'}\n\n    <!-- view -->\n    <script>\n      var attributes = <%= @attrs.to_json %>\n    </script>\n\nWhich generates the following html:\n\n    <script>\n      var attributes = {\"email\":\"some@email.com</script><script>alert(document.domain)//\"}\n    </script>\n\nWhile the generated Javascript appears valid, the browser parses the script tags first, so it sees something like this:\n\n    <script>\n      var attributes = {\"email\":\"some@email.com\n    </script>\n    <script>\n      alert(document.domain)//\"}\n    </script>\n\nThe attribute assignment causes a Javascript error, but the alert triggers just fine!\n\nWith `escape_html_entities_in_json = true`, you will receive the following innocuous output:\n\n    <script>\n      var attributes = {\"email\":\"some@email.com\\u003C/script\\u003E\\u003Cscript\\u003Ealert(document.domain)//\"}\n    </script>\n"
  },
  {
    "path": "docs/warning_types/dangerous_eval/index.markdown",
    "content": "User input in an `eval` statement is VERY dangerous, so this will always raise a warning. Brakeman looks for calls to `eval`, `instance_eval`, `class_eval`, and `module_eval`.\n"
  },
  {
    "path": "docs/warning_types/dangerous_send/index.markdown",
    "content": "Using unfiltered user data to select a Class or Method to be dynamically sent is dangerous.\n\nIt is much safer to whitelist the desired target or method.\n\nUnsafe use of method:\n\n    method = params[:method]\n    @result = User.send(method.to_sym)\n\nSafe:\n\n    method = params[:method] == 1 ? :method_a : :method_b\n    @result = User.send(method, *args)\n\nUnsafe use of target:\n\n    table = params[:table]\n    model = table.classify.constantize\n    @result = model.send(:method)\n\nSafe:\n\n    target = params[:target] == 1 ? Account : User\n    @result = target.send(:method, *args)\n\nIncluding user data in the arguments passed to an Object#send is safe, as long as the method can properly handle potentially bad data.\n\nSafe:\n  \n    args = params[\"args\"] || []\n    @result = User.send(:method, *args)\n"
  },
  {
    "path": "docs/warning_types/default_routes/index.markdown",
    "content": "The general default routes warning means there is a call to\n\n    #Rails 2.x\n    map.connect \":controller/:action/:id\"\n\nor\n\n    Rails 3.x\n    match ':controller(/:action(/:id(.:format)))'\n\nin `config/routes.rb`. This allows any public method on any controller to be called as an action.\n\nIf this warning is reported for a particular controller, it means there is a route to that controller containing `:action`.\n\nDefault routes can be dangerous if methods are made public which are not intended to be used as URLs or actions.\n"
  },
  {
    "path": "docs/warning_types/denial_of_service/index.markdown",
    "content": "Denial of Service (DoS) is any attack which causes a service to become unavailable for legitimate clients.\n\nFor issues that Brakeman detects, this typically arises in the form of memory leaks.\n\n### Symbol DoS\n\nSince Symbols are not garbage collected in Ruby versions prior to 2.2.0, creation of large numbers of Symbols could lead to a server running out of memory.\n\nBrakeman checks for instances of user input which is converted to a Symbol. When this is not restricted, an attacker could create an unlimited number of Symbols.\n\nThe best approach is to simply never convert user-controlled input to a Symbol. If this cannot be avoided, use a whitelist of acceptable values.\n\nFor example:\n\n    valid_values = [\"valid\", \"values\", \"here\"]\n\n    if valid_values.include? params[:value]\n      symbolized = params[:value].to_sym\n    end\n\n\n### Regex DoS\n\nRegular expressions can be used for DoS if the pattern and input requires exponential time to process.\n\nBrakeman will warn about dynamic regular expressions which may be controlled by an attacker. The attacker can create an \"[evil regex](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS)\" and then supply input which causes the server to use a large amount of resources.\n\nIt is recommended to avoid interpolating user input into regular expressions.\n"
  },
  {
    "path": "docs/warning_types/dynamic_render_path/index.markdown",
    "content": "When a call to `render` uses a dynamically generated path, template name, file name, or action, there is the possibility that a user can access templates that should be restricted. The issue may be worse if those templates execute code or modify the database.\n\nThis warning is shown whenever the path to be rendered is not a static string or symbol.\n\nThese warnings are often false positives, however, because it can be difficult to manipulate Rails' assumptions about paths to perform malicious behavior. Reports of dynamic render paths should be checked carefully to see if they can actually be manipulated maliciously by the user.\n"
  },
  {
    "path": "docs/warning_types/file_access/index.markdown",
    "content": "Using user input when accessing files (local or remote) will raise a warning in Brakeman.\n\nFor example\n\n    File.open(\"/tmp/#{cookie[:file]}\")\n\nwill raise an error like\n\n    Cookie value used in file name near line 4: File.open(\"/tmp/#{cookie[:file]}\")\n\nThis type of vulnerability can be used to access arbitrary files on a server (including `/etc/passwd`.\n\nIf you are using `ActiveStorage`, use [sanitized](https://api.rubyonrails.org/classes/ActiveStorage/Filename.html#method-i-sanitized) URLs:\n\n    ActiveStorage::Filename.new(\"foo/bar.jpg\").sanitized # => \"foo-bar.jpg\"\n\nNote: It replaces `/` with `-`.\n"
  },
  {
    "path": "docs/warning_types/format_validation/index.markdown",
    "content": "Calls to `validates_format_of ..., :with => //` which do not use `\\A` and `\\z` as anchors will cause this warning. Using `^` and `$` is not sufficient, as they will only match up to a new line. This allows an attacker to put whatever malicious input they would like before or after a new line character.\n\nSee [the Ruby Security Guide](http://guides.rubyonrails.org/security.html#regular-expressions) for details.\n"
  },
  {
    "path": "docs/warning_types/information_disclosure/index.markdown",
    "content": "Also known as [information leakage](https://www.owasp.org/index.php/Information_Leakage) or [information exposure](http://cwe.mitre.org/data/definitions/200.html), this vulnerability refers to system or internal information (such as debugging output, stack traces, error messages, etc.) which is displayed to an end user.\n\nFor example, Rails provides detailed exception reports by default in the development environment, but it is turned off by default in production:\n\n    # Full error reports are disabled\n    config.consider_all_requests_local = false\n\nBrakeman warns if this setting is `true` in production or there is a `show_detailed_exceptions?` method in a controller which does not return `false`.\n"
  },
  {
    "path": "docs/warning_types/link_to/index.markdown",
    "content": "In the 2.x versions of Rails, `link_to` would not escape the body of the HREF.\n\nFor example, this will popup an alert box:\n\n    link_to \"<script>alert(1)</script>\", \"http://google.com\"\n\nBrakeman warns on cases where the first parameter contains user input.\n"
  },
  {
    "path": "docs/warning_types/link_to_href/index.markdown",
    "content": "Even though Rails will escape the link provided to `link_to`, values starting with `javascript:` or `data:` are unescaped and dangerous.\n\nBrakeman will warn on if user values are used to provide the HREF value in `link_to` or if they are interpolated at the beginning of a string.\n\nThe `--url-safe-methods` option can be used to specify methods which make URLs safe. \n\nSee [here](https://github.com/presidentbeef/brakeman/pull/45) for more details.\n"
  },
  {
    "path": "docs/warning_types/mass_assignment/index.markdown",
    "content": "Mass assignment is a feature of Rails which allows an application to create a record from the values of a hash.\n\nExample:\n\n    User.new(params[:user])\n\nUnfortunately, if there is a user field called `admin` which controls administrator access, now any user can make themselves an administrator.\n\n`attr_accessible` and `attr_protected` can be used to limit mass assignment. However, Brakeman will warn unless `attr_accessible` is used, or mass assignment is completely disabled. \n\nThere are two different mass assignment warnings which can arise. The first is when mass assignment actually occurs, such as the example above. This results in a warning like\n\n    Unprotected mass assignment near line 61: User.new(params[:user])\n\nThe other warning is raised whenever a model is found which does not use `attr_accessible`. This produces generic warnings like\n\n    Mass assignment is not restricted using attr_accessible\n\nwith a list of affected models.\n\nIn Rails 3.1 and newer, mass assignment can easily be disabled:\n\n    config.active_record.whitelist_attributes = true\n\nUnfortunately, it can also easily be bypassed:\n\n    User.new(params[:user], :without_protection => true)\n\nBrakeman will warn on uses of `without_protection`.\n"
  },
  {
    "path": "docs/warning_types/redirect/index.markdown",
    "content": "Unvalidated redirects and forwards are #10 on the [OWASP Top Ten](https://www.owasp.org/index.php/Top_10_2010-A10).\n\nRedirects which rely on user-supplied values can be used to \"spoof\" websites or hide malicious links in otherwise harmless-looking URLs. They can also allow access to restricted areas of a site if the destination is not validated.\n\nBrakeman will raise warnings whenever `redirect_to` appears to be used with a user-supplied value that may allow them to change the `:host` option.\n\nFor example,\n\n    redirect_to params.merge(:action => :home)\n\nwill create a warning like\n\n    Possible unprotected redirect near line 46: redirect_to(params)\n\nThis is because `params` could contain `:host => 'evilsite.com'` which would redirect away from your site and to a malicious site.\n\nIf the first argument to `redirect_to` is a hash, then adding `:only_path => true` will limit the redirect to the current host. Another option is to specify the host explicitly.\n\n    redirect_to params.merge(:only_path => true)\n\n    redirect_to params.merge(:host => 'myhost.com')\n\nIf the first argument is a string, then it is possible to parse the string and extract the path:\n\n    redirect_to URI.parse(some_url).path \n\nIf the URL does not contain a protocol (e.g., `http://`), then you will probably get unexpected results, as `redirect_to` will prepend the current host name and a protocol.\n"
  },
  {
    "path": "docs/warning_types/remote_code_execution/index.markdown",
    "content": "Brakeman reports on several cases of remote code execution, in which a user is able to control and execute code in ways unintended by application authors.\n\nThe obvious form of this is the use of `eval` with user input.\n\nHowever, Brakeman also reports on dangerous uses of `send`, `constantize`, and other methods which allow creation of arbitrary objects or calling of arbitrary methods.\n\n"
  },
  {
    "path": "docs/warning_types/remote_code_execution_yaml_load/index.markdown",
    "content": "As seen in [CVE-2013-0156](https://groups.google.com/d/topic/rubyonrails-security/61bkgvnSGTQ/discussion), calling `YAML.load` with user input can lead to remote execution of arbitrary code. (To see a real point-and-fire exploit, see the [Metasploit payload](https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/multi/http/rails_xml_yaml_code_exec.rb)). While upgrading Rails, disabling XML parsing, or disabling YAML types in XML request parsing will fix the Rails vulnerability, manually passing user input to `YAML.load` remains unsafe.\n\nFor example:\n\n    #Do not do this!\n    YAML.load(params[:file])\n"
  },
  {
    "path": "docs/warning_types/session_manipulation/index.markdown",
    "content": "Session manipulation can occur when an application allows user-input in session keys.\nSince sessions are typically considered a source of truth (e.g. to check the logged-in user or to match CSRF tokens),\nallowing an attacker to manipulate the session may lead to unintended behavior.\n\nFor example:\n\n    user_id = session[params[:name]]\n    current_user = User.find(user_id)\n\nIn this scenario, the attacker can point the `name` parameter to some other session value (for example, `_csrf_token`) that will be interpreted\nas a user ID. If the ID matches an existing account, the attacker will now have access to that account.\n\nTo prevent this type of session manipulation, avoid using user-supplied input as session keys.\n"
  },
  {
    "path": "docs/warning_types/session_setting/index.markdown",
    "content": "Brakeman warns about several different session-related issues.\n\n### HTTP Only\n\nIt is recommended that session cookies be set to `http-only`. This helps prevent stealing of cookies via cross-site scripting.\n\n### Secret Length\n\nBrakeman will warn if the key length for the session cookies is less than 30 characters.\n\n### Session Secret in Version Control\n\nBrakeman will warn if the `config/initializers/secret_token.rb` is included in the version control. It is recommended to exclude `secret_token.rb` from version control and include it in `.gitignore`.\n"
  },
  {
    "path": "docs/warning_types/sql_injection/index.markdown",
    "content": "Injection is #1 on the 2013 [OWASP Top Ten](https://www.owasp.org/index.php/Top_10_2013-A1-Injection) web security risks. SQL injection is when a user is able to manipulate a value which is used unsafely inside a SQL query. This can lead to data leaks, data loss, elevation of privilege, and other unpleasant outcomes.\n\nBrakeman focuses on ActiveRecord methods dealing with building SQL statements.\n\nA basic (Rails 2.x) example looks like this:\n\n    User.first(:conditions => \"username = '#{params[:username]}'\")\n\nBrakeman would produce a warning like this:\n\n    Possible SQL injection near line 30: User.first(:conditions => (\"username = '#{params[:username]}'\")) \n\nThe safe way to do this query is to use a parameterized query:\n\n    User.first(:conditions => [\"username = ?\", params[:username]])\n\nBrakeman also understands the new Rails 3.x way of doing things (and local variables and concatenation):\n\n    username = params[:user][:name].downcase\n    password = params[:user][:password]\n\n    User.first.where(\"username = '\" + username + \"' AND password = '\" + password + \"'\")\n\nThis results in this kind of warning:\n\n    Possible SQL injection near line 37:\n    User.first.where(((((\"username = '\" + params[:user][:name].downcase) + \"' AND password = '\") + params[:user][:password]) + \"'\"))\n\nSee [the Ruby Security Guide](http://guides.rubyonrails.org/security.html#sql-injection) for more information and [Rails-SQLi.org](http://rails-sqli.org) for many examples of SQL injection in Rails.\n"
  },
  {
    "path": "docs/warning_types/ssl_verification_bypass/index.markdown",
    "content": "Simply using SSL isn't enough to ensure the data you are sending is secure. Man in the middle (MITM) attacks are well known and widely used. In some cases, these attacks rely on the client to establish a connection that doesn't check the validity of the SSL certificate presented by the server. In this case, the attacker can present their own certificate and act as a man in the middle.\n\nIn Ruby, this happens when the OpenSSL verification mode is set to `VERIFY_NONE`\n\n    require \"net/https\"\n\trequire \"uri\"\n\n\turi = URI.parse(\"https://ssl-site.com/\")\n\thttp = Net::HTTP.new(uri.host, uri.port)\n\thttp.use_ssl = true\n\thttp.verify_mode = OpenSSL::SSL::VERIFY_NONE\n\n\trequest = Net::HTTP::Get.new(uri.request_uri)\n\n\tresponse = http.request(request)\n\nIn this case, if an invalid certificate was presented, no verification would occur, providing an opportunity for attack. When successful, the data transmitted (cookies, request parameters, POST bodies, etc.) would all be able to be intercepted by the MITM.\n\nBrakeman would produce a warning like this:\n\n    SSL certificate verification was bypassed near line 24: http.verify_mode = OpenSSL::SSL::VERIFY_NONE\n\nTo ensure that SSL verification happens use the following mode:\n\n    http.verify_mode = OpenSSL::SSL::VERIFY_PEER\n\nIf the server certificate is invalid or context.ca_file is not set when verifying peers an OpenSSL::SSL::SSLError will be raised.\n\nFor more information on the impact of this issue, see the paper [The Most Dangerous Code in the World](https://www.cs.utexas.edu/~shmat/shmat_ccs12.pdf).\n"
  },
  {
    "path": "docs/warning_types/template_injection/index.markdown",
    "content": "User input passed into ruby templates that are evaluated is VERY dangerous, so this will always raise a warning. Brakeman looks foir calls of the form:\n\n```ruby\n  ERB.new(user_input).result\n```\n"
  },
  {
    "path": "docs/warning_types/unsafe_deserialization/index.markdown",
    "content": "Objects in Ruby may be serialized to strings. The main method for doing so is the built-in `Marshal` class. The `YAML`, `JSON`, and `CSV` libraries also have methods for dumping Ruby objects into strings, and then creating objects from those strings.\n\nDeserialization of arbitrary objects can lead to [remote code execution](/docs/warning_types/remote_code_execution), as was demonstrated with [CVE-2013-0156](https://groups.google.com/d/msg/rubyonrails-security/61bkgvnSGTQ/nehwjA8tQ8EJ). \n\nBrakeman warns when loading user input with `Marshal`, `YAML`, or `CSV`. `JSON` is covered by the checks for [CVE-2013-0333](https://groups.google.com/d/msg/rubyonrails-security/1h2DR63ViGo/GOUVafeaF1IJ)\n"
  },
  {
    "path": "docs/warning_types/unscoped_find/index.markdown",
    "content": "Unscoped `find` (and related methods) are a form of [Direct Object Reference](https://www.owasp.org/index.php/Top_10_2013-A4-Insecure_Direct_Object_References). Models which belong to another model should typically be accessed via a scoped query.\n\nFor example, if an `Account` belongs to a `User`, then this may be an unsafe unscoped find:\n\n    Account.find(params[:id])\n\nDepending on the action, this could allow an attacker to access any account they wish.\n\nInstead, it should be scoped to the currently logged-in user:\n\n    current_user = User.find(session[:user_id])\n    current_user.accounts.find(params[:id])\n"
  },
  {
    "path": "gem_common.rb",
    "content": "module Brakeman\n  module GemDependencies\n    def self.dev_dependencies spec\n      spec.add_development_dependency \"minitest\", \">= 6.0\"\n      spec.add_development_dependency \"minitest-ci\"\n      spec.add_development_dependency \"minitest-mock\"\n      spec.add_development_dependency \"simplecov\"\n    end\n\n    def self.base_dependencies spec\n      spec.add_dependency \"parallel\", \"~>1.20\"\n      spec.add_dependency \"ruby_parser\", \"~>3.22.0\"\n      spec.add_dependency \"sexp_processor\", \"~> 4.7\"\n      spec.add_dependency \"ruby2ruby\", \"~>2.5.1\"\n      spec.add_dependency \"racc\"\n    end\n\n    def self.extended_dependencies spec\n      spec.add_dependency \"csv\"\n      spec.add_dependency \"terminal-table\", \"< 5.0\"\n      spec.add_dependency \"highline\", \"~>3.0\"\n      spec.add_dependency \"erubi\", \"~>1.13\"\n      spec.add_dependency \"haml\", \"< 7.0\"\n      spec.add_dependency \"slim\", \">=1.3.6\", \"< 5.3\"\n      spec.add_dependency \"rexml\", \"~>3.0\"\n      spec.add_dependency \"reline\", \"~>0.6\"\n      spec.add_dependency \"prism\", \"~>1.0\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/app_tree.rb",
    "content": "require 'pathname'\nrequire 'brakeman/file_path'\n\nmodule Brakeman\n  class AppTree\n    VIEW_EXTENSIONS = %w[html.erb html.haml rhtml js.erb html.slim].join(\",\")\n\n    attr_reader :root\n\n    def self.from_options(options)\n      root = File.expand_path options[:app_path]\n\n      # Convert files into Regexp for matching\n      init_options = {}\n      if options[:skip_files]\n        init_options[:skip_files] = regex_for_paths(options[:skip_files])\n      end\n\n      if options[:only_files]\n        init_options[:only_files] = regex_for_paths(options[:only_files])\n      end\n      init_options[:additional_libs_path] = options[:additional_libs_path]\n      init_options[:engine_paths] = options[:engine_paths]\n      init_options[:skip_vendor] = options[:skip_vendor]\n      init_options[:follow_symlinks] = options[:follow_symlinks]\n\n      new(root, init_options)\n    end\n\n    # Accepts an array of filenames and paths with the following format and\n    # returns a Regexp to match them:\n    #   * \"path1/file1.rb\" - Matches a specific filename in the project directory.\n    #   * \"path1/\" - Matches any path that contains \"path1\" in the project directory.\n    #   * \"/path1/ - Matches any path that is rooted at \"path1\" in the project directory.\n    #\n    # TODO: This is wacky and I don't like it.\n    def self.regex_for_paths(paths)\n      path_regexes = paths.map do |f|\n        # If path ends in a file separator then we assume it is a path rather\n        # than a filename.\n        if f.end_with?(File::SEPARATOR)\n          # If path starts with a file separator then we assume that they\n          # want the project relative path to start with this path prefix.\n          if f.start_with?(File::SEPARATOR)\n            \"\\\\A#{Regexp.escape f}\"\n          # If it ends in a file separator, but does not begin with a file\n          # separator then we assume the path can match any path component in\n          # the project.\n          else\n            Regexp.escape f\n          end\n        else\n          \"#{Regexp.escape f}\\\\z\"\n        end\n      end\n      Regexp.new(\"(?:#{path_regexes.join(\"|\")})\")\n    end\n    private_class_method(:regex_for_paths)\n\n    def initialize(root, init_options = {})\n      @root = root\n      @project_root_path = Pathname.new(@root)\n      @skip_files = init_options[:skip_files]\n      @only_files = init_options[:only_files]\n      @additional_libs_path = init_options[:additional_libs_path] || []\n      @engine_paths = init_options[:engine_paths] || []\n      @absolute_engine_paths = @engine_paths.select { |path| path.start_with?(File::SEPARATOR) }\n      @relative_engine_paths = @engine_paths - @absolute_engine_paths\n      @skip_vendor = init_options[:skip_vendor]\n      @follow_symlinks = init_options[:follow_symlinks]\n      @gemspec = nil\n      @root_search_pattern = nil\n    end\n\n    # Create a new Brakeman::FilePath\n    def file_path(path)\n      Brakeman::FilePath.from_app_tree(self, path)\n    end\n\n    # Should only be used by Brakeman::FilePath.\n    # Use AppTree#file_path(path).absolute instead.\n    def expand_path(path)\n      File.expand_path(path, @root)\n    end\n\n    # Should only be used by Brakeman::FilePath\n    # Use AppTree#file_path(path).relative instead.\n    def relative_path(path)\n      pname = Pathname.new path\n      if path and not path.empty? and pname.absolute?\n        pname.relative_path_from(Pathname.new(self.root)).to_s\n      else\n        path\n      end\n    end\n\n    def exists?(path)\n      if path.is_a? Brakeman::FilePath\n        path.exists?\n      else\n        File.exist?(File.join(@root, path))\n      end\n    end\n\n    def ruby_file_paths\n      find_paths(\".\").uniq\n    end\n\n    def initializer_paths\n      @initializer_paths ||= prioritize_concerns(find_paths(\"config/initializers\"))\n    end\n\n    def controller_paths\n      @controller_paths ||= prioritize_concerns(find_paths(\"app/**/controllers\"))\n    end\n\n    def model_paths\n      @model_paths ||= prioritize_concerns(find_paths(\"app/**/models\"))\n    end\n\n    def template_paths\n      @template_paths ||= find_paths(\".\", \"*.{#{VIEW_EXTENSIONS}}\") +\n        find_paths(\".\", \"*.{erb,haml,slim}\").reject { |path| File.basename(path).count(\".\") > 1 }\n    end\n\n    def layout_exists?(name)\n      !Dir.glob(\"#{root_search_pattern}app/views/layouts/#{name}.html.{erb,haml,slim}\").empty?\n    end\n\n    def lib_paths\n      @lib_files ||= find_paths(\"lib\").reject { |path| path.relative.include? \"/generators/\" or path.relative.include? \"lib/tasks/\" or path.relative.include? \"lib/templates/\" } +\n                     find_additional_lib_paths +\n                     find_helper_paths +\n                     find_job_paths\n    end\n\n    def gemspec\n      return @gemspec unless @gemspec.nil?\n\n      gemspecs =  Dir.glob(File.join(@root, \"*.gemspec\"))\n\n      if gemspecs.length > 1 or gemspecs.empty?\n        @gemspec = false\n      else\n        @gemspec = file_path(File.basename(gemspecs.first))\n      end\n    end\n\n\n    # Call this to be able to marshall the AppTree\n    def marshallable\n      @initializer_paths = @initializer_paths.to_a\n      @controller_paths = @controller_paths.to_a\n      @template_paths = @template_paths.to_a\n      @lib_files = @file_paths.to_a\n\n      self\n    end\n\n  private\n\n    def find_helper_paths\n      find_paths \"app/helpers\"\n    end\n\n    def find_job_paths\n      find_paths \"app/jobs\"\n    end\n\n    def find_additional_lib_paths\n      @additional_libs_path.collect{ |path| find_paths path }.flatten\n    end\n\n    def find_paths(directory, extensions = \".rb\")\n      select_files(glob_files(directory, \"*\", extensions))\n    end\n\n    def glob_files(directory, name, extensions = \".rb\")\n      if @follow_symlinks\n        root_directory = \"#{root_search_pattern}#{directory}\"\n        patterns = [\"#{root_directory}/**/#{name}#{extensions}\"]\n\n        Dir.glob(\"#{root_directory}/**/*\", File::FNM_DOTMATCH).each do |path|\n          if File.symlink?(path) && File.directory?(path)\n            symlink_target = File.readlink(path)\n            if Pathname.new(symlink_target).relative?\n              symlink_target = File.join(File.dirname(path), symlink_target)\n            end\n            patterns << \"#{search_pattern(symlink_target)}/**/#{name}#{extensions}\"\n          end\n        end\n\n        files = patterns.flat_map { |pattern| Dir.glob(pattern) }\n        files.uniq.lazy\n      else\n        if directory == '.'\n          pattern = File.join(top_directories_pattern, '**', \"#{name}#{extensions}\")\n        else\n          pattern = \"#{root_search_pattern}#{directory}/**/#{name}#{extensions}\"\n        end\n\n        Dir.glob(pattern).lazy\n      end\n    end\n\n    def select_files(paths)\n      paths = select_only_files(paths)\n      paths = reject_skipped_files(paths)\n      paths = convert_to_file_paths(paths)\n      paths = reject_global_excludes(paths)\n      paths = reject_directories(paths)\n      paths\n    end\n\n    def reject_directories(paths)\n      paths.reject do |path|\n        Brakeman.logger.spin\n        File.directory?(path)\n      end\n    end\n\n    def select_only_files(paths)\n      return paths unless @only_files\n\n      paths.select do |path|\n        Brakeman.logger.spin\n        match_path @only_files, path\n      end\n    end\n\n    def reject_skipped_files(paths)\n      return paths unless @skip_files\n\n      paths.reject do |path|\n        Brakeman.logger.spin\n        match_path @skip_files, path\n      end\n    end\n\n    EXCLUDED_PATHS = regex_for_paths %w[\n      generators/\n      lib/tasks/\n      lib/templates/\n      db/\n      spec/\n      test/\n      tmp/\n    ]\n\n    def reject_global_excludes(paths)\n      paths.reject do |path|\n        relative_path = path.relative\n\n        if @skip_vendor and relative_path.include? 'vendor/' and !in_engine_paths?(path) and !in_add_libs_paths?(path)\n          true\n        else\n          match_path EXCLUDED_PATHS, path\n        end\n      end\n    end\n\n    def in_engine_paths?(path)\n      @engine_paths.any? { |p| path.absolute.include?(p) }\n    end\n\n    def in_add_libs_paths?(path)\n      @additional_libs_path.any? { |p| path.absolute.include?(p) }\n    end\n\n    def match_path files, path\n      # TODO: Converting to Pathnames and Strings seems like a lot\n      # of converting that could perhaps all be handled in Brakeman::FilePath\n      # instead?\n      absolute_path = Pathname.new(path)\n\n      # relative root never has a leading separator. But, we use a leading\n      # separator in a @skip_files entry to imply that a directory is\n      # \"absolute\" with respect to the project directory.\n      #\n      # Also directories need a trailing separator.\n      project_relative_path = if File.directory?(path)\n        File.join(\n          File::SEPARATOR,\n          absolute_path.relative_path_from(@project_root_path).to_s,\n          File::SEPARATOR\n        )\n      else\n        File.join(\n          File::SEPARATOR,\n          absolute_path.relative_path_from(@project_root_path).to_s\n        )\n      end\n\n      files.match(project_relative_path)\n    end\n\n    def top_directories_pattern\n      top_dirs = convert_to_file_paths(Dir.glob(File.join(root_search_pattern, '*/')))\n      top_dirs.reject! { |d| File.symlink?(d) or !File.directory?(d) }\n      top_dirs = reject_global_excludes(top_dirs)\n      top_dirs = reject_skipped_files(top_dirs)\n\n      if top_dirs.empty?\n        # Fall back to searching everything, otherwise the empty pattern\n        # will start searching from the global root\n        root_search_pattern\n      else\n        \"{#{top_dirs.join(',')}}\"\n      end\n    end\n\n    def root_search_pattern\n      return @root_search_pattern if @root_search_pattern\n      @root_search_pattern = search_pattern(@root)\n    end\n\n    def search_pattern(root_dir)\n      abs = @absolute_engine_paths.to_a.map { |path| path.gsub(/#{File::SEPARATOR}+$/, '') }\n      rel = @relative_engine_paths.to_a.map { |path| path.gsub(/#{File::SEPARATOR}+$/, '') }\n\n      roots = ([root_dir] + abs).join(\",\")\n      rel_engines = (rel + [\"\"]).join(\"/,\")\n      \"{#{roots}}/{#{rel_engines}}\"\n    end\n\n    def prioritize_concerns paths\n      paths.partition { |path| path.relative.include? \"concerns\" }.flatten\n    end\n\n    def convert_to_file_paths paths\n      paths.map { |path| file_path(path) }\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/call_index.rb",
    "content": "require 'set'\n\n#Stores call sites to look up later.\nclass Brakeman::CallIndex\n\n  #Initialize index with calls from FindAllCalls\n  def initialize calls\n    @calls_by_method = {}\n    @calls_by_target = {}\n\n    index_calls calls\n  end\n\n  #Find calls matching specified option hash.\n  #\n  #Options:\n  #\n  #  * :target - symbol, array of symbols, or regular expression to match target(s)\n  #  * :method - symbol, array of symbols, or regular expression to match method(s)\n  #  * :chained - boolean, whether or not to match against a whole method chain (false by default)\n  #  * :nested - boolean, whether or not to match against a method call that is a target itself (false by default)\n  def find_calls options\n    target = options[:target] || options[:targets]\n    method = options[:method] || options[:methods]\n    nested = options[:nested]\n\n    if options[:chained]\n      return find_chain options\n    #Find by narrowest category\n    elsif target.is_a? Array and method.is_a? Array\n      if target.length > method.length\n        calls = filter_by_target calls_by_methods(method), target\n      else\n        calls = calls_by_targets(target)\n        calls = filter_by_method calls, method\n      end\n\n    elsif target.is_a? Regexp and method\n      calls = filter_by_target(calls_by_method(method), target)\n\n    elsif method.is_a? Regexp and target\n      calls = filter_by_method(calls_by_target(target), method)\n\n    #Find by target, then by methods, if provided\n    elsif target\n      calls = calls_by_target target\n\n      if calls and method\n        calls = filter_by_method calls, method\n      end\n\n    #Find calls with no explicit target\n    #with either :target => nil or :target => false\n    elsif (options.key? :target or options.key? :targets) and not target and method\n      calls = calls_by_method method\n      calls = filter_by_target calls, nil\n\n    #Find calls by method\n    elsif method\n      calls = calls_by_method method\n    else\n      raise \"Invalid arguments to CallCache#find_calls: #{options.inspect}\"\n    end\n\n    return [] if calls.nil?\n\n    #Remove calls that are actually targets of other calls\n    #Unless those are explicitly desired\n    calls = filter_nested calls unless nested\n\n    calls\n  end\n\n  def remove_template_indexes template_name = nil\n    [@calls_by_method, @calls_by_target].each do |calls_by|\n      calls_by.each do |_name, calls|\n        calls.delete_if do |call|\n          from_template call, template_name\n        end\n      end\n    end\n  end\n\n  def remove_indexes_by_class classes\n    [@calls_by_method, @calls_by_target].each do |calls_by|\n      calls_by.each do |_name, calls|\n        calls.delete_if do |call|\n          call[:location][:type] == :class and classes.include? call[:location][:class]\n        end\n      end\n    end\n  end\n\n  def remove_indexes_by_file file\n    [@calls_by_method, @calls_by_target].each do |calls_by|\n      calls_by.each do |_name, calls|\n        calls.delete_if do |call|\n          call[:location][:file] == file\n        end\n      end\n    end\n  end\n\n  def index_calls calls\n    calls.each do |call|\n      @calls_by_method[call[:method]] ||= []\n      @calls_by_method[call[:method]] << call\n\n      target = call[:target]\n\n      if not target.is_a? Sexp\n        @calls_by_target[target] ||= []\n        @calls_by_target[target] << call\n      elsif target.node_type == :params or target.node_type == :session\n        @calls_by_target[target.node_type] ||= []\n        @calls_by_target[target.node_type] << call\n      end\n    end\n  end\n\n  private\n\n  def find_chain options\n    target = options[:target] || options[:targets]\n    method = options[:method] || options[:methods]\n\n    calls = calls_by_method method\n\n    return [] if calls.nil?\n\n    calls = filter_by_chain calls, target\n  end\n\n  def calls_by_target target\n    case target\n    when Array\n      calls_by_targets target\n    when Regexp\n      calls_by_targets_regex target\n    else\n      @calls_by_target[target] || []\n    end\n  end\n\n  def calls_by_targets targets\n    calls = []\n\n    targets.each do |target|\n      calls.concat @calls_by_target[target] if @calls_by_target.key? target\n    end\n\n    calls\n  end\n\n  def calls_by_targets_regex targets_regex\n    calls = []\n\n    @calls_by_target.each do |key, value|\n      case key\n      when String, Symbol\n        calls.concat value if key.match targets_regex\n      end\n    end\n\n    calls\n  end\n\n  def calls_by_method method\n    case method\n    when Array\n      calls_by_methods method\n    when Regexp\n      calls_by_methods_regex method\n    else\n      @calls_by_method[method.to_sym] || []\n    end\n  end\n\n  def calls_by_methods methods\n    methods = methods.map { |m| m.to_sym }\n    calls = []\n\n    methods.each do |method|\n      calls.concat @calls_by_method[method] if @calls_by_method.key? method\n    end\n\n    calls\n  end\n\n  def calls_by_methods_regex methods_regex\n    calls = []\n\n    @calls_by_method.each do |key, value|\n      calls.concat value if key.match methods_regex\n    end\n\n    calls\n  end\n\n  def filter calls, key, value\n    case value\n    when Array\n      values = Set.new value\n\n      calls.select do |call|\n        values.include? call[key]\n      end\n    when Regexp\n      calls.select do |call|\n        case call[key]\n        when String, Symbol\n          call[key].match value\n        end\n      end\n    else\n      calls.select do |call|\n        call[key] == value\n      end\n    end\n  end\n\n  def filter_by_method calls, method\n    filter calls, :method, method\n  end\n\n  def filter_by_target calls, target\n    filter calls, :target, target\n  end\n\n  def filter_nested calls\n    filter calls, :nested, false\n  end\n\n  def filter_by_chain calls, target\n    case target\n    when Array\n      targets = Set.new target\n\n      calls.select do |call|\n        targets.include? call[:chain].first\n      end\n    when Regexp\n      calls.select do |call|\n        case call[:chain].first\n        when String, Symbol\n          call[:chain].first.match target\n        end\n      end\n    else\n      calls.select do |call|\n        call[:chain].first == target\n      end\n    end\n  end\n\n  def from_template call, template_name\n    return false unless call[:location][:type] == :template\n    return true if template_name.nil?\n    call[:location][:template] == template_name\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/base_check.rb",
    "content": "require 'brakeman/processors/output_processor'\nrequire 'brakeman/processors/lib/processor_helper'\nrequire 'brakeman/warning'\nrequire 'brakeman/util'\nrequire 'brakeman/messages'\n\n#Basis of vulnerability checks.\nclass Brakeman::BaseCheck < Brakeman::SexpProcessor\n  include Brakeman::ProcessorHelper\n  include Brakeman::SafeCallHelper\n  include Brakeman::Util\n  include Brakeman::Messages\n  attr_reader :tracker, :warnings\n\n  # This is for legacy support.\n  # Use :high, :medium, or :low instead when creating warnings.\n  CONFIDENCE = Brakeman::Warning::CONFIDENCE\n\n  Match = Struct.new(:type, :match)\n\n  class << self\n    attr_accessor :name\n\n    def inherited(subclass)\n      subclass.name = subclass.to_s.match(/^Brakeman::(.*)$/)[1]\n    end\n  end\n\n  #Initialize Check with Checks.\n  def initialize(tracker)\n    super()\n    @app_tree = tracker.app_tree\n    @results = [] #only to check for duplicates\n    @warnings = []\n    @tracker = tracker\n    @string_interp = false\n    @current_set = nil\n    @current_template = @current_module = @current_class = @current_method = nil\n    @active_record_models = nil\n    @mass_assign_disabled = nil\n    @has_user_input = nil\n    @in_array = false\n    @safe_input_attributes = Set[:to_i, :to_f, :arel_table, :id, :uuid]\n    @comparison_ops  = Set[:==, :!=, :>, :<, :>=, :<=]\n  end\n\n  #Add result to result list, which is used to check for duplicates\n  def add_result result\n    location = get_location result\n    location, line = get_location result\n\n    @results << [line, location, result]\n  end\n\n  #Default Sexp processing. Iterates over each value in the Sexp\n  #and processes them if they are also Sexps.\n  def process_default exp\n    exp.each do |e|\n      process e if sexp? e\n    end\n\n    exp\n  end\n\n  #Process calls and check if they include user input\n  def process_call exp\n    unless @comparison_ops.include? exp.method\n      process exp.target if sexp? exp.target\n      process_call_args exp\n    end\n\n    target = exp.target\n\n    unless always_safe_method? exp.method\n      if params? target\n        @has_user_input = Match.new(:params, exp)\n      elsif cookies? target\n        @has_user_input = Match.new(:cookies, exp)\n      elsif request_headers? target\n        @has_user_input = Match.new(:request, exp)\n      elsif sexp? target and model_name? target[1] #TODO: Can this be target.target?\n        @has_user_input = Match.new(:model, exp)\n      end\n    end\n\n    exp\n  end\n\n  def process_if exp\n    #This is to ignore user input in condition\n    current_user_input = @has_user_input\n    process exp.condition\n    @has_user_input = current_user_input\n\n    process exp.then_clause if sexp? exp.then_clause\n    process exp.else_clause if sexp? exp.else_clause\n\n    exp\n  end\n\n  #Note that params are included in current expression\n  def process_params exp\n    @has_user_input = Match.new(:params, exp)\n    exp\n  end\n\n  #Note that cookies are included in current expression\n  def process_cookies exp\n    @has_user_input = Match.new(:cookies, exp)\n    exp\n  end\n\n  def process_array exp\n    @in_array = true\n    process_default exp\n  ensure\n    @in_array = false\n  end\n\n  #Does not actually process string interpolation, but notes that it occurred.\n  def process_dstr exp\n    unless array_interp? exp or @string_interp # don't overwrite existing value\n      @string_interp = Match.new(:interp, exp)\n    end\n\n    process_default exp\n  end\n\n  private\n\n  # Checking for\n  #\n  #   %W[#{a}]\n  #\n  # which will be parsed as\n  #\n  #    s(:array, s(:dstr, \"\", s(:evstr, s(:call, nil, :a))))\n  def array_interp? exp\n    @in_array and\n      string_interp? exp and\n      exp[1] == \"\".freeze and\n      exp.length == 3 # only one interpolated value\n  end\n\n  def always_safe_method? meth\n    @safe_input_attributes.include? meth or\n      @comparison_ops.include? meth\n  end\n\n  def boolean_method? method\n    method[-1] == \"?\"\n  end\n\n  TEMP_FILE_PATH = [\n    s(:call, s(:call, s(:const, :Tempfile), :new), :path).freeze,\n    s(:call, s(:call, s(:const, :Tempfile), :create), :path).freeze\n  ].freeze\n\n  def temp_file_path? exp\n    TEMP_FILE_PATH.include? exp\n  end\n\n  #Report a warning\n  def warn options\n    extra_opts = { :check => self.class.to_s }\n\n    if options[:file]\n      options[:file] = @app_tree.file_path(options[:file])\n    end\n\n    @warnings << Brakeman::Warning.new(options.merge(extra_opts))\n  end\n\n  #Run _exp_ through OutputProcessor to get a nice String.\n  def format_output exp\n    Brakeman::OutputProcessor.new.format(exp).gsub(/\\r|\\n/, \"\")\n  end\n\n  #Checks if mass assignment is disabled globally in an initializer.\n  def mass_assign_disabled?\n    return @mass_assign_disabled unless @mass_assign_disabled.nil?\n\n    @mass_assign_disabled = false\n\n    if version_between?(\"3.1.0\", \"3.9.9\") and\n      tracker.config.whitelist_attributes?\n\n      @mass_assign_disabled = true\n    elsif tracker.options[:rails4] && (!tracker.config.has_gem?(:protected_attributes) || tracker.config.whitelist_attributes?)\n\n      @mass_assign_disabled = true\n    else\n      #Check for ActiveRecord::Base.send(:attr_accessible, nil)\n      tracker.find_call(target: :\"ActiveRecord::Base\", method: :attr_accessible).each do |result|\n        call = result[:call]\n\n        if call? call\n          if call.first_arg == Sexp.new(:nil)\n            @mass_assign_disabled = true\n            break\n          end\n        end\n      end\n\n      unless @mass_assign_disabled\n        #Check for\n        #  class ActiveRecord::Base\n        #    attr_accessible nil\n        #  end\n        tracker.check_initializers([], :attr_accessible).each do |result|\n          if result.module == \"ActiveRecord\" and result.result_class == :Base\n            arg = result.call.first_arg\n\n            if arg.nil? or node_type? arg, :nil\n              @mass_assign_disabled = true\n              break\n            end\n          end\n        end\n      end\n    end\n\n    #There is a chance someone is using Rails 3.x and the `strong_parameters`\n    #gem and still using hack above, so this is a separate check for\n    #including ActiveModel::ForbiddenAttributesProtection in\n    #ActiveRecord::Base in an initializer.\n    if not @mass_assign_disabled and version_between?(\"3.1.0\", \"3.9.9\") and tracker.config.has_gem? :strong_parameters\n      matches = tracker.check_initializers([], :include)\n      forbidden_protection = Sexp.new(:colon2, Sexp.new(:const, :ActiveModel), :ForbiddenAttributesProtection)\n\n      matches.each do |result|\n        if call? result.call and result.call.first_arg == forbidden_protection\n          @mass_assign_disabled = true\n        end\n      end\n\n      unless @mass_assign_disabled\n        tracker.find_call(target: :\"ActiveRecord::Base\", method: [:send, :include]).each do |result|\n          call = result[:call]\n          if call? call and (call.first_arg == forbidden_protection or call.second_arg == forbidden_protection)\n            @mass_assign_disabled = true\n          end\n        end\n      end\n    end\n\n    @mass_assign_disabled\n  end\n\n  def original? result\n    return false if result[:call].original_line or duplicate? result\n    add_result result\n    true\n  end\n\n  #This is to avoid reporting duplicates. Checks if the result has been\n  #reported already from the same line number.\n  def duplicate? result, location = nil\n    location, line = get_location result\n\n    @results.each do |r|\n      if r[0] == line and r[1] == location\n        if tracker.options[:combine_locations]\n          return true\n        elsif r[2] == result\n          return true\n        end\n      end\n    end\n\n    false\n  end\n\n  def get_location result\n    if result.is_a? Hash\n      line = result[:call].original_line || result[:call].line\n    elsif sexp? result\n      line = result.original_line || result.line\n    else\n      raise ArgumentError\n    end\n\n    location ||= (@current_template && @current_template.name) || @current_class || @current_module || @current_set || result[:location][:class] || result[:location][:template] || result[:location][:file].to_s\n\n    location = location[:name] if location.is_a? Hash\n    location = location.name if location.is_a? Brakeman::Collection\n    location = location.to_sym\n\n    return location, line\n  end\n\n  #Checks if _exp_ includes user input in the form of cookies, parameters,\n  #request environment, or model attributes.\n  #\n  #If found, returns a struct containing a type (:cookies, :params, :request, :model) and\n  #the matching expression (Match#type and Match#match).\n  #\n  #Returns false otherwise.\n  def include_user_input? exp\n    @has_user_input = false\n    process exp\n    @has_user_input\n  end\n\n  #This is used to check for user input being used directly.\n  #\n  ##If found, returns a struct containing a type (:cookies, :params, :request) and\n  #the matching expression (Match#type and Match#match).\n  #\n  #Returns false otherwise.\n  def has_immediate_user_input? exp\n    if exp.nil?\n      false\n    elsif call? exp and not always_safe_method? exp.method\n      if params? exp\n        return Match.new(:params, exp)\n      elsif cookies? exp\n        return Match.new(:cookies, exp)\n      elsif request_headers? exp\n        return Match.new(:request, exp)\n      else\n        has_immediate_user_input? exp.target\n      end\n    elsif sexp? exp\n      case exp.node_type\n      when :dstr\n        exp.each do |e|\n          if sexp? e\n            match = has_immediate_user_input?(e)\n            return match if match\n          end\n        end\n        false\n      when :evstr\n        if sexp? exp.value\n          if exp.value.node_type == :rlist\n            exp.value.each_sexp do |e|\n              match = has_immediate_user_input?(e)\n              return match if match\n            end\n            false\n          else\n            has_immediate_user_input? exp.value\n          end\n        end\n      when :format\n        has_immediate_user_input? exp.value\n      when :if\n        (sexp? exp.then_clause and has_immediate_user_input? exp.then_clause) or\n        (sexp? exp.else_clause and has_immediate_user_input? exp.else_clause)\n      when :or\n        has_immediate_user_input? exp.lhs or\n        has_immediate_user_input? exp.rhs\n      when :splat, :kwsplat\n        exp.each_sexp do |e|\n          match = has_immediate_user_input?(e)\n          return match if match\n        end\n\n        false\n      when :hash\n        if kwsplat? exp\n          exp[1].each_sexp do |e|\n            match = has_immediate_user_input?(e)\n            return match if match\n          end\n\n          false\n        end\n      else\n        false\n      end\n    end\n  end\n\n  #Checks for a model attribute at the top level of the\n  #expression.\n  def has_immediate_model? exp, out = nil\n    out = exp if out.nil?\n\n    if sexp? exp and exp.node_type == :output\n      exp = exp.value\n    end\n\n    if call? exp\n      target = exp.target\n      method = exp.method\n\n      if always_safe_method? method\n        false\n      elsif call? target and not method.to_s[-1,1] == \"?\"\n        if has_immediate_model?(target, out)\n          exp\n        else\n          false\n        end\n      elsif model_name? target\n        exp\n      else\n        false\n      end\n    elsif sexp? exp\n      case exp.node_type\n      when :dstr\n        exp.each do |e|\n          if sexp? e and match = has_immediate_model?(e, out)\n            return match\n          end\n        end\n        false\n      when :evstr\n        if sexp? exp.value\n          if exp.value.node_type == :rlist\n            exp.value.each_sexp do |e|\n              if match = has_immediate_model?(e, out)\n                return match\n              end\n            end\n            false\n          else\n            has_immediate_model? exp.value, out\n          end\n        end\n      when :format\n        has_immediate_model? exp.value, out\n      when :if\n        ((sexp? exp.then_clause and has_immediate_model? exp.then_clause, out) or\n         (sexp? exp.else_clause and has_immediate_model? exp.else_clause, out))\n      when :or\n        has_immediate_model? exp.lhs or\n        has_immediate_model? exp.rhs\n      else\n        false\n      end\n    end\n  end\n\n  #Checks if +exp+ is a model name.\n  #\n  #Prior to using this method, either @tracker must be set to\n  #the current tracker, or else @models should contain an array of the model\n  #names, which is available via tracker.models.keys\n  def model_name? exp\n    @models ||= @tracker.models.keys\n\n    if exp.is_a? Symbol\n      @models.include? exp\n    elsif call? exp and exp.target.nil? and exp.method == :current_user\n      true\n    elsif sexp? exp\n      @models.include? class_name(exp)\n    else\n      false\n    end\n  end\n\n  #Returns true if +target+ is in +exp+\n  def include_target? exp, target\n    return false unless call? exp\n\n    exp.each do |e|\n      return true if e == target or include_target? e, target\n    end\n\n    false\n  end\n\n  def lts_version? version\n    tracker.config.has_gem? :'railslts-version' and\n    version_between? version, \"2.3.18.99\", tracker.config.gem_version(:'railslts-version')\n  end\n\n  def version_between? low_version, high_version, current_version = nil\n    tracker.config.version_between? low_version, high_version, current_version\n  end\n\n  def gemfile_or_environment gem_name = :rails\n    if gem_name and info = tracker.config.get_gem(gem_name.to_sym)\n      info\n    elsif @app_tree.exists?(\"Gemfile\")\n      @app_tree.file_path \"Gemfile\"\n    elsif @app_tree.exists?(\"gems.rb\")\n      @app_tree.file_path \"gems.rb\"\n    else\n      @app_tree.file_path \"config/environment.rb\"\n    end\n  end\n\n  def self.description\n    @description\n  end\n\n  def active_record_models\n    return @active_record_models if @active_record_models\n\n    @active_record_models = {}\n\n    tracker.models.each do |name, model|\n      if model.ancestor? :\"ActiveRecord::Base\"\n        @active_record_models[name] = model\n      end\n    end\n\n    @active_record_models\n  end\n\n  STRING_METHODS = Set[:<<, :+, :concat, :prepend]\n  private_constant :STRING_METHODS\n\n  def string_building? exp\n    return false unless call? exp and STRING_METHODS.include? exp.method\n\n    node_type? exp.target, :str, :dstr or\n    node_type? exp.first_arg, :str, :dstr or\n    string_building? exp.target or\n    string_building? exp.first_arg\n  end\n\n  I18N_CLASS = s(:const, :I18n)\n\n  def locale_call? exp\n    return unless call? exp\n\n    (exp.target == I18N_CLASS and\n     exp.method == :locale) or\n    locale_call? exp.target\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_basic_auth.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Checks if password is stored in controller\n#when using http_basic_authenticate_with\n#\n#Only for Rails >= 3.1\nclass Brakeman::CheckBasicAuth < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for the use of http_basic_authenticate_with\"\n\n  def run_check\n    return if version_between? \"0.0.0\", \"3.0.99\"\n\n    check_basic_auth_filter\n    check_basic_auth_request\n  end\n\n  def check_basic_auth_filter\n    controllers = tracker.controllers.select do |_name, c|\n      c.options[:http_basic_authenticate_with]\n    end\n\n    Hash[controllers].each do |name, controller|\n      controller.options[:http_basic_authenticate_with].each do |call|\n\n        if pass = get_password(call) and string? pass\n          warn :controller => name,\n              :warning_type => \"Basic Auth\",\n              :warning_code => :basic_auth_password,\n              :message => \"Basic authentication password stored in source code\",\n              :code => call,\n              :confidence => :high,\n              :file => controller.file,\n              :cwe_id => [259]\n          break\n        end\n      end\n    end\n  end\n\n  # Look for\n  #  authenticate_or_request_with_http_basic do |username, password|\n  #    username == \"foo\" && password == \"bar\"\n  #  end\n  def check_basic_auth_request\n    tracker.find_call(:target => nil, :method => :authenticate_or_request_with_http_basic).each do |result|\n      if include_password_literal? result\n          warn :result => result,\n              :code => @include_password,\n              :warning_type => \"Basic Auth\",\n              :warning_code => :basic_auth_password,\n              :message => \"Basic authentication password stored in source code\",\n              :confidence => :high,\n              :cwe_id => [259]\n      end\n    end\n  end\n\n  # Check if the block of a result contains a comparison of password to string\n  def include_password_literal? result\n    return false if result[:block_args].nil?\n\n    @password_var = result[:block_args].last\n    @include_password = false\n    process result[:block]\n    @include_password\n  end\n\n  # Looks for :== calls on password var\n  def process_call exp\n    target = exp.target\n\n    if node_type?(target, :lvar) and\n      target.value == @password_var and\n      exp.method == :== and\n      string? exp.first_arg\n\n      @include_password = exp\n    end\n\n    exp\n  end\n\n  def get_password call\n    arg = call.first_arg\n\n    return false if arg.nil? or not hash? arg\n\n    hash_access(arg, :password)\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_basic_auth_timing_attack.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckBasicAuthTimingAttack < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Check for timing attack in basic auth (CVE-2015-7576)\"\n\n  def run_check\n    @upgrade = case\n               when version_between?(\"0.0.0\", \"3.2.22\")\n                 \"3.2.22.1\"\n               when version_between?(\"4.0.0\", \"4.1.14\")\n                 \"4.1.14.1\"\n               when version_between?(\"4.2.0\", \"4.2.5\")\n                 \"4.2.5.1\"\n               else\n                 return\n               end\n\n    check_basic_auth_call\n  end\n\n  def check_basic_auth_call\n    tracker.find_call(target: nil, method: :http_basic_authenticate_with).each do |result|\n      warn :result => result,\n        :warning_type => \"Timing Attack\",\n        :warning_code => :CVE_2015_7576,\n        :message => msg(\"Basic authentication in \", msg_version(rails_version), \" is vulnerable to timing attacks. Upgrade to \", msg_version(@upgrade)),\n        :confidence => :high,\n        :link => \"https://groups.google.com/d/msg/rubyonrails-security/ANv0HDHEC3k/mt7wNGxbFQAJ\",\n        :cwe_id => [1254]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_content_tag.rb",
    "content": "require 'brakeman/checks/check_cross_site_scripting'\n\n#Checks for unescaped values in `content_tag`\n#\n#    content_tag :tag, body\n#                       ^-- Unescaped in Rails 2.x\n#\n#    content_tag, :tag, body, attribute => value\n#                                ^-- Unescaped in all versions\n#\n#    content_tag, :tag, body, attribute => value\n#                                            ^\n#                                            |\n#            Escaped by default, can be explicitly escaped\n#            or not by passing in (true|false) as fourth argument\nclass Brakeman::CheckContentTag < Brakeman::CheckCrossSiteScripting\n  Brakeman::Checks.add self\n\n  @description = \"Checks for XSS in calls to content_tag\"\n\n  def run_check\n    @ignore_methods = Set[:button_to, :check_box, :escapeHTML, :escape_once,\n                           :field_field, :fields_for, :h, :hidden_field,\n                           :hidden_field, :hidden_field_tag, :image_tag, :label,\n                           :mail_to, :radio_button, :select,\n                           :submit_tag, :text_area, :text_field,\n                           :text_field_tag, :url_encode, :u, :url_for,\n                           :will_paginate].merge tracker.options[:safe_methods]\n\n    @known_dangerous = []\n    @content_tags = tracker.find_call :target => false, :method => :content_tag\n\n    @models = tracker.models.keys\n    @inspect_arguments = tracker.options[:check_arguments]\n    @mark = nil\n\n    Brakeman.debug \"Checking for XSS in content_tag\"\n    @content_tags.each do |call|\n      process_result call\n    end\n\n    check_cve_2016_6316\n  end\n\n  def process_result result\n    return if duplicate? result\n\n    case result[:location][:type]\n    when :template\n      @current_template = result[:location][:template]\n    when :class\n      @current_class = result[:location][:class]\n      @current_method = result[:location][:method]\n    end\n\n    @current_file = result[:location][:file]\n\n    call = result[:call]\n    args = call.arglist\n\n    tag_name = args[1]\n    content = args[2]\n    attributes = args[3]\n    escape_attr = args[4]\n\n    @matched = false\n\n    #Silly, but still dangerous if someone uses user input in the tag type\n    check_argument result, tag_name\n\n    #Versions before 3.x do not escape body of tag, nor does the rails_xss gem\n    unless @matched or (tracker.options[:rails3] and not raw? content)\n      check_argument result, content\n    end\n\n    # This changed in Rails 6.1.6\n    if version_between? '0.0.0', '6.1.5' \n      #Attribute keys are never escaped, so check them for user input\n      if not @matched and hash? attributes and not request_value? attributes\n        hash_iterate(attributes) do |k, _v|\n          check_argument result, k\n          return if @matched\n        end\n      end\n    end\n\n    #By default, content_tag escapes attribute values passed in as a hash.\n    #But this behavior can be disabled. So only check attributes hash\n    #if they are explicitly not escaped.\n    if not @matched and attributes and (false? escape_attr or cve_2016_6316?)\n      if request_value? attributes or not hash? attributes\n        check_argument result, attributes\n      else #check hash values\n        hash_iterate(attributes) do |_k, v|\n          check_argument result, v\n          return if @matched\n        end\n      end\n    end\n  ensure\n    @current_template = @current_class = @current_method = @current_file = nil\n  end\n\n  def check_argument result, exp\n    #Check contents of raw() calls directly\n    if raw? exp\n      arg = process exp.first_arg\n    else\n      arg = process exp\n    end\n\n    if input = has_immediate_user_input?(arg)\n      message = msg(\"Unescaped \", msg_input(input), \" in \", msg_code(\"content_tag\"))\n\n      add_result result\n\n      warn :result => result,\n        :warning_type => \"Cross-Site Scripting\",\n        :warning_code => :xss_content_tag,\n        :message => message,\n        :user_input => input,\n        :confidence => :high,\n        :link_path => \"content_tag\",\n        :cwe_id => [79]\n\n    elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(arg)\n      unless IGNORE_MODEL_METHODS.include? match.method\n        add_result result\n\n        if likely_model_attribute? match\n          confidence = :high\n        else\n          confidence = :medium\n        end\n\n        warn :result => result,\n          :warning_type => \"Cross-Site Scripting\",\n          :warning_code => :xss_content_tag,\n          :message => msg(\"Unescaped model attribute in \", msg_code(\"content_tag\")),\n          :user_input => match,\n          :confidence => confidence,\n          :link_path => \"content_tag\",\n          :cwe_id => [79]\n      end\n\n    elsif @matched\n      return if @matched.type == :model and tracker.options[:ignore_model_output]\n\n      message = msg(\"Unescaped \", msg_input(@matched), \" in \", msg_code(\"content_tag\"))\n\n      add_result result\n\n      warn :result => result,\n        :warning_type => \"Cross-Site Scripting\",\n        :warning_code => :xss_content_tag,\n        :message => message,\n        :user_input => @matched,\n        :confidence => :medium,\n        :link_path => \"content_tag\",\n        :cwe_id => [79]\n    end\n  end\n\n  def process_call exp\n    if @mark\n      actually_process_call exp\n    else\n      @mark = true\n      actually_process_call exp\n      @mark = false\n    end\n\n    exp\n  end\n\n  def check_cve_2016_6316\n    if cve_2016_6316?\n      confidence = if @content_tags.any?\n                     :high\n                   else\n                     :medium\n                   end\n\n      fix_version = case\n                    when version_between?(\"3.0.0\", \"3.2.22.3\")\n                      \"3.2.22.4\"\n                    when version_between?(\"4.0.0\", \"4.2.7.0\")\n                      \"4.2.7.1\"\n                    when version_between?(\"5.0.0\", \"5.0.0\")\n                      \"5.0.0.1\"\n                    when (version.nil? and tracker.options[:rails3])\n                      \"3.2.22.4\"\n                    when (version.nil? and tracker.options[:rails4])\n                      \"4.2.7.2\"\n                    else\n                      return\n                    end\n\n      warn :warning_type => \"Cross-Site Scripting\",\n        :warning_code => :CVE_2016_6316,\n        :message => msg(msg_version(rails_version), \" \", msg_code(\"content_tag\"), \" does not escape double quotes in attribute values \", msg_cve(\"CVE-2016-6316\"), \". Upgrade to \", msg_version(fix_version)),\n        :confidence => confidence,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"https://groups.google.com/d/msg/ruby-security-ann/8B2iV2tPRSE/JkjCJkSoCgAJ\",\n        :cwe_id => [79]\n    end\n  end\n\n  def raw? exp\n    call? exp and exp.method == :raw\n  end\n\n  def cve_2016_6316?\n    version_between? \"3.0.0\", \"3.2.22.3\" or\n    version_between? \"4.0.0\", \"4.2.7.0\" or\n    version_between? \"5.0.0\", \"5.0.0.0\"\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_cookie_serialization.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckCookieSerialization < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Check for use of Marshal for cookie serialization\"\n\n  def run_check\n    tracker.find_call(target: :'Rails.application.config.action_dispatch', method: :cookies_serializer=).each do |result|\n      setting = result[:call].first_arg\n\n      if symbol? setting and [:marshal, :hybrid].include? setting.value\n        warn :result => result,\n          :warning_type => \"Remote Code Execution\",\n          :warning_code => :unsafe_cookie_serialization,\n          :message => msg(\"Use of unsafe cookie serialization strategy \", msg_code(setting.value.inspect), \" might lead to remote code execution\"),\n          :confidence => :medium,\n          :link_path => \"unsafe_deserialization\",\n          :cwe_id => [565, 502]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_create_with.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckCreateWith < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for strong params bypass in CVE-2014-3514\"\n\n  def run_check\n    @warned = false\n\n    if version_between? \"4.0.0\", \"4.0.8\"\n      suggested_version = \"4.0.9\"\n    elsif version_between? \"4.1.0\", \"4.1.4\"\n      suggested_version = \"4.1.5\"\n    else\n      return\n    end\n\n    @message = msg(msg_code(\"create_with\"), \" is vulnerable to strong params bypass. Upgrade to \", msg_version(suggested_version), \" or patch\")\n\n    tracker.find_call(:method => :create_with, :nested => true).each do |result|\n      process_result result\n    end\n\n    generic_warning unless @warned\n  end\n\n  def process_result result\n    return unless original? result\n    arg = result[:call].first_arg\n\n    confidence = danger_level arg\n\n    if confidence\n      @warned = true\n\n      warn :warning_type => \"Mass Assignment\",\n        :warning_code => :CVE_2014_3514_call,\n        :result => result,\n        :message => @message,\n        :confidence => confidence,\n        :link_path => \"https://groups.google.com/d/msg/rubyonrails-security/M4chq5Sb540/CC1Fh0Y_NWwJ\",\n        :cwe_id => [915]\n    end\n  end\n\n  #For a given create_with call, set confidence level.\n  #Ignore calls that use permit()\n  def danger_level exp\n    return unless sexp? exp\n\n    if call? exp and exp.method == :permit\n      nil\n    elsif request_value? exp\n      :high\n    elsif hash? exp\n      nil\n    elsif has_immediate_user_input?(exp)\n      :high\n    elsif include_user_input? exp\n      :medium\n    else\n      :weak\n    end\n  end\n\n  def generic_warning\n      warn :warning_type => \"Mass Assignment\",\n        :warning_code => :CVE_2014_3514,\n        :message => @message,\n        :gem_info => gemfile_or_environment,\n        :confidence => :medium,\n        :link_path => \"https://groups.google.com/d/msg/rubyonrails-security/M4chq5Sb540/CC1Fh0Y_NWwJ\",\n        :cwe_id => [915]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_cross_site_scripting.rb",
    "content": "require 'brakeman/checks/base_check'\nrequire 'brakeman/processors/lib/find_call'\nrequire 'brakeman/processors/lib/processor_helper'\nrequire 'brakeman/util'\nrequire 'set'\n\n#This check looks for unescaped output in templates which contains\n#parameters or model attributes.\n#\n#For example:\n#\n# <%= User.find(:id).name %>\n# <%= params[:id] %>\nclass Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for unescaped output in views\"\n\n  #Model methods which are known to be harmless\n  IGNORE_MODEL_METHODS = Set[:average, :count, :maximum, :minimum, :sum, :id]\n\n  MODEL_METHODS = Set[:all, :find, :first, :last, :new]\n\n  IGNORE_LIKE = /^link_to_|(_path|_tag|_url)$/\n\n  HAML_HELPERS = Sexp.new(:colon2, Sexp.new(:const, :Haml), :Helpers)\n\n  XML_HELPER = Sexp.new(:colon2, Sexp.new(:const, :Erubis), :XmlHelper)\n\n  URI = Sexp.new(:const, :URI)\n\n  CGI = Sexp.new(:const, :CGI)\n\n  FORM_BUILDER = Sexp.new(:call, Sexp.new(:const, :FormBuilder), :new)\n\n  def initialize *args\n    super\n    @matched = @mark = false\n  end\n\n  #Run check\n  def run_check\n    setup\n\n    tracker.each_template do |name, template|\n      Brakeman.debug \"Checking #{name} for XSS\"\n\n      @current_template = template\n\n      template.each_output do |out|\n        unless check_for_immediate_xss out\n          @matched = false\n          @mark = false\n          process out\n        end\n      end\n    end\n  end\n\n  def check_for_immediate_xss exp\n    return :duplicate if duplicate? exp\n\n    if exp.node_type == :output\n      out = exp.value\n    end\n\n    if raw_call? exp\n      out = exp.value.first_arg\n    elsif html_safe_call? exp\n      out = exp.value.target\n    end\n\n    return if call? out and ignore_call? out.target, out.method\n\n    if input = has_immediate_user_input?(out)\n      add_result exp\n\n      message = msg(\"Unescaped \", msg_input(input))\n\n      warn :template => @current_template,\n        :warning_type => \"Cross-Site Scripting\",\n        :warning_code => :cross_site_scripting,\n        :message => message,\n        :code => input.match,\n        :confidence => :high,\n        :cwe_id => [79]\n\n    elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(out)\n      method = if call? match\n                 match.method\n               else\n                 nil\n               end\n\n      unless IGNORE_MODEL_METHODS.include? method\n        add_result exp\n\n        if likely_model_attribute? match\n          confidence = :high\n        else\n          confidence = :medium\n        end\n\n        message = \"Unescaped model attribute\"\n        link_path = \"cross_site_scripting\"\n        warning_code = :cross_site_scripting\n\n        if node_type?(out, :call, :safe_call, :attrasgn, :safe_attrasgn) && out.method == :to_json\n          message += \" in JSON hash\"\n          link_path += \"_to_json\"\n          warning_code = :xss_to_json\n        end\n\n        warn :template => @current_template,\n          :warning_type => \"Cross-Site Scripting\",\n          :warning_code => warning_code,\n          :message => message,\n          :code => match,\n          :confidence => confidence,\n          :link_path => link_path,\n          :cwe_id => [79]\n      end\n\n    else\n      false\n    end\n  end\n\n  #Call already involves a model, but might not be acting on a record\n  def likely_model_attribute? exp\n    return false unless call? exp\n\n    method = exp.method\n\n    if MODEL_METHODS.include? method or method.to_s.start_with? \"find_by_\"\n      true\n    else\n      likely_model_attribute? exp.target\n    end\n  end\n\n  #Process an output Sexp\n  def process_output exp\n    process exp.value.dup\n  end\n\n  #Look for calls to raw()\n  #Otherwise, ignore\n  def process_escaped_output exp\n    unless check_for_immediate_xss exp\n      if not duplicate? exp\n        if raw_call? exp\n          process exp.value.first_arg\n        elsif html_safe_call? exp\n          process exp.value.target\n        end\n      end\n    end\n    exp\n  end\n\n  #Check a call for user input\n  #\n  #\n  #Since we want to report an entire call and not just part of one, use @mark\n  #to mark when a call is started. Any dangerous values inside will then\n  #report the entire call chain.\n  def process_call exp\n    if @mark\n      actually_process_call exp\n    else\n      @mark = true\n      actually_process_call exp\n      message = nil\n\n      if @matched\n        unless @matched.type and tracker.options[:ignore_model_output]\n          message = msg(\"Unescaped \", msg_input(@matched))\n        end\n\n        if message and not duplicate? exp\n          add_result exp\n\n          link_path = \"cross_site_scripting\"\n          warning_code = :cross_site_scripting\n\n          if @known_dangerous.include? exp.method\n            confidence = :high\n            if exp.method == :to_json\n              message << msg_plain(\" in JSON hash\")\n              link_path += \"_to_json\"\n              warning_code = :xss_to_json\n            end\n          else\n            confidence = :weak\n          end\n\n          warn :template => @current_template,\n            :warning_type => \"Cross-Site Scripting\",\n            :warning_code => warning_code,\n            :message => message,\n            :code => exp,\n            :user_input => @matched,\n            :confidence => confidence,\n            :link_path => link_path,\n            :cwe_id => [79]\n        end\n      end\n\n      @mark = @matched = false\n    end\n\n    exp\n  end\n\n  def actually_process_call exp\n    return if @matched\n    target = exp.target\n    if sexp? target\n      target = process target\n    end\n\n    method = exp.method\n\n    #Ignore safe items\n    if ignore_call? target, method\n      @matched = false\n    elsif sexp? target and model_name? target[1] #TODO: use method call?\n      @matched = Match.new(:model, exp)\n    elsif cookies? exp\n      @matched = Match.new(:cookies, exp)\n    elsif @inspect_arguments and params? exp\n      @matched = Match.new(:params, exp)\n    elsif @inspect_arguments\n      process_call_args exp\n    end\n  end\n\n  #Note that params have been found\n  def process_params exp\n    @matched = Match.new(:params, exp)\n    exp\n  end\n\n  #Note that cookies have been found\n  def process_cookies exp\n    @matched = Match.new(:cookies, exp)\n    exp\n  end\n\n  #Ignore calls to render\n  def process_render exp\n    exp\n  end\n\n  #Process as default\n  def process_dstr exp\n    process_default exp\n  end\n\n  #Process as default\n  def process_format exp\n    process_default exp\n  end\n\n  #Ignore output HTML escaped via HAML\n  def process_format_escaped exp\n    exp\n  end\n\n  #Ignore condition in if Sexp\n  def process_if exp\n    process exp.then_clause if sexp? exp.then_clause\n    process exp.else_clause if sexp? exp.else_clause\n    exp\n  end\n\n  def process_case exp\n    #Ignore user input in case value\n    #TODO: also ignore when values\n\n    current = 2\n    while current < exp.length\n      process exp[current] if exp[current]\n      current += 1\n    end\n\n    exp\n  end\n\n  def setup\n    @ignore_methods = Set[:==, :!=, :button_to, :check_box, :content_tag, :escapeHTML, :escape_once,\n                           :field_field, :fields_for, :form_for, :h, :hidden_field,\n                           :hidden_field, :hidden_field_tag, :image_tag, :label,\n                           :link_to, :mail_to, :radio_button, :select,\n                           :submit_tag, :text_area, :text_field,\n                           :text_field_tag, :url_encode, :u, :url_for,\n                           :will_paginate].merge tracker.options[:safe_methods]\n\n    @models = tracker.models.keys\n    @inspect_arguments = tracker.options[:check_arguments]\n\n    @known_dangerous = Set[:truncate, :concat]\n\n    if version_between? \"2.0.0\", \"3.0.5\"\n      @known_dangerous << :auto_link\n    elsif version_between? \"3.0.6\", \"3.0.99\"\n      @ignore_methods << :auto_link\n    end\n\n    if version_between? \"2.0.0\", \"2.3.14\" or tracker.config.gem_version(:'rails-html-sanitizer') == '1.0.2'\n      @known_dangerous << :strip_tags\n    end\n\n    if tracker.config.has_gem? :'rails-html-sanitizer' and\n       version_between? \"1.0.0\", \"1.0.2\", tracker.config.gem_version(:'rails-html-sanitizer')\n\n      @known_dangerous << :sanitize\n    end\n\n    json_escape_on = false\n    initializers = tracker.find_call(target: :ActiveSupport, method: :escape_html_entities_in_json=)\n    initializers.each {|result| json_escape_on = true?(result[:call].first_arg) }\n\n    if tracker.config.escape_html_entities_in_json?\n      json_escape_on = true\n    elsif version_between? \"4.0.0\", \"9.9.9\"\n      json_escape_on = true\n    end\n\n    if !json_escape_on or version_between? \"0.0.0\", \"2.0.99\"\n      @known_dangerous << :to_json\n      Brakeman.debug(\"Automatic to_json escaping not enabled, consider to_json dangerous\")\n    else\n      @safe_input_attributes << :to_json\n      Brakeman.debug(\"Automatic to_json escaping is enabled.\")\n    end\n  end\n\n  def raw_call? exp\n    exp.value.node_type == :call and exp.value.method == :raw\n  end\n\n  def html_safe_call? exp\n    call? exp.value and exp.value.method == :html_safe\n  end\n\n  def ignore_call? target, method\n    ignored_method?(target, method) or\n    safe_input_attribute?(target, method) or\n    ignored_model_method?(target, method) or\n    form_builder_method?(target, method) or\n    haml_escaped?(target, method) or\n    boolean_method?(method) or\n    cgi_escaped?(target, method) or\n    xml_escaped?(target, method)\n  end\n\n  def ignored_model_method? target, method\n    ((@matched and @matched.type == :model) or\n       model_name? target) and\n       IGNORE_MODEL_METHODS.include? method\n  end\n\n  def ignored_method? target, method\n    @ignore_methods.include? method or method.to_s =~ IGNORE_LIKE\n  end\n\n  def cgi_escaped? target, method\n    method == :escape and\n    (target == URI or target == CGI)\n  end\n\n  def haml_escaped? target, method\n    method == :html_escape and target == HAML_HELPERS\n  end\n\n  def xml_escaped? target, method\n    method == :escape_xml and target == XML_HELPER\n  end\n\n  def form_builder_method? target, method\n    target == FORM_BUILDER and @ignore_methods.include? method\n  end\n\n  def safe_input_attribute? target, method\n    target and always_safe_method? method\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_csrf_token_forgery_cve.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckCSRFTokenForgeryCVE < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for versions with CSRF token forgery vulnerability (CVE-2020-8166)\"\n\n  def run_check\n    fix_version = case\n      when version_between?('0.0.0', '5.2.4.2')\n        '5.2.4.3'\n      when version_between?('6.0.0', '6.0.3')\n        '6.0.3.1'\n      else\n        nil\n      end\n\n    if fix_version\n      warn :warning_type => \"Cross-Site Request Forgery\",\n        :warning_code => :CVE_2020_8166,\n        :message => msg(msg_version(rails_version), \" has a vulnerability that may allow CSRF token forgery. Upgrade to \", msg_version(fix_version), \" or patch\"),\n        :confidence => :medium,\n        :gem_info => gemfile_or_environment,\n        :link => \"https://groups.google.com/g/rubyonrails-security/c/NOjKiGeXUgw\",\n        :cwe_id => [352]\n    end\n  end\nend\n\n"
  },
  {
    "path": "lib/brakeman/checks/check_default_routes.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Checks if default routes are allowed in routes.rb\nclass Brakeman::CheckDefaultRoutes < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for default routes\"\n\n  def initialize *args\n    super\n    @actions_allowed_on_controller = nil\n  end\n\n  #Checks for :allow_all_actions globally and for individual routes\n  #if it is not enabled globally.\n  def run_check\n    check_for_default_routes\n    check_for_action_globs\n    check_for_cve_2014_0130\n  end\n\n  def check_for_default_routes\n    if allow_all_actions?\n      #Default routes are enabled globally\n      warn :warning_type => \"Default Routes\",\n        :warning_code => :all_default_routes,\n        :message => msg(\"All public methods in controllers are available as actions in \", msg_file(\"routes.rb\")),\n        :line => tracker.routes[:allow_all_actions].line,\n        :confidence => :high,\n        :file => \"#{tracker.app_path}/config/routes.rb\",\n        :cwe_id => [22]\n    end\n  end\n\n  def check_for_action_globs\n    return if allow_all_actions?\n    Brakeman.debug \"Checking each controller for default routes\"\n\n    tracker.routes.each do |name, actions|\n      if actions.is_a? Array and actions[0] == :allow_all_actions\n        @actions_allowed_on_controller = true\n        if actions[1].is_a? Hash and actions[1][:allow_verb]\n          verb = actions[1][:allow_verb]\n        else\n          verb = \"any\"\n        end\n        warn :controller => name,\n          :warning_type => \"Default Routes\",\n          :warning_code => :controller_default_routes,\n          :message => msg(\"Any public method in \", msg_code(name), \" can be used as an action for \", msg_code(verb), \" requests.\"),\n          :line => actions[2],\n          :confidence => :medium,\n          :file => \"#{tracker.app_path}/config/routes.rb\",\n          :cwe_id => [22]\n      end\n    end\n  end\n\n  def check_for_cve_2014_0130\n    case\n    when lts_version?(\"2.3.18.9\")\n      #TODO: Should support LTS 3.0.20 too\n      return\n    when version_between?(\"2.0.0\", \"2.3.18\")\n      upgrade = \"3.2.18\"\n    when version_between?(\"3.0.0\", \"3.2.17\")\n      upgrade = \"3.2.18\"\n    when version_between?(\"4.0.0\", \"4.0.4\")\n      upgrade = \"4.0.5\"\n    when version_between?(\"4.1.0\", \"4.1.0\")\n      upgrade = \"4.1.1\"\n    else\n      return\n    end\n\n    if allow_all_actions? or @actions_allowed_on_controller\n      confidence = :high\n    else\n      confidence = :medium\n    end\n\n    warn :warning_type => \"Remote Code Execution\",\n      :warning_code => :CVE_2014_0130,\n      :message => msg(msg_version(rails_version), \" with globbing routes is vulnerable to directory traversal and remote code execution. Patch or upgrade to \", msg_version(upgrade)),\n      :confidence => confidence,\n      :file => \"#{tracker.app_path}/config/routes.rb\",\n      :link => \"http://matasano.com/research/AnatomyOfRailsVuln-CVE-2014-0130.pdf\",\n      :cwe_id => [22]\n  end\n\n  def allow_all_actions?\n    tracker.routes[:allow_all_actions]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_deserialize.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckDeserialize < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for unsafe deserialization of objects\"\n\n  def run_check\n    check_yaml\n    check_csv\n    check_marshal\n    check_oj\n  end\n\n  def check_yaml\n    check_methods :YAML, :load_documents, :load_stream, :parse_documents, :parse_stream\n\n    # Check for safe_yaml gem use with YAML.load(..., safe: true)\n    if uses_safe_yaml?\n      tracker.find_call(target: :YAML, method: :load).each do |result|\n        call = result[:call]\n        options = call.second_arg\n\n        if hash? options and true? hash_access(options, :safe)\n          next\n        else\n          check_deserialize result, :YAML\n        end\n      end\n    else\n      check_methods :YAML, :load\n    end\n  end\n\n  def check_csv\n    check_methods :CSV, :load\n  end\n\n  def check_marshal\n    check_methods :Marshal, :load, :restore\n  end\n\n  def check_oj\n    check_methods :Oj, :object_load # Always unsafe, regardless of mode\n\n    unsafe_mode = :object\n    safe_default = oj_safe_default?\n\n    tracker.find_call(:target => :Oj, :method => :load).each do |result|\n      call = result[:call]\n      options = call.second_arg\n\n      if options and hash? options and mode = hash_access(options, :mode)\n        if symbol? mode and mode.value == unsafe_mode\n          check_deserialize result, :Oj\n        end\n      elsif not safe_default\n        check_deserialize result, :Oj\n      end\n    end\n  end\n\n  def check_methods target, *methods\n    tracker.find_call(:target => target, :methods => methods ).each do |result|\n      check_deserialize result, target\n    end\n  end\n\n  def check_deserialize result, target, arg = nil\n    return unless original? result\n\n    arg ||= result[:call].first_arg\n    method = result[:call].method\n\n    if input = has_immediate_user_input?(arg)\n      confidence = :high\n    elsif input = include_user_input?(arg)\n      confidence = :medium\n    elsif target == :Marshal\n      confidence = :low\n      message = msg(\"Use of \", msg_code(\"#{target}.#{method}\"), \" may be dangerous\")\n    end\n\n    if confidence\n      message ||= msg(msg_code(\"#{target}.#{method}\"), \" called with \", msg_input(input))\n\n      warn :result => result,\n        :warning_type => \"Remote Code Execution\",\n        :warning_code => :unsafe_deserialize,\n        :message => message,\n        :user_input => input,\n        :confidence => confidence,\n        :link_path => \"unsafe_deserialization\",\n        :cwe_id => [502]\n    end\n  end\n\n  private\n\n  def oj_safe_default?\n    safe_default = false\n\n    if tracker.find_call(target: :Oj, method: :mimic_JSON).any?\n      safe_default = true\n    elsif result = tracker.find_call(target: :Oj, method: :default_options=).first\n      options = result[:call].first_arg\n\n      if oj_safe_mode? options\n        safe_default = true\n      end\n    end\n\n    safe_default\n  end\n\n  def oj_safe_mode? options\n    if hash? options and mode = hash_access(options, :mode)\n      if symbol? mode and mode != :object\n        return true\n      end\n    end\n\n    false\n  end\n\n  def uses_safe_yaml?\n    tracker.config.has_gem? :safe_yaml\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_detailed_exceptions.rb",
    "content": "require 'brakeman/checks/base_check'\n\n# Check for detailed exceptions enabled for production\nclass Brakeman::CheckDetailedExceptions < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  LOCAL_REQUEST = s(:call, s(:call, nil, :request), :local?)\n\n  @description = \"Checks for information disclosure displayed via detailed exceptions\"\n\n  def run_check\n    check_local_request_config\n    check_detailed_exceptions\n  end\n\n  def check_local_request_config\n    if true? tracker.config.rails[:consider_all_requests_local]\n      warn :warning_type => \"Information Disclosure\",\n           :warning_code => :local_request_config,\n           :message => \"Detailed exceptions are enabled in production\",\n           :confidence => :high,\n           :file => \"config/environments/production.rb\",\n           :cwe_id => [200]\n    end\n  end\n\n  def check_detailed_exceptions\n    tracker.controllers.each do |_name, controller|\n      controller.methods_public.each do |method_name, definition|\n        src = definition.src\n        body = src.body.last\n        next unless body\n\n        if method_name == :show_detailed_exceptions? and not safe? body\n          if true? body\n            confidence = :high\n          else\n            confidence = :medium\n          end\n\n          warn :warning_type => \"Information Disclosure\",\n               :warning_code => :detailed_exceptions,\n               :message => msg(\"Detailed exceptions may be enabled in \", msg_code(\"show_detailed_exceptions?\")),\n               :confidence => confidence,\n               :code => src,\n               :file => definition[:file],\n               :cwe_id => [200]\n        end\n      end\n    end\n  end\n\n  def safe? body\n    false? body or\n    body == LOCAL_REQUEST\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_digest_dos.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckDigestDoS < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for digest authentication DoS vulnerability\"\n\n  def run_check\n    message = msg(\"Vulnerability in digest authentication \", msg_cve(\"CVE-2012-3424\"), \". Upgrade to \")\n\n    if version_between? \"3.0.0\", \"3.0.15\"\n      message << msg_version(\"3.0.16\")\n    elsif version_between? \"3.1.0\", \"3.1.6\"\n      message << msg_version(\"3.1.7\")\n    elsif version_between? \"3.2.0\", \"3.2.5\"\n      message << msg_version(\"3.2.7\")\n    else\n      return\n    end\n\n    if with_http_digest?\n      confidence = :high\n    else\n      confidence = :weak\n    end\n\n    warn :warning_type => \"Denial of Service\",\n      :warning_code => :CVE_2012_3424,\n      :message => message,\n      :confidence => confidence,\n      :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/vxJjrc15qYM/discussion\",\n      :gem_info => gemfile_or_environment,\n      :cwe_id => [287]\n  end\n\n  def with_http_digest?\n    not tracker.find_call(:target => false, :method => [:authenticate_or_request_with_http_digest, :authenticate_with_http_digest]).empty?\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_divide_by_zero.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckDivideByZero < Brakeman::BaseCheck\n  Brakeman::Checks.add_optional self\n\n  @description = \"Warns on potential division by zero\"\n\n  def run_check\n    tracker.find_call(:method => :\"/\").each do |result|\n      check_division result\n    end\n  end\n\n  def check_division result\n    return unless original? result\n\n    call = result[:call]\n\n    denominator = call.first_arg\n\n    if number? denominator and denominator.value == 0\n      numerator = call.target\n\n      if number? numerator\n        if numerator.value.is_a? Float\n          return # 0.0 / 0 is NaN and 1.0 / 0 is Infinity\n        else\n          confidence = :medium\n        end\n      else\n        confidence = :weak\n      end\n\n      warn :result => result,\n        :warning_type => \"Divide by Zero\",\n        :warning_code => :divide_by_zero,\n        :message => \"Potential division by zero\",\n        :confidence => confidence,\n        :user_input => denominator,\n        :cwe_id => [369]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_dynamic_finders.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#This check looks for regexes that include user input.\nclass Brakeman::CheckDynamicFinders < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Check unsafe usage of find_by_*\"\n\n  def run_check\n    if tracker.config.has_gem? :mysql and version_between? '2.0.0', '4.1.99'\n      tracker.find_call(:method => /^find_by_/).each do |result|\n        process_result result\n      end\n    end\n  end\n\n  def process_result result\n    return unless original? result\n\n    call = result[:call]\n\n    if potentially_dangerous? call.method\n      call.each_arg do |arg|\n        if params? arg and not safe_call? arg\n          warn :result => result,\n            :warning_type => \"SQL Injection\",\n            :warning_code => :sql_injection_dynamic_finder,\n            :message => \"MySQL integer conversion may cause 0 to match any string\",\n            :confidence => :medium,\n            :user_input => arg,\n            :cwe_id => [89]\n\n          break\n        end\n      end\n    end\n  end\n\n  def safe_call? arg\n    return false unless call? arg\n\n    meth = arg.method\n    meth == :to_s or meth == :to_i\n  end\n\n  def potentially_dangerous? method_name\n    method_name.match(/^find_by_.*(token|guid|password|api_key|activation|code|private|reset)/)\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_eol_rails.rb",
    "content": "require_relative 'eol_check'\n\nclass Brakeman::CheckEOLRails < Brakeman::EOLCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for unsupported versions of Rails\"\n\n  def run_check\n    return unless tracker.config.rails_version\n\n    check_eol_version :rails, RAILS_EOL_DATES\n  end\n\n  # https://rubyonrails.org/maintenance\n  # https://endoflife.date/rails\n  RAILS_EOL_DATES = {\n    ['2.0.0', '2.3.99'] => Date.new(2013, 6, 25),\n    ['3.0.0', '3.2.99'] => Date.new(2016, 6, 30),\n    ['4.0.0', '4.2.99'] => Date.new(2017, 4, 27),\n    ['5.0.0', '5.0.99'] => Date.new(2018, 5, 9),\n    ['5.1.0', '5.1.99'] => Date.new(2019, 8, 25),\n    ['5.2.0', '5.2.99'] => Date.new(2022, 6, 1),\n    ['6.0.0', '6.0.99'] => Date.new(2023, 6, 1),\n    ['6.1.0', '6.1.99'] => Date.new(2024, 10, 1),\n    ['7.0.0', '7.0.99'] => Date.new(2025, 4, 1),\n    ['7.1.0', '7.1.99'] => Date.new(2025, 10, 1),\n    ['7.2.0', '7.2.99'] => Date.new(2026, 8, 9),\n    ['8.0.0', '8.0.99'] => Date.new(2026, 10, 7),\n  }\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_eol_ruby.rb",
    "content": "require_relative 'eol_check'\n\nclass Brakeman::CheckEOLRuby < Brakeman::EOLCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for unsupported versions of Ruby\"\n\n  def run_check\n    return unless tracker.config.ruby_version\n\n    check_eol_version :ruby, RUBY_EOL_DATES\n  end\n\n  RUBY_EOL_DATES = {\n    ['0.0.0', '1.9.3'] => Date.new(2015, 2, 23),\n    ['2.0.0', '2.0.99'] => Date.new(2016, 2, 24),\n    ['2.1.0', '2.1.99'] => Date.new(2017, 3, 31),\n    ['2.2.0', '2.2.99'] => Date.new(2018, 3, 31),\n    ['2.3.0', '2.3.99'] => Date.new(2019, 3, 31),\n    ['2.4.0', '2.4.99'] => Date.new(2020, 3, 31),\n    ['2.5.0', '2.5.99'] => Date.new(2021, 3, 31),\n    ['2.6.0', '2.6.99'] => Date.new(2022, 3, 31),\n    ['2.7.0', '2.7.99'] => Date.new(2023, 3, 31),\n    ['3.0.0', '3.0.99'] => Date.new(2024, 3, 31),\n    ['3.1.0', '3.1.99'] => Date.new(2025, 3, 31),\n    ['3.2.0', '3.2.99'] => Date.new(2026, 3, 31),\n    ['3.3.0', '3.3.99'] => Date.new(2027, 3, 31),\n    ['3.4.0', '3.4.99'] => Date.new(2028, 3, 31),\n  }\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_escape_function.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Check for versions with vulnerable html escape method\n#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/56bffb5923ab1195\nclass Brakeman::CheckEscapeFunction < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for versions before 2.3.14 which have a vulnerable escape method\"\n\n  def run_check\n    if version_between?('2.0.0', '2.3.13') and RUBY_VERSION < '1.9.0' \n\n      warn :warning_type => 'Cross-Site Scripting',\n        :warning_code => :CVE_2011_2932,\n        :message => msg(\"Rails versions before 2.3.14 have a vulnerability in the \", msg_code(\"escape\"), \" method when used with Ruby 1.8 \", msg_cve(\"CVE-2011-2932\")),\n        :confidence => :high,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/Vr_7WSOrEZU/discussion\",\n        :cwe_id => [79]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_evaluation.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#This check looks for calls to +eval+, +instance_eval+, etc. which include\n#user input.\nclass Brakeman::CheckEvaluation < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Searches for evaluation of user input\"\n\n  #Process calls\n  def run_check\n    Brakeman.debug \"Finding eval-like calls\"\n    calls = tracker.find_call methods: [:eval, :instance_eval, :class_eval, :module_eval], nested: true\n\n    Brakeman.debug \"Processing eval-like calls\"\n    calls.each do |call|\n      process_result call\n    end\n  end\n\n  #Warns if eval includes user input\n  def process_result result\n    return unless original? result\n\n    first_arg = result[:call].first_arg\n\n    unless safe_value? first_arg\n      if input = include_user_input?(first_arg)\n        confidence = :high\n        message = msg(msg_input(input), \" evaluated as code\")\n      elsif string_evaluation? first_arg\n        confidence = :low\n        message = \"Dynamic string evaluated as code\"\n      elsif result[:call].method == :eval\n        confidence = :low\n        message = \"Dynamic code evaluation\"\n      end\n\n      if confidence\n        warn :result => result,\n          :warning_type => \"Dangerous Eval\",\n          :warning_code => :code_eval,\n          :message => message,\n          :user_input => input,\n          :confidence => confidence,\n          :cwe_id => [913, 95]\n      end\n    end\n  end\n\n  def string_evaluation? exp\n    string_interp? exp or\n      (call? exp and string? exp.target)\n  end\n\n  def safe_value? exp\n    return true unless sexp? exp\n\n    case exp.sexp_type\n    when :dstr\n      exp.all? { |e| safe_value? e}\n    when :evstr\n      safe_value? exp.value\n    when :str, :lit\n      true\n    when :call\n      always_safe_method? exp.method\n    else\n      false\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_execute.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Checks for string interpolation and parameters in calls to\n#Kernel#system, Kernel#exec, Kernel#syscall, and inside backticks.\n#\n#Examples of command injection vulnerabilities:\n#\n# system(\"rf -rf #{params[:file]}\")\n# exec(params[:command])\n# `unlink #{params[:something}`\nclass Brakeman::CheckExecute < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Finds instances of possible command injection\"\n\n  SAFE_VALUES = [s(:const, :RAILS_ROOT),\n                  s(:call, s(:const, :Rails), :root),\n                  s(:call, s(:const, :Rails), :env),\n                  s(:call, s(:const, :Process), :pid)]\n\n  SHELL_ESCAPE_MODULE_METHODS = Set[:escape, :join, :shellescape, :shelljoin]\n  SHELL_ESCAPE_MIXIN_METHODS = Set[:shellescape, :shelljoin]\n\n  # These are common shells that are known to allow the execution of commands\n  # via a -c flag. See dash_c_shell_command? for more info.\n  KNOWN_SHELL_COMMANDS = Set[\"sh\", \"bash\", \"ksh\", \"csh\", \"tcsh\", \"zsh\"]\n\n  SHELLWORDS = s(:const, :Shellwords)\n\n  #Check models, controllers, and views for command injection.\n  def run_check\n    Brakeman.debug \"Finding system calls using ``\"\n    check_for_backticks tracker\n\n    check_open_calls\n\n    Brakeman.debug \"Finding other system calls\"\n    calls = tracker.find_call :targets => [:IO, :Open3, :Kernel, :'POSIX::Spawn', :Process, nil],\n      :methods => [:capture2, :capture2e, :capture3, :exec, :pipeline, :pipeline_r,\n        :pipeline_rw, :pipeline_start, :pipeline_w, :popen, :popen2, :popen2e,\n        :popen3, :spawn, :syscall, :system], :nested => true\n\n    Brakeman.debug \"Processing system calls\"\n    calls.each do |result|\n      process_result result\n    end\n  end\n\n  private\n\n  #Processes results from Tracker#find_call.\n  def process_result result\n    call = result[:call]\n    args = call.arglist\n    first_arg = call.first_arg\n    failure = nil\n\n    case call.method\n    when :popen\n      # Normally, if we're in a `popen` call, we only are worried about shell\n      # injection when the argument is not an array, because array elements\n      # are always escaped by Ruby. However, an exception is when the array\n      # contains two values are something like \"bash -c\" because then the third\n      # element is effectively the command being run and might be a malicious\n      # executable if it comes (partially or fully) from user input.\n      if !array?(first_arg)\n        failure = include_user_input?(first_arg) ||\n                  dangerous_interp?(first_arg) ||\n                  dangerous_string_building?(first_arg)\n      elsif dash_c_shell_command?(first_arg[1], first_arg[2])\n        failure = include_user_input?(first_arg[3]) ||\n                  dangerous_interp?(first_arg[3]) ||\n                  dangerous_string_building?(first_arg[3])\n      end\n    when :pipeline, :pipline_r, :pipeline_rw, :pipeline_w, :pipeline_start\n      # Since these pipeline commands pipe together several commands,\n      # need to check each argument. If it's an array, check first argument\n      # (the command) and also check for `bash -c`. Otherwise check the argument\n      # as a unit.\n\n      args.each do |arg|\n        next unless sexp? arg\n\n        if array?(arg)\n          # Check first element of array\n          failure = include_user_input?(arg[1]) ||\n            dangerous_interp?(arg[1]) ||\n            dangerous_string_building?(arg[1])\n\n          # Check for ['bash', '-c', user_input]\n          if dash_c_shell_command?(arg[1], arg[2])\n            failure = include_user_input?(arg[3]) ||\n              dangerous_interp?(arg[3]) ||\n              dangerous_string_building?(arg[3])\n          end\n        else\n          failure = include_user_input?(arg)\n        end\n\n        break if failure\n      end\n    when :system, :exec\n      # Normally, if we're in a `system` or `exec` call, we only are worried\n      # about shell injection when there's a single argument, because comma-\n      # separated arguments are always escaped by Ruby. However, an exception is\n      # when the first two arguments are something like \"bash -c\" because then\n      # the third argument is effectively the command being run and might be\n      # a malicious executable if it comes (partially or fully) from user input.\n      if dash_c_shell_command?(first_arg, call.second_arg)\n        failure = include_user_input?(args[3]) ||\n                  dangerous_interp?(args[3]) ||\n                  dangerous_string_building?(args[3])\n      else\n        failure = include_user_input?(first_arg) ||\n                  dangerous_interp?(first_arg) ||\n                  dangerous_string_building?(first_arg)\n      end\n    when :capture2, :capture2e, :capture3\n      # Open3 capture methods can take a :stdin_data argument which is used as the\n      # the input to the called command so it is not succeptable to command injection.\n      # As such if the last argument is a hash (and therefore execution options) it\n      # should be ignored\n\n      args.pop if hash?(args.last) && args.length > 2\n      failure = include_user_input?(args) ||\n                dangerous_interp?(args) ||\n                dangerous_string_building?(args)\n    else\n      failure = include_user_input?(args) ||\n                dangerous_interp?(args) ||\n                dangerous_string_building?(args)\n    end\n\n    if failure and original? result\n\n      if failure.type == :interp #Not from user input\n        confidence = :medium\n      else\n        confidence = :high\n      end\n\n      warn :result => result,\n        :warning_type => \"Command Injection\",\n        :warning_code => :command_injection,\n        :message => \"Possible command injection\",\n        :code => call,\n        :user_input => failure,\n        :confidence => confidence,\n        :cwe_id => [77]\n    end\n  end\n\n  # @return [Boolean] true iff the command given by `first_arg`, `second_arg`\n  #   invokes a new shell process via `<shell_command> -c` (like `bash -c`)\n  def dash_c_shell_command?(first_arg, second_arg)\n    string?(first_arg) &&\n    KNOWN_SHELL_COMMANDS.include?(first_arg.value) &&\n    string?(second_arg) &&\n    second_arg.value == \"-c\"\n  end\n\n  def check_open_calls\n    tracker.find_call(:targets => [nil, :Kernel], :method => :open).each do |result|\n      if match = dangerous_open_arg?(result[:call].first_arg)\n        warn :result => result,\n          :warning_type => \"Command Injection\",\n          :warning_code => :command_injection,\n          :message => msg(\"Possible command injection in \", msg_code(\"open\")),\n          :user_input => match,\n          :confidence => :high,\n          :cwe_id => [77]\n      end\n    end\n  end\n\n  def include_user_input? exp\n    if node_type? exp, :arglist, :dstr, :evstr, :dxstr\n      exp.each_sexp do |e|\n        if res = include_user_input?(e)\n          return res\n        end\n      end\n\n      false\n    else\n      if shell_escape? exp\n        false\n      else\n        super exp\n      end\n    end\n  end\n\n  def dangerous_open_arg? exp\n    if string_interp? exp\n      # Check for input at start of string\n      exp[1] == \"\" and\n        node_type? exp[2], :evstr and\n        has_immediate_user_input? exp[2]\n    else\n      has_immediate_user_input? exp\n    end\n  end\n\n  #Looks for calls using backticks such as\n  #\n  # `rm -rf #{params[:file]}`\n  def check_for_backticks tracker\n    tracker.find_call(:target => nil, :method => :`).each do |result|\n      process_backticks result\n    end\n  end\n\n  #Processes backticks.\n  def process_backticks result\n    return unless original? result\n\n    exp = result[:call]\n\n    if input = include_user_input?(exp)\n      confidence = :high\n    elsif input = dangerous?(exp)\n      confidence = :medium\n    else\n      return\n    end\n\n    warn :result => result,\n      :warning_type => \"Command Injection\",\n      :warning_code => :command_injection,\n      :message => \"Possible command injection\",\n      :code => exp,\n      :user_input => input,\n      :confidence => confidence,\n      :cwe_id => [77]\n  end\n\n  # This method expects a :dstr or :evstr node\n  def dangerous? exp\n    exp.each_sexp do |e|\n      if call? e and e.method == :to_s\n        e = e.target\n      end\n\n      next if node_type? e, :lit, :str\n      next if SAFE_VALUES.include? e\n      next if shell_escape? e\n      next if temp_file_path? e\n\n      if node_type? e, :if\n        # If we're in a conditional, evaluate the `then` and `else` clauses to\n        # see if they're dangerous.\n        if res = dangerous?(e.sexp_body.sexp_body)\n          return res\n        end\n      elsif node_type? e, :or, :evstr, :dstr\n        if res = dangerous?(e)\n          return res\n        end\n      else\n        return e\n      end\n    end\n\n    false\n  end\n\n  def dangerous_interp? exp\n    match = include_interp? exp\n    return unless match\n    interp = match.match\n\n    interp.each_sexp do |e|\n      if res = dangerous?(e)\n        return Match.new(:interp, res)\n      end\n    end\n\n    false\n  end\n\n  #Checks if an expression contains string interpolation.\n  #\n  #Returns Match with :interp type if found.\n  def include_interp? exp\n    @string_interp = false\n    process exp\n    @string_interp\n  end\n\n  def dangerous_string_building? exp\n    if string_building?(exp) && res = dangerous?(exp)\n      return Match.new(:interp, res)\n    end\n\n    false\n  end\n\n  def shell_escape? exp\n    return false unless call? exp\n\n    if exp.target == SHELLWORDS and SHELL_ESCAPE_MODULE_METHODS.include? exp.method\n      true\n    elsif SHELL_ESCAPE_MIXIN_METHODS.include?(exp.method)\n      true\n    else\n      false\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_file_access.rb",
    "content": "require 'brakeman/checks/base_check'\nrequire 'brakeman/processors/lib/processor_helper'\n\n#Checks for user input in methods which open or manipulate files\nclass Brakeman::CheckFileAccess < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Finds possible file access using user input\"\n\n  def run_check\n    Brakeman.debug \"Finding possible file access\"\n    methods = tracker.find_call :targets => [:Dir, :File, :IO, :Kernel, :\"Net::FTP\", :\"Net::HTTP\", :PStore, :Pathname, :Shell], :methods => [:[], :chdir, :chroot, :delete, :entries, :foreach, :glob, :install, :lchmod, :lchown, :link, :load, :load_file, :makedirs, :move, :new, :open, :read, :readlines, :rename, :rmdir, :safe_unlink, :symlink, :syscopy, :sysopen, :truncate, :unlink]\n\n    methods.concat tracker.find_call :target => :YAML, :methods => [:load_file, :parse_file]\n    methods.concat tracker.find_call :target => nil, :method => [:open]\n\n    Brakeman.debug \"Finding calls to load()\"\n    methods.concat tracker.find_call :target => false, :method => :load\n\n    Brakeman.debug \"Finding calls using FileUtils\"\n    methods.concat tracker.find_call :target => :FileUtils\n\n    Brakeman.debug \"Processing found calls\"\n    methods.each do |call|\n      process_result call\n    end\n  end\n\n  def process_result result\n    return unless original? result\n    call = result[:call]\n\n    file_name = call.first_arg\n\n    return if called_on_tempfile?(file_name) || sanitized?(file_name)\n\n    if match = has_immediate_user_input?(file_name)\n      confidence = :high\n    elsif match = has_immediate_model?(file_name)\n      match = Match.new(:model, match)\n      confidence = :medium\n    elsif tracker.options[:check_arguments] and\n      match = include_user_input?(file_name)\n\n      #Check for string building in file name\n      if call?(file_name) and (file_name.method == :+ or file_name.method == :<<)\n        confidence = :high\n      else\n        confidence = :weak\n      end\n    end\n\n    if match and not temp_file_method? match.match\n\n      message = msg(msg_input(match), \" used in file name\")\n\n      warn :result => result,\n        :warning_type => \"File Access\",\n        :warning_code => :file_access,\n        :message => message,\n        :confidence => confidence,\n        :code => call,\n        :user_input => match,\n        :cwe_id => [22]\n    end\n  end\n\n  # When using Tempfile, there is no risk of unauthorized file access, since\n  # Tempfile adds a unique string onto the end of every provided filename, and\n  # ensures that the filename does not already exist in the system.\n  def called_on_tempfile? file_name\n    call?(file_name) && file_name.target == s(:const, :Tempfile)\n  end\n\n  def sanitized? file\n    call?(file) &&\n      call?(file.target) &&\n      class_name(file.target.target) == :\"ActiveStorage::Filename\"\n  end\n\n  def temp_file_method? exp\n    if call? exp\n      return true if exp.call_chain.include? :tempfile\n\n      params? exp.target and exp.method == :path\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_file_disclosure.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckFileDisclosure < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = 'Checks for versions with file existence disclosure vulnerability'\n\n  def run_check\n    fix_version = case\n      when version_between?('2.0.0', '2.3.18')\n        '3.2.21'\n      when version_between?('3.0.0', '3.2.20')\n        '3.2.21'\n      when version_between?('4.0.0', '4.0.11')\n        '4.0.12'\n      when version_between?('4.1.0', '4.1.7')\n        '4.1.8'\n      else\n        nil\n      end\n\n    if fix_version and serves_static_assets?\n      warn :warning_type => \"File Access\",\n        :warning_code => :CVE_2014_7829,\n        :message => msg(msg_version(rails_version), \" has a file existence disclosure vulnerability. Upgrade to \", msg_version(fix_version), \" or disable serving static assets\"),\n        :confidence => :high,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"https://groups.google.com/d/msg/rubyonrails-security/23fiuwb1NBA/MQVM1-5GkPMJ\",\n        :cwe_id => [22]\n    end\n  end\n\n  def serves_static_assets?\n    true? tracker.config.rails[:serve_static_assets]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_filter_skipping.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Check for filter skipping vulnerability\n#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/3420ac71aed312d6\nclass Brakeman::CheckFilterSkipping < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for versions 3.0-3.0.9 which had a vulnerability in filters\"\n\n  def run_check\n    if version_between?('3.0.0', '3.0.9') and uses_arbitrary_actions?\n\n      warn :warning_type => \"Default Routes\",\n        :warning_code => :CVE_2011_2929,\n        :message => msg(\"Rails versions before 3.0.10 have a vulnerability which allows filters to be bypassed\", msg_cve(\"CVE-2011-2929\")),\n        :confidence => :high,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/NCCsca7TEtY/discussion\",\n        :cwe_id => [20]\n    end\n  end\n\n  def uses_arbitrary_actions?\n    tracker.routes.each do |_name, actions|\n      if actions.include? :allow_all_actions\n        return true\n      end\n    end\n\n    false\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_force_ssl.rb",
    "content": "class Brakeman::CheckForceSSL < Brakeman::BaseCheck\n  Brakeman::Checks.add_optional self\n\n  @description = \"Check that force_ssl setting is enabled in production\"\n\n  def run_check\n    return if tracker.config.rails.empty? or tracker.config.rails_version.nil?\n    return if tracker.config.rails_version < \"3.1.0\"\n\n    force_ssl = tracker.config.rails[:force_ssl]\n\n    if false? force_ssl or force_ssl.nil?\n      line = if sexp? force_ssl\n               force_ssl.line\n             else\n               1\n             end\n\n      warn :warning_type => \"Missing Encryption\",\n        :warning_code => :force_ssl_disabled,\n        :message => msg(\"The application does not force use of HTTPS: \", msg_code(\"config.force_ssl\"), \" is not enabled\"),\n        :confidence => :high,\n        :file => \"config/environments/production.rb\",\n        :line => line,\n        :cwe_id => [311]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_forgery_setting.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Checks that +protect_from_forgery+ is set in the ApplicationController.\n#\n#Also warns for CSRF weakness in certain versions of Rails:\n#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2d95a3cc23e03665\nclass Brakeman::CheckForgerySetting < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Verifies that protect_from_forgery is enabled in direct subclasses of ActionController::Base\"\n\n  def run_check\n    return if tracker.config.default_protect_from_forgery?\n\n    tracker.controllers\n    .select { |_, controller| controller.parent == :\"ActionController::Base\" }\n    .each do |name, controller|\n      if controller and not controller.protect_from_forgery?\n        csrf_warning :controller => name,\n          :warning_code => :csrf_protection_missing,\n          :message => msg(msg_code(\"protect_from_forgery\"), \" should be called in \", msg_code(name)),\n          :file => controller.file,\n          :line => controller.top_line\n      elsif version_between? \"4.0.0\", \"100.0.0\" and forgery_opts = controller.options[:protect_from_forgery]\n        unless forgery_opts.is_a?(Array) and sexp?(forgery_opts.first) and\n          access_arg = hash_access(forgery_opts.first.first_arg, :with) and symbol? access_arg and\n          access_arg.value == :exception\n\n          args = {\n            :controller => name,\n            :warning_type => \"Cross-Site Request Forgery\",\n            :warning_code => :csrf_not_protected_by_raising_exception,\n            :message => msg(msg_code(\"protect_from_forgery\"), \" should be configured with \", msg_code(\"with: :exception\")),\n            :confidence => :medium,\n            :file => controller.file\n          }\n\n          args.merge!(:code => forgery_opts.first) if forgery_opts.is_a?(Array)\n\n          csrf_warning args\n        end\n\n      end\n\n      if controller.options[:protect_from_forgery]\n        check_cve_2011_0447\n      end\n    end\n  end\n\n  def csrf_warning opts\n    opts = {\n      :controller => :ApplicationController,\n      :warning_type => \"Cross-Site Request Forgery\",\n      :confidence => :high,\n      :cwe_id => [352]\n    }.merge opts\n\n    warn opts\n  end\n\n  def check_cve_2011_0447\n    @warned_cve_2011_0447 ||= false\n    return if @warned_cve_2011_0447\n\n    if version_between? \"2.1.0\", \"2.3.10\"\n      new_version = \"2.3.11\"\n    elsif version_between? \"3.0.0\", \"3.0.3\"\n      new_version = \"3.0.4\"\n    else\n      return\n    end\n\n    @warned_cve_2011_0447 = true # only warn once\n\n    csrf_warning :warning_code => :CVE_2011_0447,\n      :message => msg(\"CSRF protection is flawed in unpatched versions of \", msg_version(rails_version), \" \", msg_cve(\"CVE-2011-0447\"), \". Upgrade to \", msg_version(new_version), \" or apply patches as needed\"),\n      :gem_info => gemfile_or_environment,\n      :file => nil,\n      :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/LZWjzCPgNmU/discussion\",\n      :cwe_id => [352]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_header_dos.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckHeaderDoS < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for header DoS (CVE-2013-6414)\"\n\n  def run_check\n    if (version_between? \"3.0.0\", \"3.2.15\" or version_between? \"4.0.0\", \"4.0.1\") and not has_workaround?\n      message = msg(msg_version(rails_version), \" has a denial of service vulnerability \", msg_cve(\"CVE-2013-6414\"), \". Upgrade to \")\n\n      if version_between? \"3.0.0\", \"3.2.15\"\n        message << msg_version(\"3.2.16\")\n      else\n        message << msg_version(\"4.0.2\")\n      end\n\n      warn :warning_type => \"Denial of Service\",\n        :warning_code => :CVE_2013_6414,\n        :message => message,\n        :confidence => :medium,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"https://groups.google.com/d/msg/ruby-security-ann/A-ebV4WxzKg/KNPTbX8XAQUJ\",\n        :cwe_id => [20]\n    end\n  end\n\n  def has_workaround?\n    tracker.find_call(target: :ActiveSupport, method: :on_load).any? and\n      tracker.find_call(target: :\"ActionView::LookupContext::DetailsKey\", method: :class_eval).any?\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_i18n_xss.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckI18nXSS < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for i18n XSS (CVE-2013-4491)\"\n\n  def run_check\n    if (version_between? \"3.0.6\", \"3.2.15\" or version_between? \"4.0.0\", \"4.0.1\") and not has_workaround?\n      i18n_gem = tracker.config.gem_version :i18n\n      message = msg(msg_version(rails_version), \" has an XSS vulnerability in \", msg_version(i18n_gem, \"i18n\"), \" \", msg_cve(\"CVE-2013-4491\"), \". Upgrade to \")\n\n      if version_between? \"3.0.6\", \"3.1.99\" and version_before i18n_gem, \"0.5.1\"\n        message << msg_version(\"3.2.16 or i18n 0.5.1\")\n      elsif version_between? \"3.2.0\", \"4.0.1\" and version_before i18n_gem, \"0.6.6\"\n        message << msg_version(\"4.0.2 or i18n 0.6.6\")\n      else\n        return\n      end\n\n      warn :warning_type => \"Cross-Site Scripting\",\n        :warning_code => :CVE_2013_4491,\n        :message => message,\n        :confidence => :medium,\n        :gem_info => gemfile_or_environment(:i18n),\n        :link_path => \"https://groups.google.com/d/msg/ruby-security-ann/pLrh6DUw998/bLFEyIO4k_EJ\",\n        :cwe_id => [79]\n    end\n  end\n\n  def version_before gem_version, target\n    return true unless gem_version\n    gem_version.split('.').map(&:to_i).zip(target.split('.').map(&:to_i)).each do |gv, t|\n      if gv < t\n        return true\n      elsif gv > t\n        return false\n      end\n    end\n\n    false\n  end\n\n  def has_workaround?\n    tracker.find_call(target: :I18n, method: :const_defined?, chained: true).any? do |match|\n      match[:call].first_arg == s(:lit, :MissingTranslation)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_jruby_xml.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckJRubyXML < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for versions with JRuby XML parsing backend\"\n\n  def run_check\n    return unless RUBY_PLATFORM == \"java\"\n\n    fix_version = case\n      when version_between?('3.0.0', '3.0.99')\n        '3.2.13'\n      when version_between?('3.1.0', '3.1.11')\n        '3.1.12'\n      when version_between?('3.2.0', '3.2.12')\n        '3.2.13'\n      else\n        return\n      end\n\n    #Check for workaround\n    tracker.find_call(target: :\"ActiveSupport::XmlMini\", method: :backend=, chained: true).each do |result|\n      arg = result[:call].first_arg\n\n      return if string? arg and arg.value == \"REXML\"\n    end\n\n    warn :warning_type => \"File Access\",\n      :warning_code => :CVE_2013_1856,\n      :message => msg(msg_version(rails_version), \" with JRuby has a vulnerability in XML parser. Upgrade to \", msg_version(fix_version), \" or patch\"),\n      :confidence => :high,\n      :gem_info => gemfile_or_environment,\n      :link => \"https://groups.google.com/d/msg/rubyonrails-security/KZwsQbYsOiI/5kUV7dSCJGwJ\",\n      :cwe_id => [20]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_json_encoding.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckJSONEncoding < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for missing JSON encoding (CVE-2015-3226)\"\n\n  def run_check\n    if (version_between? \"4.1.0\", \"4.1.10\" or version_between? \"4.2.0\", \"4.2.1\") and not has_workaround?\n      message = msg(msg_version(rails_version), \" does not encode JSON keys \", msg_cve(\"CVE-2015-3226\"), \". Upgrade to \")\n\n      if version_between? \"4.1.0\", \"4.1.10\"\n        message << msg_version(\"4.1.11\")\n      else\n        message << msg_version(\"4.2.2\")\n      end\n\n      if tracker.find_call(:methods => [:to_json, :encode]).any?\n        confidence = :high\n      else\n        confidence = :medium\n      end\n\n      warn :warning_type => \"Cross-Site Scripting\",\n        :warning_code => :CVE_2015_3226,\n        :message => message,\n        :confidence => confidence,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"https://groups.google.com/d/msg/rubyonrails-security/7VlB_pck3hU/3QZrGIaQW6cJ\",\n        :cwe_id => [79]\n    end\n  end\n\n  def has_workaround?\n    workaround = s(:module, :ActiveSupport,\n                   s(:module, :JSON,\n                     s(:module, :Encoding,\n                       s(:call, nil, :private),\n                       s(:class, :EscapedString, nil,\n                         s(:defn, :to_s,\n                           s(:args),\n                           s(:self))))))\n\n    tracker.initializers.any? do |_name, initializer|\n      initializer == workaround\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_json_entity_escape.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckJSONEntityEscape < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Check if HTML escaping is disabled for JSON output\"\n\n  def run_check\n    check_config_setting\n    check_manual_disable\n  end\n\n  def check_config_setting\n    if false? tracker.config.rails.dig(:active_support, :escape_html_entities_in_json)\n      warn :warning_type => \"Cross-Site Scripting\",\n        :warning_code => :json_html_escape_config,\n        :message => msg(\"HTML entities in JSON are not escaped by default\"),\n        :confidence => :medium,\n        :file => \"config/environments/production.rb\",\n        :line => 1,\n        :cwe_id => [79]\n    end\n  end\n\n  def check_manual_disable\n    tracker.find_call(targets: [:ActiveSupport, :'ActiveSupport::JSON::Encoding'], method: :escape_html_entities_in_json=).each do |result|\n      setting = result[:call].first_arg\n\n      if false? setting\n        warn :result => result,\n          :warning_type => \"Cross-Site Scripting\",\n          :warning_code => :json_html_escape_module,\n          :message => msg(\"HTML entities in JSON are not escaped by default\"),\n          :confidence => :medium,\n          :file => \"config/environments/production.rb\",\n          :cwe_id => [79]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_json_parsing.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckJSONParsing < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for JSON parsing vulnerabilities CVE-2013-0333 and CVE-2013-0269\"\n\n  def initialize *args\n    super\n    @uses_json_parse = nil\n  end\n\n  def run_check\n    check_cve_2013_0333\n    check_cve_2013_0269\n  end\n\n  def check_cve_2013_0333\n    return unless version_between? \"0.0.0\", \"2.3.15\" or version_between? \"3.0.0\", \"3.0.19\"\n\n    unless uses_yajl? or uses_gem_backend?\n      new_version = if version_between? \"0.0.0\", \"2.3.14\"\n                      \"2.3.16\"\n                    elsif version_between? \"3.0.0\", \"3.0.19\"\n                      \"3.0.20\"\n                    end\n\n      message = msg(msg_version(rails_version), \" has a serious JSON parsing vulnerability. Upgrade to \", msg_version(new_version), \" or patch\")\n      gem_info = gemfile_or_environment\n\n      warn :warning_type => \"Remote Code Execution\",\n        :warning_code => :CVE_2013_0333,\n        :message => message,\n        :confidence => :high,\n        :gem_info => gem_info,\n        :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/1h2DR63ViGo/discussion\",\n        :cwe_id => [74] # TODO: is this the best CWE for this?\n    end\n  end\n\n  #Check if `yajl` is included in Gemfile\n  def uses_yajl?\n    tracker.config.has_gem? :yajl\n  end\n\n  #Check for `ActiveSupport::JSON.backend = \"JSONGem\"`\n  def uses_gem_backend?\n    matches = tracker.find_call(target: :'ActiveSupport::JSON', method: :backend=, chained: true)\n\n    unless matches.empty?\n      json_gem = s(:str, \"JSONGem\")\n\n      matches.each do |result|\n        if result[:call].first_arg == json_gem\n          return true\n        end\n      end\n    end\n\n    false\n  end\n\n  def check_cve_2013_0269\n    [:json, :json_pure].each do |name|\n      gem_hash = tracker.config.get_gem name\n      check_json_version name, gem_hash[:version] if gem_hash and gem_hash[:version]\n    end\n  end\n\n  def check_json_version name, version\n    return if version >= \"1.7.7\" or\n              (version >= \"1.6.8\" and version < \"1.7.0\") or\n              (version >= \"1.5.5\" and version < \"1.6.0\")\n\n    warning_type = \"Denial of Service\"\n    confidence = :medium\n    gem_name = \"#{name} gem\"\n    message = msg(msg_version(version, gem_name), \" has a symbol creation vulnerability. Upgrade to \")\n\n    if version >= \"1.7.0\"\n      confidence = :high\n      warning_type = \"Remote Code Execution\"\n      message = msg(msg_version(version, \"json gem\"), \" has a remote code execution vulnerability. Upgrade to \", msg_version(\"1.7.7\", \"json gem\"))\n    elsif version >= \"1.6.0\"\n      message << msg_version(\"1.6.8\", gem_name)\n    elsif version >= \"1.5.0\"\n      message << msg_version(\"1.5.5\", gem_name)\n    else\n      confidence = :weak\n      message << msg_version(\"1.5.5\", gem_name)\n    end\n\n    if confidence == :medium and uses_json_parse?\n      confidence = :high\n    end\n\n    warn :warning_type => warning_type,\n      :warning_code => :CVE_2013_0269,\n      :message => message,\n      :confidence => confidence,\n      :gem_info => gemfile_or_environment(name),\n      :link => \"https://groups.google.com/d/topic/rubyonrails-security/4_YvCpLzL58/discussion\",\n      :cwe_id => [74] # TODO: is this the best CWE for this?\n  end\n\n  def uses_json_parse?\n    return @uses_json_parse unless @uses_json_parse.nil?\n\n    not tracker.find_call(:target => :JSON, :method => :parse).empty?\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_link_to.rb",
    "content": "require 'brakeman/checks/check_cross_site_scripting'\n\n#Checks for calls to link_to in versions of Ruby where link_to did not\n#escape the first argument.\n#\n#See https://rails.lighthouseapp.com/projects/8994/tickets/3518-link_to-doesnt-escape-its-input\nclass Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting\n  Brakeman::Checks.add self\n\n  @description = \"Checks for XSS in link_to in versions before 3.0\"\n\n  def run_check\n    return unless version_between?(\"2.0.0\", \"2.9.9\") and not tracker.config.escape_html?\n\n    @ignore_methods = Set[:button_to, :check_box, :escapeHTML, :escape_once,\n                           :field_field, :fields_for, :h, :hidden_field,\n                           :hidden_field, :hidden_field_tag, :image_tag, :label,\n                           :mail_to, :radio_button, :select,\n                           :submit_tag, :text_area, :text_field,\n                           :text_field_tag, :url_encode, :u, :url_for,\n                           :will_paginate].merge tracker.options[:safe_methods]\n\n    @known_dangerous = []\n    #Ideally, I think this should also check to see if people are setting\n    #:escape => false\n    @models = tracker.models.keys\n    @inspect_arguments = tracker.options[:check_arguments]\n\n    tracker.find_call(:target => false, :method => :link_to).each {|call| process_result call}\n  end\n\n  def process_result result\n    return if duplicate? result\n\n    #Have to make a copy of this, otherwise it will be changed to\n    #an ignored method call by the code above.\n    call = result[:call]\n\n    first_arg = call.first_arg\n    second_arg = call.second_arg\n\n    @matched = false\n\n    #Skip if no arguments(?) or first argument is a hash\n    return if first_arg.nil? or hash? first_arg\n\n    if version_between? \"2.0.0\", \"2.2.99\"\n      check_argument result, first_arg\n\n      if second_arg and not hash? second_arg\n        check_argument result, second_arg\n      end\n    elsif second_arg\n      #Only check first argument if there is a second argument\n      #in Rails 2.3.x\n      check_argument result, first_arg\n    end\n  end\n\n  # Check the argument for possible xss exploits\n  def check_argument result, exp\n    argument = process(exp)\n    !check_user_input(result, argument) && !check_method(result, argument) && !check_matched(result, @matched)\n  end\n\n  # Check we should warn about the user input\n  def check_user_input(result, argument)\n    input = has_immediate_user_input?(argument)\n    return false unless input\n\n    message = msg(\"Unescaped \", msg_input(input), \" in \", msg_code(\"link_to\"))\n\n    warn_xss(result, message, input, :high)\n  end\n\n  # Check if we should warn about the specified method\n  def check_method(result, argument)\n    return false if tracker.options[:ignore_model_output]\n    match = has_immediate_model?(argument)\n    return false unless match\n    method = match.method\n    return false if IGNORE_MODEL_METHODS.include? method\n\n    confidence = :medium\n    confidence = :high if likely_model_attribute? match\n    warn_xss(result, msg(\"Unescaped model attribute in \", msg_code(\"link_to\")), match, confidence)\n  end\n\n  # Check if we should warn about the matched result\n  def check_matched(result, matched = nil)\n    return false unless matched\n    return false if matched.type == :model and tracker.options[:ignore_model_output]\n\n    message = msg(\"Unescaped \", msg_input(matched), \" in \", msg_code(\"link_to\"))\n\n    warn_xss(result, message, @matched, :medium)\n  end\n\n  # Create a warn for this xss\n  def warn_xss(result, message, user_input, confidence)\n    add_result(result)\n    warn :result => result,\n      :warning_type => \"Cross-Site Scripting\",\n      :warning_code => :xss_link_to,\n      :message => message,\n      :user_input => user_input,\n      :confidence => confidence,\n      :link_path => \"link_to\",\n      :cwe_id => [79]\n\n    true\n  end\n\n  def process_call exp\n    @mark = true\n    actually_process_call exp\n    exp\n  end\n\n  def actually_process_call exp\n    return if @matched\n\n    target = exp.target\n    target = process target.dup if sexp? target\n\n    #Bare records create links to the model resource,\n    #not a string that could have injection\n    #TODO: Needs test? I think this is broken?\n    return exp if model_name? target and context == [:call, :arglist]\n\n    super\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_link_to_href.rb",
    "content": "require 'brakeman/checks/check_cross_site_scripting'\n\n#Checks for calls to link_to which pass in potentially hazardous data\n#to the second argument.  While this argument must be html_safe to not break\n#the html, it must also be url safe as determined by calling a\n#:url_safe_method.  This prevents attacks such as javascript:evil() or\n#data:<encoded XSS> which is html_safe, but not safe as an href\n#Props to Nick Green for the idea.\nclass Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo\n  Brakeman::Checks.add self\n\n  @description = \"Checks to see if values used for hrefs are sanitized using a :url_safe_method to protect against javascript:/data: XSS\"\n\n  def run_check\n    @ignore_methods = Set[:button_to, :check_box,\n                           :field_field, :fields_for, :hidden_field,\n                           :hidden_field, :hidden_field_tag, :image_tag, :label,\n                           :mail_to, :polymorphic_url, :radio_button, :select, :slice,\n                           :submit_tag, :text_area, :text_field,\n                           :text_field_tag, :url_encode, :u,\n                           :will_paginate].merge(tracker.options[:url_safe_methods] || [])\n\n    @models = tracker.models.keys\n    @inspect_arguments = tracker.options[:check_arguments]\n\n    methods = tracker.find_call :target => false, :method => :link_to\n    methods.each do |call|\n      process_result call\n    end\n  end\n\n  def process_result result\n    call = result[:call]\n    @matched = false\n\n    url_arg = if result[:block]\n                process call.first_arg\n              else\n                process call.second_arg\n              end\n\n    if check_argument? url_arg\n      url_arg = url_arg.first_arg\n    end\n\n    return if call? url_arg and ignore_call? url_arg.target, url_arg.method\n\n    if input = has_immediate_user_input?(url_arg)\n      message = msg(\"Unsafe \", msg_input(input), \" in \", msg_code(\"link_to\"), \" href\")\n\n      unless duplicate? result or call_on_params? url_arg or ignore_interpolation? url_arg, input.match\n        add_result result\n        warn :result => result,\n          :warning_type => \"Cross-Site Scripting\",\n          :warning_code => :xss_link_to_href,\n          :message => message,\n          :user_input => input,\n          :confidence => :high,\n          :link_path => \"link_to_href\",\n          :cwe_id => [79]\n      end\n    elsif not tracker.options[:ignore_model_output] and input = has_immediate_model?(url_arg)\n      return if ignore_model_call? url_arg, input or duplicate? result\n      add_result result\n\n      message = msg(\"Potentially unsafe model attribute in \", msg_code(\"link_to\"), \" href\")\n\n      warn :result => result,\n        :warning_type => \"Cross-Site Scripting\",\n        :warning_code => :xss_link_to_href,\n        :message => message,\n        :user_input => input,\n        :confidence => :weak,\n        :link_path => \"link_to_href\",\n        :cwe_id => [79]\n    end\n  end\n\n  CHECK_INSIDE_METHODS = [:url_for, :h, :sanitize]\n\n  def check_argument? url_arg\n    return unless call? url_arg\n\n    target = url_arg.target\n    method = url_arg.method\n\n    CHECK_INSIDE_METHODS.include? method or\n      cgi_escaped? target, method\n  end\n\n  def ignore_model_call? url_arg, exp\n    return true unless call? exp\n\n    target = exp.target\n    method = exp.method\n\n    return true unless model_find_call? target\n\n    return true unless method.to_s =~ /url|uri|link|page|site/\n\n    ignore_call? target, method or\n      IGNORE_MODEL_METHODS.include? method or\n      ignore_interpolation? url_arg, exp\n  end\n\n  #Ignore situations where the href is an interpolated string\n  #with something before the user input\n  def ignore_interpolation? arg, suspect\n    return unless string_interp? arg\n    return true unless arg[1].chomp.empty? # plain string before interpolation\n\n    first_interp = arg.find_nodes(:evstr).first\n    return unless first_interp\n\n    first_interp[1].deep_each do |e|\n      if suspect == e\n        return false\n      end\n    end\n\n    true\n  end\n\n  def ignore_call? target, method\n    decorated_model? method or super\n  end\n\n  def decorated_model? method\n    tracker.config.has_gem? :draper and\n      method == :decorate\n  end\n\n  def ignored_method? target, method\n    @ignore_methods.include? method or\n      method.to_s =~ /_path$/ or\n      (target.nil? and method.to_s =~ /_url$/)\n  end\n\n  def model_find_call? exp\n    return unless call? exp\n\n    MODEL_METHODS.include? exp.method or\n      exp.method.to_s =~ /^find_by_/\n  end\n\n  def call_on_params? exp\n    call? exp and\n    params? exp.target and\n    exp.method != :[]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_mail_to.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Check for cross-site scripting vulnerability in mail_to :encode => :javascript\n#with certain versions of Rails (< 2.3.11 or < 3.0.4).\n#\n#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/f02a48ede8315f81\nclass Brakeman::CheckMailTo < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for mail_to XSS vulnerability in certain versions\"\n\n  def run_check\n    if (version_between? \"2.3.0\", \"2.3.10\" or version_between? \"3.0.0\", \"3.0.3\") and result = mail_to_javascript?\n      message = msg(\"Vulnerability in \", msg_code(\"mail_to\"), \" using javascript encoding \", msg_cve(\"CVE-2011-0446\"), \". Upgrade to \")\n\n      if version_between? \"2.3.0\", \"2.3.10\"\n        message << msg_version(\"2.3.11\")\n      else\n        message << msg_version(\"3.0.4\")\n      end\n\n      warn :result => result,\n        :warning_type => \"Mail Link\",\n        :warning_code => :CVE_2011_0446,\n        :message => message,\n        :confidence => :high,\n        :gem_info => gemfile_or_environment, # Probably ignored now\n        :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/8CpI7egxX4E/discussion\",\n        :cwe_id => [79]\n    end\n  end\n\n  #Check for javascript encoding of mail_to address\n  #    mail_to email, name, :encode => :javascript\n  def mail_to_javascript?\n    Brakeman.debug \"Checking calls to mail_to for javascript encoding\"\n\n    tracker.find_call(:target => false, :method => :mail_to).each do |result|\n      result[:call].each_arg do |arg|\n        if hash? arg\n          if option = hash_access(arg, :encode)\n            return result if symbol? option and option.value == :javascript\n          end\n        end\n      end\n    end\n\n    false\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_mass_assignment.rb",
    "content": "require 'brakeman/checks/base_check'\nrequire 'set'\n\n#Checks for mass assignments to models.\n#\n#See http://guides.rubyonrails.org/security.html#mass-assignment for details\nclass Brakeman::CheckMassAssignment < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Finds instances of mass assignment\"\n\n  def initialize(*)\n    super\n    @mass_assign_calls = nil\n  end\n\n  def run_check\n    check_mass_assignment\n    check_permit!\n    check_permit_all_parameters\n  end\n\n  def find_mass_assign_calls\n    return @mass_assign_calls if @mass_assign_calls\n\n    models = []\n    tracker.models.each do |name, m|\n      if m.is_a? Hash\n        p m\n      end\n      if m.unprotected_model?\n        models << name\n      end\n    end\n\n    return [] if models.empty?\n\n    Brakeman.debug \"Finding possible mass assignment calls on #{models.length} models\"\n    @mass_assign_calls = tracker.find_call :chained => true, :targets => models, :methods => [:new,\n      :attributes=,\n      :update_attributes,\n      :update_attributes!,\n      :create,\n      :create!,\n      :build,\n      :first_or_create,\n      :first_or_create!,\n      :first_or_initialize!,\n      :assign_attributes,\n      :update\n    ]\n  end\n\n  def check_mass_assignment\n    return if mass_assign_disabled?\n\n    Brakeman.debug \"Processing possible mass assignment calls\"\n    find_mass_assign_calls.each do |result|\n      process_result result\n    end\n  end\n\n  #All results should be Model.new(...) or Model.attributes=() calls\n  def process_result res\n    call = res[:call]\n\n    check = check_call call\n\n    if check and original? res\n\n      model = tracker.models[res[:chain].first]\n      attr_protected = (model and model.attr_protected)\n      first_arg = call.first_arg\n\n      if attr_protected and tracker.options[:ignore_attr_protected]\n        return\n      elsif call? first_arg and (first_arg.method == :slice or first_arg.method == :only)\n        return\n      elsif input = include_user_input?(call.arglist)\n        if not node_type? first_arg, :hash\n          if attr_protected\n            confidence = :medium\n          else\n            confidence = :high\n          end\n        else\n          return\n        end\n      elsif node_type? call.first_arg, :lit, :str\n        return\n      else\n        confidence = :weak\n        input = nil\n      end\n\n      warn :result => res,\n        :warning_type => \"Mass Assignment\",\n        :warning_code => :mass_assign_call,\n        :message => \"Unprotected mass assignment\",\n        :code => call,\n        :user_input => input,\n        :confidence => confidence,\n        :cwe_id => [915]\n    end\n\n    res\n  end\n\n  #Want to ignore calls to Model.new that have no arguments\n  def check_call call\n    process_call_args call\n\n    if call.method == :update\n      arg = call.second_arg\n    else\n      arg = call.first_arg\n    end\n\n    if arg.nil? #empty new()\n      false\n    elsif hash? arg and not include_user_input? arg\n      false\n    elsif all_literal_args? call\n      false\n    else\n      true\n    end\n  end\n\n  LITERALS = Set[:lit, :true, :false, :nil, :string]\n\n  def all_literal_args? exp\n    if call? exp\n      exp.each_arg do |arg|\n        return false unless literal? arg\n      end\n\n      true\n    else\n      exp.all? do |arg|\n        literal? arg\n      end\n    end\n\n  end\n\n  def literal? exp\n    if sexp? exp\n      if exp.node_type == :hash\n        all_literal_args? exp\n      else\n        LITERALS.include? exp.node_type\n      end\n    else\n      true\n    end\n  end\n\n  # Look for and warn about uses of Parameters#permit! for mass assignment\n  def check_permit!\n    tracker.find_call(:method => :permit!, :nested => true).each do |result|\n      if params? result[:call].target\n        unless inside_safe_method? result or calls_slice? result\n          warn_on_permit! result\n        end\n      end\n    end\n  end\n\n  # Ignore blah_some_path(params.permit!)\n  def inside_safe_method? result\n    parent_call = result.dig(:parent, :call)\n\n    call? parent_call and\n      parent_call.method.match(/_path$/)\n  end\n\n  def calls_slice? result\n    result[:chain].include? :slice or\n      (result[:full_call] and result[:full_call][:chain].include? :slice)\n  end\n\n  # Look for actual use of params in mass assignment to avoid\n  # warning about uses of Parameters#permit! without any mass assignment\n  # or when mass assignment is restricted by model instead.\n  def subsequent_mass_assignment? result\n    location = result[:location]\n    line = result[:call].line\n    find_mass_assign_calls.any? do |call|\n      call[:location] == location and\n      params? call[:call].first_arg and\n      call[:call].line >= line\n    end\n  end\n\n  def warn_on_permit! result\n    return unless original? result\n\n    confidence = if subsequent_mass_assignment? result\n                   :high\n                 else\n                   :medium\n                 end\n\n    warn :result => result,\n      :warning_type => \"Mass Assignment\",\n      :warning_code => :mass_assign_permit!,\n      :message => msg('Specify exact keys allowed for mass assignment instead of using ', msg_code('permit!'), ' which allows any keys'),\n      :confidence => confidence,\n      :cwe_id => [915]\n  end\n\n  def check_permit_all_parameters\n    tracker.find_call(target: :\"ActionController::Parameters\", method: :permit_all_parameters=).each do |result|\n      call = result[:call]\n\n      if true? call.first_arg\n        warn :result => result,\n          :warning_type => \"Mass Assignment\",\n          :warning_code => :mass_assign_permit_all,\n          :message => msg('Mass assignment is globally enabled. Disable and specify exact keys using ', msg_code('params.permit'), ' instead'),\n          :confidence => :high,\n          :cwe_id => [915]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_mime_type_dos.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckMimeTypeDoS < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for mime type denial of service (CVE-2016-0751)\"\n\n  def run_check\n    fix_version = case\n               when version_between?(\"3.0.0\", \"3.2.22\")\n                 \"3.2.22.1\"\n               when version_between?(\"4.0.0\", \"4.1.14\")\n                 \"4.1.14.1\"\n               when version_between?(\"4.2.0\", \"4.2.5\")\n                 \"4.2.5.1\"\n               else\n                 return\n               end\n\n    return if has_workaround?\n\n    message = msg(msg_version(rails_version), \" is vulnerable to denial of service via mime type caching \", msg_cve(\"CVE-2016-0751\"), \". Upgrade to \", msg_version(fix_version))\n\n    warn :warning_type => \"Denial of Service\",\n      :warning_code => :CVE_2016_0751,\n      :message => message,\n      :confidence => :medium,\n      :gem_info => gemfile_or_environment,\n      :link_path => \"https://groups.google.com/d/msg/rubyonrails-security/9oLY_FCzvoc/w9oI9XxbFQAJ\",\n      :cwe_id => [399]\n  end\n\n  def has_workaround?\n    tracker.find_call(target: :Mime, method: :const_set).any? do |match|\n      arg = match[:call].first_arg\n\n      symbol? arg and arg.value == :LOOKUP\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_model_attr_accessible.rb",
    "content": "require 'brakeman/checks/base_check'\n\n# Author: Paul Deardorff (themetric)\n# Checks models to see if important foreign keys\n# or attributes are exposed as attr_accessible when\n# they probably shouldn't be.\n\nclass Brakeman::CheckModelAttrAccessible < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Reports models which have dangerous attributes defined via attr_accessible\"\n\n  SUSP_ATTRS = [\n    [:admin, :high], # Very dangerous unless some Rails authorization used\n    [:role, :medium],\n    [:banned, :medium],\n    [:account_id, :high],\n    [/\\S*_id(s?)\\z/, :weak] # All other foreign keys have weak/low confidence\n  ]\n\n  def run_check\n    check_models do |name, model|\n      model.attr_accessible.each do |attribute|\n        next if role_limited? model, attribute\n\n        SUSP_ATTRS.each do |susp_attr, confidence|\n          if susp_attr.is_a?(Regexp) and susp_attr =~ attribute.to_s or susp_attr == attribute\n            warn :model => model,\n              :file => model.file,\n              :warning_type => \"Mass Assignment\",\n              :warning_code => :dangerous_attr_accessible,\n              :message => \"Potentially dangerous attribute available for mass assignment\",\n              :confidence => confidence,\n              :code => Sexp.new(:lit, attribute),\n              :cwe_id => [915]\n\n            break # Prevent from matching single attr multiple times\n          end\n        end\n      end\n    end\n  end\n\n  def role_limited? model, attribute\n    role_accessible = model.role_accessible\n    return if role_accessible.nil?\n    role_accessible.include? attribute\n  end\n\n  def check_models\n    tracker.models.each do |name, model|\n      if !model.attr_accessible.nil?\n        yield name, model\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_model_attributes.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Check if mass assignment is used with models\n#which inherit from ActiveRecord::Base.\nclass Brakeman::CheckModelAttributes < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Reports models which do not use attr_restricted and warns on models that use attr_protected\"\n\n  def run_check\n    return if mass_assign_disabled? or tracker.config.has_gem?(:protected_attributes)\n\n    #Roll warnings into one warning for all models\n    if tracker.options[:collapse_mass_assignment]\n      Brakeman.alert \"The `collapse_mass_assignment` option has been removed.\"\n    end\n\n    check_models do |name, model|\n      if model.attr_protected.nil?\n        warn :model => model,\n          :file => model.file,\n          :line => model.top_line,\n          :warning_type => \"Attribute Restriction\",\n          :warning_code => :no_attr_accessible,\n          :message => msg(\"Mass assignment is not restricted using \", msg_code(\"attr_accessible\")),\n          :confidence => :high,\n          :cwe_id => [915] # TODO: Should this be mass assignment?\n      elsif not tracker.options[:ignore_attr_protected]\n        message, confidence, link = check_for_attr_protected_bypass\n\n        if link\n          warning_code = :CVE_2013_0276\n        else\n          warning_code = :attr_protected_used\n        end\n\n        warn :model => model,\n          :file => model.file,\n          :line => model.attr_protected.first.line,\n          :warning_type => \"Attribute Restriction\",\n          :warning_code => warning_code,\n          :message => message,\n          :confidence => confidence,\n          :cwe_id => [915] # TODO: Should this be mass assignment?\n      end\n    end\n  end\n\n  def check_models\n    tracker.models.each do |name, model|\n      if model.unprotected_model?\n        yield name, model\n      end\n    end\n  end\n\n  def check_for_attr_protected_bypass\n    upgrade_version = case\n                      when version_between?(\"2.0.0\", \"2.3.16\")\n                        \"2.3.17\"\n                      when version_between?(\"3.0.0\", \"3.0.99\")\n                        \"3.2.11\"\n                      when version_between?(\"3.1.0\", \"3.1.10\")\n                        \"3.1.11\"\n                      when version_between?(\"3.2.0\", \"3.2.11\")\n                        \"3.2.12\"\n                      else\n                        nil\n                      end\n\n    if upgrade_version\n      message = msg(msg_code(\"attr_protected\"), \" is bypassable in \", msg_version(rails_version), \". Use \", msg_code(\"attr_accessible\"), \" or upgrade to \", msg_version(upgrade_version))\n      confidence = :high\n      link = \"https://groups.google.com/d/topic/rubyonrails-security/AFBKNY7VSH8/discussion\"\n    else\n      message = msg(msg_code(\"attr_accessible\"), \" is recommended over \", msg_code(\"attr_protected\"))\n      confidence = :medium\n      link = nil\n    end\n\n    return message, confidence, link\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_model_serialize.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckModelSerialize < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Report uses of serialize in versions vulnerable to CVE-2013-0277\"\n\n  def run_check\n    @upgrade_version = case\n                      when version_between?(\"2.0.0\", \"2.3.16\")\n                        \"2.3.17\"\n                      when version_between?(\"3.0.0\", \"3.0.99\")\n                        \"3.2.11\"\n                      else\n                        nil\n                      end\n\n    return unless @upgrade_version\n\n    tracker.models.each do |_name, model|\n      check_for_serialize model\n    end\n  end\n\n  #High confidence warning on serialized, unprotected attributes.\n  #Medium confidence warning for serialized, protected attributes.\n  def check_for_serialize model\n    if serialized_attrs = model.options[:serialize]\n      attrs = Set.new\n\n      serialized_attrs.each do |arglist|\n        arglist.each do |arg|\n          attrs << arg if symbol? arg\n        end\n      end\n\n      if unsafe_attrs = model.attr_accessible\n        attrs.delete_if { |attr| not unsafe_attrs.include? attr.value }\n      elsif protected_attrs = model.attr_protected\n        safe_attrs = Set.new\n\n        protected_attrs.each do |arglist|\n          arglist.each do |arg|\n            safe_attrs << arg if symbol? arg\n          end\n        end\n\n        attrs.delete_if { |attr| safe_attrs.include? attr }\n      end\n\n      if attrs.empty?\n        confidence = :medium\n      else\n        confidence = :high\n      end\n\n      warn :model => model,\n        :warning_type => \"Remote Code Execution\",\n        :warning_code => :CVE_2013_0277,\n        :message => msg(\"Serialized attributes are vulnerable in \", msg_version(rails_version), \", upgrade to \", msg_version(@upgrade_version), \" or patch\"),\n        :confidence => confidence,\n        :link => \"https://groups.google.com/d/topic/rubyonrails-security/KtmwSbEpzrU/discussion\",\n        :file => model.file,\n        :line => model.top_line,\n        :cwe_id => [502]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_nested_attributes.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Check for vulnerability in nested attributes in Rails 2.3.9 and 3.0.0\n#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/f9f913d328dafe0c\nclass Brakeman::CheckNestedAttributes < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for nested attributes vulnerability in Rails 2.3.9 and 3.0.0\"\n\n  def run_check\n    version = rails_version\n\n    if (version == \"2.3.9\" or version == \"3.0.0\") and uses_nested_attributes?\n      message = msg(\"Vulnerability in nested attributes \", msg_cve(\"CVE-2010-3933\"), \". Upgrade to \")\n\n      if version == \"2.3.9\"\n        message << msg_version(\"2.3.10\")\n      else\n        message << msg_version(\"3.0.1\")\n      end\n\n      warn :warning_type => \"Nested Attributes\",\n        :warning_code => :CVE_2010_3933,\n        :message => message,\n        :confidence => :high,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/-fkT0yja_gw/discussion\",\n        :cwe_id => [20]\n    end\n  end\n\n  def uses_nested_attributes?\n    active_record_models.each do |_name, model|\n      return true if model.options[:accepts_nested_attributes_for]\n    end\n\n    false\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_nested_attributes_bypass.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#https://groups.google.com/d/msg/rubyonrails-security/cawsWcQ6c8g/tegZtYdbFQAJ\nclass Brakeman::CheckNestedAttributesBypass < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for nested attributes vulnerability (CVE-2015-7577)\"\n\n  def run_check\n    if version_between? \"3.1.0\", \"3.2.22\" or\n       version_between? \"4.0.0\", \"4.1.14\" or\n       version_between? \"4.2.0\", \"4.2.5\"\n\n      unless workaround?\n        check_nested_attributes\n      end\n    end\n  end\n\n  def check_nested_attributes\n    active_record_models.each do |name, model|\n      if opts = model.options[:accepts_nested_attributes_for]\n        opts.each do |args|\n          if args.any? { |a| allow_destroy? a } and args.any? { |a| reject_if? a }\n            warn_about_nested_attributes model, args\n          end\n        end\n      end\n    end\n  end\n\n  def warn_about_nested_attributes model, args\n    message = msg(msg_version(rails_version), \" does not call \", msg_code(\":reject_if\"), \" option when \", msg_code(\":allow_destroy\"), \" is \", msg_code(\"false\"), \" \", msg_cve(\"CVE-2015-7577\"))\n\n    warn :model => model,\n      :warning_type => \"Nested Attributes\",\n      :warning_code => :CVE_2015_7577,\n      :message => message,\n      :file => model.file,\n      :line => args.line,\n      :confidence => :medium,\n      :link_path => \"https://groups.google.com/d/msg/rubyonrails-security/cawsWcQ6c8g/tegZtYdbFQAJ\",\n      :cwe_id => [284]\n  end\n\n  def allow_destroy? arg\n    hash? arg and\n      false? hash_access(arg, :allow_destroy)\n  end\n\n  def reject_if? arg\n    hash? arg and\n      hash_access(arg, :reject_if)\n  end\n\n  def workaround?\n    tracker.find_call(method: :will_be_destroyed?).any?\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_number_to_currency.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckNumberToCurrency < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for number helpers XSS vulnerabilities in certain versions\"\n\n  def initialize(*)\n    super\n    @found_any = false\n  end\n\n  def run_check\n    if version_between? \"2.0.0\", \"2.3.18\" or\n      version_between? \"3.0.0\", \"3.2.16\" or\n      version_between? \"4.0.0\", \"4.0.2\"\n\n      return if lts_version? \"2.3.18.8\"\n\n      check_number_helper_usage\n      generic_warning unless @found_any\n    end\n  end\n\n  def generic_warning\n    message = msg(msg_version(rails_version), \" has a vulnerability in number helpers \", msg_cve(\"CVE-2014-0081\"), \". Upgrade to \")\n\n    if version_between? \"2.3.0\", \"3.2.16\"\n      message << msg_version(\"3.2.17\")\n    else\n      message << msg_version(\"4.0.3\")\n    end\n\n    warn :warning_type => \"Cross-Site Scripting\",\n      :warning_code => :CVE_2014_0081,\n      :message => message,\n      :confidence => :medium,\n      :gem_info => gemfile_or_environment,\n      :link_path => \"https://groups.google.com/d/msg/ruby-security-ann/9WiRn2nhfq0/2K2KRB4LwCMJ\",\n      :cwe_id => [79]\n  end\n\n  def check_number_helper_usage\n    number_methods = [:number_to_currency, :number_to_percentage, :number_to_human]\n    tracker.find_call(:target => false, :methods => number_methods).each do |result|\n      arg = result[:call].second_arg\n      next unless arg\n\n      if not check_helper_option(result, arg) and hash? arg\n        hash_iterate(arg) do |_key, value|\n          break if check_helper_option(result, value)\n        end\n      end\n    end\n  end\n\n  def check_helper_option result, exp\n    if match = (has_immediate_user_input? exp or has_immediate_model? exp)\n      warn_on_number_helper result, match\n      @found_any = true\n    else\n      false\n    end\n  end\n\n  def warn_on_number_helper result, match\n    warn :result => result,\n      :warning_type => \"Cross-Site Scripting\",\n      :warning_code => :CVE_2014_0081_call,\n      :message => msg(\"Format options in \", msg_code(result[:call].method), \" are not safe in \", msg_version(rails_version)),\n      :confidence => :high,\n      :link_path => \"https://groups.google.com/d/msg/ruby-security-ann/9WiRn2nhfq0/2K2KRB4LwCMJ\",\n      :user_input => match,\n      :cwe_id => [79]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_page_caching_cve.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckPageCachingCVE < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Check for page caching vulnerability (CVE-2020-8159)\"\n\n  def run_check\n    gem_name = 'actionpack-page_caching'\n    gem_version = tracker.config.gem_version(gem_name.to_sym)\n    upgrade_version = '1.2.2'\n    cve = 'CVE-2020-8159'\n\n    return unless gem_version and version_between?('0.0.0', '1.2.1', gem_version)\n\n    message = msg(\"Directory traversal vulnerability in \", msg_version(gem_version, gem_name), \" \", msg_cve(cve), \". Upgrade to \", msg_version(upgrade_version, gem_name))\n\n    if uses_caches_page?\n      confidence = :high\n    else\n      confidence = :weak\n    end\n\n    warn :warning_type => 'Directory Traversal',\n      :warning_code => :CVE_2020_8159,\n      :message => message,\n      :confidence => confidence,\n      :link_path => 'https://groups.google.com/d/msg/rubyonrails-security/CFRVkEytdP8/c5gmICECAgAJ',\n      :gem_info => gemfile_or_environment(gem_name),\n      :cwe_id => [22]\n  end\n\n  def uses_caches_page?\n    tracker.controllers.any? do |name, controller|\n      controller.options.has_key? :caches_page\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_pathname.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckPathname < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Check for unexpected Pathname behavior\"\n\n  def run_check\n    check_rails_root_join\n    check_pathname_join\n\n  end\n\n  def check_rails_root_join\n    tracker.find_call(target: :'Rails.root', method: :join, nested: true).each do |result|\n      check_result result\n    end\n  end\n\n  def check_pathname_join\n    pathname_methods = [\n      :'Pathname.new',\n      :'Pathname.getwd',\n      :'Pathname.glob',\n      :'Pathname.pwd',\n    ]\n\n    tracker.find_call(targets: pathname_methods, method: :join, nested: true).each do |result|\n      check_result result\n    end\n  end\n\n  def check_result result\n    return unless original? result\n\n    result[:call].each_arg do |arg|\n      if match = has_immediate_user_input?(arg)\n        warn :result => result,\n          :warning_type => \"Path Traversal\",\n          :warning_code => :pathname_traversal,\n          :message => \"Absolute paths in `Pathname#join` cause the entire path to be relative to the absolute path, ignoring any prior values\",\n          :user_input => match,\n          :confidence => :high,\n          :cwe_id => [22]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_permit_attributes.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckPermitAttributes < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Warn on potentially dangerous attributes allowed via permit\"\n\n  SUSPICIOUS_KEYS = {\n    admin: :high,\n    account_id: :high,\n    role: :medium,\n    banned: :medium,\n  }\n\n  def run_check\n    tracker.find_call(:method => :permit).each do |result|\n      check_permit result\n    end\n  end\n\n  def check_permit result\n    return unless original? result\n\n    call = result[:call]\n\n    call.each_arg do |arg|\n      if symbol? arg\n        if SUSPICIOUS_KEYS.key? arg.value\n          warn_on_permit_key result, arg\n        end\n      end\n    end\n  end\n\n  def warn_on_permit_key result, key, confidence = nil\n    warn :result => result,\n      :warning_type => \"Mass Assignment\",\n      :warning_code => :dangerous_permit_key,\n      :message => \"Potentially dangerous key allowed for mass assignment\",\n      :confidence => (confidence || SUSPICIOUS_KEYS[key.value]),\n      :user_input => key,\n      :cwe_id => [915]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_quote_table_name.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Check for uses of quote_table_name in Rails versions before 2.3.13 and 3.0.10\n#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/6a1e473744bc389b\nclass Brakeman::CheckQuoteTableName < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for quote_table_name vulnerability in versions before 2.3.14 and 3.0.10\"\n\n  def run_check\n    if (version_between?('2.0.0', '2.3.13') or \n        version_between?('3.0.0', '3.0.9'))\n\n      if uses_quote_table_name?\n        confidence = :high\n      else\n        confidence = :medium\n      end\n\n      if rails_version =~ /^3/\n        message = msg(\"Rails versions before 3.0.10 have a vulnerability in \", msg_code(\"quote_table_name\"), \" \", msg_cve(\"CVE-2011-2930\"))\n      else\n        message = msg(\"Rails versions before 2.3.14 have a vulnerability in \", msg_code(\"quote_table_name\"), \" \", msg_cve(\"CVE-2011-2930\"))\n      end\n\n      warn :warning_type => \"SQL Injection\",\n        :warning_code => :CVE_2011_2930,\n        :message => message,\n        :confidence => confidence,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/ah5HN0S8OJs/discussion\",\n        :cwe_id => [89]\n    end\n  end\n\n  def uses_quote_table_name?\n    Brakeman.debug \"Finding calls to quote_table_name()\"\n\n    not tracker.find_call(:target => false, :method => :quote_table_name).empty?\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_ransack.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckRansack < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for dangerous use of the Ransack library\"\n\n  def run_check\n    return unless version_between? \"0.0.0\", \"3.99\", tracker.config.gem_version(:ransack)\n    check_ransack_calls\n  end\n\n  def check_ransack_calls\n    tracker.find_call(method: :ransack, nested: true).each do |result|\n      next unless original? result\n\n      call = result[:call]\n      arg = call.first_arg\n\n      # If an allow list is defined anywhere in the\n      # class or super classes, consider it safe\n      class_name = result[:chain].first\n\n      next if ransackable_allow_list?(class_name)\n\n      if input = has_immediate_user_input?(arg)\n        confidence = if tracker.find_class(class_name).nil?\n                       confidence = :low\n                     elsif result[:location][:file].relative.include? 'admin'\n                       confidence = :medium\n                     else\n                       confidence = :high\n                     end\n\n        message = msg('Unrestricted search using ', msg_code('ransack'), ' library called with ', msg_input(input), '. Limit search by defining ', msg_code('ransackable_attributes'), ' and ', msg_code('ransackable_associations'), ' methods in class or upgrade Ransack to version 4.0.0 or newer')\n\n        warn result: result,\n          warning_type: 'Missing Authorization',\n          warning_code: :ransack_search,\n          message: message,\n          user_input: input,\n          confidence: confidence,\n          cwe_id: [862],\n          link: 'https://positive.security/blog/ransack-data-exfiltration'\n      end\n    end\n  end\n\n  def ransackable_allow_list? class_name\n    tracker.find_method(:ransackable_attributes, class_name, :class) and\n      tracker.find_method(:ransackable_associations, class_name, :class)\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_redirect.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Reports any calls to +redirect_to+ which include parameters in the arguments.\n#\n#For example:\n#\n# redirect_to params.merge(:action => :elsewhere)\nclass Brakeman::CheckRedirect < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Looks for calls to redirect_to with user input as arguments\"\n\n  def run_check\n    @model_find_calls = Set[:all, :create, :create!, :find, :find_by_sql, :first, :first!, :last, :last!, :new, :sole]\n\n    if tracker.options[:rails3]\n      @model_find_calls.merge [:from, :group, :having, :joins, :lock, :order, :reorder, :select, :where]\n    end\n\n    if version_between? \"4.0.0\", \"9.9.9\"\n      @model_find_calls.merge [:find_by, :find_by!, :take]\n    end\n\n    if version_between? \"7.0.0\", \"9.9.9\"\n      @model_find_calls << :find_sole_by\n    end\n\n    methods = [:redirect_to, :redirect_back, :redirect_back_or_to]\n\n    @tracker.find_call(:target => false, :methods => methods).each do |res|\n      process_result res\n    end\n  end\n\n  def process_result result\n    return unless original? result\n\n    call = result[:call]\n    opt = call.first_arg\n\n    # Location is specified with `fallback_location:`\n    # otherwise the arguments do not contain a location and\n    # the call can be ignored\n    if call.method == :redirect_back\n      if hash? opt and location = hash_access(opt, :fallback_location)\n        opt = location\n      else\n        return\n      end\n    end\n\n    if not protected_by_raise?(call) and\n        not only_path?(call) and\n        not explicit_host?(opt) and\n        not slice_call?(opt) and\n        not safe_permit?(opt) and\n        not disallow_other_host?(call) and\n        res = include_user_input?(opt)\n\n      if res.type == :immediate and not allow_other_host?(call)\n        confidence = :high\n      else\n        confidence = :weak\n      end\n\n      warn :result => result,\n        :warning_type => \"Redirect\",\n        :warning_code => :open_redirect,\n        :message => \"Possible unprotected redirect\",\n        :code => call,\n        :user_input => res,\n        :confidence => confidence,\n        :cwe_id => [601]\n    end\n  end\n\n  #Custom check for user input. First looks to see if the user input\n  #is being output directly. This is necessary because of tracker.options[:check_arguments]\n  #which can be used to enable/disable reporting output of method calls which use\n  #user input as arguments.\n  def include_user_input? opt, immediate = :immediate\n    Brakeman.debug \"Checking if call includes user input\"\n\n    # if the first argument is an array, rails assumes you are building a\n    # polymorphic route, which will never jump off-host\n    return false if array? opt\n\n    if tracker.options[:ignore_redirect_to_model]\n      if model_instance?(opt) or decorated_model?(opt)\n        return false\n      end\n    end\n\n    if res = has_immediate_model?(opt)\n      unless call? opt and opt.method.to_s =~ /_path/\n        return Match.new(immediate, res)\n      end\n    elsif call? opt\n      if request_value? opt\n        return Match.new(immediate, opt)\n      elsif opt.method == :url_for and include_user_input? opt.first_arg\n        return Match.new(immediate, opt)\n        #Ignore helpers like some_model_url?\n      elsif opt.method.to_s =~ /_(url|path)\\z/\n        return false\n      elsif opt.method == :url_from\n        return false\n      end\n    elsif request_value? opt\n      return Match.new(immediate, opt)\n    elsif node_type? opt, :or\n      return (include_user_input?(opt.lhs) or include_user_input?(opt.rhs))\n    end\n\n    if tracker.options[:check_arguments] and call? opt\n      include_user_input? opt.first_arg, false  #I'm doubting if this is really necessary...\n    else\n      false\n    end\n  end\n\n  #Checks +redirect_to+ arguments for +only_path => true+ which essentially\n  #nullifies the danger posed by redirecting with user input\n  def only_path? call\n    arg = call.first_arg\n\n    if hash? arg\n      return has_only_path? arg\n    elsif call? arg and arg.method == :url_for\n      return check_url_for(arg)\n    elsif call? arg and hash? arg.first_arg and use_unsafe_hash_method? arg\n      return has_only_path? arg.first_arg\n    end\n\n    false\n  end\n\n  def use_unsafe_hash_method? arg\n    return call_has_param(arg, :to_unsafe_hash) || call_has_param(arg, :to_unsafe_h)\n  end\n\n  def call_has_param arg, key\n    if call? arg and call? arg.target\n      target = arg.target\n      method = target.method\n\n      node_type? target.target, :params and method == key\n    else\n      false\n    end\n  end\n\n  def has_only_path? arg\n    if value = hash_access(arg, :only_path)\n      return true if true?(value)\n    end\n\n    false\n  end\n\n  def explicit_host? arg\n    return unless sexp? arg\n\n    if hash? arg\n      if value = hash_access(arg, :host)\n        return !has_immediate_user_input?(value)\n      end\n    elsif call? arg\n      target = arg.target\n\n      if hash? target and value = hash_access(target, :host)\n        return !has_immediate_user_input?(value)\n      elsif call? arg\n        return explicit_host? target\n      end\n    end\n\n    false\n  end\n\n  #+url_for+ is only_path => true by default. This checks to see if it is\n  #set to false for some reason.\n  def check_url_for call\n    arg = call.first_arg\n\n    if hash? arg\n      if value = hash_access(arg, :only_path)\n        return false if false?(value)\n      end\n    end\n\n    true\n  end\n\n  #Returns true if exp is (probably) a model instance\n  def model_instance? exp\n    if node_type? exp, :or\n      model_instance? exp.lhs or model_instance? exp.rhs\n    elsif call? exp\n      if model_target? exp and\n        (@model_find_calls.include? exp.method or exp.method.to_s.match(/^find_by_/))\n        true\n      else\n        association?(exp.target, exp.method)\n      end\n    end\n  end\n\n  def model_target? exp\n    return false unless call? exp\n    model_name? exp.target or\n    friendly_model? exp.target or\n    model_target? exp.target\n  end\n\n  #Returns true if exp is (probably) a friendly model instance\n  #using the FriendlyId gem\n  def friendly_model? exp\n    call? exp and model_name? exp.target and exp.method == :friendly\n  end\n\n  #Returns true if exp is (probably) a decorated model instance\n  #using the Draper gem\n  def decorated_model? exp\n    if node_type? exp, :or\n      decorated_model? exp.lhs or decorated_model? exp.rhs\n    else\n      tracker.config.has_gem? :draper and\n      call? exp and\n      node_type?(exp.target, :const) and\n      exp.target.value.to_s.match(/Decorator$/) and\n      exp.method == :decorate\n    end\n  end\n\n  #Check if method is actually an association in a Model\n  def association? model_name, meth\n    if call? model_name\n      return association? model_name.target, meth\n    elsif model_name? model_name\n      model = tracker.models[class_name(model_name)]\n    else\n      return false\n    end\n\n    return false unless model\n\n    model.association? meth\n  end\n\n  def slice_call? exp\n    return unless call? exp\n    exp.method == :slice\n  end\n\n  DANGEROUS_KEYS = [:host, :subdomain, :domain, :port]\n\n  def safe_permit? exp\n    if call? exp and params? exp.target and exp.method == :permit\n      exp.each_arg do |opt|\n        if symbol? opt and DANGEROUS_KEYS.include? opt.value\n          return false\n        end\n      end\n\n      return true\n    end\n\n    false\n  end\n\n  def protected_by_raise? call\n    raise_on_redirects? and\n      not allow_other_host? call\n  end\n\n  def raise_on_redirects?\n    @raise_on_redirects ||= true?(tracker.config.rails.dig(:action_controller, :raise_on_open_redirects))\n  end\n\n  def allow_other_host? call\n    opt = call.last_arg\n\n    hash? opt and true? hash_access(opt, :allow_other_host)\n  end\n\n  def disallow_other_host? call\n    opt = call.last_arg\n\n    hash? opt and false? hash_access(opt, :allow_other_host)\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_regex_dos.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#This check looks for regexes that include user input.\nclass Brakeman::CheckRegexDoS < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  ESCAPES = {\n    s(:const, :Regexp) => [\n      :escape,\n      :quote\n    ]\n  }\n\n  @description = \"Searches regexes including user input\"\n\n  #Process calls\n  def run_check\n    Brakeman.debug \"Finding dynamic regexes\"\n    calls = tracker.find_call :method => [:brakeman_regex_interp]\n\n    Brakeman.debug \"Processing dynamic regexes\"\n    calls.each do |call|\n      process_result call\n    end\n  end\n\n  #Warns if regex includes user input\n  def process_result result\n    return unless original? result\n\n    call = result[:call]\n    components = call.sexp_body\n\n    components.any? do |component|\n      next unless sexp? component\n\n      if match = has_immediate_user_input?(component)\n        confidence = :high\n      elsif match = has_immediate_model?(component)\n        match = Match.new(:model, match)\n        confidence = :medium\n      elsif match = include_user_input?(component)\n        confidence = :weak\n      end\n\n      if match\n        message = msg(msg_input(match), \" used in regular expression\")\n\n        warn :result => result,\n          :warning_type => \"Denial of Service\",\n          :warning_code => :regex_dos,\n          :message => message,\n          :confidence => confidence,\n          :user_input => match,\n          :cwe_id => [20, 185]\n      end\n    end\n  end\n\n  def process_call(exp)\n    if escape_methods = ESCAPES[exp.target]\n      if escape_methods.include? exp.method\n        return exp\n      end\n    end\n\n    super\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_render.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Check calls to +render()+ for dangerous values\nclass Brakeman::CheckRender < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Finds calls to render that might allow file access or code execution\"\n\n  def run_check\n    tracker.find_call(:target => nil, :method => :render).each do |result|\n      process_render_result result\n    end\n  end\n\n  def process_render_result result\n    return unless node_type? result[:call], :render\n\n    case result[:call].render_type\n    when :partial, :template, :action, :file\n      check_for_dynamic_path(result)\n    when :inline\n    when :js\n    when :json\n    when :text\n    when :update\n    when :xml\n    end\n  end\n\n  #Check if path to action or file is determined dynamically\n  def check_for_dynamic_path result\n    view = result[:call][2]\n\n    if sexp? view and original? result\n      return if renderable?(view)\n\n      if input = has_immediate_user_input?(view)\n        if string_interp? view\n          confidence = :medium\n        else\n          confidence = :high\n        end\n      else\n        return\n      end\n\n      return if input.type == :model #skip models\n      return if safe_param? input.match\n\n      message = msg(\"Render path contains \", msg_input(input))\n\n      warn :result => result,\n        :warning_type => \"Dynamic Render Path\",\n        :warning_code => :dynamic_render_path,\n        :message => message,\n        :user_input => input,\n        :confidence => confidence,\n        :cwe_id => [22]\n    end\n  end\n\n  def safe_param? exp\n    if params? exp and call? exp\n      method_name = exp.method\n\n      if method_name == :[]\n        arg = exp.first_arg\n        symbol? arg and [:controller, :action].include? arg.value\n      else\n        boolean_method? method_name\n      end\n    end\n  end\n\n  def renderable? exp\n    return false unless call?(exp) and constant?(exp.target)\n\n    if exp.method == :with_content\n      exp = exp.target\n    end\n\n    return false unless constant?(exp.target)\n    target_class_name = class_name(exp.target)\n    known_renderable_class?(target_class_name) or tracker.find_method(:render_in, target_class_name)\n  end\n\n  def known_renderable_class? class_name\n    klass = tracker.find_class(class_name)\n    return false if klass.nil?\n    knowns = [\n      :\"ViewComponent::Base\",\n      :\"ViewComponentContrib::Base\",\n      :\"Phlex::HTML\"\n    ]\n    knowns.any? { |k| klass.ancestor? k }\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_render_dos.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckRenderDoS < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Warn about denial of service with render :text (CVE-2014-0082)\"\n\n  def run_check\n    if version_between? \"3.0.0\", \"3.0.20\" or\n       version_between? \"3.1.0\", \"3.1.12\" or\n       version_between? \"3.2.0\", \"3.2.16\"\n\n      tracker.find_call(:target => nil, :method => :render).each do |result|\n        if text_render? result\n          warn_about_text_render\n          break\n        end\n      end\n    end\n  end\n\n  def text_render? result\n    node_type? result[:call], :render and\n    result[:call].render_type == :text\n  end\n\n  def warn_about_text_render\n    message = msg(msg_version(rails_version), \" has a denial of service vulnerability \", msg_cve(\"CVE-2014-0082\"), \". Upgrade to \", msg_version(\"3.2.17\"))\n\n    warn :warning_type => \"Denial of Service\",\n      :warning_code => :CVE_2014_0082,\n      :message => message,\n      :confidence => :high,\n      :link_path => \"https://groups.google.com/d/msg/rubyonrails-security/LMxO_3_eCuc/ozGBEhKaJbIJ\",\n      :gem_info => gemfile_or_environment,\n      :cwe_id => [20]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_render_inline.rb",
    "content": "class Brakeman::CheckRenderInline < Brakeman::CheckCrossSiteScripting\n  Brakeman::Checks.add self\n\n  @description = \"Checks for cross-site scripting in render calls\"\n\n  def run_check\n    setup\n\n    tracker.find_call(:target => nil, :method => :render).each do |result|\n      check_render result\n    end\n  end\n\n  def check_render result\n    return unless original? result\n\n    call = result[:call]\n\n    if node_type? call, :render and\n      (call.render_type == :text or call.render_type == :inline)\n\n      unless call.render_type == :text and content_type_set? call[3]\n        render_value = call[2]\n\n        if input = has_immediate_user_input?(render_value)\n          warn :result => result,\n            :warning_type => \"Cross-Site Scripting\",\n            :warning_code => :cross_site_scripting_inline,\n            :message => msg(\"Unescaped \", msg_input(input), \" rendered inline\"),\n            :user_input => input,\n            :confidence => :high,\n            :cwe_id => [79]\n        elsif input = has_immediate_model?(render_value)\n          warn :result => result,\n            :warning_type => \"Cross-Site Scripting\",\n            :warning_code => :cross_site_scripting_inline,\n            :message => \"Unescaped model attribute rendered inline\",\n            :user_input => input,\n            :confidence => :medium,\n            :cwe_id => [79]\n        end\n      end\n    end\n  end\n\n  CONTENT_TYPES = [\"text/html\", \"text/javascript\", \"application/javascript\"]\n\n  def content_type_set? opts\n    if hash? opts\n      content_type = hash_access(opts, :content_type)\n\n      string? content_type and not CONTENT_TYPES.include? content_type.value\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_render_rce.rb",
    "content": "require 'brakeman/checks/check_render'\n\nclass Brakeman::CheckRenderRCE < Brakeman::CheckRender\n  Brakeman::Checks.add self\n\n  @description = \"Finds calls to render that might be vulnerable to CVE-2016-0752\"\n\n  def run_check\n    tracker.find_call(:target => nil, :method => :render).each do |result|\n      process_render_result result\n    end\n  end\n\n  def process_render_result result\n    return unless node_type? result[:call], :render\n\n    case result[:call].render_type\n    when :partial, :template, :action, :file\n      check_for_rce(result)\n    end\n  end\n\n  def check_for_rce result\n    return unless version_between? \"0.0.0\", \"3.2.22\" or\n                  version_between? \"4.0.0\", \"4.1.14\" or\n                  version_between? \"4.2.0\", \"4.2.5\"\n\n    view = result[:call][2]\n    if sexp? view and not duplicate? result\n      if params? view and not safe_param? view\n        add_result result\n\n        warn :result => result,\n          :warning_type => \"Remote Code Execution\",\n          :warning_code => :dynamic_render_path_rce,\n          :message => msg(\"Passing query parameters to \", msg_code(\"render\"), \" is vulnerable in \", msg_version(rails_version), \" \", msg_cve(\"CVE-2016-0752\")),\n          :user_input => view,\n          :confidence => :high,\n          :cwe_id => [22]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_response_splitting.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Warn about response splitting in Rails versions before 2.3.13\n#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/6ffc93bde0298768\nclass Brakeman::CheckResponseSplitting < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Report response splitting in Rails 2.3.0 - 2.3.13\"\n\n  def run_check\n    if version_between?('2.3.0', '2.3.13')\n\n      warn :warning_type => \"Response Splitting\",\n        :warning_code => :CVE_2011_3186,\n        :message => msg(\"Rails versions before 2.3.14 have a vulnerability content type handling allowing injection of headers \", msg_cve(\"CVE-2011-3186\")),\n        :confidence => :medium,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/b_yTveAph2g/discussion\",\n        :cwe_id => [94]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_reverse_tabnabbing.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckReverseTabnabbing < Brakeman::BaseCheck\n  Brakeman::Checks.add_optional self\n\n  @description = \"Checks for reverse tabnabbing cases on 'link_to' calls\"\n\n  def run_check\n    calls = tracker.find_call :methods => :link_to\n    calls.each do |call|\n      process_result call\n    end\n  end\n\n  def process_result result\n    return unless original? result and result[:call].last_arg\n\n    html_opts = result[:call].last_arg\n    return unless hash? html_opts\n\n    target = hash_access html_opts, :target\n    unless target &&\n          (string?(target) && target.value == \"_blank\" ||\n          symbol?(target) && target.value == :_blank)\n      return\n    end\n\n    target_url = result[:block] ? result[:call].first_arg : result[:call].second_arg\n\n    # `url_for` and `_path` calls lead to urls on to the same origin.\n    # That means that an adversary would need to run javascript on\n    # the victim application's domain. If that is the case, the adversary\n    # already has the ability to redirect the victim user anywhere.\n    # Also statically provided URLs (interpolated or otherwise) are also\n    # ignored as they produce many false positives.\n    return if !call?(target_url) || target_url.method.match(/^url_for$|_path$/)\n\n    rel = hash_access html_opts, :rel\n    confidence = :medium\n\n    if rel && string?(rel) then\n      rel_opt = rel.value\n      return if rel_opt.include?(\"noopener\") && rel_opt.include?(\"noreferrer\")\n\n      if rel_opt.include?(\"noopener\") ^ rel_opt.include?(\"noreferrer\") then\n        confidence = :weak\n      end\n    end\n\n    warn :result => result,\n      :warning_type => \"Reverse Tabnabbing\",\n      :warning_code => :reverse_tabnabbing,\n      :message => msg(\"When opening a link in a new tab without setting \", msg_code('rel: \"noopener noreferrer\"'),\n                      \", the new tab can control the parent tab's location. For example, an attacker could redirect to a phishing page.\"),\n      :confidence => confidence,\n      :user_input => rel,\n      :cwe_id => [1022]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_route_dos.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckRouteDoS < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for route DoS (CVE-2015-7581)\"\n\n  def run_check\n    fix_version = case\n                  when version_between?(\"4.0.0\", \"4.1.14\")\n                    \"4.1.14.1\"\n                  when version_between?(\"4.2.0\", \"4.2.5\")\n                    \"4.2.5.1\"\n                  else\n                    return\n                  end\n\n    if controller_wildcards?\n      message = msg(msg_version(rails_version), \" has a denial of service vulnerability with \", msg_code(\":controller\"), \" routes \", msg_cve(\"CVE-2015-7581\"), \". Upgrade to \", msg_version(fix_version))\n\n      warn :warning_type => \"Denial of Service\",\n        :warning_code => :CVE_2015_7581,\n        :message => message,\n        :confidence => :medium,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"https://groups.google.com/d/msg/rubyonrails-security/dthJ5wL69JE/YzPnFelbFQAJ\",\n        :cwe_id => [399]\n    end\n  end\n\n  def controller_wildcards?\n    tracker.routes.each do |name, actions|\n      if name == :':controllerController'\n        # awful hack for routes with :controller in them\n        return true\n      elsif string? actions and actions.value.include? \":controller\"\n        return true\n      end\n    end\n\n    false\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_safe_buffer_manipulation.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Check for unsafe manipulation of strings\n#Right now this is just a version check for\n#https://groups.google.com/group/rubyonrails-security/browse_thread/thread/edd28f1e3d04e913?pli=1\nclass Brakeman::CheckSafeBufferManipulation < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Check for Rails versions with SafeBuffer bug\"\n\n  def run_check\n\n    if version_between? \"3.0.0\", \"3.0.11\"\n      suggested_version = \"3.0.12\"\n    elsif version_between? \"3.1.0\", \"3.1.3\"\n      suggested_version = \"3.1.4\"\n    elsif version_between? \"3.2.0\", \"3.2.1\"\n      suggested_version = \"3.2.2\"\n    else\n      return\n    end\n\n    message = msg(msg_version(rails_version), \" has a vulnerability in \", msg_code(\"SafeBuffer\"), \". Upgrade to \", msg_version(suggested_version), \" or apply patches\")\n\n    warn :warning_type => \"Cross-Site Scripting\",\n      :warning_code => :safe_buffer_vuln, \n      :message => message,\n      :confidence => :medium,\n      :gem_info => gemfile_or_environment,\n      :cwe_id => [79]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_sanitize_config_cve.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckSanitizeConfigCve < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for vunerable uses of sanitize (CVE-2022-32209)\"\n\n  def run_check\n    @specific_warning = false\n\n    @gem_version = tracker.config.gem_version :'rails-html-sanitizer'\n    if version_between? \"0.0.0\", \"1.4.2\", @gem_version\n      check_config\n      check_sanitize_calls\n      check_safe_list_allowed_tags\n\n      unless @specific_warning\n        # General warning about the vulnerable version\n        cve_warning\n      end\n    end\n  end\n\n  def cve_warning confidence: :weak, result: nil\n    return if result and not original? result\n\n    message = msg(msg_version(@gem_version, 'rails-html-sanitizer'),\n                  \" is vulnerable to cross-site scripting when \",\n                  msg_code('select'),\n                  \" and \",\n                  msg_code(\"style\"),\n                  \" tags are allowed \",\n                  msg_cve(\"CVE-2022-32209\")\n                 )\n\n    unless result\n      message << \". Upgrade to 1.4.3 or newer\"\n    end\n\n    warn :warning_type => \"Cross-Site Scripting\",\n      :warning_code => :CVE_2022_32209,\n      :message => message,\n      :confidence => confidence,\n      :gem_info => gemfile_or_environment(:'rails-html-sanitizer'),\n      :link_path => \"https://groups.google.com/g/rubyonrails-security/c/ce9PhUANQ6s/m/S0fJfnkmBAAJ\",\n      :cwe_id => [79],\n      :result => result\n  end\n\n  # Look for\n  #   config.action_view.sanitized_allowed_tags = [\"select\", \"style\"]\n  def check_config\n    sanitizer_config = tracker.config.rails.dig(:action_view, :sanitized_allowed_tags)\n\n    if sanitizer_config and include_both_tags? sanitizer_config\n      @specific_warning = true\n      cve_warning confidence: :high\n    end\n  end\n\n  # Look for\n  #   sanitize ..., tags: [\"select\", \"style\"]\n  # and\n  #   Rails::Html::SafeListSanitizer.new.sanitize(..., tags: [\"select\", \"style\"])\n  def check_sanitize_calls\n    tracker.find_call(method: :sanitize, target: nil).each do |result|\n      check_tags_option result\n    end\n\n    tracker.find_call(method: :sanitize, target: :'Rails::Html::SafeListSanitizer.new').each do |result|\n      check_tags_option result\n    end\n  end\n\n  # Look for\n  #   Rails::Html::SafeListSanitizer.allowed_tags = [\"select\", \"style\"]\n  def check_safe_list_allowed_tags\n    tracker.find_call(target: :'Rails::Html::SafeListSanitizer', method: :allowed_tags=).each do |result|\n      check_result result, result[:call].first_arg\n    end\n  end\n\n  private\n\n  def check_tags_option result\n    options = result[:call].last_arg\n\n    if options\n      check_result result, hash_access(options, :tags)\n    end\n  end\n\n  def check_result result, arg\n    if include_both_tags? arg\n      @specific_warning = true\n      cve_warning confidence: :high, result: result\n    end\n  end\n\n  def include_both_tags? exp\n    return unless sexp? exp\n\n    has_tag? exp, 'select' and\n      has_tag? exp, 'style'\n  end\n\n  def has_tag? exp, tag\n    tag_sym = tag.to_sym\n\n    exp.each_sexp do |e|\n      if string? e and e.value == tag\n        return true\n      elsif symbol? e and e.value == tag_sym\n        return true\n      end\n    end\n\n    false\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_sanitize_methods.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#sanitize and sanitize_css are vulnerable:\n#CVE-2013-1855 and CVE-2013-1857\nclass Brakeman::CheckSanitizeMethods < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for versions with vulnerable sanitize and sanitize_css\"\n\n  def run_check\n    @fix_version = case\n      when version_between?('2.0.0', '2.3.17')\n        '2.3.18'\n      when version_between?('3.0.0', '3.0.99')\n        '3.2.13'\n      when version_between?('3.1.0', '3.1.11')\n        '3.1.12'\n      when version_between?('3.2.0', '3.2.12')\n        '3.2.13'\n      end\n\n    if @fix_version\n      check_cve_2013_1855\n      check_cve_2013_1857\n    end\n\n    if tracker.config.has_gem? :'rails-html-sanitizer'\n      check_rails_html_sanitizer\n    end\n\n    check_cve_2018_8048\n  end\n\n  def check_cve_2013_1855\n    check_for_cve :sanitize_css, :CVE_2013_1855, \"https://groups.google.com/d/msg/rubyonrails-security/4_QHo4BqnN8/_RrdfKk12I4J\"\n  end\n\n  def check_cve_2013_1857\n    check_for_cve :sanitize, :CVE_2013_1857, \"https://groups.google.com/d/msg/rubyonrails-security/zAAU7vGTPvI/1vZDWXqBuXgJ\"\n  end\n\n  def check_for_cve method, code, link\n    tracker.find_call(:target => false, :method => method).each do |result|\n      next if duplicate? result\n      add_result result\n\n      message = msg(msg_version(rails_version), \" has a vulnerability in \", msg_code(method), \". Upgrade to \", msg_version(@fix_version), \" or patch\")\n\n      warn :result => result,\n        :warning_type => \"Cross-Site Scripting\",\n        :warning_code => code,\n        :message => message,\n        :confidence => :high,\n        :link_path => link,\n        :cwe_id => [79]\n    end\n  end\n\n  def check_rails_html_sanitizer\n    rhs_version = tracker.config.gem_version(:'rails-html-sanitizer')\n\n    if version_between? \"1.0.0\", \"1.0.2\", rhs_version\n      warn_sanitizer_cve \"CVE-2015-7578\", \"https://groups.google.com/d/msg/rubyonrails-security/uh--W4TDwmI/JbvSRpdbFQAJ\", \"1.0.3\"\n      warn_sanitizer_cve \"CVE-2015-7580\", \"https://groups.google.com/d/msg/rubyonrails-security/uh--W4TDwmI/m_CVZtdbFQAJ\", \"1.0.3\"\n    end\n\n    if version_between? \"1.0.0\", \"1.0.3\", rhs_version\n      warn_sanitizer_cve \"CVE-2018-3741\", \"https://groups.google.com/d/msg/rubyonrails-security/tP7W3kLc5u4/uDy2Br7xBgAJ\", \"1.0.4\"\n    end\n  end\n\n  def check_cve_2018_8048\n    if loofah_vulnerable_cve_2018_8048?\n      message = msg(msg_version(tracker.config.gem_version(:loofah), \"loofah gem\"), \" is vulnerable (CVE-2018-8048). Upgrade to 2.2.1\")\n\n      if tracker.find_call(:target => false, :method => :sanitize).any?\n        confidence = :high\n      else\n        confidence = :medium\n      end\n\n      warn :warning_type => \"Cross-Site Scripting\",\n        :warning_code => :CVE_2018_8048,\n        :message => message,\n        :gem_info => gemfile_or_environment(:loofah),\n        :confidence => confidence,\n        :link_path => \"https://github.com/flavorjones/loofah/issues/144\",\n        :cwe_id => [79]\n    end\n  end\n\n  def loofah_vulnerable_cve_2018_8048?\n    loofah_version = tracker.config.gem_version(:loofah)\n\n    # 2.2.1 is fix version\n    loofah_version and version_between?(\"0.0.0\", \"2.2.0\", loofah_version)\n  end\n\n  def warn_sanitizer_cve cve, link, upgrade_version\n    message = msg(msg_version(tracker.config.gem_version(:'rails-html-sanitizer'), \"rails-html-sanitizer\"), \" is vulnerable \", msg_cve(cve), \". Upgrade to \", msg_version(upgrade_version, \"rails-html-sanitizer\"))\n\n    if tracker.find_call(:target => false, :method => :sanitize).any?\n      confidence = :high\n    else\n      confidence = :medium\n    end\n\n    warn :warning_type => \"Cross-Site Scripting\",\n      :warning_code => cve.tr('-', '_').to_sym,\n      :message => message,\n      :gem_info => gemfile_or_environment(:'rails-html-sanitizer'),\n      :confidence => confidence,\n      :link_path => link,\n      :cwe_id => [79]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_secrets.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckSecrets < Brakeman::BaseCheck\n  Brakeman::Checks.add_optional self\n\n  @description = \"Checks for secrets stored in source code\"\n\n  def run_check\n    check_constants\n  end\n\n  def check_constants\n    @warned = Set.new\n\n    @tracker.constants.each do |constant|\n      name = constant.name_array.last\n      value = constant.value\n\n      if string? value and not value.value.empty? and looks_like_secret? name\n        match = [name, value, value.line]\n\n        unless @warned.include? match\n          @warned << match\n\n          warn :warning_code => :secret_in_source,\n            :warning_type => \"Authentication\",\n            :message => msg(\"Hardcoded value for \", msg_code(name), \" in source code\"),\n            :confidence => :medium,\n            :file => constant.file,\n            :line => constant.line,\n            :cwe_id => [798]\n        end\n      end\n    end\n  end\n\n  def looks_like_secret? name\n    # REST_AUTH_SITE_KEY is the pepper in Devise\n    name.match(/password|secret|(rest_auth_site|api)_key$/i)\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_select_tag.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Checks for CVE-2012-3463, unescaped input in :prompt option of select_tag:\n#https://groups.google.com/d/topic/rubyonrails-security/fV3QUToSMSw/discussion\nclass Brakeman::CheckSelectTag < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Looks for unsafe uses of select_tag() in some versions of Rails 3.x\"\n\n  def run_check\n\n    if version_between? \"3.0.0\", \"3.0.16\"\n      suggested_version = \"3.0.17\"\n    elsif version_between? \"3.1.0\", \"3.1.7\"\n      suggested_version = \"3.1.8\"\n    elsif version_between? \"3.2.0\", \"3.2.7\"\n      suggested_version = \"3.2.8\"\n    else\n      return\n    end\n\n    @ignore_methods = Set[:escapeHTML, :escape_once, :h].merge tracker.options[:safe_methods]\n\n    @message = msg(\"Upgrade to \", msg_version(suggested_version), \". In \", msg_version(rails_version), \" \", msg_code(\"select_tag\"), \" is vulnerable \", msg_cve(\"CVE-2012-3463\"))\n\n    calls = tracker.find_call(:target => nil, :method => :select_tag).select do |result|\n      result[:location][:type] == :template\n    end\n\n    calls.each do |result|\n      process_result result\n    end\n  end\n\n  #Check if select_tag is called with user input in :prompt option\n  def process_result result\n    return unless original? result\n\n    #Only concerned if user input is supplied for :prompt option\n    last_arg = result[:call].last_arg\n\n    if hash? last_arg\n      prompt_option = hash_access last_arg, :prompt\n\n      if call? prompt_option and @ignore_methods.include? prompt_option.method\n        return\n      elsif sexp? prompt_option and input = include_user_input?(prompt_option)\n\n        warn :warning_type => \"Cross-Site Scripting\",\n          :warning_code => :CVE_2012_3463,\n          :result => result,\n          :message => @message,\n          :confidence => :high,\n          :user_input => input,\n          :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/fV3QUToSMSw/discussion\",\n          :cwe_id => [79]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_select_vulnerability.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Checks for select() helper vulnerability in some versions of Rails 3\n#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/9da0c515a6c4664\nclass Brakeman::CheckSelectVulnerability < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Looks for unsafe uses of select() helper\"\n\n  def run_check\n\n    if lts_version? \"2.3.18.7\"\n      return\n    elsif version_between? \"3.0.0\", \"3.0.11\"\n      suggested_version = \"3.0.12\"\n    elsif version_between? \"3.1.0\", \"3.1.3\"\n      suggested_version = \"3.1.4\"\n    elsif version_between? \"3.2.0\", \"3.2.1\"\n      suggested_version = \"3.2.2\"\n    elsif version_between? \"2.0.0\", \"2.3.14\"\n      suggested_version = \"3 or use options_for_select\"\n    else\n      return\n    end\n\n    @message = msg(\"Upgrade to \", msg_version(suggested_version), \". In \", msg_version(rails_version), \" \", msg_code(\"select\"), \" helper is vulnerable\")\n\n    calls = tracker.find_call(:target => nil, :method => :select).select do |result|\n      result[:location][:type] == :template\n    end\n\n    calls.each do |result|\n      process_result result\n    end\n  end\n\n  def process_result result\n    return if duplicate? result\n\n    third_arg = result[:call].third_arg\n\n    #Check for user input in options parameter\n    if sexp? third_arg and include_user_input? third_arg\n      add_result result\n\n      if string_interp? third_arg\n        confidence = :medium\n      else\n        confidence = :weak\n      end\n\n      warn :template => result[:location][:template],\n          :warning_type => \"Cross-Site Scripting\",\n          :warning_code => :select_options_vuln,\n          :result => result,\n          :message => @message,\n          :confidence => confidence,\n          :cwe_id => [79]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_send.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Checks if user supplied data is passed to send\nclass Brakeman::CheckSend < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Check for unsafe use of Object#send\"\n\n  def run_check\n    @send_methods = [:send, :try, :__send__, :public_send]\n    Brakeman.debug(\"Finding instances of #send\")\n    calls = tracker.find_call :methods => @send_methods, :nested => true\n\n    calls.each do |call|\n      process_result call\n    end\n  end\n\n  def process_result result\n    return unless original? result\n\n    send_call = get_send result[:call]\n    process_call_args send_call\n    process send_call.target\n\n    if input = has_immediate_user_input?(send_call.first_arg)\n      warn :result => result,\n        :warning_type => \"Dangerous Send\",\n        :warning_code => :dangerous_send,\n        :message => \"User controlled method execution\",\n        :user_input => input,\n        :confidence => :high,\n        :cwe_id => [77]\n    end\n  end\n\n  # Recursively check call chain for send call\n  def get_send exp\n    if call? exp\n      if @send_methods.include? exp.method\n        return exp\n      else\n        get_send exp.target\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_send_file.rb",
    "content": "require 'brakeman/checks/check_file_access'\nrequire 'brakeman/processors/lib/processor_helper'\n\n#Checks for user input in send_file()\nclass Brakeman::CheckSendFile < Brakeman::CheckFileAccess\n  Brakeman::Checks.add self\n\n  @description = \"Check for user input in uses of send_file\"\n\n  def run_check\n    Brakeman.debug \"Finding all calls to send_file()\"\n\n    methods = tracker.find_call :target => false, :method => :send_file\n\n    methods.each do |call|\n      process_result call\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_session_manipulation.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckSessionManipulation < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Check for user input in session keys\"\n\n  def run_check\n    tracker.find_call(:method => :[]=, :target => :session).each do |result|\n      process_result result\n    end\n  end\n\n  def process_result result\n    return unless original? result\n\n    index = result[:call].first_arg\n\n    if input = has_immediate_user_input?(index)\n      if params? index\n        confidence = :high\n      else\n        confidence = :medium\n      end\n\n      warn :result => result,\n        :warning_type => \"Session Manipulation\",\n        :warning_code => :session_key_manipulation,\n        :message => msg(msg_input(input), \" used as key in session hash\"),\n        :user_input => input,\n        :confidence => confidence,\n        :cwe_id => [20] # TODO: what cwe should this be? it seems like it's looking for authz bypass\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_session_settings.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Checks for session key length and http_only settings\nclass Brakeman::CheckSessionSettings < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for session key length and http_only settings\"\n\n  def initialize *args\n    super\n\n    unless tracker.options[:rails3]\n      @session_settings = Sexp.new(:colon2, Sexp.new(:const, :ActionController), :Base)\n    else\n      @session_settings = nil\n    end\n  end\n\n  def run_check\n    settings = tracker.config.session_settings \n\n    check_for_issues settings, @app_tree.file_path(\"config/environment.rb\")\n\n    session_store = @app_tree.file_path(\"config/initializers/session_store.rb\")\n    secret_token = @app_tree.file_path(\"config/initializers/secret_token.rb\")\n\n    [session_store, secret_token].each do |file|\n      if tracker.initializers[file] and not ignored? file.basename\n        process tracker.initializers[file]\n      end\n    end\n\n    if tracker.options[:rails4]\n      check_secrets_yaml\n    end\n  end\n\n  #Looks for ActionController::Base.session = { ... }\n  #in Rails 2.x apps\n  #\n  #and App::Application.config.secret_token =\n  #in Rails 3.x apps\n  #\n  #and App::Application.config.secret_key_base =\n  #in Rails 4.x apps\n  def process_attrasgn exp\n    if not tracker.options[:rails3] and exp.target == @session_settings and exp.method == :session=\n      check_for_issues exp.first_arg, @app_tree.file_path(\"config/initializers/session_store.rb\")\n    end\n\n    if tracker.options[:rails3] and settings_target?(exp.target) and\n      (exp.method == :secret_token= or exp.method == :secret_key_base=) and string? exp.first_arg\n\n      warn_about_secret_token exp.line, @app_tree.file_path(\"config/initializers/secret_token.rb\")\n    end\n\n    exp\n  end\n\n  #Looks for Rails3::Application.config.session_store :cookie_store, { ... }\n  #in Rails 3.x apps\n  def process_call exp\n    if tracker.options[:rails3] and settings_target?(exp.target) and exp.method == :session_store\n      check_for_rails3_issues exp.second_arg, @app_tree.file_path(\"config/initializers/session_store.rb\")\n    end\n\n    exp\n  end\n\n  private\n\n  def settings_target? exp\n    call? exp and\n    exp.method == :config and\n    node_type? exp.target, :colon2 and\n    exp.target.rhs == :Application\n  end\n\n  def check_for_issues settings, file\n    if settings and hash? settings\n      if value = (hash_access(settings, :session_http_only) ||\n                  hash_access(settings, :http_only) ||\n                  hash_access(settings, :httponly))\n\n        if false? value\n          warn_about_http_only value.line, file\n        end\n      end\n\n      if value = hash_access(settings, :secret)\n        if string? value\n          warn_about_secret_token value.line, file\n        end\n      end\n    end\n  end\n\n  def check_for_rails3_issues settings, file\n    if settings and hash? settings\n      if value = hash_access(settings, :httponly)\n        if false? value\n          warn_about_http_only value.line, file\n        end\n      end\n\n      if value = hash_access(settings, :secure)\n        if false? value\n          warn_about_secure_only value.line, file\n        end\n      end\n    end\n  end\n\n  def check_secrets_yaml\n    secrets_file = @app_tree.file_path(\"config/secrets.yml\")\n\n    if secrets_file.exists? and not ignored? \"secrets.yml\" and not ignored? \"config/*.yml\"\n      yaml = secrets_file.read\n      require 'yaml'\n      begin\n        secrets = YAML.safe_load yaml, aliases: true\n      rescue Psych::SyntaxError, RuntimeError => e\n        Brakeman.alert \"#{self.class}: Unable to parse `#{secrets_file}`\"\n        Brakeman.debug \"Failed to parse #{secrets_file}: #{e.inspect}\"\n        return\n      end\n\n      if secrets && secrets[\"production\"] and secret = secrets[\"production\"][\"secret_key_base\"]\n        unless secret.include? \"<%=\"\n          line = yaml.lines.find_index { |l| l.include? secret } + 1\n\n          warn_about_secret_token line, @app_tree.file_path(secrets_file)\n        end\n      end\n    end\n  end\n\n  def warn_about_http_only line, file\n    warn :warning_type => \"Session Setting\",\n      :warning_code => :http_cookies,\n      :message => \"Session cookies should be set to HTTP only\",\n      :confidence => :high,\n      :line => line,\n      :file => file,\n      :cwe_id => [1004]\n\n  end\n\n  def warn_about_secret_token line, file\n    warn :warning_type => \"Session Setting\",\n      :warning_code => :session_secret,\n      :message => \"Session secret should not be included in version control\",\n      :confidence => :high,\n      :line => line,\n      :file => file,\n      :cwe_id => [798]\n  end\n\n  def warn_about_secure_only line, file\n    warn :warning_type => \"Session Setting\",\n      :warning_code => :secure_cookies,\n      :message => \"Session cookie should be set to secure only\",\n      :confidence => :high,\n      :line => line,\n      :file => file,\n      :cwe_id => [614]\n  end\n\n  def ignored? file\n    [\".\", \"config\", \"config/initializers\"].each do |dir|\n      ignore_file = @app_tree.file_path(\"#{dir}/.gitignore\")\n      if @app_tree.exists? ignore_file\n        input = ignore_file.read \n\n        return true if input.include? file\n      end\n    end\n\n    false\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_simple_format.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckSimpleFormat < Brakeman::CheckCrossSiteScripting\n  Brakeman::Checks.add self\n\n  @description = \"Checks for simple_format XSS vulnerability (CVE-2013-6416) in certain versions\"\n\n  def initialize *args\n    super\n    @found_any = false\n  end\n\n  def run_check\n    if version_between? \"4.0.0\", \"4.0.1\"\n      @inspect_arguments = true\n      @ignore_methods = Set[:h, :escapeHTML]\n\n      check_simple_format_usage\n      generic_warning unless @found_any\n    end\n  end\n\n  def generic_warning\n    message = msg(msg_version(rails_version), \" has a vulnerability in \", msg_code(\"simple_format\"), \" \", msg_cve(\"CVE-2013-6416\"), \". Upgrade to \", msg_version(\"4.0.2\"))\n\n    warn :warning_type => \"Cross-Site Scripting\",\n      :warning_code => :CVE_2013_6416,\n      :message => message,\n      :confidence => :medium,\n      :gem_info => gemfile_or_environment,\n      :link_path => \"https://groups.google.com/d/msg/ruby-security-ann/5ZI1-H5OoIM/ZNq4FoR2GnIJ\",\n      :cwe_id => [79]\n  end\n\n  def check_simple_format_usage\n    tracker.find_call(:target => false, :method => :simple_format).each do |result|\n      @matched = false\n      process_call result[:call]\n      if @matched\n        warn_on_simple_format result, @matched\n      end\n    end\n  end\n\n  def process_call exp\n    @mark = true\n    actually_process_call exp\n    exp\n  end\n\n  def warn_on_simple_format result, match\n    return unless original? result\n\n    @found_any = true\n\n    warn :result => result,\n      :warning_type => \"Cross-Site Scripting\",\n      :warning_code => :CVE_2013_6416_call,\n      :message => msg(\"Values passed to \", msg_code(\"simple_format\"), \" are not safe in \", msg_version(rails_version)),\n      :confidence => :high,\n      :link_path => \"https://groups.google.com/d/msg/ruby-security-ann/5ZI1-H5OoIM/ZNq4FoR2GnIJ\",\n      :user_input => match,\n      :cwe_id => [79]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_single_quotes.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Checks for versions which do not escape single quotes.\n#https://groups.google.com/d/topic/rubyonrails-security/kKGNeMrnmiY/discussion\nclass Brakeman::CheckSingleQuotes < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n  RACK_UTILS = Sexp.new(:colon2, Sexp.new(:const, :Rack), :Utils)\n\n  @description = \"Check for versions which do not escape single quotes (CVE-2012-3464)\"\n\n  def initialize *args\n    super\n    @inside_erb = @inside_util = @inside_html_escape = @uses_rack_escape = false\n  end\n\n  def run_check\n    return if uses_rack_escape?\n\n    if version_between? '2.0.0', '2.3.14'\n      message = msg(\"All Rails 2.x versions do not escape single quotes \", msg_cve(\"CVE-2012-3464\"))\n    else\n      message = msg(msg_version(rails_version), \" does not escape single quotes \", msg_cve(\"CVE-2012-3464\"), \". Upgrade to \")\n\n      case\n      when version_between?('3.0.0', '3.0.16')\n        message << msg_version('3.0.17')\n      when version_between?('3.1.0', '3.1.7')\n        message << msg_version('3.1.8')\n      when version_between?('3.2.0', '3.2.7')\n        message << msg_version('3.2.8')\n      else\n        return\n      end\n    end\n\n    warn :warning_type => \"Cross-Site Scripting\",\n      :warning_code => :CVE_2012_3464,\n      :message => message,\n      :confidence => :medium,\n      :gem_info => gemfile_or_environment,\n      :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/kKGNeMrnmiY/discussion\",\n      :cwe_id => [79]\n  end\n\n  #Process initializers to see if they use workaround\n  #by replacing Erb::Util.html_escape\n  def uses_rack_escape?\n    @tracker.initializers.each do |_name, src|\n      process src\n    end\n\n    @uses_rack_escape\n  end\n\n  #Look for\n  #\n  #    class ERB\n  def process_class exp\n    if exp.class_name == :ERB\n      @inside_erb = true\n      process_all exp.body\n      @inside_erb = false\n    end\n\n    exp\n  end\n\n  #Look for\n  #\n  #    module Util\n  def process_module exp\n    if @inside_erb and exp.module_name == :Util\n      @inside_util = true\n      process_all exp.body\n      @inside_util = false\n    end\n\n    exp\n  end\n\n  #Look for\n  #\n  #    def html_escape\n  def process_defn exp\n    if @inside_util and exp.method_name == :html_escape\n      @inside_html_escape = true\n      process_all exp.body\n      @inside_html_escape = false\n    end\n\n    exp\n  end\n\n  #Look for\n  #\n  #    Rack::Utils.escape_html\n  def process_call exp\n    if @inside_html_escape and exp.target == RACK_UTILS and exp.method == :escape_html\n      @uses_rack_escape = true\n    else\n      process exp.target if exp.target\n    end\n\n    exp\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_skip_before_filter.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#At the moment, this looks for\n#\n#  skip_before_filter :verify_authenticity_token, :except => [...]\n#\n#which is essentially a skip-by-default approach (no actions are checked EXCEPT the\n#ones listed) versus a enforce-by-default approach (ONLY the actions listed will skip\n#the check)\nclass Brakeman::CheckSkipBeforeFilter < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Warn when skipping CSRF or authentication checks by default\"\n\n  def run_check\n    tracker.controllers.each do |_name, controller|\n      controller.skip_filters.each do |filter|\n        process_skip_filter filter, controller\n      end\n    end\n  end\n\n  def process_skip_filter filter, controller\n    case skip_except_value filter\n    when :verify_authenticity_token\n      warn :class => controller.name, #ugh this should be a controller warning, too\n        :warning_type => \"Cross-Site Request Forgery\",\n        :warning_code => :csrf_blacklist,\n        :message => msg(\"List specific actions (\", msg_code(\":only => [..]\"), \") when skipping CSRF check\"),\n        :code => filter,\n        :confidence => :medium,\n        :file => controller.file,\n        :cwe_id => [352]\n\n    when :login_required, :authenticate_user!, :require_user\n      warn :controller => controller.name,\n        :warning_code => :auth_blacklist,\n        :warning_type => \"Authentication\",\n        :message => msg(\"List specific actions (\", msg_code(\":only => [..]\"), \") when skipping authentication\"),\n        :code => filter,\n        :confidence => :medium,\n        :link_path => \"authentication_whitelist\",\n        :file => controller.file,\n        :cwe_id => [287]\n    end\n  end\n\n  def skip_except_value filter\n    return false unless call? filter\n\n    first_arg = filter.first_arg\n    last_arg = filter.last_arg\n\n    if symbol? first_arg and hash? last_arg\n      if hash_access(last_arg, :except)\n        return first_arg.value\n      end\n    end\n\n    false\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_sprockets_path_traversal.rb",
    "content": "class Brakeman::CheckSprocketsPathTraversal < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for CVE-2018-3760\"\n\n  def run_check\n    sprockets_version = tracker.config.gem_version(:sprockets)\n\n    return unless sprockets_version\n    return if has_workaround?\n\n    case\n    when version_between?(\"0.0.0\", \"2.12.4\", sprockets_version)\n      upgrade_version = \"2.12.5\"\n      confidence = :weak\n    when version_between?(\"3.0.0\", \"3.7.1\", sprockets_version)\n      upgrade_version = \"3.7.2\"\n      confidence = :high\n    when version_between?(\"4.0.0.beta1\", \"4.0.0.beta7\", sprockets_version)\n      upgrade_version = \"4.0.0.beta8\"\n      confidence = :high\n    else\n      return\n    end\n\n    message = msg(msg_version(sprockets_version, \"sprockets\"), \" has a path traversal vulnerability \", msg_cve(\"CVE-2018-3760\"), \". Upgrade to \", msg_version(upgrade_version, \"sprockets\"), \" or newer\")\n\n    warn :warning_type => \"Path Traversal\",\n      :warning_code => :CVE_2018_3760,\n      :message => message,\n      :confidence => confidence,\n      :gem_info => gemfile_or_environment(:sprockets),\n      :link_path => \"https://groups.google.com/d/msg/rubyonrails-security/ft_J--l55fM/7roDfQ50BwAJ\",\n      :cwe_id => [22, 200]\n  end\n\n  def has_workaround?\n    false? (tracker.config.rails[:assets] and tracker.config.rails[:assets][:compile])\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_sql.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#This check tests for find calls which do not use Rails' auto SQL escaping\n#\n#For example:\n# Project.find(:all, :conditions => \"name = '\" + params[:name] + \"'\")\n#\n# Project.find(:all, :conditions => \"name = '#{params[:name]}'\")\n#\n# User.find_by_sql(\"SELECT * FROM projects WHERE name = '#{params[:name]}'\")\nclass Brakeman::CheckSQL < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Check for SQL injection\"\n\n  def run_check\n    # Avoid reporting `user_input` on silly values when generating warning.\n    # Note that we retroactively find `user_input` inside the \"dangerous\" value.\n    @safe_input_attributes.merge IGNORE_METHODS_IN_SQL\n\n    @sql_targets = [:average, :calculate, :count, :count_by_sql, :delete_all, :destroy_all,\n                    :find_by_sql, :maximum, :minimum, :pluck, :sum, :update_all]\n    @sql_targets.concat [:from, :group, :having, :joins, :lock, :order, :reorder, :where] if tracker.options[:rails3]\n    @sql_targets.concat [:find_by, :find_by!, :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, :not] if tracker.options[:rails4]\n\n    if tracker.options[:rails6]\n      @sql_targets.concat [:delete_by, :destroy_by, :rewhere, :reselect]\n\n      @sql_targets.delete :delete_all\n      @sql_targets.delete :destroy_all\n    end\n\n    if version_between?(\"6.1.0\", \"9.9.9\")\n      @sql_targets.delete :order\n      @sql_targets.delete :reorder\n      @sql_targets.delete :pluck\n    end\n\n    if version_between?(\"2.0.0\", \"3.9.9\") or tracker.config.rails_version.nil?\n      @sql_targets << :first << :last << :all\n    end\n\n    if version_between?(\"2.0.0\", \"4.0.99\") or tracker.config.rails_version.nil?\n      @sql_targets << :find\n    end\n\n    @connection_calls = [:delete, :execute, :insert, :select_all, :select_one,\n      :select_rows, :select_value, :select_values]\n\n    if tracker.options[:rails3]\n      @connection_calls.concat [:exec_delete, :exec_insert, :exec_query, :exec_update]\n    else\n      @connection_calls.concat [:add_limit!, :add_offset_limit!, :add_lock!]\n    end\n\n    @expected_targets = active_record_models.keys + [:connection, :\"ActiveRecord::Base\", :Arel]\n\n    Brakeman.debug \"Finding possible SQL calls on models\"\n    calls = tracker.find_call(:methods => @sql_targets, :nested => true)\n\n    narrow_targets = [:exists?, :select]\n    calls.concat tracker.find_call(:targets => active_record_models.keys, :methods => narrow_targets, :chained => true)\n\n    Brakeman.debug \"Finding possible SQL calls with no target\"\n    calls.concat tracker.find_call(:target => nil, :methods => @sql_targets)\n\n    Brakeman.debug \"Finding possible SQL calls using constantized()\"\n    calls.concat tracker.find_call(:methods => @sql_targets).select { |result| constantize_call? result }\n\n    calls.concat tracker.find_call(:targets => @expected_targets, :methods => @connection_calls, :chained => true).select { |result| connect_call? result }\n\n    calls.concat tracker.find_call(:target => :Arel, :method => :sql)\n\n    Brakeman.debug \"Finding calls to named_scope or scope\"\n    calls.concat find_scope_calls\n\n    Brakeman.debug \"Processing possible SQL calls\"\n    calls.each { |call| process_result call }\n  end\n\n  #Find calls to named_scope() or scope() in models\n  #RP 3 TODO\n  def find_scope_calls\n    scope_calls = []\n\n    # Used in pre-3.1.0 versions of Rails\n    ar_scope_calls(:named_scope) do |model, args|\n      call = make_call(nil, :named_scope, args).line(args.line)\n      scope_calls << scope_call_hash(call, model, :named_scope)\n    end\n\n    # Use in 3.1.0 and later\n    ar_scope_calls(:scope) do |model, args|\n      second_arg = args[2]\n      next unless sexp? second_arg\n\n      if second_arg.node_type == :iter and node_type? second_arg.block, :block, :call, :safe_call\n        process_scope_with_block(model, args)\n      elsif call? second_arg\n        call = second_arg\n        scope_calls << scope_call_hash(call, model, call.method)\n      else\n        call = make_call(nil, :scope, args).line(args.line)\n        scope_calls << scope_call_hash(call, model, :scope)\n      end\n    end\n\n    scope_calls\n  end\n\n  def ar_scope_calls(symbol_name, &block)\n    active_record_models.each do |name, model|\n      model_args = model.options[symbol_name]\n      if model_args\n        model_args.each do |args|\n          yield model, args\n        end\n      end\n    end\n  end\n\n  def scope_call_hash(call, model, method)\n    { :call => call, :location => { :type => :class, :class => model.name, :file => model.file }, :method => :named_scope }\n  end\n\n\n  def process_scope_with_block model, args\n    scope_name = args[1][1]\n    block = args[-1][-1]\n\n    # Search lambda for calls to query methods\n    if block.node_type == :block\n      find_calls = Brakeman::FindAllCalls.new(tracker)\n      find_calls.process_source(block, :class => model.name, :method => scope_name, :file => model.file)\n      find_calls.calls.each { |call| process_result(call) if @sql_targets.include?(call[:method]) }\n    elsif call? block\n      while call? block\n        process_result :target => block.target, :method => block.method, :call => block,\n          :location => { :type => :class, :class => model.name, :method => scope_name, :file => model.file }\n\n        block = block.target\n      end\n    end\n  end\n\n  #Process possible SQL injection sites:\n  #\n  # Model#find\n  #\n  # Model#(named_)scope\n  #\n  # Model#(find|count)_by_sql\n  #\n  # Model#all\n  #\n  ### Rails 3\n  #\n  # Model#(where|having)\n  # Model#(order|group)\n  #\n  ### Find Options Hash\n  #\n  # Dangerous keys that accept SQL:\n  #\n  # * conditions\n  # * order\n  # * having\n  # * joins\n  # * select\n  # * from\n  # * lock\n  #\n  def process_result result\n    return if duplicate?(result) or result[:call].original_line\n\n    call = result[:call]\n    method = call.method\n\n    dangerous_value = case method\n                      when :find\n                        check_find_arguments call.second_arg\n                      when :exists?\n                        check_exists call.first_arg\n                      when :delete_all, :destroy_all\n                        check_find_arguments call.first_arg\n                      when :named_scope, :scope\n                        check_scope_arguments call\n                      when :find_by_sql, :count_by_sql\n                        check_by_sql_arguments call.first_arg\n                      when :calculate\n                        if call.num_args > 2\n                          unsafe_sql?(call.second_arg) or check_find_arguments(call.third_arg)\n                        elsif call.num_args > 1\n                          unsafe_sql?(call.second_arg)\n                        end\n                      when :last, :first, :all\n                        check_find_arguments call.first_arg\n                      when :average, :count, :maximum, :minimum, :sum\n                        if call.num_args > 1\n                          if version_between?(\"0.0.0\", \"4.9.9\") # In Rails 5+ these do not accept multiple arguments\n                            check_find_arguments(call.first_arg) or check_find_arguments(call.second_arg)\n                          end\n                        else\n                          check_find_arguments call.first_arg\n                        end\n                      when :where, :rewhere, :having, :find_by, :find_by!, :find_or_create_by, :find_or_create_by!, :find_or_initialize_by,:not, :delete_by, :destroy_by\n                        check_query_arguments call.arglist\n                      when :order, :group, :reorder\n                        check_order_arguments call.arglist\n                      when :joins\n                        check_joins_arguments call.first_arg\n                      when :from\n                        unsafe_sql? call.first_arg\n                      when :lock\n                        check_lock_arguments call.first_arg\n                      when :pluck\n                        unsafe_sql? call.first_arg\n                      when :sql\n                        unsafe_sql? call.first_arg\n                      when :update_all, :select, :reselect\n                        check_update_all_arguments call.args\n                      when *@connection_calls\n                        check_by_sql_arguments call.first_arg\n                      else\n                        Brakeman.debug \"Unhandled SQL method: #{method}\"\n                      end\n\n    if dangerous_value\n      add_result result\n\n      input = include_user_input? dangerous_value\n      if input\n        confidence = :high\n        user_input = input\n      else\n        confidence = :medium\n        user_input = dangerous_value\n      end\n\n      if result[:call].target and result[:chain] and not @expected_targets.include? result[:chain].first\n        confidence = case confidence\n                     when :high\n                       :medium\n                     when :medium\n                       :weak\n                     else\n                       confidence\n                     end\n      end\n\n      warn :result => result,\n        :warning_type => \"SQL Injection\",\n        :warning_code => :sql_injection,\n        :message => \"Possible SQL injection\",\n        :user_input => user_input,\n        :confidence => confidence,\n        :cwe_id => [89]\n    end\n\n    if check_for_limit_or_offset_vulnerability call.last_arg\n      if include_user_input? call.last_arg\n        confidence = :high\n      else\n        confidence = :weak\n      end\n\n      warn :result => result,\n        :warning_type => \"SQL Injection\",\n        :warning_code => :sql_injection_limit_offset,\n        :message => msg(\"Upgrade to Rails >= 2.1.2 to escape \", msg_code(\":limit\"), \" and \", msg_code(\"offset\"), \". Possible SQL injection\"),\n        :confidence => confidence,\n        :cwe_id => [89]\n    end\n  end\n\n\n  #The 'find' methods accept a number of different types of parameters:\n  #\n  # * The first argument might be :all, :first, or :last\n  # * The first argument might be an integer ID or an array of IDs\n  # * The second argument might be a hash of options, some of which are\n  #   dangerous and some of which are not\n  # * The second argument might contain SQL fragments as values\n  # * The second argument might contain properly parameterized SQL fragments in arrays\n  # * The second argument might contain improperly parameterized SQL fragments in arrays\n  #\n  #This method should only be passed the second argument.\n  def check_find_arguments arg\n    return nil if not sexp? arg or node_type? arg, :lit, :string, :str, :true, :false, :nil\n\n    unsafe_sql? arg\n  end\n\n  def check_scope_arguments call\n    scope_arg = call.second_arg #first arg is name of scope\n\n    node_type?(scope_arg, :iter) ? unsafe_sql?(scope_arg.block) : unsafe_sql?(scope_arg)\n  end\n\n  def check_query_arguments arg\n    return unless sexp? arg\n    first_arg = arg[1]\n\n    if node_type? arg, :arglist\n      if arg.length > 2 and string_interp? first_arg\n        # Model.where(\"blah = ?\", blah)\n        return check_string_interp first_arg\n      else\n        arg = first_arg\n      end\n    end\n\n    if request_value? arg\n      unless call? arg and params? arg.target and [:permit, :slice, :to_h, :to_hash, :symbolize_keys].include? arg.method\n        # Model.where(params[:where])\n        arg\n      end\n    elsif hash? arg and not kwsplat? arg\n      #This is generally going to be a hash of column names and values, which\n      #would escape the values. But the keys _could_ be user input.\n      check_hash_keys arg\n    elsif node_type? arg, :lit, :str\n      nil\n    elsif node_type? arg, :or\n      check_query_arguments(arg.lhs) or\n        check_query_arguments(arg.rhs)\n    else\n      #Hashes are safe...but we check above for hash, so...?\n      unsafe_sql? arg, :ignore_hash\n    end\n  end\n\n  #Checks each argument to order/reorder/group for possible SQL.\n  #Anything used with these methods is passed in verbatim.\n  def check_order_arguments args\n    return unless sexp? args\n\n    if node_type? args, :arglist\n      check_update_all_arguments(args)\n    else\n      unsafe_sql? args\n    end\n  end\n\n  #find_by_sql and count_by_sql can take either a straight SQL string\n  #or an array with values to bind.\n  def check_by_sql_arguments arg\n    return unless sexp? arg\n\n    #This is kind of unnecessary, because unsafe_sql? will handle an array\n    #correctly, but might be better to be explicit.\n    array?(arg) ? unsafe_sql?(arg[1]) : unsafe_sql?(arg)\n  end\n\n  #joins can take a string, hash of associations, or an array of both(?)\n  #We only care about the possible string values.\n  def check_joins_arguments arg\n    return unless sexp? arg and not node_type? arg, :hash, :string, :str\n\n    if array? arg\n      arg.each do |a|\n        unsafe_arg = check_joins_arguments a\n        return unsafe_arg if unsafe_arg\n      end\n\n      nil\n    else\n      unsafe_sql? arg\n    end\n  end\n\n  def check_update_all_arguments args\n    args.each do |arg|\n      unsafe_arg = unsafe_sql? arg\n      return unsafe_arg if unsafe_arg\n    end\n\n    nil\n  end\n\n  #Model#lock essentially only cares about strings. But those strings can be\n  #any SQL fragment. This does not apply to all databases. (For those who do not\n  #support it, the lock method does nothing).\n  def check_lock_arguments arg\n    return unless sexp? arg and not node_type? arg, :hash, :array, :string, :str\n\n    unsafe_sql?(arg, :ignore_hash)\n  end\n\n\n  #Check hash keys for user input.\n  #(Seems unlikely, but if a user can control the column names queried, that\n  #could be bad)\n  def check_hash_keys exp\n    hash_iterate(exp) do |key, _value|\n      unless symbol?(key)\n        unsafe_key = unsafe_sql? key\n        return unsafe_key if unsafe_key\n      end\n    end\n\n    false\n  end\n\n  #Check an interpolated string for dangerous values.\n  #\n  #This method assumes values interpolated into strings are unsafe by default,\n  #unless safe_value? explicitly returns true.\n  def check_string_interp arg\n    arg.each do |exp|\n      if dangerous = unsafe_string_interp?(exp)\n        return dangerous\n      end\n    end\n\n    nil\n  end\n\n  TO_STRING_METHODS = [:chomp, :chop, :lstrip, :rstrip, :scrub, :squish, :strip,\n                       :strip_heredoc, :to_s, :tr]\n\n  #Returns value if interpolated value is not something safe\n  def unsafe_string_interp? exp\n    if node_type? exp, :evstr\n      value = exp.value\n    else\n      value = exp\n    end\n\n    if not sexp? value\n      nil\n    elsif call? value and TO_STRING_METHODS.include? value.method\n      unsafe_string_interp? value.target\n    elsif call? value and safe_literal_target? value\n      nil\n    else\n      case value.node_type\n      when :or\n        unsafe_string_interp?(value.lhs) || unsafe_string_interp?(value.rhs)\n      when :dstr\n        if dangerous = check_string_interp(value)\n          return dangerous\n        end\n      else\n        if safe_value? value\n          nil\n        elsif string_building? value\n          check_for_string_building value\n        else\n          value\n        end\n      end\n    end\n  end\n\n  #Checks the given expression for unsafe SQL values. If an unsafe value is\n  #found, returns that value (may be the given _exp_ or a subexpression).\n  #\n  #Otherwise, returns false/nil.\n  def unsafe_sql? exp, ignore_hash = false\n    return unless sexp?(exp)\n\n    dangerous_value = find_dangerous_value exp, ignore_hash\n    safe_value?(dangerous_value) ? false : dangerous_value\n  end\n\n  #Check _exp_ for dangerous values. Used by unsafe_sql?\n  def find_dangerous_value exp, ignore_hash\n    case exp.node_type\n    when :lit, :str, :const, :colon2, :true, :false, :nil\n      nil\n    when :array\n      #Assume this is an array like\n      #\n      #  [\"blah = ? AND thing = ?\", ...]\n      #\n      #and check first value\n      unsafe_sql? exp[1]\n    when :dstr\n      check_string_interp exp\n    when :hash\n      if kwsplat? exp and has_immediate_user_input? exp\n        exp\n      elsif not ignore_hash\n        check_hash_values exp\n      else\n        nil\n      end\n    when :if\n      unsafe_sql? exp.then_clause or unsafe_sql? exp.else_clause\n    when :call\n      unless IGNORE_METHODS_IN_SQL.include? exp.method\n        if has_immediate_user_input? exp\n          exp\n        elsif TO_STRING_METHODS.include? exp.method\n          find_dangerous_value exp.target, ignore_hash\n        else\n          check_call exp\n        end\n      end\n    when :or\n      if unsafe = (unsafe_sql?(exp.lhs) || unsafe_sql?(exp.rhs))\n        unsafe\n      else\n        nil\n      end\n    when :block, :rlist\n      unsafe_sql? exp.last\n    else\n      if has_immediate_user_input? exp\n        exp\n      else\n        nil\n      end\n    end\n  end\n\n  #Checks hash values associated with these keys:\n  #\n  # * conditions\n  # * order\n  # * having\n  # * joins\n  # * select\n  # * from\n  # * lock\n  def check_hash_values exp\n    hash_iterate(exp) do |key, value|\n      if symbol? key\n        unsafe = case key.value\n                 when :conditions, :having, :select\n                   check_query_arguments value\n                 when :order, :group\n                   check_order_arguments value\n                 when :joins\n                   check_joins_arguments value\n                 when :lock\n                   check_lock_arguments value\n                 when :from\n                   unsafe_sql? value\n                 else\n                   nil\n                 end\n\n        return unsafe if unsafe\n      end\n    end\n\n    false\n  end\n\n  def check_for_string_building exp\n    return unless call? exp\n\n    target = exp.target\n    method = exp.method\n    arg = exp.first_arg\n\n    if STRING_METHODS.include? method\n      check_str_target_or_arg(target, arg) or\n      check_interp_target_or_arg(target, arg) or\n      check_for_string_building(target) or\n      check_for_string_building(arg)\n    else\n      nil\n    end\n  end\n\n  def check_str_target_or_arg target, arg\n    if string? target\n      check_string_arg arg\n    elsif string? arg\n      check_string_arg target\n    end\n  end\n\n  def check_interp_target_or_arg target, arg\n    if string_interp? target or string_interp? arg\n      check_string_arg target and\n      check_string_arg arg\n    end\n  end\n\n  def check_string_arg exp\n    if safe_value? exp\n      nil\n    elsif string_building? exp\n      check_for_string_building exp\n    elsif string_interp? exp\n      check_string_interp exp\n    elsif call? exp and exp.method == :to_s\n      check_string_arg exp.target\n    else\n      exp\n    end\n  end\n\n  IGNORE_METHODS_IN_SQL = Set[:id, :merge_conditions, :table_name, :quoted_table_name,\n    :quoted_primary_key, :to_i, :to_f, :sanitize_sql, :sanitize_sql_array,\n    :sanitize_sql_for_assignment, :sanitize_sql_for_conditions, :sanitize_sql_hash,\n    :sanitize_sql_hash_for_assignment, :sanitize_sql_hash_for_conditions,\n    :to_sql, :sanitize, :primary_key, :table_name_prefix, :table_name_suffix,\n    :where_values_hash, :foreign_key, :uuid, :escape, :escape_string,\n    :polymorphic_name\n  ]\n\n  def ignore_methods_in_sql\n    @ignore_methods_in_sql ||= IGNORE_METHODS_IN_SQL + (tracker.options[:sql_safe_methods] || [])\n  end\n\n  def safe_value? exp\n    return true unless sexp? exp\n\n    case exp.node_type\n    when :str, :lit, :const, :colon2, :nil, :true, :false\n      true\n    when :call\n      if exp.method == :to_s or exp.method == :to_sym\n        safe_value? exp.target\n      else\n        ignore_call? exp\n      end\n    when :if\n      safe_value? exp.then_clause and safe_value? exp.else_clause\n    when :block, :rlist\n      safe_value? exp.last\n    when :or\n      safe_value? exp.lhs and safe_value? exp.rhs\n    when :dstr\n      not unsafe_string_interp? exp\n    else\n      false\n    end\n  end\n\n  def ignore_call? exp\n    return unless call? exp\n\n    ignore_methods_in_sql.include? exp.method or\n      quote_call? exp or\n      arel? exp or\n      exp.method.to_s.end_with? \"_id\" or\n      number_target? exp or\n      date_target? exp or\n      locale_call? exp\n  end\n\n  QUOTE_METHODS = [:quote, :quote_column_name, :quoted_date, :quote_string, :quote_table_name]\n\n  def quote_call? exp\n    if call? exp.target\n      exp.target.method == :connection and QUOTE_METHODS.include? exp.method\n    elsif exp.target.nil?\n      exp.method == :quote_value\n    end\n  end\n\n  AREL_METHODS = [:all, :and, :arel_table, :as, :eq, :eq_any, :exists, :group,\n                  :gt, :gteq, :having, :in, :join_sources, :limit, :lt, :lteq, :not,\n                  :not_eq, :on, :or, :order, :project, :skip, :take, :where, :with]\n\n  def arel? exp\n    call? exp and (AREL_METHODS.include? exp.method or arel? exp.target)\n  end\n\n  #Check call for string building\n  def check_call exp\n    return unless call? exp\n    unsafe = check_for_string_building exp\n\n    if unsafe\n      unsafe\n    elsif call? exp.target\n      check_call exp.target\n    else\n      nil\n    end\n  end\n\n  def check_exists arg\n    if call? arg and arg.method == :to_s\n      false\n    else\n      check_find_arguments arg\n    end\n  end\n\n  #Prior to Rails 2.1.1, the :offset and :limit parameters were not\n  #escaping input properly.\n  #\n  #http://www.rorsecurity.info/2008/09/08/sql-injection-issue-in-limit-and-offset-parameter/\n  def check_for_limit_or_offset_vulnerability options\n    return false if rails_version.nil? or rails_version >= \"2.1.1\" or not hash?(options)\n\n    return true if hash_access(options, :limit) or hash_access(options, :offset)\n\n    false\n  end\n\n  #Look for something like this:\n  #\n  # params[:x].constantize.find('something')\n  #\n  # s(:call,\n  #   s(:call,\n  #     s(:call,\n  #       s(:call, nil, :params, s(:arglist)),\n  #       :[],\n  #       s(:arglist, s(:lit, :x))),\n  #     :constantize,\n  #     s(:arglist)),\n  #   :find,\n  #   s(:arglist, s(:str, \"something\")))\n  def constantize_call? result\n    call = result[:call]\n    call? call.target and call.target.method == :constantize\n  end\n\n  SELF_CLASS = s(:call, s(:self), :class)\n\n  def connect_call? result\n    call = result[:call]\n    target = call.target\n\n    if call? target and target.method == :connection\n      target = target.target\n      klass = class_name(target)\n\n      target.nil? or\n      target == SELF_CLASS or\n      node_type? target, :self or\n      klass == :\"ActiveRecord::Base\" or\n      active_record_models.include? klass\n    end\n  end\n\n  def number_target? exp\n    return unless call? exp\n\n    if number? exp.target\n      true\n    elsif call? exp.target\n      number_target? exp.target\n    else\n      false\n    end\n  end\n\n  DATE_CLASS = s(:const, :Date)\n\n  def date_target? exp\n    return unless call? exp\n\n    if exp.target == DATE_CLASS\n      true\n    elsif call? exp.target\n      date_target? exp.target\n    else\n     false\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_sql_cves.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckSQLCVEs < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for several SQL CVEs\"\n\n  def run_check\n    check_rails_versions_against_cve_issues\n    check_cve_2014_0080\n  end\n\n  def check_rails_versions_against_cve_issues\n    issues = [\n      {\n        :cve => \"CVE-2012-2660\",\n        :versions => [%w[2.0.0 2.3.14 2.3.17], %w[3.0.0 3.0.12 3.0.13], %w[3.1.0 3.1.4 3.1.5], %w[3.2.0 3.2.3 3.2.4]],\n        :url => \"https://groups.google.com/d/topic/rubyonrails-security/8SA-M3as7A8/discussion\"\n      },\n      {\n        :cve => \"CVE-2012-2661\",\n        :versions => [%w[3.0.0 3.0.12 3.0.13], %w[3.1.0 3.1.4 3.1.5], %w[3.2.0 3.2.3 3.2.5]],\n        :url => \"https://groups.google.com/d/topic/rubyonrails-security/dUaiOOGWL1k/discussion\"\n      },\n      {\n        :cve => \"CVE-2012-2695\",\n        :versions => [%w[2.0.0 2.3.14 2.3.15], %w[3.0.0 3.0.13 3.0.14], %w[3.1.0 3.1.5 3.1.6], %w[3.2.0 3.2.5 3.2.6]],\n        :url => \"https://groups.google.com/d/topic/rubyonrails-security/l4L0TEVAz1k/discussion\"\n      },\n      {\n        :cve => \"CVE-2012-5664\",\n        :versions => [%w[2.0.0 2.3.14 2.3.15], %w[3.0.0 3.0.17 3.0.18], %w[3.1.0 3.1.8 3.1.9], %w[3.2.0 3.2.9 3.2.18]],\n        :url => \"https://groups.google.com/d/topic/rubyonrails-security/DCNTNp_qjFM/discussion\"\n      },\n      {\n        :cve => \"CVE-2013-0155\",\n        :versions => [%w[2.0.0 2.3.15 2.3.16], %w[3.0.0 3.0.18 3.0.19], %w[3.1.0 3.1.9 3.1.10], %w[3.2.0 3.2.10 3.2.11]],\n        :url => \"https://groups.google.com/d/topic/rubyonrails-security/c7jT-EeN9eI/discussion\"\n      },\n      {\n        :cve => \"CVE-2016-6317\",\n        :versions => [%w[4.2.0 4.2.7.0 4.2.7.1]],\n        :url => \"https://groups.google.com/d/msg/ruby-security-ann/WccgKSKiPZA/9DrsDVSoCgAJ\"\n      },\n\n    ]\n\n    unless lts_version? '2.3.18.6'\n     issues << {\n        :cve => \"CVE-2013-6417\",\n        :versions => [%w[2.0.0 3.2.15 3.2.16], %w[4.0.0 4.0.1 4.0.2]],\n        :url => \"https://groups.google.com/d/msg/ruby-security-ann/niK4drpSHT4/g8JW8ZsayRkJ\"\n      }\n    end\n\n    if tracker.config.has_gem? :pg\n      issues << {\n        :cve => \"CVE-2014-3482\",\n        :versions => [%w[2.0.0 2.9.9 3.2.19], %w[3.0.0 3.2.18 3.2.19], %w[4.0.0 4.0.6 4.0.7], %w[4.1.0 4.1.2 4.1.3]],\n        :url => \"https://groups.google.com/d/msg/rubyonrails-security/wDxePLJGZdI/WP7EasCJTA4J\"\n      } <<\n      {\n        :cve => \"CVE-2014-3483\",\n        :versions => [%w[2.0.0 2.9.9 3.2.19], %w[3.0.0 3.2.18 3.2.19], %w[4.0.0 4.0.6 4.0.7], %w[4.1.0 4.1.2 4.1.3]],\n        :url => \"https://groups.google.com/d/msg/rubyonrails-security/wDxePLJGZdI/WP7EasCJTA4J\" }\n    end\n\n    issues.each do |cve_issue|\n      cve_warning_for cve_issue[:versions], cve_issue[:cve], cve_issue[:url]\n    end\n  end\n\n  def cve_warning_for versions, cve, link\n    upgrade_version = upgrade_version? versions\n    return unless upgrade_version\n\n    code = cve.tr('-', '_').to_sym\n\n    warn :warning_type => 'SQL Injection',\n      :warning_code => code,\n      :message => msg(msg_version(rails_version), \" contains a SQL injection vulnerability \", msg_cve(cve), \". Upgrade to \", msg_version(upgrade_version)),\n      :confidence => :high,\n      :gem_info => gemfile_or_environment,\n      :link_path => link,\n      :cwe_id => [89]\n  end\n\n  def upgrade_version? versions\n    versions.each do |low, high, upgrade|\n      return upgrade if version_between? low, high\n    end\n\n    false\n  end\n\n  def check_cve_2014_0080\n    return unless version_between? \"4.0.0\", \"4.0.2\" and\n                  @tracker.config.has_gem? :pg\n\n    warn :warning_type => 'SQL Injection',\n      :warning_code => :CVE_2014_0080,\n      :message => msg(msg_version(rails_version), \" contains a SQL injection vulnerability \", msg_cve(\"CVE-2014-0080\"), \" with PostgreSQL. Upgrade to \", msg_version(\"4.0.3\")),\n      :confidence => :high,\n      :gem_info => gemfile_or_environment(:pg),\n      :link_path => \"https://groups.google.com/d/msg/rubyonrails-security/Wu96YkTUR6s/pPLBMZrlwvYJ\",\n      :cwe_id => [89]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_ssl_verify.rb",
    "content": "require 'brakeman/checks/base_check'\n\n# Checks if verify_mode= is called with OpenSSL::SSL::VERIFY_NONE\n\nclass Brakeman::CheckSSLVerify < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  SSL_VERIFY_NONE = s(:colon2, s(:colon2, s(:const, :OpenSSL), :SSL), :VERIFY_NONE)\n\n  @description = \"Checks for OpenSSL::SSL::VERIFY_NONE\"\n\n  def run_check\n    check_open_ssl_verify_none\n    check_http_start\n  end\n\n  def check_open_ssl_verify_none\n    tracker.find_call(:method => :verify_mode=).each {|call| process_verify_mode_result(call) }\n  end\n\n  def process_verify_mode_result result\n    if result[:call].last_arg == SSL_VERIFY_NONE\n      warn_about_ssl_verification_bypass result\n    end\n  end\n\n  def check_http_start\n    tracker.find_call(:target => :'Net::HTTP', :method => :start).each { |call| process_http_start_result call }\n  end\n\n  def process_http_start_result result\n    arg = result[:call].last_arg\n\n    if hash? arg and hash_access(arg, :verify_mode) == SSL_VERIFY_NONE\n      warn_about_ssl_verification_bypass result\n    end\n  end\n\n  def warn_about_ssl_verification_bypass result\n    return unless original? result\n\n    warn :result => result,\n      :warning_type => \"SSL Verification Bypass\",\n      :warning_code => :ssl_verification_bypass,\n      :message => \"SSL certificate verification was bypassed\",\n      :confidence => :high,\n      :cwe_id => [295]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_strip_tags.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Check for uses of strip_tags in Rails versions before 3.0.17, 3.1.8, 3.2.8 (including 2.3.x):\n#https://groups.google.com/d/topic/rubyonrails-security/FgVEtBajcTY/discussion\n#\n#Check for uses of strip_tags in Rails versions before 2.3.13 and 3.0.10:\n#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2b9130749b74ea12\n#\n#Check for user of strip_tags with rails-html-sanitizer 1.0.2:\n#https://groups.google.com/d/msg/rubyonrails-security/OU9ugTZcbjc/PjEP46pbFQAJ\nclass Brakeman::CheckStripTags < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Report strip_tags vulnerabilities\"\n\n  def run_check\n    if uses_strip_tags?\n      cve_2011_2931\n      cve_2012_3465\n    end\n\n    cve_2015_7579\n  end\n\n  def cve_2011_2931\n    if version_between?('2.0.0', '2.3.12') or version_between?('3.0.0', '3.0.9')\n      if rails_version =~ /^3/\n        message = msg(\"Versions before 3.0.10 have a vulnerability in \", msg_code(\"strip_tags\"), \" \", msg_cve(\"CVE-2011-2931\"))\n      else\n        message = msg(\"Versions before 2.3.13 have a vulnerability in \", msg_code(\"strip_tags\"), \" \", msg_cve(\"CVE-2011-2931\"))\n      end\n\n      warn :warning_type => \"Cross-Site Scripting\",\n        :warning_code => :CVE_2011_2931,\n        :message => message,\n        :gem_info => gemfile_or_environment,\n        :confidence => :high,\n        :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/K5EwdJt06hI/discussion\",\n        :cwe_id => [79]\n    end\n  end\n\n  def cve_2012_3465\n    message = msg(msg_version(rails_version), \" has a vulnerability in \", msg_code(\"strip_tags\"), \" \", msg_cve(\"CVE-2012-3465\"), \". Upgrade to \")\n\n    case\n    when (version_between?('2.0.0', '2.3.14') and tracker.config.escape_html?)\n      message = msg(\"All Rails 2.x versions have a vulnerability in \", msg_code(\"strip_tags\"), \" \", msg_cve(\"CVE-2012-3465\"))\n    when version_between?('3.0.10', '3.0.16')\n      message << msg_version('3.0.17')\n    when version_between?('3.1.0', '3.1.7')\n      message << msg_version('3.1.8')\n    when version_between?('3.2.0', '3.2.7')\n      message << msg_version('3.2.8')\n    else\n      return\n    end\n\n    warn :warning_type => \"Cross-Site Scripting\",\n      :warning_code => :CVE_2012_3465,\n      :message => message,\n      :confidence => :high,\n      :gem_info => gemfile_or_environment,\n      :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/FgVEtBajcTY/discussion\",\n      :cwe_id => [79]\n  end\n\n  def cve_2015_7579\n    if tracker.config.gem_version(:'rails-html-sanitizer') == '1.0.2'\n      if uses_strip_tags?\n        confidence = :high\n      else\n        confidence = :medium\n      end\n\n      message = msg(msg_version(\"1.0.2\", \"rails-html-sanitizer\"), \" is vulnerable (CVE-2015-7579). Upgrade to \", msg_version(\"1.0.3\", \"rails-html-sanitizer\"))\n\n      warn :warning_type => \"Cross-Site Scripting\",\n        :warning_code => :CVE_2015_7579,\n        :message => message,\n        :confidence => confidence,\n        :gem_info => gemfile_or_environment(:\"rails-html-sanitizer\"),\n        :link_path => \"https://groups.google.com/d/msg/rubyonrails-security/OU9ugTZcbjc/PjEP46pbFQAJ\",\n        :cwe_id => [79]\n\n    end\n  end\n\n  def uses_strip_tags?\n    Brakeman.debug \"Finding calls to strip_tags()\"\n\n    not tracker.find_call(:target => false, :method => :strip_tags, :nested => true).empty?\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_symbol_dos.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckSymbolDoS < Brakeman::BaseCheck\n  Brakeman::Checks.add_optional self\n\n  UNSAFE_METHODS = [:to_sym, :literal_to_sym, :intern, :symbolize_keys, :symbolize_keys!]\n\n  @description = \"Checks for symbol denial of service\"\n\n  def run_check\n    return if rails_version and rails_version >= \"5.0.0\"\n    return if tracker.config.ruby_version and tracker.config.ruby_version >= \"2.2\"\n\n    tracker.find_call(:methods => UNSAFE_METHODS, :nested => true).each do |result|\n      check_unsafe_symbol_creation(result)\n    end\n  end\n\n  def check_unsafe_symbol_creation result\n    return unless original? result\n\n    call = result[:call]\n\n    if result[:method] == :literal_to_sym\n      args = call.select { |e| sexp? e }\n    else\n      args = [call.target]\n    end\n\n    if input = args.map{ |arg| has_immediate_user_input?(arg) }.compact.first\n      confidence = :high\n    elsif input = args.map{ |arg| include_user_input?(arg) }.compact.first\n      confidence = :medium\n    end\n\n\n    if confidence\n      return if safe_parameter? input.match\n      return if symbolizing_attributes? input\n\n      message = msg(\"Symbol conversion from unsafe string in \", msg_input(input))\n\n      warn :result => result,\n        :warning_type => \"Denial of Service\",\n        :warning_code => :unsafe_symbol_creation,\n        :message => message,\n        :user_input => input,\n        :confidence => confidence,\n        :cwe_id => [20]\n    end\n  end\n\n  def safe_parameter? input\n    if call? input\n      if node_type? input.target, :params\n        input.method == :[] and\n        symbol? input.first_arg and\n        [:controller, :action].include? input.first_arg.value\n      else\n        safe_parameter? input.target\n      end\n    else\n      false\n    end\n  end\n\n  def symbolizing_attributes? input\n    input.type == :model and\n      call? input.match and\n      input.match.method == :attributes\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_symbol_dos_cve.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckSymbolDoSCVE < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for versions with ActiveRecord symbol denial of service vulnerability\"\n\n  def run_check\n    fix_version = case\n      when version_between?('2.0.0', '2.3.17')\n        '2.3.18'\n      when version_between?('3.1.0', '3.1.11')\n        '3.1.12'\n      when version_between?('3.2.0', '3.2.12')\n        '3.2.13'\n      else\n        nil\n      end\n\n    if fix_version && active_record_models.any?\n      warn :warning_type => \"Denial of Service\",\n        :warning_code => :CVE_2013_1854,\n        :message => msg(msg_version(rails_version), \" has a denial of service vulnerability in ActiveRecord. Upgrade to \", msg_version(fix_version), \" or patch\"),\n        :confidence => :medium,\n        :gem_info => gemfile_or_environment,\n        :link => \"https://groups.google.com/d/msg/rubyonrails-security/jgJ4cjjS8FE/BGbHRxnDRTIJ\",\n        :cwe_id => [20]\n    end\n  end\nend\n\n"
  },
  {
    "path": "lib/brakeman/checks/check_template_injection.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckTemplateInjection < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Searches for evaluation of user input through template injection\"\n\n  #Process calls\n  def run_check\n    Brakeman.debug \"Finding ERB.new calls\"\n    erb_calls = tracker.find_call :target => :ERB, :method => :new, :nested => true\n\n    Brakeman.debug \"Processing ERB.new calls\"\n    erb_calls.each do |call|\n      process_result call\n    end\n  end\n\n  #Warns if eval includes user input\n  def process_result result\n    return unless original? result\n\n    if input = include_user_input?(result[:call].arglist)\n      warn :result => result,\n        :warning_type => \"Template Injection\",\n        :warning_code => :erb_template_injection,\n        :message => msg(msg_input(input), \" used directly in \", msg_code(\"ERB\"), \" template, which might enable remote code execution\"),\n        :user_input => input,\n        :confidence => :high,\n        :cwe_id => [1336]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_translate_bug.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Check for vulnerability in translate() helper that allows cross-site scripting\nclass Brakeman::CheckTranslateBug < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Report XSS vulnerability in translate helper\"\n\n  def run_check\n    return if lts_version? '2.3.18.6'\n    if (version_between?('2.3.0', '2.3.99') and tracker.config.escape_html?) or\n        version_between?('3.0.0', '3.0.10') or\n        version_between?('3.1.0', '3.1.1')\n\n      confidence = if uses_translate?\n        :high\n      else\n        :medium\n      end\n\n      description = [\" has a vulnerability in the translate helper with keys ending in \", msg_code(\"_html\")]\n\n      message = if rails_version =~ /^3\\.1/\n                  msg(msg_version(rails_version), *description, \". Upgrade to \", msg_version(\"3.1.2\"))\n                elsif rails_version =~ /^3\\.0/\n                  msg(msg_version(rails_version), *description, \". Upgrade to \", msg_version(\"3.0.11\"))\n                else\n                  msg(\"Rails 2.3.x using the rails_xss plugin\", *description)\n                end\n\n      warn :warning_type => \"Cross-Site Scripting\",\n        :warning_code => :translate_vuln,\n        :message => message,\n        :confidence => confidence,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2b61d70fb73c7cc5\",\n        :cwe_id => [79]\n    end\n  end\n\n  def uses_translate?\n    Brakeman.debug \"Finding calls to translate() or t()\"\n\n    tracker.find_call(:target => nil, :methods => [:t, :translate]).any?\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_unsafe_reflection.rb",
    "content": "require 'brakeman/checks/base_check'\n\n# Checks for string interpolation and parameters in calls to\n# String#constantize, String#safe_constantize, Module#const_get and Module#qualified_const_get.\n#\n# Exploit examples at: http://blog.conviso.com.br/exploiting-unsafe-reflection-in-rubyrails-applications/\nclass Brakeman::CheckUnsafeReflection < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for unsafe reflection\"\n\n  def run_check\n    reflection_methods = [:constantize, :safe_constantize, :const_get, :qualified_const_get]\n\n    tracker.find_call(:methods => reflection_methods, :nested => true).each do |result|\n      check_unsafe_reflection result\n    end\n  end\n\n  def check_unsafe_reflection result\n    return unless original? result\n\n    call = result[:call]\n    method = call.method\n\n    case method\n    when :constantize, :safe_constantize\n      arg = call.target\n    else\n      arg = call.first_arg\n    end\n\n    if input = has_immediate_user_input?(arg)\n      confidence = :high\n    elsif input = include_user_input?(arg)\n      confidence = :medium\n    end\n\n    if confidence\n      case method\n      when :constantize, :safe_constantize\n        message = msg(\"Unsafe reflection method \", msg_code(method), \" called on \", msg_input(input))\n      else\n        message = msg(\"Unsafe reflection method \", msg_code(method), \" called with \", msg_input(input))\n      end\n\n      warn :result => result,\n        :warning_type => \"Remote Code Execution\",\n        :warning_code => :unsafe_constantize,\n        :message => message,\n        :user_input => input,\n        :confidence => confidence,\n        :cwe_id => [470]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_unsafe_reflection_methods.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckUnsafeReflectionMethods < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for unsafe reflection to access methods\"\n\n  def run_check\n    check_method\n    check_tap\n    check_to_proc\n  end\n\n  def check_method\n    tracker.find_call(method: :method, nested: true).each do |result|\n      argument = result[:call].first_arg\n\n      if user_input = include_user_input?(argument)\n        warn_unsafe_reflection(result, user_input)\n      end\n    end\n  end\n\n  def check_tap\n    tracker.find_call(method: :tap, nested: true).each do |result|\n      argument = result[:call].first_arg\n\n      # Argument is passed like a.tap(&argument)\n      if node_type? argument, :block_pass\n        argument = argument.value\n      end\n\n      if user_input = include_user_input?(argument)\n        warn_unsafe_reflection(result, user_input)\n      end\n    end\n  end\n\n  def check_to_proc\n    tracker.find_call(method: :to_proc, nested: true).each do |result|\n      target = result[:call].target\n\n      if user_input = include_user_input?(target)\n        warn_unsafe_reflection(result, user_input)\n      end\n    end\n  end\n\n  def warn_unsafe_reflection result, input\n    return unless original? result\n    method = result[:call].method\n\n    confidence = if input.type == :params\n      :high\n    else\n      :medium\n    end\n\n    message = msg(\"Unsafe reflection method \", msg_code(method), \" called with \", msg_input(input))\n\n    warn :result => result,\n      :warning_type => \"Remote Code Execution\",\n      :warning_code => :unsafe_method_reflection,\n      :message => message,\n      :user_input => input,\n      :confidence => confidence,\n      :cwe_id => [470]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_unscoped_find.rb",
    "content": "require 'brakeman/checks/base_check'\n\n# Checks for unscoped calls to models' #find and #find_by_id methods.\nclass Brakeman::CheckUnscopedFind < Brakeman::BaseCheck\n  Brakeman::Checks.add_optional self\n\n  @description = \"Check for unscoped ActiveRecord queries\"\n\n  def run_check\n    Brakeman.debug(\"Finding instances of #find on models with associations\")\n\n    associated_model_names = active_record_models.keys.select do |name|\n      if belongs_to = active_record_models[name].associations[:belongs_to]\n        not optional_belongs_to? belongs_to\n      else\n        false\n      end\n    end\n\n    calls = tracker.find_call :method => [:find, :find_by_id, :find_by_id!],\n                              :targets => associated_model_names\n\n    calls.each do |call|\n      process_result call\n    end\n\n    tracker.find_call(:methods => [:find_by, :find_by!], :targets => associated_model_names).each do |result|\n      arg = result[:call].first_arg\n\n      if hash? arg and hash_access(arg, :id)\n        process_result result\n      end\n    end\n  end\n\n  def process_result result\n    return if duplicate? result or result[:call].original_line\n\n    # Not interested unless argument is user controlled.\n    inputs = result[:call].args.map { |arg| include_user_input?(arg) }\n    return unless input = inputs.compact.first\n\n    add_result result\n\n    warn :result => result,\n      :warning_type => \"Unscoped Find\",\n      :warning_code => :unscoped_find,\n      :message      => msg(\"Unscoped call to \", msg_code(\"#{result[:target]}##{result[:method]}\")),\n      :code         => result[:call],\n      :confidence   => :weak,\n      :user_input   => input,\n      :cwe_id => [285]\n  end\n\n  def optional_belongs_to? exp\n    return false unless exp.is_a? Array\n\n    exp.each do |e|\n      if hash? e and true? hash_access(e, :optional)\n        return true\n      end\n    end\n\n    false\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_validation_regex.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Reports any calls to +validates_format_of+ which do not use +\\A+ and +\\z+\n#as anchors in the given regular expression.\n#\n#For example:\n#\n# #Allows anything after new line\n# validates_format_of :user_name, :with => /^\\w+$/\nclass Brakeman::CheckValidationRegex < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Report uses of validates_format_of with improper anchors\"\n\n  WITH = Sexp.new(:lit, :with)\n  FORMAT = Sexp.new(:lit, :format)\n\n  def run_check\n    active_record_models.each do |name, model|\n      @current_model = model\n      format_validations = model.options[:validates_format_of]\n\n      if format_validations\n        format_validations.each do |v|\n          process_validates_format_of v\n        end\n      end\n\n      validates = model.options[:validates]\n\n      if validates\n        validates.each do |v|\n          process_validates v\n        end\n      end\n    end\n  end\n\n  #Check validates_format_of\n  def process_validates_format_of validator\n    if value = hash_access(validator.last, WITH)\n      check_regex value, validator\n    end\n  end\n\n  #Check validates ..., :format => ...\n  def process_validates validator\n    hash_arg = validator.last\n    return unless hash? hash_arg\n\n    value = hash_access(hash_arg, FORMAT)\n\n    if hash? value\n      value = hash_access(value, WITH)\n    end\n\n    if value\n      check_regex value, validator\n    end\n  end\n\n  # Match secure regexp without extended option\n  SECURE_REGEXP_PATTERN = %r{\n    \\A\n    \\\\A\n    .*\n    \\\\[zZ]\n    \\z\n  }x\n\n  # Match secure of regexp with extended option\n  EXTENDED_SECURE_REGEXP_PATTERN = %r{\n    \\A\n    \\s*\n    \\\\A\n    .*\n    \\\\[zZ]\n    \\s*\n    \\z\n  }mx\n\n  #Issue warning if the regular expression does not use\n  #+\\A+ and +\\z+\n  def check_regex value, validator\n    return unless regexp? value\n\n    regex = value.value\n    unless secure_regex?(regex)\n      warn :model => @current_model,\n      :warning_type => \"Format Validation\",\n      :warning_code => :validation_regex,\n      :message => msg(\"Insufficient validation for \", msg_code(get_name validator), \" using \", msg_code(regex.inspect), \". Use \", msg_code(\"\\\\A\"), \" and \", msg_code(\"\\\\z\"), \" as anchors\"),\n      :line => value.line,\n      :confidence => :high,\n      :cwe_id => [777]\n    end\n  end\n\n  #Get the name of the attribute being validated.\n  def get_name validator\n    name = validator[1]\n\n    if sexp? name\n      name.value\n    else\n      name\n    end\n  end\n\n  private\n\n  def secure_regex?(regex)\n    extended_regex = Regexp::EXTENDED == regex.options & Regexp::EXTENDED\n    regex_pattern = extended_regex ? EXTENDED_SECURE_REGEXP_PATTERN : SECURE_REGEXP_PATTERN\n    regex_pattern =~ regex.source\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_verb_confusion.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckVerbConfusion < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Check for uses of `request.get?` that might have unintentional behavior\"\n\n  #Process calls\n  def run_check\n    calls = tracker.find_call(target: :request, methods: [:get?])\n\n    calls.each do |call|\n      process_result call\n    end\n  end\n\n  def process_result result\n    @current_result = result\n    @matched_call = result[:call]\n    klass = tracker.find_class(result[:location][:class])\n\n    # TODO: abstract into tracker.find_location ?\n    if klass.nil?\n      Brakeman.debug \"No class found: #{result[:location][:class]}\"\n      return\n    end\n\n    method = klass.get_method(result[:location][:method])\n\n    if method.nil?\n      Brakeman.debug \"No method found: #{result[:location][:method]}\"\n      return\n    end\n\n    process method.src\n  end\n\n  def process_if exp\n    if exp.condition == @matched_call\n      # Found `if request.get?`\n\n      # Do not warn if there is an `elsif` clause\n      if node_type? exp.else_clause, :if\n        return exp\n      end\n\n      warn_about_result @current_result, exp\n    end\n\n    exp\n  end\n\n  def warn_about_result result, code\n    return unless original? result\n\n    confidence = :weak\n    message = msg('Potential HTTP verb confusion. ',\n                  msg_code('HEAD'),\n                  ' is routed like ',\n                  msg_code('GET'),\n                  ' but ',\n                  msg_code('request.get?'),\n                  ' will return ',\n                  msg_code('false')\n                 )\n\n    warn :result => result,\n      :warning_type => \"HTTP Verb Confusion\",\n      :warning_code => :http_verb_confusion,\n      :message => message,\n      :code => code,\n      :user_input => result[:call],\n      :confidence => confidence,\n      :cwe_id => [352]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_weak_hash.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckWeakHash < Brakeman::BaseCheck\n  Brakeman::Checks.add_optional self\n\n  @description = \"Checks for use of weak hashes like MD5\"\n\n  DIGEST_CALLS = [:base64digest, :digest, :hexdigest, :new]\n\n  def run_check\n    tracker.find_call(:targets => [:'Digest::MD5', :'Digest::SHA1', :'OpenSSL::Digest::MD5', :'OpenSSL::Digest::SHA1'], :nested => true).each do |result|\n      process_hash_result result\n    end\n\n    tracker.find_call(:target => :'Digest::HMAC', :methods => [:new, :hexdigest], :nested => true).each do |result|\n      process_hmac_result result\n    end\n\n    tracker.find_call(:targets => [:'OpenSSL::Digest::Digest', :'OpenSSL::Digest'], :method => :new).each do |result|\n      process_openssl_result result\n    end\n  end\n\n  def process_hash_result result\n    return unless original? result\n\n    input = nil\n    call = result[:call]\n\n    if DIGEST_CALLS.include? call.method\n      if input = user_input_as_arg?(call)\n        confidence = :high\n      elsif input = hashing_password?(call)\n        confidence = :high\n      else\n        confidence = :medium\n      end\n    else\n      confidence = :medium\n    end\n\n    message = msg(\"Weak hashing algorithm used\")\n\n    case call.target.last\n    when :MD5\n      message << \": \" << msg_lit(\"MD5\")\n    when :SHA1\n      message << \": \" << msg_lit(\"SHA1\")\n    end\n\n    warn :result => result,\n      :warning_type => \"Weak Hash\",\n      :warning_code => :weak_hash_digest,\n      :message => message,\n      :confidence => confidence,\n      :user_input => input,\n      :cwe_id => [328]\n  end\n\n  def process_hmac_result result\n    return unless original? result\n\n    call = result[:call]\n\n    message = msg(\"Weak hashing algorithm used in HMAC\")\n\n    case call.third_arg.last\n    when :MD5\n      message << \": \" << msg_lit(\"MD5\")\n    when :SHA1\n      message << \": \" << msg_lit(\"SHA1\")\n    end\n\n    warn :result => result,\n      :warning_type => \"Weak Hash\",\n      :warning_code => :weak_hash_hmac,\n      :message => message,\n      :confidence => :medium,\n      :cwe_id => [328]\n  end\n\n  def process_openssl_result result\n    return unless original? result\n\n    arg = result[:call].first_arg\n\n    if string? arg\n      alg = arg.value.upcase\n\n      if alg == 'MD5' or alg == 'SHA1'\n        warn :result => result,\n          :warning_type => \"Weak Hash\",\n          :warning_code => :weak_hash_digest,\n          :message => msg(\"Weak hashing algorithm used: \", msg_lit(alg)),\n          :confidence => :medium,\n          :cwe_id => [328]\n      end\n    end\n  end\n\n  def user_input_as_arg? call\n    call.each_arg do |arg|\n      if input = include_user_input?(arg)\n        return input\n      end\n    end\n\n    nil\n  end\n\n  def hashing_password? call\n    call.each_arg do |arg|\n      @has_password = false\n\n      process arg\n\n      if @has_password\n        return @has_password\n      end\n    end\n\n    nil\n  end\n\n  def process_call exp\n    if exp.method == :password\n      @has_password = exp\n    else\n      process_default exp\n    end\n\n    exp\n  end\n\n  def process_ivar exp\n    if exp.value == :@password\n      @has_password = exp\n    end\n\n    exp\n  end\n\n  def process_lvar exp\n    if exp.value == :password\n      @has_password = exp\n    end\n\n    exp\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_weak_rsa_key.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckWeakRSAKey < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for weak uses RSA keys\"\n\n  def run_check\n    check_rsa_key_creation\n    check_rsa_operations\n  end\n\n  def check_rsa_key_creation\n    tracker.find_call(targets: [:'OpenSSL::PKey::RSA'], method: [:new, :generate], nested: true).each do |result|\n      key_size_arg = result[:call].first_arg\n      check_key_size(result, key_size_arg)\n    end\n\n    tracker.find_call(targets: [:'OpenSSL::PKey'], method: [:generate_key], nested: true).each do |result|\n      call = result[:call]\n      key_type = call.first_arg\n      options_arg = call.second_arg\n\n      next unless options_arg and hash? options_arg\n\n      if string? key_type and key_type.value.upcase == 'RSA'\n        key_size_arg = (hash_access(options_arg, :rsa_keygen_bits) || hash_access(options_arg, s(:str, 'rsa_key_gen_bits')))\n        check_key_size(result, key_size_arg)\n      end\n    end\n  end\n\n  def check_rsa_operations\n    tracker.find_call(targets: [:'OpenSSL::PKey::RSA.new'], methods: [:public_encrypt, :public_decrypt, :private_encrypt, :private_decrypt], nested: true).each do |result|\n      padding_arg = result[:call].second_arg\n      check_padding(result, padding_arg)\n    end\n\n    tracker.find_call(targets: [:'OpenSSL::PKey.generate_key'], methods: [:encrypt, :decrypt, :sign, :verify, :sign_raw, :verify_raw], nested: true).each do |result|\n      call = result[:call]\n      options_arg = call.last_arg\n\n      if options_arg and hash? options_arg\n        padding_arg = (hash_access(options_arg, :rsa_padding_mode) || hash_access(options_arg, s(:str, 'rsa_padding_mode')))\n      else\n        padding_arg = nil\n      end\n\n      check_padding(result, padding_arg)\n    end\n  end\n\n  def check_key_size result, key_size_arg\n    return unless number? key_size_arg\n    return unless original? result\n\n    key_size = key_size_arg.value\n\n    if key_size < 1024\n      confidence = :high\n      message = msg(\"RSA key with size \", msg_code(key_size.to_s), \" is considered very weak. Use at least 2048 bit key size\")\n    elsif key_size < 2048\n      confidence = :medium\n      message = msg(\"RSA key with size \", msg_code(key_size.to_s), \" is considered weak. Use at least 2048 bit key size\")\n    else\n      return\n    end\n\n    warn result: result,\n      warning_type: \"Weak Cryptography\",\n      warning_code: :small_rsa_key_size,\n      message: message,\n      confidence: confidence,\n      user_input: key_size_arg,\n      cwe_id: [326]\n  end\n\n  PKCS1_PADDING = s(:colon2, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :PKCS1_PADDING).freeze\n  PKCS1_PADDING_STR = s(:str, 'pkcs1').freeze\n  SSLV23_PADDING = s(:colon2, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :SSLV23_PADDING).freeze\n  SSLV23_PADDING_STR = s(:str, 'sslv23').freeze\n  NO_PADDING = s(:colon2, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :NO_PADDING).freeze\n  NO_PADDING_STR = s(:str, 'none').freeze\n\n  def check_padding result, padding_arg\n    return unless original? result\n\n    if string? padding_arg\n      padding_arg = padding_arg.deep_clone(padding_arg.line)\n      padding_arg.value = padding_arg.value.downcase\n    end\n\n    case padding_arg\n    when PKCS1_PADDING, PKCS1_PADDING_STR, nil\n      message = \"Use of padding mode PKCS1 (default if not specified), which is known to be insecure. Use OAEP instead\"\n    when SSLV23_PADDING, SSLV23_PADDING_STR\n      message = \"Use of padding mode SSLV23 for RSA key, which is only useful for outdated versions of SSL. Use OAEP instead\"\n    when NO_PADDING, NO_PADDING_STR\n      message = \"No padding mode used for RSA key. A safe padding mode (OAEP) should be specified for RSA keys\"\n    else\n      return\n    end\n\n    warn result: result,\n      warning_type: \"Weak Cryptography\",\n      warning_code: :insecure_rsa_padding_mode,\n      message: message,\n      confidence: :high,\n      user_input: padding_arg,\n      cwe_id: [780]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_without_protection.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Check for bypassing mass assignment protection\n#with without_protection => true\n#\n#Only for Rails 3.1\nclass Brakeman::CheckWithoutProtection < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Check for mass assignment using without_protection\"\n\n  def run_check\n    if version_between? \"0.0.0\", \"3.0.99\"\n      return\n    end\n\n    return if active_record_models.empty?\n\n    Brakeman.debug \"Finding all mass assignments\"\n    calls = tracker.find_call :targets => active_record_models.keys, :methods => [:new,\n      :attributes=, \n      :update_attributes, \n      :update_attributes!,\n      :create,\n      :create!]\n\n    Brakeman.debug \"Processing all mass assignments\"\n    calls.each do |result|\n      process_result result\n    end\n  end\n\n  #All results should be Model.new(...) or Model.attributes=() calls\n  def process_result res\n    call = res[:call]\n    last_arg = call.last_arg\n\n    if hash? last_arg and not call.original_line and not duplicate? res\n\n      if value = hash_access(last_arg, :without_protection)\n        if true? value\n          add_result res\n\n          if input = include_user_input?(call.arglist)\n            confidence = :high\n          elsif all_literals? call\n            return\n          else\n            confidence = :medium\n          end\n\n          warn :result => res, \n            :warning_type => \"Mass Assignment\", \n            :warning_code => :mass_assign_without_protection,\n            :message => \"Unprotected mass assignment\",\n            :code => call, \n            :user_input => input,\n            :confidence => confidence,\n            :cwe_id => [915]\n\n        end\n      end\n    end\n  end\n\n  def all_literals? call\n    call.each_arg do |arg|\n      if hash? arg\n        hash_iterate arg do |k, v|\n          unless node_type? k, :str, :lit, :false, :true and node_type? v, :str, :lit, :false, :true\n            return false\n          end\n        end\n      else\n        return false\n      end\n    end\n\n    true\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_xml_dos.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckXMLDoS < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for XML denial of service (CVE-2015-3227)\"\n\n  def run_check\n    version = rails_version\n\n    fix_version = case\n                  when version_between?(\"2.0.0\", \"3.2.21\")\n                    \"3.2.22\"\n                  when version_between?(\"4.1.0\", \"4.1.10\")\n                    \"4.1.11\"\n                  when version_between?(\"4.2.0\", \"4.2.1\")\n                    \"4.2.2\"\n                  when version_between?(\"4.0.0\", \"4.0.99\")\n                    \"4.2.2\"\n                  else\n                    return\n                  end\n\n    return if has_workaround?\n\n    message = msg(msg_version(version), \" is vulnerable to denial of service via XML parsing \", msg_cve(\"CVE-2015-3227\"), \". Upgrade to \", msg_version(fix_version))\n\n    warn :warning_type => \"Denial of Service\",\n      :warning_code => :CVE_2015_3227,\n      :message => message,\n      :confidence => :medium,\n      :gem_info => gemfile_or_environment,\n      :link_path => \"https://groups.google.com/d/msg/rubyonrails-security/bahr2JLnxvk/x4EocXnHPp8J\",\n      :cwe_id => [125]\n  end\n\n  def has_workaround?\n    tracker.find_call(target: :\"ActiveSupport::XmlMini\", method: :backend=).any? do |match|\n      arg = match[:call].first_arg\n      if string? arg\n        value = arg.value\n        value == 'Nokogiri' or value == 'LibXML'\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/check_yaml_parsing.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckYAMLParsing < Brakeman::BaseCheck\n  Brakeman::Checks.add self\n\n  @description = \"Checks for YAML parsing vulnerabilities (CVE-2013-0156)\"\n\n  def run_check\n    return unless version_between? \"0.0.0\", \"2.3.14\" or\n                  version_between? \"3.0.0\", \"3.0.18\" or\n                  version_between? \"3.1.0\", \"3.1.9\" or\n                  version_between? \"3.2.0\", \"3.2.10\"\n\n    unless disabled_xml_parser? or disabled_xml_dangerous_types?\n      new_version = if version_between? \"0.0.0\", \"2.3.14\"\n                      \"2.3.15\"\n                    elsif version_between? \"3.0.0\", \"3.0.18\"\n                      \"3.0.19\"\n                    elsif version_between? \"3.1.0\", \"3.1.9\"\n                      \"3.1.10\"\n                    elsif version_between? \"3.2.0\", \"3.2.10\"\n                      \"3.2.11\"\n                    end\n\n      message = msg(msg_version(rails_version), \" has a remote code execution vulnerability. Upgrade to \", msg_version(new_version), \" or disable XML parsing\")\n\n      warn :warning_type => \"Remote Code Execution\",\n        :warning_code => :CVE_2013_0156,\n        :message => message,\n        :confidence => :high,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/61bkgvnSGTQ/discussion\",\n        :cwe_id => [20]\n    end\n\n    #Warn if app accepts YAML\n    if version_between?(\"0.0.0\", \"2.3.14\") and enabled_yaml_parser?\n      message = \"Parsing YAML request parameters enables remote code execution: disable YAML parser\"\n\n      warn :warning_type => \"Remote Code Execution\",\n        :warning_code => :CVE_2013_0156,\n        :message => message,\n        :confidence => :high,\n        :gem_info => gemfile_or_environment,\n        :link_path => \"https://groups.google.com/d/topic/rubyonrails-security/61bkgvnSGTQ/discussion\",\n        :cwe_id => [20]\n    end\n  end\n\n  def disabled_xml_parser?\n    if version_between? \"0.0.0\", \"2.3.14\"\n      #Look for ActionController::Base.param_parsers.delete(Mime::XML)\n      matches = tracker.find_call(target: :\"ActionController::Base.param_parsers\", method: :delete)\n    else\n      #Look for ActionDispatch::ParamsParser::DEFAULT_PARSERS.delete(Mime::XML)\n      matches = tracker.find_call(target: :\"ActionDispatch::ParamsParser::DEFAULT_PARSERS\", method: :delete)\n    end\n\n    unless matches.empty?\n      mime_xml = s(:colon2, s(:const, :Mime), :XML)\n\n      matches.each do |result|\n        if result[:call].first_arg == mime_xml\n          return true\n        end\n      end\n    end\n\n    false\n  end\n\n  #Look for ActionController::Base.param_parsers[Mime::YAML] = :yaml\n  #in Rails 2.x apps\n  def enabled_yaml_parser?\n    matches = tracker.find_call(target: :'ActionController::Base.param_parsers', method: :[]=)\n\n    mime_yaml = s(:colon2, s(:const, :Mime), :YAML)\n\n    matches.each do |result|\n      if result[:call].first_arg == mime_yaml and\n        symbol? result[:call].second_arg and\n        result[:call].second_arg.value == :yaml\n\n        return true\n      end\n    end\n\n    false\n  end\n\n  def disabled_xml_dangerous_types?\n    if version_between? \"0.0.0\", \"2.3.14\"\n      matches = tracker.find_call(target: :\"ActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING\", method: :delete)\n    else\n      matches = tracker.find_call(target: :\"ActiveSupport::XmlMini::PARSING\", method: :delete)\n    end\n\n    symbols_off = false\n    yaml_off = false\n\n    matches.each do |result|\n      arg = result[:call].first_arg\n\n      if string? arg\n        if arg.value == \"yaml\"\n          yaml_off = true\n        elsif arg.value == \"symbol\"\n          symbols_off = true\n        end\n      end\n    end\n\n    symbols_off and yaml_off\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks/eol_check.rb",
    "content": "require 'date'\nrequire 'brakeman/checks/base_check'\n\n# Not used directly - base check for EOLRails and EOLRuby\nclass Brakeman::EOLCheck < Brakeman::BaseCheck\n  def check_eol_version library, eol_dates\n    version = case library\n              when :rails\n                tracker.config.rails_version\n              when :ruby\n                tracker.config.ruby_version\n              else\n                raise 'Implement using tracker.config.gem_version'\n              end\n\n    eol_dates.each do |(start_version, end_version), eol_date|\n      if version_between? start_version, end_version, version\n        case\n        when Date.today >= eol_date\n          warn_about_unsupported_version library, eol_date, version\n        when (Date.today + 30) >= eol_date\n          warn_about_soon_unsupported_version library, eol_date, version, :medium\n        when (Date.today + 60) >= eol_date\n          warn_about_soon_unsupported_version library, eol_date, version, :low\n        end\n\n        break\n      end\n    end\n  end\n\n  def warn_about_soon_unsupported_version library, eol_date, version, confidence\n    warn warning_type: 'Unmaintained Dependency',\n      warning_code: :\"pending_eol_#{library}\",\n      message: msg(\"Support for \", msg_version(version, library.capitalize), \" ends on #{eol_date}\"),\n      confidence: confidence,\n      gem_info: gemfile_or_environment(library),\n      :cwe_id => [1104]\n  end\n\n  def warn_about_unsupported_version library, eol_date, version\n    warn warning_type: 'Unmaintained Dependency',\n      warning_code: :\"eol_#{library}\",\n      message: msg(\"Support for \", msg_version(version, library.capitalize), \" ended on #{eol_date}\"),\n      confidence: :high,\n      gem_info: gemfile_or_environment(library),\n      :cwe_id => [1104]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/checks.rb",
    "content": "require 'thread'\nrequire 'brakeman/differ'\n\n#Collects up results from running different checks.\n#\n#Checks can be added with +Check.add(check_class)+\n#\n#All .rb files in checks/ will be loaded.\nclass Brakeman::Checks\n  @checks = []\n  @optional_checks = []\n\n  attr_reader :warnings, :controller_warnings, :model_warnings, :template_warnings, :checks_run\n\n  #Add a check. This will call +_klass_.new+ when running tests\n  def self.add klass\n    @checks << klass unless @checks.include? klass\n  end\n\n  #Add an optional check\n  def self.add_optional klass\n    @optional_checks << klass unless @checks.include? klass\n  end\n\n  def self.checks\n    @checks + @optional_checks\n  end\n\n  def self.optional_checks\n    @optional_checks\n  end\n\n  def self.initialize_checks check_directory = \"\"\n    #Load all files in check_directory\n    Dir.glob(File.join(check_directory, \"*.rb\")).sort.each do |f|\n      require f\n    end\n  end\n\n  def self.missing_checks check_args\n    check_args = check_args.to_a.map(&:to_s).to_set\n\n    if check_args == Set['CheckNone']\n      return []\n    else\n      loaded = self.checks.map { |name| name.to_s.gsub('Brakeman::', '') }.to_set\n      missing = check_args - loaded\n\n      unless missing.empty?\n        return missing\n      end\n    end\n\n    []\n  end\n\n  #No need to use this directly.\n  def initialize options = { }\n    if options[:min_confidence]\n      @min_confidence = options[:min_confidence]\n    else\n      @min_confidence = Brakeman.get_defaults[:min_confidence]\n    end\n\n    @warnings = []\n    @template_warnings = []\n    @model_warnings = []\n    @controller_warnings = []\n    @checks_run = []\n  end\n\n  #Add Warning to list of warnings to report.\n  #Warnings are split into four different arrays\n  #for template, controller, model, and generic warnings.\n  #\n  #Will not add warnings which are below the minimum confidence level.\n  def add_warning warning\n    unless warning.confidence > @min_confidence\n      case warning.warning_set\n      when :template\n        @template_warnings << warning\n      when :warning\n        @warnings << warning\n      when :controller\n        @controller_warnings << warning\n      when :model\n        @model_warnings << warning\n      else\n        raise \"Unknown warning: #{warning.warning_set}\"\n      end\n    end\n  end\n\n  #Return a hash of arrays of new and fixed warnings\n  #\n  #    diff = checks.diff old_checks\n  #    diff[:fixed]  # [...]\n  #    diff[:new]    # [...]\n  def diff other_checks\n    my_warnings = self.all_warnings\n    other_warnings = other_checks.all_warnings\n    Brakeman::Differ.new(my_warnings, other_warnings).diff\n  end\n\n  #Return an array of all warnings found.\n  def all_warnings\n    @warnings + @template_warnings + @controller_warnings + @model_warnings\n  end\n\n  #Run all the checks on the given Tracker.\n  #Returns a new instance of Checks with the results.\n  def self.run_checks(tracker)\n    checks = self.checks_to_run(tracker)\n    check_runner = self.new :min_confidence => tracker.options[:min_confidence]\n    self.actually_run_checks(checks, check_runner, tracker)\n  end\n\n  def self.actually_run_checks(checks, check_runner, tracker)\n    threads = [] # Results for parallel\n    results = [] # Results for sequential\n    parallel = tracker.options[:parallel_checks]\n    error_mutex = Mutex.new\n\n    message = if parallel\n      \"Running #{checks.length} checks in parallel\"\n    else\n      \"Running #{checks.length} checks\"\n    end\n\n    Brakeman.process_step(message) do\n      checks.each do |c|\n        check_name = get_check_name c\n        Brakeman.debug \" - #{check_name}\"\n\n        if parallel\n          threads << Thread.new do\n            self.run_a_check(c, error_mutex, tracker)\n          end\n        else\n          results << self.run_a_check(c, error_mutex, tracker)\n        end\n\n        #Maintain list of which checks were run\n        #mainly for reporting purposes\n        check_runner.checks_run << check_name[5..-1]\n      end\n\n      threads.each { |t| t.join }\n\n      if parallel\n        threads.each do |thread|\n          thread.value.each do |warning|\n            check_runner.add_warning warning\n          end\n        end\n      else\n        results.each do |warnings|\n          warnings.each do |warning|\n            check_runner.add_warning warning\n          end\n        end\n      end\n    end\n\n    check_runner\n  end\n\n  private\n\n  def self.get_check_name check_class\n    check_class.to_s.split(\"::\").last\n  end\n\n  def self.checks_to_run tracker\n    to_run = if tracker.options[:run_all_checks] or tracker.options[:run_checks]\n               @checks + @optional_checks\n             else\n               @checks.dup\n             end.to_set\n\n    if enabled_checks = tracker.options[:enable_checks]\n      @optional_checks.each do |c|\n        if enabled_checks.include? self.get_check_name(c)\n          to_run << c\n        end\n      end\n    end\n\n    self.filter_checks to_run, tracker\n  end\n\n  def self.filter_checks checks, tracker\n    skipped = tracker.options[:skip_checks]\n    explicit = tracker.options[:run_checks]\n    enabled = tracker.options[:enable_checks] || []\n\n    checks.reject do |c|\n      check_name = self.get_check_name(c)\n\n      skipped.include? check_name or\n        (explicit and not explicit.include? check_name and not enabled.include? check_name)\n    end\n  end\n\n  def self.run_a_check klass, mutex, tracker\n    check = klass.new(tracker)\n\n    begin\n      check.run_check\n    rescue => e\n      mutex.synchronize do\n        tracker.error e\n      end\n    end\n\n    check.warnings\n  end\nend\n\n#Load all files in checks/ directory\nDir.glob(\"#{File.expand_path(File.dirname(__FILE__))}/checks/*.rb\").sort.each do |f|\n  require f.match(/(brakeman\\/checks\\/.*)\\.rb$/)[0]\nend\n"
  },
  {
    "path": "lib/brakeman/codeclimate/engine_configuration.rb",
    "content": "require 'pathname'\n\nmodule Brakeman\n  module Codeclimate\n    class EngineConfiguration\n\n      def initialize(engine_config = {})\n        @engine_config = engine_config\n      end\n\n      def options\n        default_options.merge(configured_options)\n      end\n\n      private\n\n      attr_reader :engine_config\n\n      def default_options\n        @default_options = {\n          :output_format => :codeclimate,\n          :quiet => true,\n          :pager => false,\n          :app_path => Dir.pwd\n        }\n        if system(\"test -w /dev/stdout\")\n          @default_options[:output_files] = [\"/dev/stdout\"]\n        end\n        @default_options\n      end\n\n      def configured_options\n        @configured_options = {}\n        # ATM this gets parsed as a string instead of bool: \"config\":{ \"debug\":\"true\" }\n        if brakeman_configuration[\"debug\"] && brakeman_configuration[\"debug\"].to_s.downcase == \"true\"\n          @configured_options[:debug] = true\n          @configured_options[:report_progress] = false\n        end\n\n        if active_include_paths\n          @configured_options[:only_files] = active_include_paths\n        end\n\n        if brakeman_configuration[\"app_path\"]\n          @configured_options[:path_prefix] = brakeman_configuration[\"app_path\"]\n          path = Pathname.new(Dir.pwd) + brakeman_configuration[\"app_path\"]\n          @configured_options[:app_path] = path.to_s\n        end\n        @configured_options\n      end\n\n      def brakeman_configuration\n        if engine_config[\"config\"]\n          engine_config[\"config\"]\n        else\n          {}\n        end\n      end\n\n      def active_include_paths\n        @active_include_paths ||=\n          if brakeman_configuration[\"app_path\"]\n            stripped_include_paths(brakeman_configuration[\"app_path\"])\n          else\n            engine_config[\"include_paths\"] && engine_config[\"include_paths\"].compact\n          end\n      end\n\n      def stripped_include_paths(prefix)\n        subprefixes = path_subprefixes(prefix)\n        engine_config[\"include_paths\"] && engine_config[\"include_paths\"].map do |path|\n          next unless path\n          stripped_include_path(prefix, subprefixes, path)\n        end.compact\n      end\n\n      def path_subprefixes(path)\n        Pathname.new(path).each_filename.inject([]) do |memo, piece|\n         memo <<\n           if memo.any?\n             File.join(memo.last, piece)\n           else\n             File.join(piece)\n           end\n        end\n      end\n\n      def stripped_include_path(prefix, subprefixes, path)\n        if path.start_with?(prefix)\n          path.sub(%r{^#{prefix}/?}, \"/\")\n        elsif subprefixes.any? { |subprefix| path =~ %r{^#{subprefix}/?$} }\n          \"/\"\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/commandline.rb",
    "content": "require 'brakeman/options'\n\nmodule Brakeman\n\n  # Implements handling of running Brakeman from the command line.\n  class Commandline\n    class << self\n\n      # Main method to run Brakeman from the command line.\n      #\n      # If no options are provided, ARGV will be parsed and used instead.\n      # Otherwise, the options are expected to be a Hash like the one returned\n      # after ARGV is parsed.\n      def start options = nil, app_path = \".\"\n\n        unless options\n          options, app_path = parse_options ARGV\n        end\n\n        run options, app_path\n      end\n\n      # Runs everything:\n      #\n      # - `set_interrupt_handler`\n      # - `early_exit_options`\n      # - `set_options`\n      # - `check_latest`\n      # - `run_report`\n      def run options, default_app_path = \".\"\n        set_interrupt_handler options\n        early_exit_options options\n        set_options options, default_app_path\n        check_latest(options[:ensure_latest]) if options[:ensure_latest]\n        run_report options\n\n        quit\n      end\n\n      # Check for the latest version.\n      #\n      # If the latest version is newer than the current version\n      # and age, exit.\n      def check_latest(days_old = 0)\n        if days_old == true\n          days_old = 0\n        end\n\n        if error = Brakeman.ensure_latest(days_old:)\n          quit Brakeman::Not_Latest_Version_Exit_Code, error\n        end\n      end\n\n      # Runs a comparison report based on the options provided.\n      def compare_results options\n        require 'json'\n        vulns = Brakeman.compare options.merge(:quiet => options[:quiet])\n\n        if options[:comparison_output_file]\n          File.open options[:comparison_output_file], \"w\" do |f|\n            f.puts JSON.pretty_generate(vulns)\n          end\n\n          Brakeman.announce \"Comparison saved in '#{options[:comparison_output_file]}'\"\n        else\n          puts JSON.pretty_generate(vulns)\n        end\n\n        Brakeman.cleanup(false)\n\n        if options[:exit_on_warn] && vulns[:new].count > 0\n          quit Brakeman::Warnings_Found_Exit_Code\n        end\n      end\n\n      # Handle options that exit without generating a report.\n      def early_exit_options options\n        if options[:list_checks] or options[:list_optional_checks]\n          Brakeman.list_checks options\n          quit\n        elsif options[:create_config]\n          Brakeman.dump_config options\n          quit\n        elsif options[:show_help]\n          puts Brakeman::Options.create_option_parser({})\n          quit\n        elsif options[:show_version]\n          require 'brakeman/version'\n          puts \"brakeman #{Brakeman::Version}\"\n          quit\n        end\n      end\n\n      # Parse ARGV-style array of options.\n      #\n      # Exits if options are invalid.\n      #\n      # Returns an option hash and the app_path.\n      def parse_options argv\n        begin\n          options, _ = Brakeman::Options.parse! argv\n        rescue OptionParser::ParseError => e\n          $stderr.puts e.message\n          $stderr.puts \"Please see `brakeman --help` for valid options\"\n          quit(-1)\n        end\n\n        if argv[-1]\n          app_path = argv[-1]\n        else\n          app_path = \".\"\n        end\n\n        if options[:ensure_ignore_notes] and options[:previous_results_json]\n          warn '[Notice] --ensure-ignore-notes may not be used at the same ' \\\n               'time as --compare. Deactivating --ensure-ignore-notes. ' \\\n               'Please see `brakeman --help` for valid options'\n          options[:ensure_ignore_notes] = false\n        end\n\n        return options, app_path\n      end\n\n      # Exits with the given exit code and prints out the message, if given.\n      #\n      # Override this method for different behavior.\n      def quit exit_code = 0, message = nil\n        warn message if message\n        Brakeman.cleanup\n        exit exit_code\n      end\n\n      # Runs a regular report based on the options provided.\n      def regular_report options\n        tracker = run_brakeman options\n\n        ensure_ignore_notes_failed = false\n        if tracker.options[:ensure_ignore_notes]\n          fingerprints = Brakeman::ignore_file_entries_with_empty_notes tracker.ignored_filter&.file\n\n          unless fingerprints.empty?\n            ensure_ignore_notes_failed = true\n            warn '[Error] Notes required for all ignored warnings when ' \\\n              '--ensure-ignore-notes is set. No notes provided for these ' \\\n              'warnings: '\n            fingerprints.each { |f| warn f }\n          end\n        end\n\n        if tracker.options[:exit_on_warn] and not tracker.filtered_warnings.empty?\n          quit Brakeman::Warnings_Found_Exit_Code\n        end\n\n        if tracker.options[:exit_on_error] and tracker.errors.any?\n          quit Brakeman::Errors_Found_Exit_Code\n        end\n\n        if tracker.options[:ensure_no_obsolete_ignore_entries] && tracker.unused_fingerprints.any?\n          warn '[Error] Obsolete ignore entries were found, exiting with an error code.'\n          quit Brakeman::Obsolete_Ignore_Entries_Exit_Code\n        end\n\n        if ensure_ignore_notes_failed\n          quit Brakeman::Empty_Ignore_Note_Exit_Code\n        end\n      end\n\n      # Actually run Brakeman.\n      #\n      # Returns a Tracker object.\n      def run_brakeman options\n        Brakeman.run options.merge(:print_report => true, :quiet => options[:quiet])\n      end\n\n      # Run either a comparison or regular report based on options provided.\n      def run_report options\n        begin\n          if options[:previous_results_json]\n            compare_results options\n          else\n            regular_report options\n          end\n        rescue Brakeman::NoApplication => e\n          quit Brakeman::No_App_Found_Exit_Code, e.message\n        rescue Brakeman::MissingChecksError => e\n          quit Brakeman::Missing_Checks_Exit_Code, e.message\n        end\n      end\n\n      # Sets interrupt handler to gracefully handle Ctrl+C\n      def set_interrupt_handler options\n        trap(\"INT\") do\n          warn \"\\nInterrupted - exiting.\"\n\n          if options[:debug]\n            warn caller\n          end\n\n          Brakeman.cleanup\n\n          exit!\n        end\n      end\n\n      # Modifies options, including setting the app_path\n      # if none is given in the options hash.\n      def set_options options, default_app_path = \".\"\n        unless options[:app_path]\n          options[:app_path] = default_app_path\n        end\n\n        if options[:quiet].nil?\n          options[:quiet] = :command_line\n        end\n\n        options\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/differ.rb",
    "content": "# extracting the diff logic to it's own class for consistency. Currently handles\n# an array of Brakeman::Warnings or plain hash representations.  \nclass Brakeman::Differ\n  attr_reader :old_warnings, :new_warnings\n\n  def initialize new_warnings, old_warnings\n    @new_warnings = new_warnings\n    @old_warnings = old_warnings\n  end\n\n  def diff\n    warnings = {}\n    warnings[:new] = @new_warnings - @old_warnings\n    warnings[:fixed] = @old_warnings - @new_warnings\n\n    second_pass(warnings)\n  end\n\n  # second pass to cleanup any vulns which have changed in line number only.\n  # Given a list of new warnings, delete pairs of new/fixed vulns that differ\n  # only by line number.\n  def second_pass(warnings)\n    new_fingerprints = Set.new(warnings[:new].map(&method(:fingerprint)))\n    fixed_fingerprints = Set.new(warnings[:fixed].map(&method(:fingerprint)))\n\n    # Remove warnings which fingerprints are both in :new and :fixed\n    shared_fingerprints = new_fingerprints.intersection(fixed_fingerprints)\n\n    unless shared_fingerprints.empty?\n      warnings[:new].delete_if do |warning|\n        shared_fingerprints.include?(fingerprint(warning))\n      end\n\n      warnings[:fixed].delete_if do |warning|\n        shared_fingerprints.include?(fingerprint(warning))\n      end\n    end\n\n    warnings\n  end\n\n  def fingerprint(warning)\n    if warning.is_a?(Brakeman::Warning)\n      warning.fingerprint\n    else\n      warning[:fingerprint]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/file_parser.rb",
    "content": "require 'parallel'\n\nmodule Brakeman\n  ASTFile = Struct.new(:path, :ast)\n\n  # This class handles reading and parsing files.\n  class FileParser\n    attr_reader :file_list, :errors\n\n    def initialize app_tree, timeout, parallel = true, use_prism = false\n      @use_prism = use_prism\n\n      if @use_prism\n        begin\n          require 'prism'\n          Brakeman.debug 'Using Prism parser'\n        rescue LoadError => e\n          Brakeman.debug \"Asked to use Prism, but failed to load: #{e}\"\n          @use_prism = false\n        end\n      end\n\n      @app_tree = app_tree\n      @timeout = timeout\n      @file_list = []\n      @errors = []\n      @parallel = parallel\n    end\n\n    def parse_files list\n      if @parallel\n        parallel_options = {}\n      else\n        # Disable parallelism\n        parallel_options = { in_threads: 0 }\n      end\n\n      # Parse the files in parallel.\n      # By default, the parsing will be in separate processes.\n      # So we map the result to ASTFiles and/or Exceptions\n      # then partition them into ASTFiles and Exceptions\n      # and add the Exceptions to @errors\n      #\n      # Basically just a funky way to deal with two possible\n      # return types that are returned from isolated processes.\n      #\n      # Note this method no longer uses read_files\n      @file_list, new_errors = Parallel.map(list, parallel_options) do |file_name|\n        Brakeman.logger.spin\n        file_path = @app_tree.file_path(file_name)\n        contents = file_path.read\n\n        begin\n          if ast = parse_ruby(contents, file_path.relative)\n            ASTFile.new(file_name, ast)\n          end\n        rescue Exception => e\n          e\n        end\n      end.compact.partition do |result|\n        result.is_a? ASTFile\n      end\n\n      errors.concat new_errors\n    end\n\n    def read_files list\n      list.each do |path|\n        file = @app_tree.file_path(path)\n\n        begin\n          result = yield file, file.read\n\n          if result\n            @file_list << result\n          end\n        rescue Exception => e\n          @errors << e\n        end\n      end\n    end\n\n    # _path_ can be a string or a Brakeman::FilePath\n    def parse_ruby input, path\n      if path.is_a? Brakeman::FilePath\n        path = path.relative\n      end\n\n      Brakeman.debug \"Parsing #{path}\"\n\n      if @use_prism\n        begin\n          parse_with_prism input, path\n        rescue => e\n          Brakeman.debug \"Prism failed to parse #{path}: #{e}\"\n\n          parse_with_ruby_parser input, path\n        end\n      else\n        parse_with_ruby_parser input, path\n      end\n    end\n\n    private\n\n    def parse_with_prism input, path\n      Prism::Translation::RubyParser.parse(input, path)\n    end\n\n    def parse_with_ruby_parser input, path\n      begin\n        RubyParser.new.parse input, path, @timeout\n      rescue Racc::ParseError => e\n        raise e.exception(e.message + \"\\nCould not parse #{path}\")\n      rescue Timeout::Error => e\n        raise Exception.new(\"Parsing #{path} took too long (> #{@timeout} seconds). Try increasing the limit with --parser-timeout\")\n      rescue => e\n        raise e.exception(e.message + \"\\nWhile processing #{path}\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/file_path.rb",
    "content": "require 'pathname'\n\nmodule Brakeman\n  # Class to represent file paths within Brakeman.\n  # FilePath objects track both the relative and absolute paths\n  # to make it easier to manage paths.\n  class FilePath\n    attr_reader :absolute, :relative\n    @cache = {}\n\n    # Create a new FilePath using an AppTree object.\n    #\n    # Note that if the path is already a FilePath, that path will\n    # be returned unaltered.\n    #\n    # Additionally, paths are cached. If the absolute path already has\n    # a FilePath in the cache, that existing FilePath will be returned.\n    def self.from_app_tree app_tree, path\n      return path if path.is_a? Brakeman::FilePath\n\n      absolute = app_tree.expand_path(path).freeze\n\n      if fp = @cache[absolute]\n        return fp\n      end\n\n      relative = app_tree.relative_path(path).freeze\n\n      self.new(absolute, relative).tap { |fp| @cache[absolute] = fp }\n    end\n\n    # Create a new FilePath with the given absolute and relative paths.\n    def initialize absolute_path, relative_path\n      @absolute = absolute_path\n      @relative = relative_path\n    end\n\n    # Just the file name, no path\n    def basename\n      @basename ||= File.basename(self.relative)\n    end\n\n    # Read file from absolute path.\n    def read\n      File.read self.absolute\n    end\n\n    # Check if absolute path exists.\n    def exists?\n      File.exist? self.absolute\n    end\n\n    # Compare FilePaths. Raises an ArgumentError unless both objects are FilePaths.\n    def <=> rhs\n      raise ArgumentError unless rhs.is_a? Brakeman::FilePath\n      self.relative <=> rhs.relative\n    end\n\n    # Compare FilePaths. Raises an ArgumentError unless both objects are FilePaths.\n    def == rhs\n      return false unless rhs.is_a? Brakeman::FilePath\n\n      self.absolute == rhs.absolute\n    end\n\n    # Returns a string with the absolute path.\n    def to_str\n      self.absolute\n    end\n\n    # Required for Pathname compatibility.\n    # Ruby 3.5+ requires Pathname#initialize to receive a String or an object with to_path method.\n    alias to_path to_str\n\n    # Returns a string with the absolute path.\n    def to_s\n      self.to_str\n    end\n\n    def hash\n      @hash ||= [@absolute, @relative].hash\n    end\n\n    def eql? rhs\n      @absolute == rhs.absolute and\n        @relative == rhs.relative\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/format/style.css",
    "content": "/* CSS style used for HTML reports */\n\nbody {\n  font-family: sans-serif;\n  color: #161616;\n}\n\na {\n  color: #161616;\n}\n\np {\n  font-weight: bold;\n  font-size: 11pt;\n  color: #2D0200;\n }\n\n th {\n   background-color: #980905;\n   border-bottom: 5px solid #530200;\n   color: white;\n   font-size: 11pt;\n   padding: 1px 8px 1px 8px;\n }\n\n td {\n   border-bottom: 2px solid white;\n   font-family: monospace;\n   padding: 5px 8px 1px 8px;\n }\n\n table {\n   background-color: #FCF4D4;\n   border-collapse: collapse;\n }\n\n h1 {\n   color: #2D0200;\n   font-size: 14pt;\n }\n\n h2 {\n   color: #2D0200;\n   font-size: 12pt;\n }\n\n span.high-confidence {\n   font-weight:bold;\n   color: red;\n }\n\n span.med-confidence {\n }\n\n span.weak-confidence {\n   color:gray;\n }\n\n div.warning_message {\n   cursor: pointer;\n }\n\n div.warning_message:hover {\n   background-color: white;\n }\n\n table caption {\n   background-color: #FFE;\n   padding: 2px;\n }\n\n table.context {\n   margin-top: 5px;\n   margin-bottom: 5px;\n   border-left: 1px solid #90e960;\n   color: #212121;\n }\n\n tr.context {\n   background-color: white;\n }\n\n tr.first {\n   border-top: 1px solid #7ecc54;\n   padding-top: 2px;\n }\n\n tr.error {\n  background-color: #f4c1c1 !important\n }\n\n tr.near_error {\n  background-color: #f4d4d4 !important\n }\n\n tr.alt {\n   background-color: #e8f4d4;\n }\n\n td.context {\n   padding: 2px 10px 0px 6px;\n   border-bottom: none;\n }\n\n td.context_line {\n   padding: 2px 8px 0px 7px;\n   border-right: 1px solid #b3bda4;\n   border-bottom: none;\n   color: #6e7465;\n }\n\n pre.context {\n   margin-bottom: 1px;\n }\n\n .user_input {\n   background-color: #fcecab;\n }\n\n div.render_path {\n   display: none;\n   background-color: #ffe;\n   padding: 5px;\n   margin: 2px 0px 2px 0px;\n }\n\n div.template_name {\n   cursor: pointer;\n }\n\n div.template_name:hover {\n   background-color: white;\n }\n\n span.code {\n   font-family: monospace;\n }\n\n span.filename {\n   font-family: monospace;\n }\n"
  },
  {
    "path": "lib/brakeman/logger.rb",
    "content": "module Brakeman\n  module Logger\n    def self.get_logger options, dest = $stderr\n      case\n      when options[:debug]\n        Debug.new(options, dest)\n      when options[:quiet]\n        Quiet.new(options, dest)\n      when options[:report_progress] == false\n        Plain.new(options, dest)\n      when dest.tty?\n        Console.new(options, dest)\n      else\n        Plain.new(options, dest)\n      end\n    end\n\n    class Base\n      def initialize(options, log_destination = $stderr)\n        @dest = log_destination\n        @show_timing = options[:debug] || options[:show_timing]\n      end\n\n      # Output a message to the log.\n      # If newline is `false`, does not output a newline after message.\n      def log(message, newline: true)\n        if newline\n          @dest.puts message\n        else\n          @dest.write message\n        end\n      end\n\n      # Notify about important information - use sparingly\n      def announce(message); end\n\n      # Notify regarding errors - use sparingly\n      def alert(message); end\n\n      # Output debug information\n      def debug(message); end\n\n      # Wraps a step in the scanning process\n      def context(description, &)\n        yield self\n      end\n\n      # Wraps a substep (e.g. processing one file)\n      def single_context(description, &)\n        yield\n      end\n\n      # Update progress towards a known total\n      def update_progress(current, total, type = 'files'); end\n\n      # Show a spinner\n      def spin; end\n\n      # Called on exit\n      def cleanup(newline = true); end\n\n      def show_timing? = @show_timing\n\n      # Use ANSI codes to color a string\n      def color(message, *)\n        if @highline\n          @highline.color(message, *)\n        else\n          message\n        end\n      end\n\n      def color?\n        @highline and @highline.use_color?\n      end\n\n      private\n\n      def load_highline(output_color)\n        if @dest.tty? or output_color == :force\n          Brakeman.load_brakeman_dependency 'highline'\n          @highline = HighLine.new\n          @highline.use_color = !!output_color\n        else\n          @highline = nil\n        end\n      end\n    end\n\n    class Plain < Base\n      def initialize(options, *)\n        super\n\n        load_highline(options[:output_color])\n      end\n\n      def announce(message)\n        log color(message, :bold, :green)\n      end\n\n      def alert(message)\n        log color(message, :red)\n      end\n\n      def context(description, &)\n        log \"#{color(description, :green)}...\"\n\n        if show_timing?\n          time_step(description, &)\n        else\n          yield\n        end\n      end\n\n      def time_step(description, &)\n        start_t = Time.now\n        yield\n        duration = Time.now - start_t\n\n        log color((\"Completed #{description.to_s.downcase} in %0.2fs\" % duration), :gray)\n      end\n    end\n\n    class Quiet < Base\n      def initialize(*)\n        super\n      end\n    end\n\n    class Debug < Plain\n      def debug(message)\n        log color(message, :gray)\n      end\n\n      def context(description, &)\n        log \"#{description}...\"\n\n        time_step(description, &)\n      end\n\n      def single_context(description, &)\n        debug \"Processing #{description}\"\n\n        if show_timing?\n          # Even in debug, only show timing for each file if asked\n          time_step(description, &)\n        else\n          yield\n        end\n      end\n    end\n\n    class Console < Base\n      attr_reader :prefix\n\n      def initialize(options, *)\n        super\n\n        load_highline(options[:output_color])\n        require 'reline'\n        require 'reline/io/ansi'\n\n        @prefix = ''\n        @post_fix_pos = 0\n        @reline = Reline::ANSI.new\n        @reline.output = @dest\n        @report_progress = options[:report_progress]\n        @spinner = [\"⣀\", \"⣄\", \"⣤\", \"⣦\", \"⣶\", \"⣷\", \"⣿\"]\n        @percenter = [\"⣀\", \"⣤\", \"⣶\", \"⣿\"]\n        @spindex = 0\n        @last_spin = Time.now\n        @reline.hide_cursor\n      end\n\n      def announce message\n        clear_line\n        log color(message, :bold, :green)\n        rewrite_prefix\n      end\n\n      def alert message\n        clear_line\n        log color(message, :red)\n        rewrite_prefix\n      end\n\n      def context(description, &)\n        write_prefix description\n\n        time_step(description, &)\n      ensure\n        clear_prefix\n      end\n\n      def time_step(description, &)\n        if show_timing?\n          start_t = Time.now\n          yield\n          duration = Time.now - start_t\n\n          write_after color(('%0.2fs' % duration), :gray)\n          log ''\n        else\n          yield\n        end\n      end\n\n      def update_progress current, total, type = 'files'\n        percent = ((current / total.to_f) * 100).to_i\n        tenths = [(percent / 10), 0].max\n\n        lead = color(@percenter[percent % 10 / 3], :bold, :red)\n        done_blocks = color(\"⣿\" * tenths, :red)\n        remaining = color(\"⣀\" * (9 - tenths), :gray)\n        write_after \"#{done_blocks}#{lead}#{remaining}\"\n      end\n\n      def write_prefix pref\n        set_prefix pref\n        rewrite_prefix\n      end\n\n      # If an alert was written, redo prefix on next line\n      def rewrite_prefix\n        log(@prefix, newline: false)\n        @reline.erase_after_cursor\n      end\n\n      def write_after message\n        @reline.move_cursor_column(@post_fix_pos)\n        log(message, newline: false)\n        @reline.erase_after_cursor\n      end\n\n      def set_prefix message\n        @prefix = \"#{color('»', :bold, :cyan)} #{color(message, :green)}\"\n        @post_fix_pos = HighLine::Wrapper.actual_length(@prefix) + 1\n      end\n\n      def clear_prefix\n        @prefix = ''\n        @post_fix_pos = 0\n        clear_line\n      end\n\n      def clear_line\n        @reline.move_cursor_column(0)\n        @reline.erase_after_cursor\n      end\n\n      def spin\n        return unless (Time.now - @last_spin) > 0.2\n\n        write_after color(@spinner[@spindex], :bold, :red)\n        @spindex = (@spindex + 1) % @spinner.length\n        @last_spin = Time.now\n      end\n\n      def cleanup(newline = true)\n        @reline.show_cursor\n        log('') if newline\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/messages.rb",
    "content": "module Brakeman\n  module Messages\n    # Create a new message from a list of messages.\n    # Strings are converted to Brakeman::Messages::Plain objects.\n    def msg *args\n      parts = args.map do |a|\n        if a.is_a? String\n          Plain.new(a)\n        else\n          a\n        end\n      end\n\n      Message.new(*parts)\n    end\n\n    # Create a new code message fragment\n    def msg_code code\n      Code.new code\n    end\n\n    # Create a new message fragment with a CVE identifier\n    def msg_cve cve\n      CVE.new cve\n    end\n\n    # Create a new message fragment representing a file name\n    def msg_file str\n      Messages::FileName.new str\n    end\n\n    # Create a new message fragment from a user input type (e.g. `:params`).\n    # The input type will be converted to a friendly version (e.g. \"parameter value\").\n    def msg_input input\n      Input.new input\n    end\n\n    # Create a new message fragment which will not be modified during output\n    def msg_lit str\n      Literal.new str\n    end\n\n    # Create a new plain string message fragment\n    def msg_plain str\n      Plain.new str\n    end\n\n    # Create a message fragment representing the version of a library\n    def msg_version version, lib = \"Rails\"\n      Version.new version, lib\n    end\n  end\nend\n\n# Class to represent a list of message types\nclass Brakeman::Messages::Message\n  def initialize *args\n    @parts = args.map do |a|\n      case a\n      when String, Symbol\n        Brakeman::Messages::Plain.new(a.to_s)\n      else\n        a\n      end\n    end\n  end\n\n  def << msg\n    if msg.is_a? String\n      @parts << Brakeman::Messages::Plain.new(msg)\n    else\n      @parts << msg\n    end\n  end\n\n  def to_s\n    output = @parts.map(&:to_s).join\n\n    case @parts.first\n    when Brakeman::Messages::Code, Brakeman::Messages::Literal, Brakeman::Messages::Version\n    else\n      output[0] = output[0].capitalize\n    end\n\n    output\n  end\n\n  def to_html\n    require 'cgi/escape'\n\n    output = @parts.map(&:to_html).join\n\n    case @parts.first\n    when Brakeman::Messages::Code, Brakeman::Messages::Literal, Brakeman::Messages::Version\n    else\n      output[0] = output[0].capitalize\n    end\n\n    output\n  end\nend\n\nclass Brakeman::Messages::Code\n  def initialize code\n    @code = code.to_s\n  end\n\n  def to_s\n    \"`#{@code}`\"\n  end\n\n  def to_html\n    \"<span class=\\\"code\\\">#{CGI.escapeHTML(@code)}</span>\"\n  end\nend\n\nclass Brakeman::Messages::CVE\n  def initialize cve\n    @cve = cve\n  end\n\n  def to_s\n    \"(#{@cve})\"\n  end\n\n  def to_html\n    \"(<a href=\\\"https://cve.mitre.org/cgi-bin/cvename.cgi?name=#{@cve}\\\" target=\\\"_blank\\\" rel=\\\"noreferrer\\\">#{@cve}</a>)\"\n  end\nend\n\nclass Brakeman::Messages::FileName\n  def initialize file\n    @file = file\n  end\n\n  def to_s\n    \"`#{@file}`\"\n  end\n\n  def to_html\n    \"<span class=\\\"filename\\\">#{CGI.escapeHTML(@file)}</span>\"\n  end\nend\n\nclass Brakeman::Messages::Input\n  def initialize input\n    @input = input\n    @value = friendly_type_of(@input)\n  end\n\n  def friendly_type_of input_type\n    if input_type.is_a? Brakeman::BaseCheck::Match\n      input_type = input_type.type\n    end\n\n    case input_type\n    when :params\n      \"parameter value\"\n    when :cookies\n      \"cookie value\"\n    when :request\n      \"request value\"\n    when :model\n      \"model attribute\"\n    else\n      \"user input\"\n    end\n  end\n\n  def to_s\n    @value\n  end\n\n  def to_html\n    self.to_s\n  end\nend\n\nclass Brakeman::Messages::Literal\n  def initialize value\n    @value = value.to_s\n  end\n\n  def to_s\n    @value\n  end\n\n  def to_html\n    @value\n  end\nend\n\nclass Brakeman::Messages::Plain\n  def initialize string\n    @value = string\n  end\n\n  def to_s\n    @value\n  end\n\n  def to_html\n    CGI.escapeHTML(@value)\n  end\nend\n\nclass Brakeman::Messages::Version\n  def initialize version, lib\n    @version = version\n    @library = lib\n  end\n\n  def to_s\n    \"#{@library} #{@version}\"\n  end\n\n  def to_html\n    CGI.escapeHTML(self.to_s)\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/options.rb",
    "content": "require 'optparse'\nrequire 'set'\n\n#Parses command line arguments for Brakeman\nmodule Brakeman::Options\n\n  class << self\n\n    #Parse argument array\n    def parse args\n      get_options args\n    end\n\n    #Parse arguments and remove them from the array as they are matched\n    def parse! args\n      get_options args, true\n    end\n\n    #Return hash of options and the parser\n    def get_options args, destructive = false\n      options = {}\n\n      parser = create_option_parser options\n\n      if destructive\n        parser.parse! args\n      else\n        parser.parse args\n      end\n\n      if options[:previous_results_json] and options[:output_files]\n        options[:comparison_output_file] = options[:output_files].shift\n      end\n\n      return options, parser\n    end\n\n    def create_option_parser options\n      OptionParser.new do |opts|\n        opts.banner = \"Usage: brakeman [options] rails/root/path\"\n\n        opts.on \"-n\", \"--no-threads\", \"Run checks and file parsing sequentially\" do\n          options[:parallel_checks] = false\n        end\n\n        opts.on \"--[no-]progress\", \"Show progress reports\" do |progress|\n          options[:report_progress] = progress\n        end\n\n        opts.on \"-p\", \"--path PATH\", \"Specify path to Rails application\" do |path|\n          options[:app_path] = path\n        end\n\n        opts.on \"-q\", \"--[no-]quiet\", \"Suppress informational messages\" do |quiet|\n          options[:quiet] = quiet\n        end\n\n        opts.on( \"-z\", \"--[no-]exit-on-warn\", \"Exit code is non-zero if warnings found (Default)\") do |exit_on_warn|\n          options[:exit_on_warn] = exit_on_warn\n        end\n\n        opts.on \"--[no-]exit-on-error\", \"Exit code is non-zero if errors raised (Default)\" do |exit_on_error|\n          options[:exit_on_error] = exit_on_error\n        end\n\n        opts.on \"--ensure-latest [DAYS]\", Integer, \"Fail when Brakeman is outdated. Optionally set minimum age in days.\" do |days|\n          if days and not (1..15).include? days\n            raise OptionParser::InvalidArgument\n          end\n\n          options[:ensure_latest] = days || true\n        end\n\n        opts.on \"--ensure-ignore-notes\", \"Fail when an ignored warnings does not include a note\" do\n          options[:ensure_ignore_notes] = true\n        end\n\n        opts.on \"--ensure-no-obsolete-ignore-entries\", \"Fail when an obsolete ignore entry is found\" do\n          options[:ensure_no_obsolete_ignore_entries] = true\n        end\n\n        opts.on \"-3\", \"--rails3\", \"Force Rails 3 mode\" do\n          options[:rails3] = true\n        end\n\n        opts.on \"-4\", \"--rails4\", \"Force Rails 4 mode\" do\n          options[:rails3] = true\n          options[:rails4] = true\n        end\n\n        opts.on \"-5\", \"--rails5\", \"Force Rails 5 mode\" do\n          options[:rails3] = true\n          options[:rails4] = true\n          options[:rails5] = true\n        end\n\n        opts.on \"-6\", \"--rails6\", \"Force Rails 6 mode\" do\n          options[:rails3] = true\n          options[:rails4] = true\n          options[:rails5] = true\n          options[:rails6] = true\n        end\n\n        opts.on \"-7\", \"--rails7\", \"Force Rails 7 mode\" do\n          options[:rails3] = true\n          options[:rails4] = true\n          options[:rails5] = true\n          options[:rails6] = true\n          options[:rails7] = true\n        end\n\n        opts.on \"-8\", \"--rails8\", \"Force Rails 8 mode\" do\n          options[:rails3] = true\n          options[:rails4] = true\n          options[:rails5] = true\n          options[:rails6] = true\n          options[:rails7] = true\n          options[:rails8] = true\n        end\n\n        opts.separator \"\"\n        opts.separator \"Scanning options:\"\n\n        opts.on \"-A\", \"--run-all-checks\", \"Run all default and optional checks\" do\n          options[:run_all_checks] = true\n        end\n\n        opts.on \"-a\", \"--[no-]assume-routes\", \"Assume all controller methods are actions (Default)\" do |assume|\n          options[:assume_all_routes] = assume\n        end\n\n        opts.on \"-e\", \"--escape-html\", \"Escape HTML by default\" do\n          options[:escape_html] = true\n        end\n\n        opts.on \"--faster\", \"Faster, but less accurate scan\" do\n          options[:ignore_ifs] = true\n          options[:disable_constant_tracking] = true\n        end\n\n        opts.on \"--ignore-model-output\", \"Consider model attributes XSS-safe\" do\n          options[:ignore_model_output] = true\n        end\n\n        opts.on \"--ignore-protected\", \"Consider models with attr_protected safe\" do\n          options[:ignore_attr_protected] = true\n        end\n\n        opts.on \"--interprocedural\", \"Process method calls to known methods\" do\n          options[:interprocedural] = true\n        end\n\n        opts.on \"--no-branching\", \"Disable flow sensitivity on conditionals\" do\n          options[:ignore_ifs] = true\n        end\n\n        opts.on \"--branch-limit LIMIT\", Integer, \"Limit depth of values in branches (-1 for no limit)\" do |limit|\n          options[:branch_limit] = limit\n        end\n\n        opts.on \"--parser-timeout SECONDS\", Integer, \"Set parse timeout (Default: 10)\" do |timeout|\n          options[:parser_timeout] = timeout\n        end\n\n        opts.on \"--[no-]prism\", \"Use the Prism parser\" do |use_prism|\n          if use_prism\n            min_prism_version = '1.0.0'\n\n            begin\n              gem 'prism', \">=#{min_prism_version}\"\n              require 'prism'\n            rescue Gem::MissingSpecVersionError, Gem::MissingSpecError, Gem::LoadError => e\n              $stderr.puts \"Please install `prism` version #{min_prism_version} or newer:\"\n              raise e\n            end\n          end\n\n          options[:use_prism] = use_prism\n        end\n\n        opts.on \"-r\", \"--report-direct\", \"Only report direct use of untrusted data\" do |option|\n          options[:check_arguments] = !option\n        end\n\n        opts.on \"-s\", \"--safe-methods meth1,meth2,etc\", Array, \"Set methods as safe for unescaped output in views\" do |methods|\n          options[:safe_methods] ||= Set.new\n          options[:safe_methods].merge methods.map {|e| e.to_sym }\n        end\n\n        opts.on \"--sql-safe-methods meth1,meth2,etc\", Array, \"Do not warn of SQL if the input is wrapped in a safe method\" do |methods|\n          options[:sql_safe_methods] ||= Set.new\n          options[:sql_safe_methods].merge methods.map {|e| e.to_sym }\n        end\n\n        opts.on \"--url-safe-methods method1,method2,etc\", Array, \"Do not warn of XSS if the link_to href parameter is wrapped in a safe method\" do |methods|\n          options[:url_safe_methods] ||= Set.new\n          options[:url_safe_methods].merge methods.map {|e| e.to_sym }\n        end\n\n        opts.on \"--skip-files file1,path2,etc\", Array, \"Skip processing of these files/directories. Directories are application relative and must end in \\\"#{File::SEPARATOR}\\\"\" do |files|\n          options[:skip_files] ||= Set.new\n          options[:skip_files].merge files\n        end\n\n        opts.on \"--only-files file1,path2,etc\", Array, \"Process only these files/directories. Directories are application relative and must end in \\\"#{File::SEPARATOR}\\\"\" do |files|\n          options[:only_files] ||= Set.new\n          options[:only_files].merge files\n        end\n\n        opts.on \"--[no-]skip-vendor\", \"Skip processing vendor directory (Default)\" do |skip|\n          options[:skip_vendor] = skip\n        end\n\n        opts.on \"--add-libs-path path1,path2,etc\", Array, \"An application relative lib directory (ex. app/mailers) to process\" do |paths|\n          options[:additional_libs_path] ||= Set.new\n          options[:additional_libs_path].merge paths\n        end\n\n        opts.on \"--add-engines-path path1,path2,etc\", Array, \"Include these engines in the scan\" do |paths|\n          options[:engine_paths] ||= Set.new\n          options[:engine_paths].merge paths\n        end\n\n        opts.on '--[no-]follow-symlinks', 'Follow symbolic links for directions' do |follow_symlinks|\n          options[:follow_symlinks] = follow_symlinks\n        end\n\n        opts.on '--gemfile GEMFILE', 'Specify Gemfile to scan' do |gemfile|\n          options[:gemfile] = gemfile\n        end\n\n        opts.on \"-E\", \"--enable Check1,Check2,etc\", Array, \"Enable the specified checks\" do |checks|\n          checks.map! do |check|\n            if check.start_with? \"Check\"\n              check\n            else\n              \"Check#{check}\"\n            end\n          end\n\n          options[:enable_checks] ||= Set.new\n          options[:enable_checks].merge checks\n        end\n\n        opts.on \"-t\", \"--test Check1,Check2,etc\", Array, \"Only run the specified checks\" do |checks|\n          checks.each_with_index do |s, index|\n            if s[0,5] != \"Check\"\n              checks[index] = \"Check#{s}\"\n            end\n          end\n\n          options[:run_checks] ||= Set.new\n          options[:run_checks].merge checks\n        end\n\n        opts.on \"-x\", \"--except Check1,Check2,etc\", Array, \"Skip the specified checks\" do |skip|\n          skip.each do |s|\n            if s[0,5] != \"Check\"\n              s = \"Check#{s}\"\n            end\n\n            options[:skip_checks] ||= Set.new\n            options[:skip_checks] << s\n          end\n        end\n\n        opts.on \"--add-checks-path path1,path2,etc\", Array, \"A directory containing additional out-of-tree checks to run\" do |paths|\n          options[:additional_checks_path] ||= Set.new\n          options[:additional_checks_path].merge paths.map {|p| File.expand_path p}\n        end\n\n        opts.separator \"\"\n        opts.separator \"Output options:\"\n\n        opts.on \"-d\", \"--debug\", \"Lots of output\" do\n          options[:debug] = true\n        end\n\n        opts.on \"--timing\", \"Measure time for scan steps\" do\n          options[:show_timing] = true\n        end\n\n        opts.on \"-f\",\n          \"--format TYPE\",\n          [:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif, :sonar, :github],\n          \"Specify output formats. Default is text\" do |type|\n\n          type = \"s\" if type == :text\n          options[:output_format] = :\"to_#{type}\"\n        end\n\n        opts.on \"--css-file CSSFile\", \"Specify CSS to use for HTML output\" do |file|\n          options[:html_style] = File.expand_path file\n        end\n\n        opts.on \"-i IGNOREFILE\", \"--ignore-config IGNOREFILE\", \"Use configuration to ignore warnings\" do |file|\n          options[:ignore_file] = file\n        end\n\n        opts.on \"-I\", \"--interactive-ignore\", \"Interactively ignore warnings\" do\n          options[:interactive_ignore] = true\n        end\n\n        opts.on \"--show-ignored\", \"Show files that are usually ignored by the ignore configuration file\" do\n          options[:show_ignored] = true\n        end\n\n        opts.on \"-l\", \"--[no-]combine-locations\", \"Combine warning locations (Default)\" do |combine|\n          options[:combine_locations] = combine\n        end\n\n        opts.on \"--[no-]highlights\", \"Highlight user input in report\" do |highlight|\n          options[:highlight_user_input] = highlight\n        end\n\n        opts.on \"--[no-]color\", \"Use ANSI colors in report (Default)\" do |color|\n          if color\n            options[:output_color] = :force\n          else\n            options[:output_color] = color\n          end\n        end\n\n        opts.on \"-m\", \"--routes\", \"Report controller information\" do\n          options[:report_routes] = true\n        end\n\n        opts.on \"--message-limit LENGTH\", \"Limit message length in HTML report\" do |limit|\n          options[:message_limit] = limit.to_i\n        end\n\n        opts.on \"--[no-]pager\", \"Use pager for output to terminal (Default)\" do |pager|\n          options[:pager] = pager\n        end\n\n        opts.on \"--table-width WIDTH\", \"Limit table width in text report\" do |width|\n          options[:table_width] = width.to_i\n        end\n\n        opts.on \"-o\", \"--output FILE\", \"Specify files for output. Defaults to stdout. Multiple '-o's allowed\" do |file|\n          options[:output_files] ||= []\n          options[:output_files].push(file)\n        end\n\n        opts.on \"--[no-]separate-models\", \"Warn on each model without attr_accessible (Default)\" do |separate|\n          options[:collapse_mass_assignment] = !separate\n        end\n\n        opts.on \"--[no-]summary\", \"Only output summary of warnings\" do |summary_only|\n          if summary_only\n            options[:summary_only] = :summary_only\n          else\n            options[:summary_only] = :no_summary\n          end\n        end\n\n        opts.on \"--absolute-paths\", \"Output absolute file paths in reports\" do\n          options[:absolute_paths] = true\n        end\n\n        opts.on \"--github-repo USER/REPO[/PATH][@REF]\", \"Output links to GitHub in markdown and HTML reports using specified repo\" do |repo|\n          options[:github_repo] = repo\n        end\n\n        opts.on \"--text-fields field1,field2,etc.\", Array, \"Specify fields for text report format\" do |format|\n          valid_options = [:category, :category_id, :check, :code, :confidence, :cwe, :file, :fingerprint, :line, :link, :message, :render_path]\n\n          options[:text_fields] = format.map(&:to_sym)\n\n          if options[:text_fields] == [:all]\n            options[:text_fields] = valid_options\n          else\n            invalid_options = (options[:text_fields] - valid_options)\n\n            unless invalid_options.empty?\n              raise OptionParser::ParseError, \"\\nInvalid format options: #{invalid_options.inspect}\"\n            end\n          end\n        end\n\n        opts.on \"-w\",\n          \"--confidence-level LEVEL\",\n          [\"1\", \"2\", \"3\"],\n          \"Set minimal confidence level (1 - 3)\" do |level|\n\n          options[:min_confidence] =  3 - level.to_i\n        end\n\n        opts.on \"--compare FILE\", \"Compare the results of a previous Brakeman scan (only JSON is supported)\" do |file|\n          options[:previous_results_json] = File.expand_path(file)\n        end\n\n        opts.separator \"\"\n        opts.separator \"Configuration files:\"\n\n        opts.on \"-c\", \"--config-file FILE\", \"Use specified configuration file\" do |file|\n          options[:config_file] = File.expand_path(file)\n        end\n\n        opts.on \"-C\", \"--create-config [FILE]\", \"Output configuration file based on options\" do |file|\n          if file\n            options[:create_config] = file\n          else\n            options[:create_config] = true\n          end\n        end\n\n        opts.on \"--allow-check-paths-in-config\", \"Allow loading checks from configuration file (Unsafe)\" do\n          options[:allow_check_paths_in_config] = true\n        end\n\n        opts.separator \"\"\n\n        opts.on \"-k\", \"--checks\", \"List all available vulnerability checks\" do\n          options[:list_checks] = true\n        end\n\n        opts.on \"--optional-checks\", \"List optional checks\" do\n          options[:list_optional_checks] = true\n        end\n\n        opts.on \"-v\", \"--version\", \"Show Brakeman version\" do\n          options[:show_version] = true\n        end\n\n        opts.on \"--force-scan\", \"Scan application even if rails is not detected\" do\n          options[:force_scan] = true\n        end\n\n        opts.on_tail \"-h\", \"--help\", \"Display this message\" do\n          options[:show_help] = true\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/parsers/haml6_embedded.rb",
    "content": "[:Coffee, :CoffeeScript, :Markdown, :Sass].each do |name|\n  klass = Module.const_get(\"Haml::Filters::#{name}\")\n\n  klass.define_method(:compile) do |node|\n    temple = [:multi]\n    temple << [:static, \"<script>\\n\"]\n    temple << compile_with_tilt(node)\n    temple << [:static, \"</script>\"]\n    temple\n  end\n\n  klass.define_method(:compile_with_tilt) do |node|\n    # From Haml\n    text = ::Haml::Util.unescape_interpolation(node.value[:text]).gsub(/(\\\\+)n/) do |s|\n      escapes = $1.size\n      next s if escapes % 2 == 0\n      \"#{'\\\\' * (escapes - 1)}\\n\"\n    end\n    text.prepend(\"\\n\").sub!(/\\n\"\\z/, '\"')\n\n    [:dynamic, \"BrakemanFilter.render(#{text})\"]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/parsers/haml_embedded.rb",
    "content": "module Brakeman\n  module FakeHamlFilter\n    # Copied from Haml 4 - force delayed compilation\n    def compile(compiler, text)\n      filter = self\n      compiler.instance_eval do\n        text = unescape_interpolation(text).gsub(/(\\\\+)n/) do |s|\n          escapes = $1.size\n          next s if escapes % 2 == 0\n          (\"\\\\\" * (escapes - 1)) + \"\\n\"\n        end\n        # We need to add a newline at the beginning to get the\n        # filter lines to line up (since the Haml filter contains\n        # a line that doesn't show up in the source, namely the\n        # filter name). Then we need to escape the trailing\n        # newline so that the whole filter block doesn't take up\n        # too many.\n        text = \"\\n\" + text.sub(/\\n\"\\Z/, \"\\\\n\\\"\")\n        push_script <<RUBY.rstrip, :escape_html => false\nfind_and_preserve(#{filter.inspect}.render_with_options(#{text}, _hamlout.options))\nRUBY\n        return\n      end\n    end\n  end\nend\n\n# Fake CoffeeScript filter for Haml\nmodule Haml::Filters::Coffee\n  include Haml::Filters::Base\n  extend Brakeman::FakeHamlFilter\nend\n\n# Fake Markdown filter for Haml\nmodule Haml::Filters::Markdown\n  include Haml::Filters::Base\n  extend Brakeman::FakeHamlFilter\nend\n\n# Fake Sass filter for Haml\nmodule Haml::Filters::Sass\n  include Haml::Filters::Base\n  extend Brakeman::FakeHamlFilter\nend\n"
  },
  {
    "path": "lib/brakeman/parsers/rails_erubi.rb",
    "content": "# frozen_string_literal: true\n# Copied almost verbatim from Rails\n# https://github.com/rails/rails/blob/5359cf8a5b093b04170e884ee8da5a1e076b8a0d/actionview/lib/action_view/template/handlers/erb/erubi.rb#L9\n\nBrakeman.load_brakeman_dependency \"erubi\"\n\nmodule Brakeman\n  class Erubi < ::Erubi::Engine\n    # :nodoc: all\n    def initialize(input, properties = {})\n      @newline_pending = 0\n\n      # Dup properties so that we don't modify argument\n      properties = Hash[properties]\n\n      properties[:bufvar]     ||= \"@output_buffer\"\n      properties[:preamble]   ||= \"\"\n      properties[:postamble]  ||= \"#{properties[:bufvar]}\"\n\n      # Tell Erubi whether the template will be compiled with `frozen_string_literal: true`\n      # properties[:freeze_template_literals] = !Template.frozen_string_literal\n      properties[:freeze_template_literals] = false\n\n      properties[:escapefunc] = \"\"\n\n      super\n    end\n\n  private\n    def add_text(text)\n      return if text.empty?\n\n      if text == \"\\n\"\n        @newline_pending += 1\n      else\n        with_buffer do\n          src << \".safe_append='\"\n          src << \"\\n\" * @newline_pending if @newline_pending > 0\n          src << text.gsub(/['\\\\]/, '\\\\\\\\\\&') << @text_end\n        end\n        @newline_pending = 0\n      end\n    end\n\n    BLOCK_EXPR = /((\\s|\\))do|\\{)(\\s*\\|[^|]*\\|)?\\s*\\Z/\n\n    def add_expression(indicator, code)\n      flush_newline_if_pending(src)\n\n      with_buffer do\n        if (indicator == \"==\") || @escape\n          src << \".safe_expr_append=\"\n        else\n          src << \".append=\"\n        end\n\n        if BLOCK_EXPR.match?(code)\n          src << \" \" << code\n        else\n          src << \"(\" << code << \")\"\n        end\n      end\n    end\n\n    def add_code(code)\n      flush_newline_if_pending(src)\n      super\n    end\n\n    def add_postamble(_)\n      flush_newline_if_pending(src)\n      super\n    end\n\n    def flush_newline_if_pending(src)\n      if @newline_pending > 0\n        with_buffer { src << \".safe_append='#{\"\\n\" * @newline_pending}\" << @text_end }\n        @newline_pending = 0\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/parsers/slim_embedded.rb",
    "content": "# Fake filters for Slim\nmodule Slim\n  class Embedded\n    class TiltEngine\n      alias_method :on_slim_embedded, :on_slim_embedded # silence redefined method warning\n      def on_slim_embedded(engine, body, attrs)\n        # Override this method to avoid Slim trying to load sass/scss and failing\n        case engine\n        when :sass, :scss, :coffee\n          tilt_engine = nil # Doesn't really matter, ignored below\n        else\n          # Original Slim code\n          tilt_engine = Tilt[engine] || raise(Temple::FilterError, \"Tilt engine #{engine} is not available.\")\n        end\n\n        tilt_options = options[engine.to_sym] || {}\n        tilt_options[:default_encoding] ||= 'utf-8'\n\n        [:multi, tilt_render(tilt_engine, tilt_options, collect_text(body)), collect_newlines(body)]\n      end\n    end\n\n    class SassEngine\n      protected\n\n      alias_method :tilt_render, :tilt_render # silence redefined method warning\n      def tilt_render(tilt_engine, tilt_options, text)\n        [:dynamic,\n         \"BrakemanFilter.render(#{text.inspect}, #{self.class})\"]\n      end\n    end\n\n    class CoffeeEngine < TiltEngine\n      protected\n\n      def tilt_render(tilt_engine, tilt_options, text)\n        [:dynamic,\n         \"BrakemanFilter.render(#{text.inspect}, #{self.class})\"]\n      end\n    end\n\n    # Override the engine for CoffeeScript, because Slim doesn't have\n    # one, it just uses Tilt's\n    register :coffee, JavaScriptEngine, engine: CoffeeEngine\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/parsers/template_parser.rb",
    "content": "module Brakeman\n  class TemplateParser\n    include Brakeman::Util\n    attr_reader :tracker\n    KNOWN_TEMPLATE_EXTENSIONS = /.*\\.(erb|haml|rhtml|slim)$/\n\n    TemplateFile = Struct.new(:path, :ast, :name, :type)\n\n    def initialize tracker, file_parser\n      @tracker = tracker\n      @file_parser = file_parser\n      @slim_smart = nil # Load slim/smart ?\n    end\n\n    def parse_template path, text\n      type = path.relative.match(KNOWN_TEMPLATE_EXTENSIONS)[1].to_sym\n      type = :erb if type == :rhtml\n      name = template_path_to_name path\n      Brakeman.debug \"Parsing #{path}\"\n\n      begin\n        src = case type\n              when :erb\n                type = :erubi if erubi?\n                parse_erb path, text\n              when :haml\n                type = :haml6 if haml6?\n                parse_haml path, text\n              when :slim\n                parse_slim path, text\n              else\n                tracker.error \"Unknown template type in #{path}\"\n                nil\n              end\n\n        if src and ast = @file_parser.parse_ruby(src, path)\n          @file_parser.file_list << TemplateFile.new(path, ast, name, type)\n        end\n      rescue Racc::ParseError => e\n        tracker.error e, \"Could not parse #{path}\"\n      rescue StandardError, LoadError => e\n        tracker.error e.exception(e.message + \"\\nWhile processing #{path}\"), e.backtrace\n      end\n\n      nil\n    end\n\n    def parse_erb path, text\n      if erubi?\n        require 'brakeman/parsers/rails_erubi'\n        Brakeman::Erubi.new(text, :filename => path).src\n      else\n        require 'erb'\n        src = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+\n          ERB.new(text, trim_mode: '-').src\n        else\n          ERB.new(text, nil, '-').src\n        end\n        src.sub!(/^#.*\\n/, '')\n        src\n      end\n    end\n\n    def erubi?\n      tracker.config.escape_html? or\n        tracker.config.erubi?\n    end\n\n    def parse_haml path, text\n      if haml6?\n        require_relative 'haml6_embedded'\n\n        Haml::Template.new(filename: path.relative,\n                           :escape_html => tracker.config.escape_html?,\n                           generator: Temple::Generators::RailsOutputBuffer,\n                           use_html_safe: true,\n                           buffer_class: 'ActionView::OutputBuffer',\n                           disable_capture: true,\n                          ) { text }.precompiled_template\n      else\n        require_relative 'haml_embedded'\n\n        Haml::Engine.new(text,\n                         :filename => path,\n                         :escape_html => tracker.config.escape_html?,\n                         :escape_filter_interpolations => tracker.config.escape_filter_interpolations?\n                        ).precompiled.gsub(/([^\\\\])\\\\n/, '\\1')\n      end\n    rescue Haml::Error => e\n      tracker.error e, [\"While compiling HAML in #{path}\"] << e.backtrace\n      nil\n    end\n\n    def haml6?\n      return @haml6 unless @haml6.nil?\n\n      Brakeman.load_brakeman_dependency 'haml'\n      major_version = Haml::VERSION.split('.').first.to_i\n\n      if major_version >= 6\n        @haml6 = true\n      else\n        @haml6 = false\n      end\n    end\n\n    def parse_slim path, text\n      Brakeman.load_brakeman_dependency 'slim'\n\n      if @slim_smart.nil? and load_slim_smart?\n        @slim_smart = true\n        Brakeman.load_brakeman_dependency 'slim/smart'\n      else\n        @slim_smart = false\n      end\n\n      require_relative 'slim_embedded'\n\n      Slim::Template.new(path,\n                         :disable_capture => true,\n                         :generator => Temple::Generators::RailsOutputBuffer) { text }.precompiled_template\n    end\n\n    def load_slim_smart?\n      return !@slim_smart unless @slim_smart.nil?\n\n      # Terrible hack to find\n      #   gem \"slim\", \"~> 3.0.1\", require: [\"slim\", \"slim/smart\"]\n      if tracker.app_tree.exists? 'Gemfile'\n        gemfile_contents = tracker.app_tree.file_path('Gemfile').read\n        if gemfile_contents.include? 'slim/smart'\n          return true\n        end\n      end\n\n      false\n    end\n\n    def self.parse_inline_erb tracker, text\n      fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])\n      tp = self.new(tracker, fp)\n      src = tp.parse_erb '_inline_', text\n      type = tp.erubi? ? :erubi : :erb\n\n      return type, fp.parse_ruby(src, \"_inline_\")\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processor.rb",
    "content": "#Load all files in processors/\nDir.glob(\"#{File.expand_path(File.dirname(__FILE__))}/processors/*.rb\").each { |f| require f.match(/brakeman\\/processors.*/)[0] }\nrequire 'brakeman/tracker'\nrequire 'set'\nrequire 'pathname'\n\nmodule Brakeman\n  #Makes calls to the appropriate processor.\n  #\n  #The ControllerProcessor, TemplateProcessor, and ModelProcessor will\n  #update the Tracker with information about what is parsed.\n  class Processor\n    include Util\n\n    def initialize(app_tree, options)\n      @tracker = Tracker.new(app_tree, self, options)\n    end\n\n    def tracked_events\n      @tracker\n    end\n\n    #Process configuration file source\n    def process_config src, file_name\n      ConfigProcessor.new(@tracker).process_config src, file_name\n    end\n\n    #Process Gemfile\n    def process_gems gem_files\n      GemProcessor.new(@tracker).process_gems gem_files\n    end\n\n    #Process route file source\n    def process_routes src\n      RoutesProcessor.new(@tracker).process_routes src\n    end\n\n    #Process controller source. +file_name+ is used for reporting\n    def process_controller src, file_name\n      if contains_class? src\n        ControllerProcessor.new(@tracker).process_controller src, file_name\n      else\n        LibraryProcessor.new(@tracker).process_library src, file_name\n      end\n    end\n\n    #Process variable aliasing in controller source and save it in the\n    #tracker.\n    def process_controller_alias name, src, only_method = nil, file = nil\n      ControllerAliasProcessor.new(@tracker, only_method).process_controller name, src, file\n    end\n\n    #Process a model source\n    def process_model src, file_name\n      result = ModelProcessor.new(@tracker).process_model src, file_name\n      AliasProcessor.new(@tracker, file_name).process result if result\n    end\n\n    #Process either an ERB or HAML template\n    def process_template name, src, type, called_from = nil, file_name = nil\n      case type\n      when :erb\n        result = ErbTemplateProcessor.new(@tracker, name, called_from, file_name).process src\n      when :haml\n        result = HamlTemplateProcessor.new(@tracker, name, called_from, file_name).process src\n      when :haml6\n        result = Haml6TemplateProcessor.new(@tracker, name, called_from, file_name).process src\n      when :erubi\n        result = ErubiTemplateProcessor.new(@tracker, name, called_from, file_name).process src\n      when :slim\n        result = SlimTemplateProcessor.new(@tracker, name, called_from, file_name).process src\n      else\n        abort \"Unknown template type: #{type} (#{name})\"\n      end\n\n      #Each template which is rendered is stored separately\n      #with a new name.\n      if called_from\n        name = (\"#{name}.#{called_from}\").to_sym\n      end\n\n      @tracker.templates[name].src = result\n      @tracker.templates[name].type = type\n    end\n\n    #Process any calls to render() within a template\n    def process_template_alias template\n      TemplateAliasProcessor.new(@tracker, template).process_safely template.src\n    end\n\n    #Process source for initializing files\n    def process_initializer file_name, src\n      res = BaseProcessor.new(@tracker).process_file src, file_name\n      res = AliasProcessor.new(@tracker).process_safely res, nil, file_name\n      @tracker.initializers[file_name] = res\n    end\n\n    #Process source for a library file\n    def process_lib src, file_name\n      LibraryProcessor.new(@tracker).process_library src, file_name\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/alias_processor.rb",
    "content": "require 'brakeman/util'\nrequire 'ruby_parser/bm_sexp_processor'\nrequire 'brakeman/processors/lib/processor_helper'\nrequire 'brakeman/processors/lib/safe_call_helper'\nrequire 'brakeman/processors/lib/call_conversion_helper'\n\n#Returns an s-expression with aliases replaced with their value.\n#This does not preserve semantics (due to side effects, etc.), but it makes\n#processing easier when searching for various things.\nclass Brakeman::AliasProcessor < Brakeman::SexpProcessor\n  include Brakeman::ProcessorHelper\n  include Brakeman::SafeCallHelper\n  include Brakeman::Util\n  include Brakeman::CallConversionHelper\n\n  attr_reader :result, :tracker\n\n  #Returns a new AliasProcessor with an empty environment.\n  #\n  #The recommended usage is:\n  #\n  # AliasProcessor.new.process_safely src\n  def initialize tracker = nil, current_file = nil\n    super()\n    @env = SexpProcessor::Environment.new\n    @inside_if = false\n    @ignore_ifs = nil\n    @exp_context = []\n    @tracker = tracker #set in subclass as necessary\n    @helper_method_cache = {}\n    @helper_method_info = Hash.new({})\n    @or_depth_limit = (tracker && tracker.options[:branch_limit]) || 5 #arbitrary default\n    @meth_env = nil\n    @current_file = current_file\n    @mass_limit = (tracker && tracker.options[:mass_limit]) || 1000 # arbitrary default\n    set_env_defaults\n  end\n\n  #This method processes the given Sexp, but copies it first so\n  #the original argument will not be modified.\n  #\n  #_set_env_ should be an instance of SexpProcessor::Environment. If provided,\n  #it will be used as the starting environment.\n  #\n  #This method returns a new Sexp with variables replaced with their values,\n  #where possible.\n  def process_safely src, set_env = nil, current_file = @current_file\n    @current_file = current_file\n    @env = set_env || SexpProcessor::Environment.new\n    @result = src.deep_clone\n    process @result\n    @result\n  end\n\n  #Process a Sexp. If the Sexp has a value associated with it in the\n  #environment, that value will be returned.\n  def process_default exp\n    @exp_context.push exp\n\n    begin\n      exp.map! do |e|\n        if sexp? e and not e.empty?\n          process e\n        else\n          e\n        end\n      end\n    rescue => err\n      if @tracker\n        @tracker.error err\n      else\n        raise err\n      end\n    end\n\n    result = replace(exp)\n\n    @exp_context.pop\n\n    result\n  end\n\n  def replace exp, int = 0\n    return exp if int > 3\n\n    if replacement = env[exp]\n      if not duplicate? replacement and replacement.mass < @mass_limit\n        replace(replacement.deep_clone(exp.line), int + 1)\n      else\n        exp\n      end\n    elsif tracker and replacement = tracker.constant_lookup(exp) and not duplicate? replacement\n      replace(replacement.deep_clone(exp.line), int + 1)\n    else\n      exp\n    end\n  end\n\n  def process_bracket_call exp\n    # TODO: What is even happening in this method?\n    r = replace(exp)\n\n    if r != exp\n      return r\n    end\n\n    exp.arglist = process_default(exp.arglist)\n\n    r = replace(exp)\n\n    if r != exp\n      return r\n    end\n\n    t = process(exp.target.deep_clone)\n\n    # sometimes t[blah] has a match in the env\n    # but we don't want to actually set the target\n    # in case the target is big...which is what this\n    # whole method is trying to avoid\n    if t != exp.target\n      e = exp.deep_clone\n      e.target = t\n\n      r = replace(e)\n\n      if r != e\n        return r\n      end\n    else\n      t = exp.target # put it back?\n    end\n\n    if hash? t\n      if v = process_hash_access(t, exp.first_arg)\n        v.deep_clone(exp.line)\n      else\n        case t.node_type\n        when :params\n          exp.target = PARAMS_SEXP.deep_clone(exp.target.line)\n        when :session\n          exp.target = SESSION_SEXP.deep_clone(exp.target.line)\n        when :cookies\n          exp.target = COOKIES_SEXP.deep_clone(exp.target.line)\n        end\n\n        exp\n      end\n    elsif array? t\n      if v = process_array_access(t, exp.args)\n        v.deep_clone(exp.line)\n      else\n        exp\n      end\n    elsif t\n      exp.target = t\n      exp\n    else\n      if exp.target # `self` target is reported as `nil` https://github.com/seattlerb/ruby_parser/issues/250\n        exp.target = process_default exp.target\n      end\n\n      exp\n    end\n  end\n\n  ARRAY_CONST = s(:const, :Array)\n  HASH_CONST = s(:const, :Hash)\n  RAILS_TEST = s(:call, s(:call, s(:const, :Rails), :env), :test?)\n  RAILS_DEV = s(:call, s(:call, s(:const, :Rails), :env), :development?)\n\n  #Process a method call.\n  def process_call exp\n    return exp if process_call_defn? exp\n    target_var = exp.target\n    target_var &&= target_var.deep_clone\n    if exp.node_type == :safe_call\n      exp.node_type = :call\n    end\n\n    if exp.method == :[]\n      return process_bracket_call exp\n    else\n      exp = process_default exp\n    end\n\n    #In case it is replaced with something else\n    unless call? exp\n      return exp\n    end\n\n    # If x(*[1,2,3]) change to x(1,2,3)\n    # if that's the only argument\n    if splat_array? exp.first_arg and exp.second_arg.nil?\n      exp.arglist = exp.first_arg[1].sexp_body\n    end\n\n    target = exp.target\n    method = exp.method\n    first_arg = exp.first_arg\n\n    if method == :send or method == :__send__ or method == :try\n      collapse_send_call exp, first_arg\n    end\n\n    if node_type? target, :or and [:+, :-, :*, :/].include? method\n      res = process_or_simple_operation(exp)\n      return res if res\n    elsif target == ARRAY_CONST and method == :new\n      return Sexp.new(:array, *exp.args).line(exp.line)\n    elsif target == HASH_CONST and method == :new and first_arg.nil? and !node_type?(@exp_context.last, :iter)\n      return Sexp.new(:hash).line(exp.line)\n    elsif exp == RAILS_TEST or exp == RAILS_DEV\n      return Sexp.new(:false).line(exp.line)\n    end\n\n    # For the simplest case of `Foo.thing`\n    if node_type? target, :const and first_arg.nil?\n      if tracker and (klass = tracker.find_class(class_name(target.value)))\n        if return_value = klass.get_simple_method_return_value(:class, method)\n          return return_value.deep_clone(exp.line)\n        end\n      end\n    end\n\n    #See if it is possible to simplify some basic cases\n    #of addition/concatenation.\n    case method\n    when :+\n      if array? target and array? first_arg\n        exp = join_arrays(target, first_arg, exp)\n      elsif string? first_arg\n        exp = join_strings(target, first_arg, exp)\n      elsif number? first_arg\n        exp = math_op(:+, target, first_arg, exp)\n      end\n    when :-, :*, :/\n      if method == :* and array? target\n        if string? first_arg\n          exp = process_array_join(target, first_arg)\n        end\n      else\n        exp = math_op(method, target, first_arg, exp)\n      end\n    when :[]\n      # TODO: This might never be used because of process_bracket_call above\n      if array? target\n        exp = process_array_access(target, exp.args, exp)\n      elsif hash? target\n        exp = process_hash_access(target, first_arg, exp)\n      end\n    when :fetch\n      if array? target\n        # Not dealing with default value\n        # so just pass in first argument, but process_array_access expects\n        # an array of arguments.\n        exp = process_array_access(target, [first_arg], exp)\n      elsif hash? target\n        exp = process_hash_access(target, first_arg, exp)\n      end\n    when :merge!, :update\n      if hash? target and hash? first_arg\n         target = process_hash_merge! target, first_arg\n         env[target_var] = target\n         return target\n      end\n    when :merge\n      if hash? target and hash? first_arg\n        return process_hash_merge(target, first_arg)\n      end\n    when :<<\n      if string? target and string? first_arg\n        target.value += first_arg.value\n        env[target_var] = target\n        return target\n      elsif string? target and string_interp? first_arg\n        exp = Sexp.new(:dstr, target.value + first_arg[1]).concat(first_arg.sexp_body(2)).line(exp.line)\n        env[target_var] = exp\n      elsif string? first_arg and string_interp? target\n        if string? target.last\n          target.last.value += first_arg.value\n        elsif target.last.is_a? String\n          # TODO Use target.last += ?\n          target.last << first_arg.value\n        else\n          target << first_arg\n        end\n        env[target_var] = target\n        return first_arg\n      elsif new_string? target\n        env[target_var] = first_arg\n        return first_arg\n      elsif array? target\n        target << first_arg\n        env[target_var] = target\n        return target\n      else\n        target = find_push_target(target_var)\n        env[target] = exp unless target.nil? # Happens in TemplateAliasProcessor\n      end\n    when :push\n      if array? target\n        target << first_arg\n        env[target_var] = target\n        return target\n      end\n    when :first\n      if array? target and first_arg.nil? and sexp? target[1]\n        exp = target[1]\n      end\n    when :freeze, :dup, :presence\n      unless target.nil?\n        exp = target\n      end\n    when :join\n      if array? target and (string? first_arg or first_arg.nil?)\n        exp = process_array_join(target, first_arg)\n      end\n    when :!\n      #  Convert `!!a` to boolean\n      if call? target and target.method == :!\n        exp = s(:or, s(:true).line(exp.line), s(:false).line(exp.line)).line(exp.line)\n      end\n    when :values\n      # Hash literal\n      if node_type? target, :hash\n        exp = hash_values(target)\n      end\n    when :values_at\n      if node_type? target, :hash\n        res = hash_values_at target, exp.args\n\n        # Only convert to array of values if _all_ keys\n        # are present in the hash.\n        unless res.any?(&:nil?)\n          exp = res\n        end\n      end\n    when :presence_in\n      arg = exp.first_arg\n\n      if node_type? arg, :array\n        # 1.presence_in [1,2,3]\n        if arg.include? target\n          exp = target\n        elsif all_literals? arg\n          exp = safe_literal(exp.line)\n        end\n      end\n    end\n\n    exp\n  end\n\n  # Painful conversion of Array#join into string interpolation\n  def process_array_join array, join_str\n    # Empty array\n    if array.length == 1\n      return s(:str, '').line(array.line)\n    end\n\n    result = s().line(array.line)\n\n    join_value = if string? join_str\n                   join_str.value\n                 else\n                   nil\n                 end\n\n    if array.length > 2\n      array[1..-2].each do |e|\n        result << join_item(e, join_value)\n      end\n    end\n\n    result << join_item(array.last, nil)\n\n    # Combine the strings at the beginning because that's what RubyParser does\n    combined_first = +\"\"\n    result.each do |e|\n      if string? e\n        combined_first << e.value\n      elsif e.is_a? String\n        combined_first << e\n      else\n        break\n      end\n    end\n\n    # Remove the strings at the beginning\n    result.reject! do |e|\n      if e.is_a? String or string? e\n        true\n      else\n        break\n      end\n    end\n\n    result.unshift combined_first\n\n    # Have to fix up strings that follow interpolation\n    string = result.reduce(s(:dstr).line(array.line)) do |memo, e|\n      if string? e and node_type? memo.last, :evstr\n        e.value = \"#{join_value}#{e.value}\"\n      elsif join_value and node_type? memo.last, :evstr and node_type? e, :evstr\n        memo << s(:str, join_value).line(e.line)\n      end\n\n      memo << e\n    end\n\n    # Convert (:dstr, \"hello world\")\n    # to (:str, \"hello world\")\n    if string.length == 2 and string.last.is_a? String\n      string[0] = :str\n    end\n\n    string\n  end\n\n  def join_item item, join_value\n    if item.nil? || item.is_a?(String)\n      \"#{item}#{join_value}\"\n    elsif string? item or symbol? item or number? item\n      s(:str, \"#{item.value}#{join_value}\").line(item.line)\n    else\n      s(:evstr, item).line(item.line)\n    end\n  end\n\n  TEMP_FILE_CLASS = s(:const, :Tempfile)\n\n  def temp_file_open? exp\n    call? exp and\n      exp.target == TEMP_FILE_CLASS and\n      exp.method == :open\n  end\n\n  def temp_file_create? exp\n    call? exp and\n      exp.target == TEMP_FILE_CLASS and\n      exp.method == :create\n  end\n\n  def temp_file_new line\n    s(:call, TEMP_FILE_CLASS, :new).line(line)\n  end\n\n  def splat_array? exp\n    node_type? exp, :splat and\n      node_type? exp[1], :array\n  end\n\n  def process_iter exp\n    @exp_context.push exp\n    exp[1] = process exp.block_call\n    if array_detect_all_literals? exp[1]\n      return safe_literal(exp.line)\n    end\n\n    @exp_context.pop\n\n    env.scope do\n      call = exp.block_call\n      block_args = exp.block_args\n\n      if call? call and [:each, :map].include? call.method and all_literals? call.target and block_args.length == 2 and block_args.last.is_a? Symbol\n        # Iterating over an array of all literal values\n        local = Sexp.new(:lvar, block_args.last)\n        env.current[local] = safe_literal(exp.line)\n      elsif temp_file_open? call\n        local = Sexp.new(:lvar, block_args.last)\n        env.current[local] = temp_file_new(exp.line)\n      elsif temp_file_create? call\n        local = Sexp.new(:lvar, block_args.last)\n        env.current[local] = temp_file_new(exp.line)\n      else\n        block_args.each do |e|\n          #Force block arg(s) to be local\n          if node_type? e, :lasgn\n            env.current[Sexp.new(:lvar, e.lhs)] = Sexp.new(:lvar, e.lhs)\n          elsif node_type? e, :kwarg\n            env.current[Sexp.new(:lvar, e[1])] = e[2]\n          elsif node_type? e, :masgn, :shadow\n            e[1..-1].each do |var|\n              local = Sexp.new(:lvar, var)\n              env.current[local] = local\n            end\n          elsif e.is_a? Symbol\n            local = Sexp.new(:lvar, e)\n            env.current[local] = local\n          elsif e.nil? # trailing comma, argument destructuring\n            next # Punt for now\n          else\n            raise \"Unexpected value in block args: #{e.inspect}\"\n          end\n        end\n      end\n\n      block = exp.block\n\n      if block? block\n        process_all! block\n      else\n        exp[3] = process block\n      end\n    end\n\n    exp\n  end\n\n  #Process a new scope.\n  def process_scope exp\n    env.scope do\n      process exp.block\n    end\n    exp\n  end\n\n  #Start new scope for block.\n  def process_block exp\n    env.scope do\n      process_default exp\n    end\n  end\n\n  #Process a method definition.\n  def process_defn exp\n    meth_env do\n      exp.body = process_all! exp.body\n    end\n    exp\n  end\n\n  def meth_env\n    begin\n      env.scope do\n        set_env_defaults\n        @meth_env = env.current\n        yield\n      end\n    ensure\n      @meth_env = nil\n    end\n  end\n\n  #Process a method definition on self.\n  def process_defs exp\n    meth_env do\n      exp.body = process_all! exp.body\n    end\n    exp\n  end\n\n  # Handles x = y = z = 1\n  def get_rhs exp\n    if node_type? exp, :lasgn, :iasgn, :gasgn, :attrasgn, :safe_attrasgn, :cvdecl, :cdecl\n      get_rhs(exp.rhs)\n    else\n      exp\n    end\n  end\n\n  #Local assignment\n  # x = 1\n  def process_lasgn exp\n    self_assign = self_assign?(exp.lhs, exp.rhs)\n    exp.rhs = process exp.rhs if sexp? exp.rhs\n    return exp if exp.rhs.nil?\n\n    local = Sexp.new(:lvar, exp.lhs).line(exp.line || -2)\n\n    if self_assign\n      # Skip branching\n      env[local] = get_rhs(exp)\n    else\n      set_value local, get_rhs(exp)\n    end\n\n    exp\n  end\n\n  #Instance variable assignment\n  # @x = 1\n  def process_iasgn exp\n    self_assign = self_assign?(exp.lhs, exp.rhs)\n    exp.rhs = process exp.rhs\n    ivar = Sexp.new(:ivar, exp.lhs).line(exp.line)\n\n    if self_assign\n      if env[ivar].nil? and @meth_env\n        @meth_env[ivar] = get_rhs(exp)\n      else\n        env[ivar] = get_rhs(exp)\n      end\n    else\n      set_value ivar, get_rhs(exp)\n    end\n\n    exp\n  end\n\n  #Global assignment\n  # $x = 1\n  def process_gasgn exp\n    match = Sexp.new(:gvar, exp.lhs)\n    exp.rhs = process(exp.rhs)\n    value = get_rhs(exp)\n\n    if value\n      value.line = exp.line\n\n      set_value match, value\n    end\n\n    exp\n  end\n\n  #Class variable assignment\n  # @@x = 1\n  def process_cvdecl exp\n    match = Sexp.new(:cvar, exp.lhs)\n    exp.rhs = process(exp.rhs)\n    value = get_rhs(exp)\n\n    set_value match, value\n\n    exp\n  end\n\n  #'Attribute' assignment\n  # x.y = 1\n  #or\n  # x[:y] = 1\n  def process_attrasgn exp\n    tar_variable = exp.target\n    target = process(exp.target)\n    method = exp.method\n    index_arg = exp.first_arg\n    value_arg = exp.second_arg\n\n    if method == :[]=\n      index = exp.first_arg = process(index_arg)\n      value = exp.second_arg = process(value_arg)\n      match = Sexp.new(:call, target, :[], index)\n\n      set_value match, value\n\n      if hash? target\n        env[tar_variable] = hash_insert target.deep_clone, index, value\n      end\n\n      unless node_type? target, :hash\n        exp.target = target\n      end\n    elsif method.to_s[-1,1] == \"=\"\n      exp.first_arg = process(index_arg)\n      value = get_rhs(exp)\n      #This is what we'll replace with the value\n      match = Sexp.new(:call, target, method.to_s[0..-2].to_sym)\n\n      set_value match, value\n      exp.target = target\n    else\n      raise \"Unrecognized assignment: #{exp}\"\n    end\n    exp\n  end\n\n  # Multiple/parallel assignment:\n  #\n  # x, y = z, w\n  def process_masgn exp\n    exp[2] = process exp[2] if sexp? exp[2]\n\n    if node_type? exp[2], :to_ary and array? exp[2][1]\n      exp[2] = exp[2][1]\n    end\n\n    unless array? exp[1] and array? exp[2]\n      # Already processed RHS, don't do it again\n      # https://github.com/presidentbeef/brakeman/issues/1877\n      return exp\n    end\n\n    vars = exp[1].dup\n    vals = exp[2].dup\n\n    vars.shift\n    vals.shift\n\n    # Call each assignment as if it is normal\n    vars.each_with_index do |var, i|\n      val = vals[i]\n      next unless val # TODO: Break if there are no vals left?\n\n      # This happens with nested destructuring like\n      #   x, (a, b) = blah\n      if node_type? var, :masgn\n        # Need to add value to masgn exp\n        m = var.dup\n        m[2] = s(:to_ary, val)\n\n        process_masgn m\n      elsif node_type? var, :splat\n        # Assign the rest of the values to the variable:\n        #\n        #   a, *b = 1, 2, 3\n        #\n        #   b == [2, 3]\n\n\n        assign = var[1].dup # var is s(:splat, s(:lasgn, :b))\n\n        if i == vars.length - 1 # Last variable being assigned, slurp up the rest\n          assign.rhs = s(:array, *vals[i..]) # val is the \"rest\" of the values\n        else\n          # Calculate how many values to assign based on how many variables\n          # there are.\n          #\n          # If there are more values than variables, the splat gets an empty array.\n\n          assign.rhs = s(:array, *vals[i, (vals.length - vars.length + 1)]).line(vals.line)\n        end\n\n        process assign\n      else\n        assign = var.dup\n        assign.rhs = val\n        process assign\n      end\n    end\n\n    exp\n  end\n\n  def process_hash exp\n    exp = process_default(exp)\n\n    # Handle { **kw }\n    if node_type? exp, :hash\n      if exp.any? { |e| node_type? e, :kwsplat and node_type? e.value, :hash }\n        kwsplats, rest = exp.partition { |e| node_type? e, :kwsplat and node_type? e.value, :hash }\n        exp = Sexp.new.concat(rest).line(exp.line)\n\n        kwsplats.each do |e|\n          exp = process_hash_merge! exp, e.value\n        end\n      end\n    end\n\n    # Return early unless there might be short-hand syntax,\n    # since handling it is kind of expensive.\n    return exp unless exp.any? { |e| e.nil? }\n\n    # Need to handle short-hand hash syntax\n    new_hash = [:hash]\n    hash_iterate(exp) do |key, value|\n      # e.g. { a: }\n      if value.nil? and symbol? key\n        # Only handling local variables for now, not calls\n        lvar = s(:lvar, key.value)\n        if var_value = env[lvar]\n          new_hash << key << var_value.deep_clone(key.line || 0)\n        else\n          # If the value is unknown, assume it was a call\n          # and set the value to a call\n          new_hash.concat << key << s(:call, nil, key.value).line(key.line || 0)\n        end\n      else\n        new_hash.concat << key << value\n      end\n    end\n\n    Sexp.from_array(new_hash).line(exp.line || 0)\n  end\n\n  #Merge values into hash when processing\n  #\n  # h.merge! :something => \"value\"\n  def process_hash_merge! hash, args\n    hash = hash.deep_clone\n    hash_iterate args do |key, replacement|\n      hash_insert hash, key, replacement\n      match = Sexp.new(:call, hash, :[], key)\n      env[match] = replacement\n    end\n    hash\n  end\n\n  #Return a new hash Sexp with the given values merged into it.\n  #\n  #+args+ should be a hash Sexp as well.\n  def process_hash_merge hash, args\n    hash = hash.deep_clone\n    hash_iterate args do |key, replacement|\n      hash_insert hash, key, replacement\n    end\n    hash\n  end\n\n  #Assignments like this\n  # x[:y] ||= 1\n  def process_op_asgn1 exp\n    target_var = exp[1]\n    target_var &&= target_var.deep_clone\n\n    target = exp[1] = process(exp[1])\n    index = exp[2][1] = process(exp[2][1])\n    value = exp[4] = process(exp[4])\n    match = Sexp.new(:call, target, :[], index)\n\n    if exp[3] == :\"||\"\n      unless env[match]\n        if request_value? target\n          env[match] = match.combine(value)\n        else\n          env[match] = value\n        end\n      end\n    else\n      new_value = process s(:call, s(:call, target_var, :[], index), exp[3], value).line(exp.line)\n\n      env[match] = new_value\n    end\n\n    exp\n  end\n\n  #Assignments like this\n  # x.y ||= 1\n  def process_op_asgn2 exp\n    return process_default(exp) if exp[3] != :\"||\"\n\n    target = exp[1] = process(exp[1])\n    value = exp[4] = process(exp[4])\n    method = exp[2]\n\n    match = Sexp.new(:call, target, method.to_s[0..-2].to_sym)\n\n    unless env[match]\n      env[match] = value\n    end\n\n    exp\n  end\n\n  #This is the right hand side value of a multiple assignment,\n  #like `x = y, z`\n  def process_svalue exp\n    exp.value\n  end\n\n  #Constant assignments like\n  # BIG_CONSTANT = 234810983\n  def process_cdecl exp\n    if sexp? exp.rhs\n      exp.rhs = process exp.rhs\n    end\n\n    if @tracker\n      @tracker.add_constant exp.lhs,\n        exp.rhs,\n        :file => @current_file,\n        :module => @current_module,\n        :class => @current_class,\n        :method => @current_method\n    end\n\n    if exp.lhs.is_a? Symbol\n      match = Sexp.new(:const, exp.lhs)\n    else\n      match = exp.lhs\n    end\n\n    env[match] = get_rhs(exp)\n\n    exp\n  end\n\n  def hash_or_array_include_all_literals? exp\n    return unless call? exp and sexp? exp.target\n    target = exp.target\n\n    case target.node_type\n    when :hash\n      hash_include_all_literals? exp\n    else\n      array_include_all_literals? exp\n    end\n  end\n\n  # Check if exp is a call to Array#include? on an array literal\n  # that contains all literal values. For example:\n  #\n  #    [1, 2, \"a\"].include? x\n  #\n  def array_include_all_literals? exp\n    call? exp and\n    exp.method == :include? and\n    (all_literals? exp.target or dir_glob? exp.target)\n  end\n\n  def array_detect_all_literals? exp\n    call? exp and\n    [:detect, :find].include? exp.method and\n    exp.first_arg.nil? and\n    (all_literals? exp.target or dir_glob? exp.target)\n  end\n\n  # Check if exp is a call to Array#include? on an array literal\n  # that contains all literal values. For example:\n  #\n  #    x.in? [1, 2, \"a\"]\n  #\n  def in_array_all_literals? exp\n    call? exp and\n      exp.method == :in? and\n      all_literals? exp.first_arg\n  end\n\n  # Check if exp is a call to Hash#include? on a hash literal\n  # that contains all literal values. For example:\n  #\n  #    {x: 1}.include? x\n  def hash_include_all_literals? exp\n    call? exp and\n    exp.method == :include? and\n    all_literals? exp.target, :hash\n  end\n\n  #Sets @inside_if = true\n  def process_if exp\n    if @ignore_ifs.nil?\n      @ignore_ifs = @tracker && @tracker.options[:ignore_ifs]\n    end\n\n    condition = exp.condition = process exp.condition\n\n    #Check if a branch is obviously going to be taken\n    if true? condition\n      no_branch = true\n      exps = [exp.then_clause, nil]\n    elsif false? condition\n      no_branch = true\n      exps = [nil, exp.else_clause]\n    elsif equality_check? condition and condition.target == condition.first_arg\n      no_branch = true\n      exps = [exp.then_clause, nil]\n    else\n      no_branch = false\n      exps = [exp.then_clause, exp.else_clause]\n    end\n\n    if @ignore_ifs or no_branch\n      exps.each_with_index do |branch, i|\n        exp[2 + i] = process_if_branch branch\n      end\n    else\n      # Translate `if !...` into `unless ...`\n      # Technically they are different but that's only if someone overrides `!`\n      if call? condition and condition.method == :!\n        condition = condition.target\n        exps.reverse!\n      end\n\n      was_inside = @inside_if\n      @inside_if = true\n\n      branch_scopes = []\n      exps.each_with_index do |branch, i|\n        scope do\n          @branch_env = env.current\n          branch_index = 2 + i # s(:if, condition, then_branch, else_branch)\n         exp[branch_index] = if i == 0 and hash_or_array_include_all_literals? condition\n            # If the condition is [\"a\", \"b\"].include? x\n            # set x to safe_literal inside the true branch\n            var = condition.first_arg\n            value = safe_literal(var.line)\n            process_branch_with_value(var, value, branch, i)\n          elsif i == 0 and in_array_all_literals? condition\n            # If the condition is x.in? [\"a\", \"b\"]\n            # set x to safe_literal inside the true branch\n            var = condition.target\n            value = safe_literal(var.line)\n            process_branch_with_value(var, value, branch, i)\n          elsif i == 0 and equality_check? condition\n            # For conditions like a == b,\n            # set a to b inside the true branch\n            var = condition.target\n            value = condition.first_arg\n            process_branch_with_value(var, value, branch, i)\n          elsif i == 1 and hash_or_array_include_all_literals? condition and early_return? branch\n            var = condition.first_arg\n            env.current[var] = safe_literal(var.line)\n            process_if_branch branch\n          else\n            process_if_branch branch\n          end\n          branch_scopes << env.current\n          @branch_env = nil\n        end\n      end\n\n      @inside_if = was_inside\n\n      branch_scopes.each do |s|\n        merge_if_branch s\n      end\n    end\n\n    exp\n  end\n\n  def process_branch_with_value var, value, branch, branch_index\n    previous_value = env.current[var]\n    env.current[var] = value\n    result = process_if_branch branch\n    env.current[var] = previous_value\n    result\n  end\n\n  def early_return? exp\n    return true if node_type? exp, :return\n    return true if call? exp and [:fail, :raise].include? exp.method\n\n    if node_type? exp, :block, :rlist\n      node_type? exp.last, :return or\n        (call? exp and [:fail, :raise].include? exp.method)\n    else\n      false\n    end\n  end\n\n  def equality_check? exp\n    call? exp and\n      exp.method == :==\n  end\n\n  # Not a list of values\n  #   when :example\n  def simple_when? exp\n    node_type? exp[1], :array and\n      exp[1].length == 2 and # only one element in the array\n      not node_type? exp[1][1], :splat, :array\n  end\n\n  # A list of literal values\n  #\n  #   when 1,2,3\n  #\n  # or\n  #\n  #   when *[:a, :b]\n  def all_literals_when? exp\n    if array? exp[1] # pretty sure this is always true\n      all_literals? exp[1] or # simple list, not actually array\n        (splat_array? exp[1][1] and\n         all_literals? exp[1][1][1])\n    end\n  end\n\n  def process_case exp\n    if @ignore_ifs.nil?\n      @ignore_ifs = @tracker && @tracker.options[:ignore_ifs]\n    end\n\n    if @ignore_ifs\n      process_default exp\n      return exp\n    end\n\n    branch_scopes = []\n    was_inside = @inside_if\n    @inside_if = true\n\n    exp[1] = process exp[1] if exp[1]\n\n    case_value = if node_type? exp[1], :lvar, :ivar, :call\n      exp[1].deep_clone\n    end\n\n    exp.each_sexp do |e|\n      if node_type? e, :when\n        scope do\n          # Process the when value for matching\n          process_default e[1]\n\n          # Moved here to avoid @branch_env being cleared out\n          # in process_default\n          # Maybe in the future don't set it to nil?\n          @branch_env = env.current\n\n          # set value of case var if possible\n          if case_value\n            if simple_when? e\n              @branch_env[case_value] = e[1][1]\n            elsif all_literals_when? e\n              @branch_env[case_value] = safe_literal(e.line + 1)\n            end\n          end\n\n          # when blocks aren't blocks, they are lists of expressions\n          process_default e\n\n          branch_scopes << env.current\n\n          @branch_env = nil\n        end\n      end\n    end\n\n    # else clause\n    if sexp? exp.last\n      scope do\n        @branch_env = env.current\n\n        process_default exp[-1]\n\n        branch_scopes << env.current\n\n        @branch_env = nil\n      end\n    end\n\n    @inside_if = was_inside\n\n    branch_scopes.each do |s|\n      merge_if_branch s\n    end\n\n    exp\n  end\n\n  def process_if_branch exp\n    if sexp? exp\n      if block? exp\n        process_default exp\n      else\n        process exp\n      end\n    end\n  end\n\n  def merge_if_branch branch_env\n    branch_env.each do |k, v|\n      next if v.nil?\n\n      current_val = env[k]\n\n      if current_val\n        unless same_value?(current_val, v)\n          if too_deep? current_val\n            # Give up branching, start over with latest value\n            env[k] = v\n          else\n            env[k] = current_val.combine(v, k.line)\n          end\n        end\n      else\n        env[k] = v\n      end\n    end\n  end\n\n  def too_deep? exp\n    @or_depth_limit >= 0 and\n    node_type? exp, :or and\n    exp.or_depth and\n    exp.or_depth >= @or_depth_limit\n  end\n\n  # Change x.send(:y, 1) to x.y(1)\n  def collapse_send_call exp, first_arg\n    # Handle try(&:id)\n    if node_type? first_arg, :block_pass\n      first_arg = first_arg[1]\n    end\n\n    return unless symbol? first_arg or string? first_arg\n    exp.method = first_arg.value.to_sym\n    args = exp.args\n    exp.pop # remove last arg\n    if args.length > 1\n      exp.arglist = args.sexp_body\n    end\n  end\n\n  #Returns a new SexpProcessor::Environment containing only instance variables.\n  #This is useful, for example, when processing views.\n  def only_ivars include_request_vars = false, lenv = nil\n    lenv ||= env\n    res = SexpProcessor::Environment.new\n\n    if include_request_vars\n      lenv.all.each do |k, v|\n        #TODO Why would this have nil values?\n        if (k.node_type == :ivar or request_value? k) and not v.nil?\n          res[k] = v.dup\n        end\n      end\n    else\n      lenv.all.each do |k, v|\n        #TODO Why would this have nil values?\n        if k.node_type == :ivar and not v.nil?\n          res[k] = v.dup\n        end\n      end\n    end\n\n    res\n  end\n\n  def only_request_vars\n    res = SexpProcessor::Environment.new\n\n    env.all.each do |k, v|\n      if request_value? k and not v.nil?\n        res[k] = v.dup\n      end\n    end\n\n    res\n  end\n\n  def get_call_value call\n    method_name = call.method\n\n    #Look for helper methods and see if we can get a return value\n    if found_method = tracker.find_method(method_name, @current_class)\n      helper = found_method.src\n\n      if sexp? helper\n        value = process_helper_method helper, call.args\n        value.line(call.line)\n        return value\n      else\n        raise \"Unexpected value for method: #{found_method}\"\n      end\n    else\n      call\n    end\n  end\n\n  def process_helper_method method_exp, args\n    method_name = method_exp.method_name\n    Brakeman.debug \"Processing method #{method_name}\"\n\n    info = @helper_method_info[method_name]\n\n    #If method uses instance variables, then include those and request\n    #variables (params, etc) in the method environment. Otherwise,\n    #only include request variables.\n    if info[:uses_ivars]\n      meth_env = only_ivars(:include_request_vars)\n    else\n      meth_env = only_request_vars\n    end\n\n    #Add arguments to method environment\n    assign_args method_exp, args, meth_env\n\n\n    #Find return values if method does not depend on environment/args\n    values = @helper_method_cache[method_name]\n\n    unless values\n      #Serialize environment for cache key\n      meth_values = meth_env.instance_variable_get(:@env).to_a\n      meth_values.sort!\n      meth_values = meth_values.to_s\n\n      digest = Digest::SHA1.new.update(meth_values << method_name.to_s).to_s.to_sym\n\n      values = @helper_method_cache[digest]\n    end\n\n    if values\n      #Use values from cache\n      values[:ivar_values].each do |var, val|\n        env[var] = val\n      end\n\n      values[:return_value]\n    else\n      #Find return value for method\n      frv = Brakeman::FindReturnValue.new\n      value = frv.get_return_value(method_exp.body_list, meth_env)\n\n      ivars = {}\n\n      only_ivars(false, meth_env).all.each do |var, val|\n        env[var] = val\n        ivars[var] = val\n      end\n\n      if not frv.uses_ivars? and args.length == 0\n        #Store return value without ivars and args if they are not used\n        @helper_method_cache[method_exp.method_name] = { :return_value => value, :ivar_values => ivars }\n      else\n        @helper_method_cache[digest] = { :return_value => value, :ivar_values => ivars }\n      end\n\n      #Store information about method, just ivar usage for now\n      @helper_method_info[method_name] = { :uses_ivars => frv.uses_ivars? }\n\n      value\n    end\n  end\n\n  def assign_args method_exp, args, meth_env = SexpProcessor::Environment.new\n    formal_args = method_exp.formal_args\n\n    formal_args.each_with_index do |arg, index|\n      next if index == 0\n\n      if arg.is_a? Symbol and sexp? args[index - 1]\n        meth_env[Sexp.new(:lvar, arg)] = args[index - 1]\n      end\n    end\n\n    meth_env\n  end\n\n  #Finds the inner most call target which is not the target of a call to <<\n  def find_push_target exp\n    if call? exp and exp.method == :<<\n      find_push_target exp.target\n    else\n      exp\n    end\n  end\n\n  def duplicate? exp\n    @exp_context[0..-2].reverse_each do |e|\n      return true if exp == e\n    end\n\n    false\n  end\n\n  def find_method *args\n    nil\n  end\n\n  #Return true if lhs == rhs or lhs is an or expression and\n  #rhs is one of its values\n  def same_value? lhs, rhs\n    if lhs == rhs\n      true\n    elsif node_type? lhs, :or\n      lhs.rhs == rhs or lhs.lhs == rhs\n    else\n      false\n    end\n  end\n\n  def self_assign? var, value\n    self_assign_var?(var, value) or self_assign_target?(var, value)\n  end\n\n  #Return true if for x += blah or @x += blah\n  def self_assign_var? var, value\n    call? value and\n    value.method == :+ and\n    node_type? value.target, :lvar, :ivar and\n    value.target.value == var\n  end\n\n  #Return true for x = x.blah\n  def self_assign_target? var, value\n    target = top_target(value)\n\n    if node_type? target, :lvar, :ivar\n      target = target.value\n    end\n\n    var == target\n  end\n\n  #Returns last non-nil target in a call chain\n  def top_target exp, last = nil\n    if call? exp\n      top_target exp.target, exp\n    elsif node_type? exp, :iter\n      top_target exp.block_call, last\n    else\n      exp || last\n    end\n  end\n\n  def value_from_if exp\n    if block? exp.else_clause or block? exp.then_clause\n      #If either clause is more than a single expression, just use entire\n      #if expression for now\n      exp\n    elsif exp.else_clause.nil?\n      exp.then_clause\n    elsif exp.then_clause.nil?\n      exp.else_clause\n    else\n      condition = exp.condition\n\n      if true? condition\n        exp.then_clause\n      elsif false? condition\n        exp.else_clause\n      else\n        exp.then_clause.combine(exp.else_clause, exp.line)\n      end\n    end\n  end\n\n  def value_from_case exp\n    result = []\n\n    exp.each do |e|\n      if node_type? e, :when\n        result << e.last\n      end\n    end\n\n    result << exp.last if exp.last # else\n\n    result.reduce do |c, e|\n      if c.nil?\n        e\n      elsif node_type? e, :if\n        c.combine(value_from_if e)\n      elsif raise? e\n        c # ignore exceptions\n      elsif e\n        c.combine e\n      else # when e is nil\n        c\n      end\n    end\n  end\n\n  def raise? exp\n    call? exp and exp.method == :raise\n  end\n\n  STRING_NEW = s(:call, s(:const, :String), :new)\n\n  # String.new ?\n  def new_string? exp\n    exp == STRING_NEW\n  end\n\n  #Set variable to given value.\n  #Creates \"branched\" versions of values when appropriate.\n  #Avoids creating multiple branched versions inside same\n  #if branch.\n  def set_value var, value\n    if node_type? value, :if\n      value = value_from_if(value)\n    elsif node_type? value, :case\n      value = value_from_case(value)\n    end\n\n    if @ignore_ifs or not @inside_if\n      if @meth_env and node_type? var, :ivar and env[var].nil?\n        @meth_env[var] = value\n      else\n        env[var] = value\n      end\n    elsif env.current[var]\n      env.current[var] = value\n    elsif @branch_env and @branch_env[var]\n      @branch_env[var] = value\n    elsif @branch_env and @meth_env and node_type? var, :ivar\n      @branch_env[var] = value\n    else\n      env.current[var] = value\n    end\n  end\n\n  #If possible, distribute operation over both sides of an or.\n  #For example,\n  #\n  #    (1 or 2) * 5\n  #\n  #Becomes\n  #\n  #    (5 or 10)\n  #\n  #Only works for strings and numbers right now.\n  def process_or_simple_operation exp\n    arg = exp.first_arg\n    return nil unless string? arg or number? arg\n\n    target = exp.target\n    lhs = process_or_target(target.lhs, exp.dup)\n    rhs = process_or_target(target.rhs, exp.dup)\n\n    if lhs and rhs\n      if same_value? lhs, rhs\n        lhs\n      else\n        exp.target.lhs = lhs\n        exp.target.rhs = rhs\n        exp.target\n      end\n    else\n      nil\n    end\n  end\n\n  def process_or_target value, copy\n    if string? value or number? value\n      copy.target = value\n      process copy\n    else\n      false\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/base_processor.rb",
    "content": "require 'brakeman/processors/lib/processor_helper'\nrequire 'brakeman/processors/lib/safe_call_helper'\nrequire 'brakeman/util'\n\n#Base processor for most processors.\nclass Brakeman::BaseProcessor < Brakeman::SexpProcessor\n  include Brakeman::ProcessorHelper\n  include Brakeman::SafeCallHelper\n  include Brakeman::Util\n\n  IGNORE = Sexp.new(:ignore).line(0)\n\n  #Return a new Processor.\n  def initialize tracker\n    super()\n    @last = nil\n    @tracker = tracker\n    @app_tree = tracker.app_tree if tracker\n    @current_template = @current_module = @current_class = @current_method = @current_file = nil\n  end\n\n  def process_file exp, current_file\n    @current_file = current_file\n    process exp\n  end\n\n  def ignore\n    IGNORE\n  end\n\n  #Process a new scope. Removes expressions that are set to nil.\n  def process_scope exp\n    #NOPE?\n  end\n\n  #Default processing.\n  def process_default exp\n    exp = exp.dup\n\n    exp.each_with_index do |e, i|\n      exp[i] = process e if sexp? e and not e.empty?\n    end\n\n    exp\n  end\n\n  #Process an if statement.\n  def process_if exp\n    exp = exp.dup\n    condition = exp[1] = process exp.condition\n\n    if true? condition\n      exp[2] = process exp.then_clause if exp.then_clause\n      exp[3] = nil\n    elsif false? condition\n      exp[2] = nil\n      exp[3] = process exp.else_clause if exp.else_clause\n    else\n      exp[2] = process exp.then_clause if exp.then_clause\n      exp[3] = process exp.else_clause if exp.else_clause\n    end\n\n    exp\n  end\n\n  #Processes calls with blocks.\n  #\n  #s(:iter, CALL, {:lasgn|:masgn}, BLOCK)\n  def process_iter exp\n    exp = exp.dup\n    call = process exp.block_call\n    #deal with assignments somehow\n    if exp.block\n      block = process exp.block\n      block = nil if block.empty?\n    else\n      block = nil\n    end\n\n    call = Sexp.new(:iter, call, exp.block_args, block).compact\n    call.line(exp.line)\n    call\n  end\n\n  #String with interpolation.\n  def process_dstr exp\n    exp = exp.dup\n    exp.shift\n    exp.map! do |e|\n      if e.is_a? String\n        e\n      else\n        res = process e\n        if res.empty?\n          nil\n        else\n          res\n        end\n      end\n    end.compact!\n\n    exp.unshift :dstr\n  end\n\n  #Processes a block. Changes Sexp node type to :rlist\n  def process_block exp\n    exp = exp.dup\n    exp.shift\n\n    exp.map! do |e|\n      process e\n    end\n\n    exp.unshift :rlist\n  end\n\n  alias process_rlist process_block\n\n  #Processes the inside of an interpolated String.\n  def process_evstr exp\n    exp = exp.dup\n    if exp[1]\n      exp[1] = process exp[1]\n    end\n\n    exp\n  end\n\n  #Processes a hash\n  def process_hash exp\n    exp = exp.dup\n    exp.shift\n    exp.map! do |e|\n      if sexp? e\n        process e\n      else\n        e\n      end\n    end\n\n    exp.unshift :hash\n  end\n\n  #Processes the values in an argument list\n  def process_arglist exp\n    exp = exp.dup\n    exp.shift\n    exp.map! do |e|\n      process e\n    end\n\n    exp.unshift :arglist\n  end\n\n  #Processes a local assignment\n  def process_lasgn exp\n    exp = exp.dup\n    exp.rhs = process exp.rhs\n    exp\n  end\n\n  alias :process_iasgn :process_lasgn\n\n  #Processes an instance variable assignment\n  def process_iasgn exp\n    exp = exp.dup\n    exp.rhs = process exp.rhs\n    exp\n  end\n\n  #Processes an attribute assignment, which can be either x.y = 1 or x[:y] = 1\n  def process_attrasgn exp\n    exp = exp.dup\n    exp.target = process exp.target\n    exp.arglist = process exp.arglist\n    exp\n  end\n\n  #Ignore ignore Sexps\n  def process_ignore exp\n    exp\n  end\n\n  def process_cdecl exp\n    if @tracker\n      @tracker.add_constant exp.lhs,\n        exp.rhs,\n        :file => current_file,\n        :module => @current_module,\n        :class => @current_class,\n        :method => @current_method\n    end\n\n    exp\n  end\n\n  #Convenience method for `make_render exp, true`\n  def make_render_in_view exp\n    make_render exp, true\n  end\n\n  #Generates :render node from call to render.\n  def make_render exp, in_view = false \n    render_type, value, rest = find_render_type exp, in_view\n    rest = process rest\n    result = Sexp.new(:render, render_type, value, rest)\n    result.line(exp.line)\n\n    result\n  end\n\n  #Determines the type of a call to render.\n  #\n  #Possible types are:\n  #:action, :default, :file, :inline, :js, :json, :nothing, :partial,\n  #:template, :text, :update, :xml\n  #\n  #And also :layout for inside templates\n  def find_render_type call, in_view = false\n    rest = Sexp.new(:hash).line(call.line)\n    type = nil\n    value = nil\n    first_arg = call.first_arg\n\n    if call.second_arg.nil? and first_arg == Sexp.new(:lit, :update)\n      return :update, nil, Sexp.new(:arglist, *call.args[0..-2]) #TODO HUH?\n    end\n\n    #Look for render :action, ... or render \"action\", ...\n    if string? first_arg or symbol? first_arg\n      if @current_template and @tracker.options[:rails3]\n        type = :partial\n        value = first_arg\n      else\n        type = :action\n        value = first_arg\n      end\n    elsif first_arg.is_a? Symbol or first_arg.is_a? String\n      type = :action\n      value = Sexp.new(:lit, first_arg.to_sym).line(call.line)\n    elsif first_arg.nil?\n      type = :default\n    elsif not hash? first_arg\n      # Maybe do partial if in view?\n      type = :action\n      value = first_arg\n    end\n\n    types_in_hash = Set[:action, :file, :inline, :js, :json, :nothing, :partial, :template, :text, :update, :xml]\n\n    #render :layout => \"blah\" means something else when in a template\n    if in_view\n      types_in_hash << :layout\n    end\n\n    last_arg = call.last_arg\n\n    #Look for \"type\" of render in options hash\n    #For example, render :file => \"blah\"\n    if hash? last_arg\n      hash_iterate(last_arg) do |key, val|\n        if symbol? key and types_in_hash.include? key.value\n          type = key.value\n          value = val\n        else  \n          rest << key << val\n        end\n      end\n    end\n\n    type ||= :default\n    value ||= :default\n\n    if type == :inline and string? value and not hash_access(rest, :type)\n      value, rest = make_inline_render(value, rest)\n    end\n\n    return type, value, rest\n  end\n\n  def make_inline_render value, options\n    require 'brakeman/parsers/template_parser'\n\n    class_or_module = (@current_class || @current_module)\n\n    class_or_module = if class_or_module.nil?\n                        \"Unknown\"\n                      else\n                        class_or_module.name\n                      end\n\n    template_name = \"#@current_method/inline@#{value.line}:#{class_or_module}\".to_sym\n    type, ast = Brakeman::TemplateParser.parse_inline_erb(@tracker, value.value)\n    ast = ast.deep_clone(value.line)\n    @tracker.processor.process_template(template_name, ast, type, nil, @current_file)\n    @tracker.processor.process_template_alias(@tracker.templates[template_name])\n\n    return s(:lit, template_name).line(value.line), options\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/config_processor.rb",
    "content": "require 'brakeman/processors/base_processor'\nrequire 'brakeman/processors/alias_processor'\nrequire 'brakeman/processors/lib/rails4_config_processor.rb'\nrequire 'brakeman/processors/lib/rails3_config_processor.rb'\nrequire 'brakeman/processors/lib/rails2_config_processor.rb'\n\nclass Brakeman::ConfigProcessor\n  def self.new tracker\n    if tracker.options[:rails4]\n      Brakeman::Rails4ConfigProcessor.new tracker\n    elsif tracker.options[:rails3]\n      Brakeman::Rails3ConfigProcessor.new tracker\n    else\n      Brakeman::Rails2ConfigProcessor.new tracker\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/controller_alias_processor.rb",
    "content": "require 'brakeman/processors/alias_processor'\nrequire 'brakeman/processors/lib/render_helper'\nrequire 'brakeman/processors/lib/render_path'\nrequire 'brakeman/processors/lib/find_return_value'\n\n#Processes aliasing in controllers, but includes following\n#renders in routes and putting variables into templates\nclass Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor\n  include Brakeman::RenderHelper\n\n  #If only_method is specified, only that method will be processed,\n  #other methods will be skipped.\n  #This is for rescanning just a single action.\n  def initialize tracker, only_method = nil\n    super tracker\n    @app_tree = tracker.app_tree\n    @only_method = only_method\n    @rendered = false\n    @current_class = @current_module = @current_method = nil\n    @method_cache = {} #Cache method lookups\n  end\n\n  def process_controller name, src, current_file\n    if not node_type? src, :class\n      Brakeman.debug \"#{name} is not a class, it's a #{src.node_type}\"\n      return\n    else\n      @current_class = name\n      @current_file = @app_tree.file_path(current_file)\n\n      process_default src\n\n      process_mixins\n    end\n  end\n\n  #Process modules mixed into the controller, in case they contain actions.\n  def process_mixins\n    controller = @tracker.controllers[@current_class]\n    original_file = @current_file\n\n    controller.includes.each do |i|\n      mixin = @tracker.libs[i]\n\n      next unless mixin\n\n      #Process methods in alphabetical order for consistency\n      methods = mixin.methods_public.keys.map { |n| n.to_s }.sort.map { |n| n.to_sym }\n\n      methods.each do |name|\n        #Need to process the method like it was in a controller in order\n        #to get the renders set\n        processor = Brakeman::ControllerProcessor.new(@tracker, mixin.file)\n        method = mixin.get_method(name).src.deep_clone\n\n        if node_type? method, :defn\n          method = processor.process_defn method\n        else\n          #Should be a defn, but this will catch other cases\n          method = processor.process method\n        end\n\n        @current_file = mixin.file\n        #Then process it like any other method in the controller\n        process method\n      end\n    end\n  ensure\n    @current_file = original_file\n  end\n\n  #Skip it, must be an inner class\n  def process_class exp\n    exp\n  end\n\n  #Processes a method definition, which may include\n  #processing any rendered templates.\n  def process_defn exp\n    meth_name = exp.method_name\n\n    Brakeman.debug \"Processing #{@current_class}##{meth_name}\"\n\n    #Skip if instructed to only process a specific method\n    #(but don't skip if this method was called from elsewhere)\n    return exp if @current_method.nil? and @only_method and @only_method != meth_name\n\n    is_route = route? meth_name\n    other_method = @current_method\n    @current_method = meth_name\n    @rendered = false if is_route\n\n    meth_env do\n      if is_route\n        before_filter_list(@current_method, @current_class).each do |f|\n          process_before_filter f\n        end\n      end\n\n      process_all exp.body\n\n      if is_route and not @rendered\n        process_default_render exp\n      end\n    end\n\n    @current_method = other_method\n    exp\n  end\n\n  #Look for calls to head()\n  def process_call exp\n    exp = super\n    return exp unless call? exp\n\n    method = exp.method\n\n    if method == :head\n      @rendered = true\n    elsif exp.target.nil? and method == :template_exists?\n      env[exp.first_arg] = Sexp.new(:lit, :\"brakeman:existing_template\")\n    elsif @tracker.options[:interprocedural] and\n      @current_method and (exp.target.nil? or exp.target.node_type == :self)\n\n      exp = get_call_value(exp)\n    end\n\n    exp\n  end\n\n  #Check for +respond_to+\n  def process_iter exp\n    super\n\n    if call? exp.block_call and exp.block_call.method == :respond_to\n      @rendered = true\n    end\n\n    exp\n  end\n\n  #Processes a call to a before filter.\n  #Basically, adds any instance variable assignments to the environment.\n  #TODO: method arguments?\n  def process_before_filter name\n    filter = tracker.find_method name, @current_class\n\n    if filter.nil?\n      Brakeman.debug \"Could not find filter #{name}\"\n      return\n    end\n\n    method = filter.src\n\n    if ivars = @tracker.filter_cache[[filter.owner, name]]\n      ivars.each do |variable, value|\n        env[variable] = value\n      end\n    else\n      processor = Brakeman::AliasProcessor.new @tracker\n      processor.process_safely(method.body_list, only_ivars(:include_request_vars))\n\n      ivars = processor.only_ivars(:include_request_vars).all\n\n      @tracker.filter_cache[[filter.owner, name]] = ivars\n\n      ivars.each do |variable, value|\n        env[variable] = value\n      end\n    end\n  end\n\n  #Processes the default template for the current action\n  def process_default_render exp\n    process_layout\n    process_template template_name, nil, nil, nil\n  end\n\n  #Process template and add the current class and method name as called_from info\n  def process_template name, args, _, line\n    # If line is null, assume implicit render and set the end of the action\n    # method as the line number\n    if line.nil? and controller = @tracker.controllers[@current_class]\n      if meth = controller.get_method(@current_method)\n        if line = meth.src && meth.src.last && meth.src.last.line\n          line += 1\n        else\n          line = 1\n        end\n      end\n    end\n\n    render_path = Brakeman::RenderPath.new.add_controller_render(@current_class, @current_method, line, @current_file)\n    super name, args, render_path, line\n  end\n\n  #Turns a method name into a template name\n  def template_name name = nil\n    name ||= @current_method\n    name = name.to_s\n    if name.include? \"/\"\n      name\n    else\n      controller = @current_class.to_s.gsub(\"Controller\", \"\")\n      controller.gsub!(\"::\", \"/\")\n      underscore(controller + \"/\" + name.to_s)\n    end\n  end\n\n  #Determines default layout name\n  def layout_name\n    controller = @tracker.controllers[@current_class]\n\n    return controller.layout if controller.layout\n    return false if controller.layout == false\n\n    app_controller = @tracker.controllers[:ApplicationController]\n\n    return app_controller.layout if app_controller and app_controller.layout\n\n    nil\n  end\n\n  #Returns true if the given method name is also a route\n  def route? method\n    if @tracker.routes[:allow_all_actions] or @tracker.options[:assume_all_routes]\n      true\n    else\n      routes = @tracker.routes[@current_class]\n      routes and (routes.include? :allow_all_actions or routes.include? method)\n    end\n  end\n\n  #Get list of filters, including those that are inherited\n  def before_filter_list method, klass\n    controller = @tracker.controllers[klass]\n\n    if controller\n      controller.before_filter_list self, method\n    else\n      []\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/controller_processor.rb",
    "content": "require 'brakeman/processors/base_processor'\nrequire 'brakeman/processors/lib/module_helper'\nrequire 'brakeman/tracker/controller'\n\n#Processes controller. Results are put in tracker.controllers\nclass Brakeman::ControllerProcessor < Brakeman::BaseProcessor\n  include Brakeman::ModuleHelper\n\n  FORMAT_HTML = Sexp.new(:call, Sexp.new(:lvar, :format), :html)\n\n  def initialize tracker, current_file = nil\n    super(tracker)\n    @visibility = :public\n    @current_file = current_file\n    @concerns = Set.new\n  end\n\n  #Use this method to process a Controller\n  def process_controller src, current_file = @current_file\n    @current_file = current_file\n    process src\n  end\n\n  #s(:class, NAME, PARENT, s(:scope ...))\n  def process_class exp\n    name = class_name(exp.class_name)\n    parent = class_name(exp.parent_name)\n\n    #If inside a real controller, treat any other classes as libraries.\n    #But if not inside a controller already, then the class may include\n    #a real controller, so we can't take this shortcut.\n    if @current_class and @current_class.name.to_s.end_with? \"Controller\"\n      Brakeman.debug \"Treating inner class as library: #{name}\"\n      Brakeman::LibraryProcessor.new(@tracker).process_library exp, @current_file\n      return exp\n    end\n\n    if not name.to_s.end_with? \"Controller\"\n      Brakeman.debug \"Adding noncontroller as library: #{name}\"\n      #Set the class to be a module in order to get the right namespacing.\n      #Add class to libraries, in case it is needed later (e.g. it's used\n      #as a parent class for a controller.)\n      #However, still want to process it in this class, so have to set\n      #@current_class to this not-really-a-controller thing.\n      process_module exp, parent\n\n      return exp\n    end\n\n    handle_class(exp, @tracker.controllers, Brakeman::Controller) do\n      set_layout_name\n    end\n\n    exp\n  end\n\n  def process_module exp, parent = nil\n    handle_module exp, Brakeman::Controller, parent\n  end\n\n  def process_concern concern_name\n    return unless @current_class\n\n    if mod = @tracker.find_class(concern_name)\n      if mod.options[:included] and not @concerns.include? concern_name\n        @concerns << concern_name\n        process mod.options[:included].deep_clone\n      end\n    end\n  end\n\n  #Look for specific calls inside the controller\n  def process_call exp\n    return exp if process_call_defn? exp\n\n    target = exp.target\n    if sexp? target\n      target = process target\n    end\n\n    method = exp.method\n    first_arg = exp.first_arg\n    last_arg = exp.last_arg\n\n    #Methods called inside class definition\n    #like attr_* and other settings\n    if @current_method.nil? and target.nil? and @current_class\n      if first_arg.nil? #No args\n        case method\n        when :private, :protected, :public\n          @visibility = method\n        when :protect_from_forgery\n          @current_class.options[:protect_from_forgery] = true\n        else\n          #??\n        end\n      else\n        case method\n        when :include\n          if @current_class\n            concern = class_name(first_arg)\n            @current_class.add_include concern\n            process_concern concern\n          end\n        when :before_filter, :append_before_filter, :before_action, :append_before_action\n          if node_type? exp.first_arg, :iter\n            add_lambda_filter exp\n          else\n            @current_class.add_before_filter exp\n          end\n        when :prepend_before_filter, :prepend_before_action\n          if node_type? exp.first_arg, :iter\n            add_lambda_filter exp\n          else\n            @current_class.prepend_before_filter exp\n          end\n        when :skip_before_filter, :skip_filter, :skip_before_action, :skip_action_callback\n          @current_class.skip_filter exp\n        when :layout\n          if string? last_arg\n            #layout \"some_layout\"\n\n            name = last_arg.value.to_s\n            if @app_tree.layout_exists?(name)\n              @current_class.layout = \"layouts/#{name}\"\n            else\n              Brakeman.debug \"Layout not found: #{name}\"\n            end\n          elsif node_type? last_arg, :nil, :false\n            #layout :false or layout nil\n            @current_class.layout = false\n          end\n        else\n          @current_class.add_option method, exp\n        end\n      end\n\n      exp\n    elsif target == nil and method == :render\n      make_render exp\n    elsif exp == FORMAT_HTML and context[1] != :iter\n      #This is an empty call to\n      # format.html\n      #Which renders the default template if no arguments\n      #Need to make more generic, though.\n      call = Sexp.new :render, :default, @current_method\n      call.line(exp.line)\n      call\n    else\n      call = make_call target, method, process_all!(exp.args)\n      call.line(exp.line)\n      call\n    end\n  end\n\n  #Look for before_filters and add fake ones if necessary\n  def process_iter exp\n    if @current_method.nil? and call? exp.block_call\n      block_call_name = exp.block_call.method\n\n      if block_call_name == :before_filter  or block_call_name == :before_action\n        add_fake_filter exp\n      else\n        super\n      end\n    else\n      super\n    end\n  end\n\n  #Sets default layout for renders inside Controller\n  def set_layout_name\n    return if @current_class.layout\n\n    name = underscore(@current_class.name.to_s.split(\"::\")[-1].gsub(\"Controller\", ''))\n\n    #There is a layout for this Controller\n    if @app_tree.layout_exists?(name)\n      @current_class.layout = \"layouts/#{name}\"\n    end\n  end\n\n  #This is to handle before_filter do |controller| ... end\n  #\n  #We build a new method and process that the same way as usual\n  #methods and filters.\n  def add_fake_filter exp\n    unless @current_class\n      Brakeman.debug \"Skipping before_filter outside controller: #{exp}\"\n      return exp\n    end\n\n    filter_name = (\"fake_filter\" + rand.to_s[/\\d+$/]).to_sym\n    args = exp.block_call.arglist\n    args.insert(1, Sexp.new(:lit, filter_name).line(exp.line))\n    before_filter_call = make_call(nil, :before_filter, args).line(exp.line)\n\n    if exp.block_args.length > 1\n      block_variable = exp.block_args[1]\n    else\n      block_variable = :temp\n    end\n\n    if node_type? exp.block, :block\n      block_inner = exp.block.sexp_body\n    else\n      block_inner = [exp.block]\n    end\n\n    #Build Sexp for filter method\n    body = Sexp.new(:lasgn,\n                    block_variable,\n                    Sexp.new(:call, Sexp.new(:const, @current_class.name).line(exp.line), :new).line(exp.line)).line(exp.line)\n\n    filter_method = Sexp.new(:defn, filter_name, Sexp.new(:args).line(exp.line), body).concat(block_inner).line(exp.line)\n\n    vis = @visibility\n    @visibility = :private\n    process_defn filter_method\n    @visibility = vis\n    process before_filter_call\n    exp\n  end\n\n  def add_lambda_filter exp\n    # Convert into regular block call\n    e = exp.dup\n    lambda_node = e.delete_at(3)\n    result = Sexp.new(:iter, e).line(e.line)\n\n    # Add block arguments\n    if node_type? lambda_node[2], :args\n      result << lambda_node[2].last\n    else\n      result << s(:args)\n    end\n\n    # Add block contents\n    if sexp? lambda_node[3]\n      result << lambda_node[3]\n    end\n\n    add_fake_filter result\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/erb_template_processor.rb",
    "content": "require 'brakeman/processors/template_processor'\n\n#Processes ERB templates\n#(those ending in .html.erb or .rthml).\nclass Brakeman::ErbTemplateProcessor < Brakeman::TemplateProcessor\n  \n  #s(:call, TARGET, :method, ARGS)\n  def process_call exp\n    target = exp.target\n    if sexp? target\n      target = process target\n    end\n    method = exp.method\n    \n    #_erbout is the default output variable for erb\n    if node_type? target, :lvar and target.value == :_erbout\n      if method == :concat or method == :<<\n        @inside_concat = true\n        exp.arglist = process(exp.arglist)\n        @inside_concat = false\n\n        if exp.second_arg\n          raise \"Did not expect more than a single argument to _erbout.concat\"\n        end\n\n        arg = normalize_output(exp.first_arg)\n\n        if arg.node_type == :str #ignore plain strings\n          ignore\n        else\n          add_output arg\n        end\n      elsif method == :force_encoding\n        ignore\n      else\n        abort \"Unrecognized action on _erbout: #{method}\"\n      end\n    elsif target == nil and method == :render\n      exp.arglist = process(exp.arglist)\n      make_render_in_view exp\n    else\n      exp.target = target\n      exp.arglist = process(exp.arglist)\n      exp\n    end\n  end\n\n  #Process block, removing irrelevant expressions\n  def process_block exp\n    exp = exp.dup\n    exp.shift\n    if @inside_concat\n      @inside_concat = false\n      exp[0..-2].each do |e|\n        process e\n      end\n      @inside_concat = true\n      process exp.last\n    else\n      exp.map! do |e|\n        res = process e\n        if res.empty? or res == ignore\n          nil\n        elsif node_type?(res, :lvar) and res.value == :_erbout\n          nil\n\n        else\n          res\n        end\n      end\n      block = Sexp.new(:rlist).concat(exp).compact\n      block.line(exp.line)\n      block\n    end\n  end\n\nend\n"
  },
  {
    "path": "lib/brakeman/processors/erubi_template_procesor.rb",
    "content": "require 'brakeman/processors/template_processor'\n\n#Processes ERB templates using Erubi instead of erb.\nclass Brakeman::ErubiTemplateProcessor < Brakeman::TemplateProcessor\n\n  #s(:call, TARGET, :method, ARGS)\n  def process_call exp\n    target = exp.target\n    if sexp? target\n      target = process target\n    end\n\n    exp.target = target\n    exp.arglist = process exp.arglist\n    method = exp.method\n\n    #_buf is the default output variable for Erubi\n    if node_type?(target, :lvar, :ivar) and (target.value == :_buf or target.value == :@output_buffer)\n      if method == :<< or method == :safe_concat\n\n        arg = normalize_output(exp.first_arg)\n\n        if arg.node_type == :str #ignore plain strings\n          ignore\n        elsif node_type? target, :ivar and target.value == :@output_buffer\n          add_escaped_output arg\n        else\n          add_output arg\n        end\n      elsif method == :to_s\n        ignore\n      else\n        abort \"Unrecognized action on buffer: #{method}\"\n      end\n    elsif target == nil and method == :render\n      make_render_in_view exp\n    else\n      exp\n    end\n  end\n\n  #Process blocks, ignoring :ignore exps\n  def process_block exp\n    exp = exp.dup\n    exp.shift\n    exp.map! do |e|\n      res = process e\n      if res.empty? or res == ignore\n        nil\n      else\n        res\n      end\n    end\n    block = Sexp.new(:rlist).concat(exp).compact\n    block.line(exp.line)\n    block\n  end\n\n  #Look for assignments to output buffer that look like this:\n  #  @output_buffer.append = some_output\n  #  @output_buffer.safe_append = some_output\n  #  @output_buffer.safe_expr_append = some_output\n  def process_attrasgn exp\n    if exp.target.node_type == :ivar and exp.target.value == :@output_buffer\n      if append_method?(exp.method)\n        exp.first_arg = process(exp.first_arg)\n        arg = normalize_output(exp.first_arg)\n\n        if arg.node_type == :str\n          ignore\n        elsif safe_append_method?(exp.method)\n          add_output arg\n        else\n          add_escaped_output arg\n        end\n      else\n        super\n      end\n    else\n      super\n    end\n  end\n\n  private\n  def append_method?(method)\n    method == :append= || safe_append_method?(method)\n  end\n\n  def safe_append_method?(method)\n    method == :safe_append= || method == :safe_expr_append=\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/gem_processor.rb",
    "content": "require 'brakeman/processors/lib/basic_processor'\n\n#Processes Gemfile and Gemfile.lock\nclass Brakeman::GemProcessor < Brakeman::BasicProcessor\n\n  def initialize *args\n    super\n    @gem_name_version = /^\\s*([-_+.A-Za-z0-9]+) \\((\\w(\\.\\w+)*)\\)/\n    @ruby_version = /^\\s+ruby (\\d\\.\\d.\\d+)/\n  end\n\n  def process_gems gem_files\n    @gem_files = gem_files\n    @gemfile = gem_files[:gemfile] && gem_files[:gemfile][:file]\n    @gemspec = gem_files[:gemspec] && gem_files[:gemspec][:file]\n\n\n    if @gemspec\n      process gem_files[:gemspec][:src]\n    end\n\n    if @gemfile\n      process gem_files[:gemfile][:src]\n    end\n\n    if gem_files[:gemlock]\n      process_gem_lock\n    end\n\n    @tracker.config.set_rails_version\n  end\n\n  # Known issue: Brakeman does not yet support `gem` calls with multiple\n  # \"version requirements\". Consider the following example from the ruby docs:\n  #\n  #     gem 'rake', '>= 1.1.a', '< 2'\n  #\n  # We are assuming that `second_arg` (eg. '>= 1.1.a') is the only requirement.\n  # Perhaps we should instantiate an array of `::Gem::Requirement`s or even a\n  # `::Gem::Dependency` and pass that to `Tracker::Config#add_gem`?\n  def process_call exp\n    if exp.target == nil\n      if exp.method == :gem\n        gem_name = exp.first_arg\n        return exp unless string? gem_name\n\n        gem_version = exp.second_arg\n\n        version = if string? gem_version\n                    gem_version.value\n                  else\n                    nil\n                  end\n\n        @tracker.config.add_gem gem_name.value, version, @gemfile, exp.line\n      elsif exp.method == :ruby\n        version = exp.first_arg\n        if string? version\n          @tracker.config.set_ruby_version version.value, @gemfile, exp.line\n        end\n      end\n    elsif @inside_gemspec and exp.method == :add_dependency\n      if string? exp.first_arg and string? exp.second_arg\n        @tracker.config.add_gem exp.first_arg.value, exp.second_arg.value, @gemspec, exp.line\n      end\n    end\n\n    exp\n  end\n\n  GEM_SPEC = s(:colon2, s(:const, :Gem), :Specification)\n\n  def process_iter exp\n    if exp.block_call.target == GEM_SPEC and exp.block_call.method == :new\n      @inside_gemspec = true\n      process exp.block if sexp? exp.block\n\n      exp\n    else\n      process_default exp\n    end\n  ensure\n    @inside_gemspec = false\n  end\n\n  def process_gem_lock\n    line_num = 1\n    file = @gem_files[:gemlock][:file]\n    @gem_files[:gemlock][:src].each_line do |line|\n      set_gem_version_and_file line, file, line_num\n      line_num += 1\n    end\n  end\n\n  # Supports .rc2 but not ~>, >=, or <=\n  def set_gem_version_and_file line, file, line_num\n    if line =~ @gem_name_version\n      @tracker.config.add_gem $1, $2, file, line_num\n    elsif line =~ @ruby_version\n      @tracker.config.set_ruby_version $1, file, line_num\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/haml6_template_processor.rb",
    "content": "require 'brakeman/processors/haml_template_processor'\n\nclass Brakeman::Haml6TemplateProcessor < Brakeman::HamlTemplateProcessor\n\n  OUTPUT_BUFFER = s(:ivar, :@output_buffer)\n  HAML_UTILS = s(:colon2, s(:colon3, :Haml), :Util)\n  HAML_UTILS2 = s(:colon2, s(:const, :Haml), :Util)\n  # @output_buffer = output_buffer || ActionView::OutputBuffer.new\n  AV_SAFE_BUFFER = s(:or, s(:call, nil, :output_buffer), s(:call, s(:colon2, s(:const, :ActionView), :OutputBuffer), :new))\n  EMBEDDED_FILTER = s(:const, :BrakemanFilter)\n\n  def initialize(*)\n    super\n\n    # Because of how Haml 6 handles line breaks -\n    # we have to track where _haml_compiler variables are assigned.\n    # then change the line number of where they are output to where\n    # they are assigned.\n    #\n    # Like this:\n    #\n    #   ; _haml_compiler1 = (params[:x]; \n    #   ; ); @output_buffer.safe_concat((((::Haml::Util.escape_html_safe((_haml_compiler1))).to_s).to_s));\n    #\n    #  `_haml_compiler1` is output a line after it's assigned,\n    #  but the assignment matches the \"real\" line where it is output in the template.\n    @compiler_assigns = {}\n  end\n\n  # @output_buffer.safe_concat\n  def buffer_append? exp\n    call? exp and\n      output_buffer? exp.target and\n      exp.method == :safe_concat\n  end\n\n  def process_lasgn exp\n    if exp.lhs.match?(/_haml_compiler\\d+/)\n      @compiler_assigns[exp.lhs] = exp.rhs\n      ignore\n    else\n      exp\n    end\n  end\n\n  def process_lvar exp\n    if exp.value.match?(/_haml_compiler\\d+/)\n      exp = @compiler_assigns[exp.value] || exp\n    end\n\n    exp\n  end\n\n  def is_escaped? exp\n    return unless call? exp\n\n    html_escaped? exp or\n      javascript_escaped? exp\n  end\n\n  def javascript_escaped? call\n    # TODO: Adding here to match existing behavior for HAML,\n    # but really this is not safe and needs to be revisited\n      call.method == :j or\n        call.method == :escape_javascript\n  end\n\n  def html_escaped? call\n    (call.target == HAML_UTILS or call.target == HAML_UTILS2) and\n      (call.method == :escape_html or call.method == :escape_html_safe)\n  end\n\n  def output_buffer? exp\n    exp == OUTPUT_BUFFER or\n      exp == AV_SAFE_BUFFER\n  end\n\n  def normalize_output arg\n    arg = super(arg)\n\n    if embedded_filter? arg\n      super(arg.first_arg)\n    else\n      arg\n    end\n  end\n\n  # Handle our \"fake\" embedded filters\n  def embedded_filter? arg\n    call? arg and arg.method == :render and arg.target == EMBEDDED_FILTER\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/haml_template_processor.rb",
    "content": "require 'brakeman/processors/template_processor'\n\n#Processes HAML templates.\nclass Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor\n  HAMLOUT = s(:call, nil, :_hamlout)\n  HAML_BUFFER = s(:call, HAMLOUT, :buffer)\n  HAML_HELPERS = s(:colon2, s(:const, :Haml), :Helpers)\n  HAML_HELPERS2 = s(:colon2, s(:colon3, :Haml), :Helpers)\n  JAVASCRIPT_FILTER = s(:colon2, s(:colon2, s(:const, :Haml), :Filters), :Javascript)\n  COFFEE_FILTER = s(:colon2, s(:colon2, s(:const, :Haml), :Filters), :Coffee)\n  ATTRIBUTE_BUILDER = s(:colon2, s(:colon3, :Haml), :AttributeBuilder)\n\n  def initialize *args\n    super\n    @javascript = false\n  end\n\n  #Processes call, looking for template output\n  def process_call exp\n    exp = process_default exp\n\n    if buffer_append? exp\n      output = normalize_output(exp.first_arg)\n      res = get_pushed_value(output)\n    end\n\n    res or exp\n  end\n\n  # _haml_out.buffer << ...\n  def buffer_append? exp\n    call? exp and\n      exp.target == HAML_BUFFER and\n      exp.method == :<<\n  end\n\n  PRESERVE_METHODS = [:find_and_preserve, :preserve]\n\n  def find_and_preserve? exp\n    call? exp and\n      PRESERVE_METHODS.include?(exp.method) and\n      exp.first_arg\n  end\n\n  #If inside an output stream, only return the final expression\n  def process_block exp\n    exp = exp.dup\n    exp.shift\n\n    exp.map! do |e|\n      res = process e\n      if res.empty?\n        nil\n      else\n        res\n      end\n    end\n\n    Sexp.new(:rlist).concat(exp).compact\n  end\n\n  #HAML likes to put interpolated values into _hamlout.push_text\n  #but we want to handle those individually\n  def build_output_from_push_text exp, default = :output\n    if string_interp? exp\n      exp.map! do |e|\n        if sexp? e\n          if node_type? e, :evstr and e[1]\n            e = e.value\n          end\n\n          get_pushed_value e, default\n        else\n          e\n        end\n      end\n    end\n  end\n\n  ESCAPE_METHODS = [\n    :html_escape,\n    :html_escape_without_haml_xss,\n    :escape_once,\n    :escape_once_without_haml_xss\n  ]\n\n  def is_escaped? exp\n    return unless call? exp\n\n    haml_helpers? exp.target and ESCAPE_METHODS.include? exp.method\n  end\n\n  def get_pushed_value exp, default = :output\n    return exp unless sexp? exp\n\n    case exp.node_type\n    when :format\n      exp.node_type = :output\n      @current_template.add_output exp\n      exp\n    when :format_escaped\n      exp.node_type = :escaped_output\n      @current_template.add_output exp\n      exp\n    when :str, :ignore, :output, :escaped_output\n      exp\n    when :block, :rlist\n      exp.map! { |e| get_pushed_value(e, default) }\n    when :dstr\n      build_output_from_push_text(exp, default)\n    when :if\n      clauses = [get_pushed_value(exp.then_clause, default), get_pushed_value(exp.else_clause, default)].compact\n\n      if clauses.length > 1\n        s(:or, *clauses).line(exp.line)\n      else\n        clauses.first\n      end\n    when :call\n      if exp.method == :to_s or exp.method == :strip\n        get_pushed_value(exp.target, default)\n      elsif is_escaped? exp\n        get_pushed_value(exp.first_arg, :escaped_output)\n      elsif @javascript and (exp.method == :j or exp.method == :escape_javascript) # TODO: Remove - this is not safe\n        get_pushed_value(exp.first_arg, :escaped_output)\n      elsif find_and_preserve? exp or fix_textareas? exp\n        get_pushed_value(exp.first_arg, default)\n      elsif raw? exp\n        get_pushed_value(exp.first_arg, :output)\n      elsif hamlout_attributes? exp\n        ignore # ignore _hamlout.attributes calls\n      elsif exp.target.nil? and exp.method == :render\n        #Process call to render()\n        exp.arglist = process exp.arglist\n        make_render_in_view exp\n      elsif exp.method == :render_with_options\n        if exp.target == JAVASCRIPT_FILTER or exp.target == COFFEE_FILTER\n          @javascript = true\n        end\n\n        get_pushed_value(exp.first_arg, default)\n        @javascript = false\n      elsif haml_attribute_builder? exp\n        ignore # probably safe... seems escaped by default?\n      else\n        add_output exp, default\n      end\n    else\n      add_output exp, default\n    end\n  end\n\n  def haml_helpers? exp\n    # Sometimes its Haml::Helpers and\n    # sometimes its ::Haml::Helpers\n    exp == HAML_HELPERS or\n      exp == HAML_HELPERS2\n  end\n\n  def hamlout_attributes? exp\n    call? exp and\n      exp.target == HAMLOUT and\n      exp.method == :attributes\n  end\n\n  def haml_attribute_builder? exp\n    call? exp and\n      exp.target == ATTRIBUTE_BUILDER and\n      escaped_builder_method? exp\n  end\n\n  def escaped_builder_method? exp\n    case exp.method\n    when :build, :build_aria, :build_boolean, :build_data, :build_id, :escape_html\n      true? exp.first_arg\n    else\n      false\n    end\n  end\n\n  def fix_textareas? exp\n    call? exp and\n      exp.target == HAMLOUT and\n      exp.method == :fix_textareas! \n  end\n\n  def raw? exp\n    call? exp and\n      exp.method == :raw\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/basic_processor.rb",
    "content": "require 'brakeman/processors/lib/processor_helper'\nrequire 'brakeman/processors/lib/safe_call_helper'\nrequire 'brakeman/util'\n\nclass Brakeman::BasicProcessor < Brakeman::SexpProcessor\n  include Brakeman::ProcessorHelper\n  include Brakeman::SafeCallHelper\n  include Brakeman::Util\n\n  def initialize tracker\n    super()\n    @tracker = tracker\n    @current_template = @current_module = @current_class = @current_method = nil\n  end\n\n  def process_default exp\n    process_all exp\n  end\n\n  def process_if exp\n    condition = exp.condition\n\n    process condition\n\n    if true? condition\n      process exp.then_clause\n    elsif false? condition\n      process exp.else_clause\n    else\n      [exp.then_clause, exp.else_clause].compact.map do |e|\n        process e\n      end\n    end\n\n    exp\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/call_conversion_helper.rb",
    "content": "module Brakeman\n  module CallConversionHelper\n    # Join two array literals into one.\n    def join_arrays lhs, rhs, original_exp = nil\n      if array? lhs and array? rhs\n        result = Sexp.new(:array)\n        result.line(lhs.line || rhs.line || 1)\n        result.concat lhs[1..-1]\n        result.concat rhs[1..-1]\n        result\n      else\n        original_exp\n      end\n    end\n\n    STRING_LENGTH_LIMIT = 50\n\n    # Join two string literals into one.\n    def join_strings lhs, rhs, original_exp = nil\n      if string? lhs and string? rhs\n        if (lhs.value.length + rhs.value.length > STRING_LENGTH_LIMIT)\n          # Avoid gigantic strings\n          lhs\n        else\n          result = Sexp.new(:str).line(lhs.line)\n          result.value = lhs.value + rhs.value\n          result\n        end\n      elsif call? lhs and lhs.method == :+ and string? lhs.first_arg and string? rhs\n        joined = join_strings lhs.first_arg, rhs\n        lhs.first_arg = joined\n        lhs\n      elsif safe_literal? lhs or safe_literal? rhs\n        safe_literal(lhs.line)\n      else\n        original_exp\n      end\n    rescue Encoding::CompatibilityError => e\n      # If the two strings are different encodings, we can't join them.\n      Brakeman.debug e.inspect\n      original_exp\n    end\n\n    def math_op op, lhs, rhs, original_exp = nil\n      if number? lhs and number? rhs\n        if op == :/ and rhs.value == 0 and not lhs.value.is_a? Float\n          # Avoid division by zero\n          return original_exp\n        else\n          value = lhs.value.send(op, rhs.value)\n          Sexp.new(:lit, value).line(lhs.line)\n        end\n      elsif call? lhs and lhs.method == :+ and number? lhs.first_arg and number? rhs\n        # (x + 1) + 2 -> (x + 3)\n        lhs.first_arg = Sexp.new(:lit, lhs.first_arg.value + rhs.value).line(lhs.first_arg.line)\n        lhs\n      elsif safe_literal? lhs or safe_literal? rhs\n        safe_literal(lhs.line)\n      else\n        original_exp\n      end\n    end\n\n    # Process single integer access to an array.\n    #\n    # Returns the value inside the array, if possible.\n    def process_array_access array, args, original_exp = nil\n      if args.length == 1 and integer? args.first\n        index = args.first.value\n\n        #Have to do this because first element is :array and we have to skip it\n        array[1..-1][index] or original_exp\n      elsif all_literals? array\n        safe_literal(array.line)\n      else\n        original_exp\n      end\n    end\n\n    # Process hash access by returning the value associated\n    # with the given argument.\n    def process_hash_access hash, index, original_exp = nil\n      if value = hash_access(hash, index)\n        value # deep_clone?\n      elsif all_literals? hash, :hash\n        safe_literal(hash.line)\n      else\n        original_exp\n      end\n    end\n\n    # You must check the return value for `nil`s -\n    # which indicate a key could not be found.\n    def hash_values_at hash, keys\n      values = keys.map do |key|\n        process_hash_access hash, key\n      end\n\n      Sexp.new(:array).concat(values).line(hash.line)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/file_type_detector.rb",
    "content": "module Brakeman\n  class FileTypeDetector < BaseProcessor\n    def initialize\n      super(nil)\n      reset\n    end\n\n    def detect_type(file)\n      reset\n      process(file.ast)\n\n      if @file_type.nil?\n        @file_type = guess_from_path(file.path.relative)\n      end\n\n      @file_type || :lib\n    end\n\n    MODEL_CLASSES = [\n      :'ActiveRecord::Base',\n      :ApplicationRecord\n    ]\n\n    def process_class exp\n      name = class_name(exp.class_name)\n      parent = class_name(exp.parent_name)\n\n      if name.match(/Controller$/)\n        @file_type = :controller\n        return exp\n      elsif MODEL_CLASSES.include? parent\n        @file_type = :model\n        return exp\n      end\n\n      super\n    end\n\n    def guess_from_path path\n      case\n      when path.include?('app/models')\n        :model\n      when path.include?('app/controllers')\n        :controller\n      when path.include?('config/initializers')\n        :initializer\n      when path.include?('lib/')\n        :lib\n      when path.match?(%r{config/environments/(?!production\\.rb)$})\n        :skip\n      when path.match?(%r{environments/production\\.rb$})\n        :skip\n      when path.match?(%r{application\\.rb$})\n        :skip\n      when path.match?(%r{config/routes\\.rb$})\n        :skip\n      end\n    end\n\n    private\n\n    def reset\n      @file_type = nil\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/find_all_calls.rb",
    "content": "require 'brakeman/processors/lib/basic_processor'\n\nclass Brakeman::FindAllCalls < Brakeman::BasicProcessor\n  attr_reader :calls\n\n  def initialize tracker\n    super\n\n    @in_target = false\n    @processing_class = false\n    @calls = []\n    @cache = {}\n  end\n\n  #Process the given source. Provide either class and method being searched\n  #or the template. These names are used when reporting results.\n  def process_source exp, opts\n    @current_class = opts[:class]\n    @current_method = opts[:method]\n    @current_template = opts[:template]\n    @current_file = opts[:file]\n    @current_call = nil\n    @full_call = nil\n    process exp\n  end\n\n  #For whatever reason, originally the indexing of calls\n  #was performed on individual method bodies (see process_defn).\n  #This method explicitly indexes all calls everywhere given any\n  #source.\n  def process_all_source exp, opts\n    @processing_class = true\n    process_source exp, opts\n  ensure\n    @processing_class = false\n  end\n\n  #Process body of method\n  def process_defn exp\n    return exp unless @current_method or @processing_class\n\n    # 'Normal' processing assumes the method name was given\n    # as an option to `process_source` but for `process_all_source`\n    # we don't want to do that.\n    if @current_method.nil?\n      @current_method = exp.method_name\n      process_all exp.body\n      @current_method = nil\n    else\n      process_all exp.body\n    end\n\n    exp\n  end\n\n  alias process_defs process_defn\n\n  #Process body of block\n  def process_rlist exp\n    process_all exp\n  end\n\n  def process_call exp\n    @calls << create_call_hash(exp).freeze\n    exp\n  end\n\n  def process_iter exp\n    call = exp.block_call\n\n    if call.node_type == :call\n      call_hash = create_call_hash(call)\n\n      call_hash[:block] = exp.block\n      call_hash[:block_args] = exp.block_args\n      call_hash.freeze\n\n      @calls << call_hash\n\n      process exp.block\n    else\n      #Probably a :render call with block\n      process call\n      process exp.block\n    end\n\n    exp\n  end\n\n  #Calls to render() are converted to s(:render, ...) but we would\n  #like them in the call cache still for speed\n  def process_render exp\n    process_all exp\n\n    add_simple_call :render, exp\n\n    exp\n  end\n\n  #Technically, `` is call to Kernel#`\n  #But we just need them in the call cache for speed\n  def process_dxstr exp\n    process exp.last if sexp? exp.last\n\n    add_simple_call :`, exp\n\n    exp\n  end\n\n  #:\"string\" is equivalent to \"string\".to_sym\n  def process_dsym exp\n    exp.each { |arg| process arg if sexp? arg }\n\n    add_simple_call :literal_to_sym, exp\n\n    exp\n  end\n\n  # Process a dynamic regex like a call\n  def process_dregx exp\n    exp.each { |arg| process arg if sexp? arg }\n\n    add_simple_call :brakeman_regex_interp, exp\n\n    exp\n  end\n\n  #Process an assignment like a call\n  def process_attrasgn exp\n    process_call exp\n  end\n\n  private\n\n  def add_simple_call method_name, exp\n    @calls << { :target => nil,\n                :method => method_name,\n                :call => exp,\n                :nested => false,\n                :location => make_location,\n                :parent => @current_call,\n                :full_call => @full_call }.freeze\n  end\n\n  #Gets the target of a call as a Symbol\n  #if possible\n  def get_target exp, include_calls = false\n    if sexp? exp\n      case exp.node_type\n      when :ivar, :lvar, :const, :lit\n        exp.value\n      when :true, :false\n        exp[0]\n      when :colon2\n        class_name exp\n      when :self\n        @current_class || @current_module || nil\n      when :params, :session, :cookies\n        exp.node_type\n      when :call, :safe_call\n        if include_calls\n          if exp.target.nil?\n            exp.method\n          else\n            t = get_target(exp.target, :include_calls)\n            if t.is_a? Symbol\n              :\"#{t}.#{exp.method}\"\n            else\n              exp\n            end\n          end\n        else\n          exp\n        end\n      else\n        exp\n      end\n    else\n      exp\n    end\n  end\n\n  #Returns method chain as an array\n  #For example, User.human.alive.all would return [:User, :human, :alive, :all]\n  def get_chain call\n    if node_type? call, :call, :attrasgn, :safe_call, :safe_attrasgn\n      get_chain(call.target) + [call.method]\n    elsif call.nil?\n      []\n    else\n      [get_target(call)]\n    end\n  end\n\n  def make_location\n    if @current_template\n      key = [@current_template, @current_file]\n      cached = @cache[key]\n      return cached if cached\n\n      @cache[key] = { :type => :template,\n        :template => @current_template,\n        :file => @current_file }\n    else\n      key = [@current_class, @current_method, @current_file]\n      cached = @cache[key]\n      return cached if cached\n      @cache[key] = { :type => :class,\n        :class => @current_class,\n        :method => @current_method,\n        :file => @current_file }\n    end\n\n  end\n\n  #Return info hash for a call Sexp\n  def create_call_hash exp\n    target = get_target exp.target\n    target_symbol = get_target(target, :include_calls)\n\n    method = exp.method\n\n    call_hash = {\n      :target => target_symbol,\n      :method => method,\n      :call => exp,\n      :nested => @in_target,\n      :chain => get_chain(exp),\n      :location => make_location,\n      :parent => @current_call,\n      :full_call => @full_call\n    }\n\n    unless @in_target\n      @full_call = call_hash\n      call_hash[:full_call] = call_hash\n    end\n\n    # Process up the call chain\n    if call? target or node_type? target, :dxstr # need to index `` even if target of a call\n      already_in_target = @in_target\n      @in_target = true\n      process target\n      @in_target = already_in_target\n    end\n\n    # Process call arguments\n    # but add the current call as the 'parent'\n    # to any calls in the arguments\n    old_parent = @current_call\n    @current_call = call_hash\n\n    # Do not set @full_call when processing arguments\n    old_full_call = @full_call\n    @full_call = nil\n\n    process_call_args exp\n\n    @current_call = old_parent\n    @full_call = old_full_call\n\n    call_hash\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/find_call.rb",
    "content": "require 'brakeman/processors/lib/basic_processor'\n\n#Finds method calls matching the given target(s).\n#   #-- This should be deprecated --#\n#   #--  Do not use for new code  --#\n#\n#Targets/methods can be:\n#\n# - nil: matches anything, including nothing\n# - Empty array: matches nothing\n# - Symbol: matches single target/method exactly\n# - Array of symbols: matches against any of the symbols\n# - Regular expression: matches the expression\n# - Array of regular expressions: matches any of the expressions\n#\n#If a target is also the name of a class, methods called on instances\n#of that class will also be matched, in a very limited way.\n#(Any methods called on Klass.new, basically. More useful when used\n#in conjunction with AliasProcessor.)\n#\n#Examples:\n#\n# #To find any uses of this class:\n# FindCall.new :FindCall, nil\n#\n# #Find system calls without a target\n# FindCall.new [], [:system, :exec, :syscall]\n#\n# #Find all calls to length(), no matter the target\n# FindCall.new nil, :length\n#\n# #Find all calls to sub, sub!, gsub, or gsub!\n# FindCall.new nil, /^g?sub!?$/\nclass Brakeman::FindCall < Brakeman::BasicProcessor\n\n  def initialize targets, methods, tracker\n    super tracker\n    @calls = []\n    @find_targets = targets\n    @find_methods = methods\n    @current_class = nil\n    @current_method = nil\n  end\n\n  #Returns a list of results.\n  #\n  #A result looks like:\n  #\n  # s(:result, :ClassName, :method_name, s(:call, ...))\n  def matches\n    @calls\n  end\n\n  #Process the given source. Provide either class and method being searched\n  #or the template. These names are used when reporting results.\n  #\n  #Use FindCall#matches to retrieve results.\n  def process_source exp\n    process exp\n  end\n\n  #Process body of method\n  def process_defn exp\n    process_all exp.body\n  end\n\n  alias :process_defs :process_defn\n\n  #Look for matching calls and add them to results\n  def process_call exp\n    target = get_target exp.target\n    method = exp.method\n\n    process_call_args exp\n\n    if match(@find_targets, target) and match(@find_methods, method)\n      @calls << Sexp.new(:result, @current_module, @current_class, @current_method, exp).line(exp.line)\n    end\n    \n    exp\n  end\n\n  #Process an assignment like a call\n  def process_attrasgn exp\n    process_call exp\n  end\n\n  private\n\n  #Gets the target of a call as a Symbol\n  #if possible\n  def get_target exp\n    if sexp? exp\n      case exp.node_type\n      when :ivar, :lvar, :const, :lit\n        exp.value\n      when :colon2\n        class_name exp\n      else\n        exp\n      end\n    else\n      exp\n    end\n  end\n\n  #Checks if the search terms match the given item\n  def match search_terms, item\n    case search_terms\n    when Symbol\n      if search_terms == item\n        true\n      else\n        false\n      end\n    when Enumerable\n      if search_terms.empty?\n        item == nil\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/find_return_value.rb",
    "content": "require 'brakeman/processors/alias_processor'\n\n#Attempts to determine the return value of a method.\n#\n#Preferred usage:\n#\n#  Brakeman::FindReturnValue.return_value exp\nclass Brakeman::FindReturnValue\n  include Brakeman::Util\n\n  #Returns a guess at the return value of a given method or other block of code.\n  #\n  #If multiple return values are possible, returns all values in an :or Sexp.\n  def self.return_value exp, env = nil\n    self.new.get_return_value exp, env\n  end\n\n  def initialize\n    @uses_ivars = false\n    @return_values = []\n  end\n\n  def uses_ivars?\n    @uses_ivars\n  end\n\n  #Find return value of Sexp. Takes an optional starting environment.\n  def get_return_value exp, env = nil\n    process_method exp, env\n    value = make_return_value\n    value.original_line = exp.line\n    value\n  end\n\n  #Process method (or, actually, any Sexp) for return value.\n  def process_method exp, env = nil\n    exp = Brakeman::AliasProcessor.new.process_safely exp, env\n\n    find_explicit_return_values exp\n\n    if node_type? exp, :defn, :defs\n      body = exp.body\n\n      unless body.empty?\n        @return_values << last_value(body)\n      else\n        Brakeman.debug \"FindReturnValue: Empty method? #{exp.inspect}\"\n      end\n    elsif exp\n      @return_values << last_value(exp)\n    else\n       Brakeman.debug \"FindReturnValue: Given something strange? #{exp.inspect}\"\n    end\n\n    exp\n  end\n\n  #Searches expression for return statements.\n  def find_explicit_return_values exp\n    todo = [exp]\n\n    until todo.empty?\n      current = todo.shift\n\n      @uses_ivars = true if node_type? current, :ivar\n\n      if node_type? current, :return\n        @return_values << last_value(current.value) if current.value\n      elsif sexp? current\n        todo = current[1..-1].concat todo\n      end\n    end\n  end\n\n  #Determines the \"last value\" of an expression.\n  def last_value exp\n    case exp.node_type\n    when :rlist, :block, :scope, Sexp\n      last_value exp.last\n    when :if\n      then_clause = exp.then_clause\n      else_clause = exp.else_clause\n\n      if then_clause.nil? and else_clause.nil?\n        nil\n      elsif then_clause.nil?\n        last_value else_clause\n      elsif else_clause.nil?\n        last_value then_clause\n      else\n        true_branch = last_value then_clause\n        false_branch = last_value else_clause\n\n        if true_branch and false_branch\n          value = make_or(true_branch, false_branch)\n          value.original_line = value.rhs.line\n          value\n        else #Unlikely?\n          true_branch or false_branch\n        end\n      end\n    when :lasgn, :iasgn, :op_asgn_or, :attrasgn\n      last_value exp.rhs\n    when :rescue\n      values = []\n\n      exp.each_sexp do |e|\n        if node_type? e, :resbody\n          if e.last\n            values << last_value(e.last)\n          end\n        elsif sexp? e\n          values << last_value(e)\n        end\n      end\n\n      values.reject! do |v|\n        v.nil? or node_type? v, :nil\n      end\n\n      if values.length > 1\n        values.inject do |m, v|\n          make_or(m, v)\n        end\n      else\n        values.first\n      end\n    when :return\n      if exp.value\n        last_value exp.value\n      else\n        nil\n      end\n    when :nil\n      nil\n    else\n      exp.original_line = exp.line unless exp.original_line\n      exp\n    end\n  end\n\n  def make_or lhs, rhs\n    #Better checks in future\n    if lhs == rhs\n      lhs\n    else\n      Sexp.new(:or, lhs, rhs)\n    end\n  end\n\n  #Turns the array of return values into an :or Sexp\n  def make_return_value\n    @return_values.compact!\n    @return_values.uniq!\n\n    if @return_values.empty?\n      Sexp.new(:nil)\n    elsif @return_values.length == 1\n      @return_values.first\n    else\n      @return_values.reduce do |value, sexp|\n        make_or value, sexp\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/module_helper.rb",
    "content": "module Brakeman::ModuleHelper\n  def handle_module exp, tracker_class, parent = nil\n    name = class_name(exp.module_name)\n\n    if @current_module\n      outer_module = @current_module\n      name = (outer_module.name.to_s + \"::\" + name.to_s).to_sym\n    end\n\n    if @current_class\n      name = (@current_class.name.to_s + \"::\" + name.to_s).to_sym\n    end\n\n    if @tracker.libs[name]\n      @current_module = @tracker.libs[name]\n      @current_module.add_file @current_file, exp\n    else\n      @current_module = tracker_class.new name, parent, @current_file, exp, @tracker\n      @tracker.libs[name] = @current_module\n    end\n\n    exp.body = process_all! exp.body\n\n    if outer_module\n      @current_module = outer_module\n    else\n      @current_module = nil\n    end\n\n    exp\n  end\n\n  def handle_class exp, collection, tracker_class\n    name = class_name(exp.class_name)\n    parent = class_name exp.parent_name\n\n    if @current_class\n      outer_class = @current_class\n      name = (outer_class.name.to_s + \"::\" + name.to_s).to_sym\n    end\n\n    if @current_module\n      name = (@current_module.name.to_s + \"::\" + name.to_s).to_sym\n    end\n\n    if collection[name]\n      @current_class = collection[name]\n      @current_class.add_file @current_file, exp\n    else\n      @current_class = tracker_class.new name, parent, @current_file, exp, @tracker\n      collection[name] = @current_class\n    end\n\n    exp.body = process_all! exp.body\n\n    yield if block_given?\n\n    if outer_class\n      @current_class = outer_class\n    else\n      @current_class = nil\n    end\n\n    exp\n  end\n\n  def process_defs exp\n    name = exp.method_name\n\n    if node_type? exp[1], :self\n      if @current_class\n        target = @current_class.name\n      elsif @current_module\n        target = @current_module.name\n      else\n        target = nil\n      end\n    else\n      target = class_name exp[1]\n    end\n\n    @current_method = name\n    res = Sexp.new :defs, target, name, exp.formal_args, *process_all!(exp.body)\n    res.line(exp.line)\n    @current_method = nil\n\n    # TODO: if target is not self/nil, then\n    # the method should be added to `target`, not current class\n\n    if @current_class\n      @current_class.add_method @visibility, name, res, @current_file\n    elsif @current_module\n      @current_module.add_method @visibility, name, res, @current_file\n    end\n    res\n  end\n\n  def process_defn exp\n    name = exp.method_name\n\n    @current_method = name\n\n    if @inside_sclass\n      res = Sexp.new :defs, s(:self), name, exp.formal_args, *process_all!(exp.body)\n    else\n      res = Sexp.new :defn, name, exp.formal_args, *process_all!(exp.body)\n    end\n\n    res.line(exp.line)\n    @current_method = nil\n\n    if @current_class\n      @current_class.add_method @visibility, name, res, @current_file\n    elsif @current_module\n      @current_module.add_method @visibility, name, res, @current_file\n    end\n\n    res\n  end\n\n  # class << self\n  def process_sclass exp\n    @inside_sclass = true\n\n    process_all! exp\n\n    exp\n  ensure\n    @inside_sclass = false\n  end\n\n  def make_defs exp\n    # 'What if' there was some crazy code that had a\n    # defs inside a def inside an sclass? :|\n    return exp if node_type? exp, :defs\n\n    raise \"Unexpected node type: #{exp.node_type}\" unless node_type? exp, :defn\n\n    Sexp.new(:defs, s(:self), exp.method_name, exp.formal_args, *exp.body).line(exp.line)\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/processor_helper.rb",
    "content": "#Contains a couple shared methods for Processors.\nmodule Brakeman::ProcessorHelper\n  def process_all exp\n    exp.each_sexp do |e|\n      process e\n    end\n    exp\n  end\n\n  def process_all! exp\n    exp.map! do |e|\n      if sexp? e\n        process e\n      else\n        e\n      end\n    end\n\n    exp\n  end\n\n  #Process the arguments of a method call. Does not store results.\n  #\n  #This method is used because Sexp#args and Sexp#arglist create new objects.\n  def process_call_args exp\n    exp.each_arg do |a|\n      process a if sexp? a\n    end\n\n    exp\n  end\n\n  def process_class exp\n    current_class = @current_class\n    @current_class = class_name exp[1]\n    process_all exp.body\n    @current_class = current_class\n    exp\n  end\n\n  #Sets the current module.\n  def process_module exp\n    module_name = class_name(exp.class_name).to_s\n    prev_module = @current_module\n\n    if prev_module\n      @current_module = \"#{prev_module}::#{module_name}\"\n    else\n      @current_module = module_name\n    end\n\n    if block_given?\n      yield\n    else\n      process_all exp.body\n    end\n\n    @current_module = prev_module\n\n    exp\n  end\n\n  # e.g. private defn\n  def process_call_defn? exp\n    if call? exp and exp.target.nil? and node_type? exp.first_arg, :defn, :defs and [:private, :public, :protected].include? exp.method\n      prev_visibility = @visibility\n      @visibility = exp.method\n      process exp.first_arg\n      @visibility = prev_visibility\n      exp\n    else\n      false\n    end\n  end\n\n  def current_file\n    case\n    when @current_file\n      @current_file\n    when @current_class.is_a?(Brakeman::Collection)\n      @current_class.file\n    when @current_module.is_a?(Brakeman::Collection)\n      @current_module.file\n    else\n      nil\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/rails2_config_processor.rb",
    "content": "require 'brakeman/processors/lib/basic_processor'\n\n#Processes configuration. Results are put in tracker.config.\n#\n#Configuration of Rails via Rails::Initializer are stored in tracker.config.rails.\n#For example:\n#\n#  Rails::Initializer.run |config|\n#    config.action_controller.session_store = :cookie_store\n#  end\n#\n#will be stored in\n#\n#  tracker.config[:rails][:action_controller][:session_store]\n#\n#Values for tracker.config.rails will still be Sexps.\nclass Brakeman::Rails2ConfigProcessor < Brakeman::BasicProcessor\n  #Replace block variable in\n  #\n  #  Rails::Initializer.run |config|\n  #\n  #with this value so we can keep track of it.\n  RAILS_CONFIG = Sexp.new(:const, :\"!BRAKEMAN_RAILS_CONFIG\")\n\n  def initialize *args\n    super\n  end\n\n  #Use this method to process configuration file\n  def process_config src, current_file\n    @current_file = current_file\n    res = Brakeman::ConfigAliasProcessor.new.process_safely(src, nil, current_file)\n    process res\n  end\n\n  # Check if config is set to use Erubis\n  # but because it's 2026 we're going to use Erubi\n  def process_call exp\n    target = exp.target\n    target = process target if sexp? target\n\n    if exp.method == :gem and exp.first_arg.value == \"erubis\"\n      Brakeman.debug \"[Notice] Using Erubi for ERB templates\"\n      @tracker.config.erubi = true\n    end\n\n    exp\n  end\n\n  #Look for configuration settings\n  def process_attrasgn exp\n    if exp.target == RAILS_CONFIG\n      #Get rid of '=' at end\n      attribute = exp.method.to_s[0..-2].to_sym\n      if exp.num_args > 1\n        #Multiple arguments?...not sure if this will ever happen\n        @tracker.config.rails[attribute] = exp.args\n      else\n        @tracker.config.rails[attribute] = exp.first_arg\n      end\n    elsif include_rails_config? exp\n      options = get_rails_config exp\n      level = @tracker.config.rails\n      options[0..-2].each do |o|\n        level[o] ||= {}\n        level = level[o]\n      end\n\n      level[options.last] = exp.first_arg\n    end\n\n    exp\n  end\n\n  #Check for Rails version\n  def process_cdecl exp\n    #Set Rails version required\n    if exp.lhs == :RAILS_GEM_VERSION\n      @tracker.config.set_rails_version exp.rhs.value\n    end\n\n    exp\n  end\n\n  #Check if an expression includes a call to set Rails config\n  def include_rails_config? exp\n    target = exp.target\n    if call? target\n      if target.target == RAILS_CONFIG\n        true\n      else\n        include_rails_config? target\n      end\n    elsif target == RAILS_CONFIG\n      true\n    else\n      false\n    end\n  end\n\n  #Returns an array of symbols for each 'level' in the config\n  #\n  #  config.action_controller.session_store = :cookie\n  #\n  #becomes\n  #\n  #  [:action_controller, :session_store]\n  def get_rails_config exp\n    if node_type? exp, :attrasgn\n      attribute = exp.method.to_s[0..-2].to_sym\n      get_rails_config(exp.target) << attribute\n    elsif call? exp\n      if exp.target == RAILS_CONFIG\n        [exp.method]\n      else\n        get_rails_config(exp.target) << exp.method\n      end\n    else\n      raise \"WHAT\"\n    end\n  end\nend\n\n#This is necessary to replace block variable so we can track config settings\nclass Brakeman::ConfigAliasProcessor < Brakeman::AliasProcessor\n\n  RAILS_INIT = Sexp.new(:colon2, Sexp.new(:const, :Rails), :Initializer)\n\n  #Look for a call to \n  #\n  #  Rails::Initializer.run do |config|\n  #    ...\n  #  end\n  #\n  #and replace config with RAILS_CONFIG\n  def process_iter exp\n    target = exp.block_call.target\n    method = exp.block_call.method\n\n    if sexp? target and target == RAILS_INIT and method == :run\n      env[Sexp.new(:lvar, exp.block_args.value)] = Brakeman::Rails2ConfigProcessor::RAILS_CONFIG\n    end\n\n    process_default exp\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/rails2_route_processor.rb",
    "content": "require 'brakeman/processors/lib/basic_processor'\n\n#Processes the Sexp from routes.rb. Stores results in tracker.routes.\n#\n#Note that it is only interested in determining what methods on which\n#controllers are used as routes, not the generated URLs for routes.\nclass Brakeman::Rails2RoutesProcessor < Brakeman::BasicProcessor\n  include Brakeman::RouteHelper\n\n  attr_reader :map, :nested, :current_controller\n\n  def initialize tracker\n    super\n    @map = Sexp.new(:lvar, :map)\n    @nested = nil  #used for identifying nested targets\n    @prefix = [] #Controller name prefix (a module name, usually)\n    @current_controller = nil\n    @with_options = nil #For use inside map.with_options\n    @current_file = \"config/routes.rb\"\n  end\n\n  #Call this with parsed route file information.\n  #\n  #This method first calls RouteAliasProcessor#process_safely on the +exp+,\n  #so it does not modify the +exp+.\n  def process_routes exp\n    process Brakeman::RouteAliasProcessor.new.process_safely(exp, nil, @current_file)\n  end\n\n  #Looking for mapping of routes\n  def process_call exp\n    target = exp.target\n\n    if target == map or (not target.nil? and target == nested)\n      process_map exp\n    else\n      process_default exp\n    end\n\n    exp\n  end\n\n  #Process a map.something call\n  #based on the method used\n  def process_map exp\n    args = exp.args\n\n    case exp.method\n    when :resource\n      process_resource args\n    when :resources\n      process_resources args\n    when :connect, :root\n      process_connect args\n    else\n      process_named_route args\n    end\n\n    exp\n  end\n\n  #Look for map calls that take a block.\n  #Otherwise, just do the default processing.\n  def process_iter exp\n    target = exp.block_call.target\n\n    if target == map or target == nested\n      method = exp.block_call.method\n      case method\n      when :namespace\n        process_namespace exp\n      when :resources, :resource\n        process_resources exp.block_call.args\n        process_default exp.block if exp.block\n      when :with_options\n        process_with_options exp\n      end\n      exp\n    else\n      process_default exp\n    end\n  end\n\n  #Process\n  # map.resources :x, :controller => :y, :member => ...\n  #etc.\n  def process_resources exp\n    controller = check_for_controller_name exp\n    if controller\n      self.current_controller = controller\n      process_resource_options exp[-1]\n    else\n      exp.each do |argument|\n        if node_type? argument, :lit\n          self.current_controller = exp.first.value\n          add_resources_routes\n          process_resource_options exp.last\n        end\n      end\n    end\n  end\n\n  #Process all the options that might be in the hash passed to\n  #map.resource, et al.\n  def process_resource_options exp\n    if exp.nil? and @with_options\n      exp = @with_options\n    elsif @with_options\n      exp = exp.concat @with_options[1..-1]\n    end\n    return unless exp.node_type == :hash\n\n    hash_iterate(exp) do |option, value|\n      case option[1]\n      when :controller, :requirements, :singular, :path_prefix, :as,\n        :path_names, :shallow, :name_prefix, :member_path, :nested_member_path,\n        :belongs_to, :conditions, :active_scaffold\n        #should be able to skip\n      when :collection, :member, :new\n        process_collection value\n      when :has_one\n        save_controller = current_controller\n        process_resource value[1..-1] #Verify this is proper behavior\n        self.current_controller = save_controller\n      when :has_many\n        save_controller = current_controller\n        process_resources value[1..-1]\n        self.current_controller = save_controller\n      when :only\n        process_option_only value\n      when :except\n        process_option_except value\n      else\n        Brakeman.alert \"Unhandled resource option, please report: #{option}\"\n      end\n    end\n  end\n\n  #Process route option :only => ...\n  def process_option_only exp\n    routes = @tracker.routes[@current_controller]\n    [:index, :new, :create, :show, :edit, :update, :destroy].each do |r|\n      routes.delete r\n    end\n\n    if exp.node_type == :array\n      exp[1..-1].each do |e|\n        routes << e.value\n      end\n    end\n  end\n\n  #Process route option :except => ...\n  def process_option_except exp\n    return unless exp.node_type == :array\n    routes = @tracker.routes[@current_controller]\n\n    exp[1..-1].each do |e|\n      routes.delete e.value\n    end\n  end\n\n  #  map.resource :x, ..\n  def process_resource exp\n    controller = check_for_controller_name exp\n    if controller\n      self.current_controller = controller\n      process_resource_options exp.last\n    else\n      exp.each do |argument|\n        if node_type? argument, :lit\n          self.current_controller = pluralize(exp.first.value.to_s)\n          add_resource_routes\n          process_resource_options exp.last\n        end\n      end\n    end\n  end\n\n  #Process\n  # map.connect '/something', :controller => 'blah', :action => 'whatever'\n  def process_connect exp\n    return if exp.empty?\n\n    controller = check_for_controller_name exp\n    self.current_controller = controller if controller\n\n    #Check for default route\n    if string? exp.first\n      if exp.first.value == \":controller/:action/:id\"\n        @tracker.routes[:allow_all_actions] = exp.first\n      elsif exp.first.value.include? \":action\"\n        @tracker.routes[@current_controller] = [:allow_all_actions, exp.line]\n        return\n      end\n    end\n\n    #This -seems- redundant, but people might connect actions\n    #to a controller which already allows them all\n    return if @tracker.routes[@current_controller].is_a? Array and @tracker.routes[@current_controller][0] == :allow_all_actions\n\n    exp.last.each_with_index do |e,i|\n      if symbol? e and e.value == :action\n        action = exp.last[i + 1]\n\n        if node_type? action, :lit\n          @tracker.routes[@current_controller] << action.value.to_sym\n        end\n\n        return\n      end\n    end\n  end\n\n  # map.with_options :controller => 'something' do |something|\n  #   something.resources :blah\n  # end\n  def process_with_options exp\n    @with_options = exp.block_call.last_arg\n    @nested = Sexp.new(:lvar, exp.block_args.value)\n\n    self.current_controller = check_for_controller_name exp.block_call.args\n\n    #process block\n    process exp.block\n\n    @with_options = nil\n    @nested = nil\n  end\n\n  # map.namespace :something do |something|\n  #   something.resources :blah\n  # end\n  def process_namespace exp\n    call = exp.block_call\n    formal_args = exp.block_args\n    block = exp.block\n\n    @prefix << camelize(call.first_arg.value)\n\n    if formal_args\n      @nested = Sexp.new(:lvar, formal_args.value)\n    end\n\n    process block\n\n    @prefix.pop\n  end\n\n  # map.something_abnormal '/blah', :controller => 'something', :action => 'wohoo'\n  def process_named_route exp\n    process_connect exp\n  end\n\n  #Process collection option\n  # :collection => { :some_action => :http_actions }\n  def process_collection exp\n    return unless exp.node_type == :hash\n    routes = @tracker.routes[@current_controller]\n\n    hash_iterate(exp) do |action, _type|\n      routes << action.value\n    end\n  end\n\n  private\n\n  #Checks an argument list for a hash that has a key :controller.\n  #If it does, returns the value.\n  #\n  #Otherwise, returns nil.\n  def check_for_controller_name args\n    args.each do |a|\n      if hash? a and value = hash_access(a, :controller)\n        return value.value if string? value or symbol? value\n      end\n    end\n\n    nil\n  end\nend\n\n#This is for a really specific case where a hash is used as arguments\n#to one of the map methods.\nclass Brakeman::RouteAliasProcessor < Brakeman::AliasProcessor\n\n  #This replaces\n  # { :some => :hash }.keys\n  #with\n  # [:some]\n  def process_call exp\n    process_default exp\n\n    if hash? exp.target and exp.method == :keys\n      keys = get_keys exp.target\n      exp.clear\n      keys.each_with_index do |e,i|\n        exp[i] = e\n      end\n    end\n    exp\n  end\n\n  #Returns an array Sexp containing the keys from the hash\n  def get_keys hash\n    keys = Sexp.new(:array)\n    hash_iterate(hash) do |key, _value|\n      keys << key\n    end\n\n    keys\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/rails3_config_processor.rb",
    "content": "\nrequire 'brakeman/processors/lib/basic_processor'\n\n#Processes configuration. Results are put in tracker.config.\n#\n#Configuration of Rails via Rails::Initializer are stored in tracker.config.rails.\n#For example:\n#\n#  MyApp::Application.configure do\n#    config.active_record.whitelist_attributes = true\n#  end\n#\n#will be stored in\n#\n#  tracker.config.rails[:active_record][:whitelist_attributes]\n#\n#Values for tracker.config.rails will still be Sexps.\nclass Brakeman::Rails3ConfigProcessor < Brakeman::BasicProcessor\n  RAILS_CONFIG = Sexp.new(:call, nil, :config)\n  RAILS_APPLICATION = Sexp.new(:colon2, s(:const, :Rails), :Application)\n\n  def initialize *args\n    super\n    @inside_config = false\n  end\n\n  #Use this method to process configuration file\n  def process_config src, current_file\n    @current_file = current_file\n    res = Brakeman::AliasProcessor.new(@tracker).process_safely(src, nil, @current_file)\n    process res\n  end\n\n  #Look for MyApp::Application.configure do ... end\n  def process_iter exp\n    call = exp.block_call\n\n    if node_type?(call.target, :colon2) and\n      call.target.rhs == :Application and\n      call.method == :configure\n\n      @inside_config = true\n      process exp.block if sexp? exp.block\n      @inside_config = false\n    end\n\n    exp\n  end\n\n  #Look for class Application < Rails::Application\n  def process_class exp\n    if application_class? exp\n      @inside_config = true\n      process_all exp.body if sexp? exp.body\n      @inside_config = false\n    end\n\n    exp\n  end\n\n  def application_class? exp\n    return unless node_type? exp, :class\n\n    exp.class_name == :Application or\n    (node_type? exp.class_name, :colon2 and exp.class_name.rhs == :Application) or\n    (exp.parent_name == RAILS_APPLICATION)\n  end\n\n  #Look for configuration settings that\n  #are just a call like\n  #\n  #  config.load_defaults 5.2\n  def process_call exp\n    return exp unless @inside_config\n\n    if exp.target == RAILS_CONFIG and exp.first_arg\n      @tracker.config.rails[exp.method] = exp.first_arg\n    end\n\n    exp\n  end\n\n  #Look for configuration settings\n  def process_attrasgn exp\n    return exp unless @inside_config\n\n    if exp.target == RAILS_CONFIG\n      #Get rid of '=' at end\n      attribute = exp.method.to_s[0..-2].to_sym\n      if exp.num_args > 1\n        #Multiple arguments?...not sure if this will ever happen\n        @tracker.config.rails[attribute] = exp.args\n      else\n        @tracker.config.rails[attribute] = exp.first_arg\n      end\n    elsif include_rails_config? exp\n      options_path = get_rails_config exp\n      @tracker.config.set_rails_config(value: exp.first_arg, path: options_path, overwrite: true)\n    end\n\n    exp\n  end\n\n  #Check if an expression includes a call to set Rails config\n  def include_rails_config? exp\n    target = exp.target\n    if call? target\n      if target.target == RAILS_CONFIG\n        true\n      else\n        include_rails_config? target\n      end\n    elsif target == RAILS_CONFIG\n      true\n    else\n      false\n    end\n  end\n\n  #Returns an array of symbols for each 'level' in the config\n  #\n  #  config.action_controller.session_store = :cookie\n  #\n  #becomes\n  #\n  #  [:action_controller, :session_store]\n  def get_rails_config exp\n    if node_type? exp, :attrasgn\n      attribute = exp.method.to_s[0..-2].to_sym\n      get_rails_config(exp.target) << attribute\n    elsif call? exp\n      if exp.target == RAILS_CONFIG\n        [exp.method]\n      else\n        get_rails_config(exp.target) << exp.method\n      end\n    else\n      raise \"WHAT\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/rails3_route_processor.rb",
    "content": "require 'brakeman/processors/lib/basic_processor'\n\n#Processes the Sexp from routes.rb. Stores results in tracker.routes.\n#\n#Note that it is only interested in determining what methods on which\n#controllers are used as routes, not the generated URLs for routes.\nclass Brakeman::Rails3RoutesProcessor < Brakeman::BasicProcessor\n  include Brakeman::RouteHelper\n\n  attr_reader :map, :nested, :current_controller\n\n  def initialize tracker\n    super\n    @map = Sexp.new(:lvar, :map)\n    @nested = nil  #used for identifying nested targets\n    @prefix = [] #Controller name prefix (a module name, usually)\n    @current_controller = nil\n    @with_options = nil #For use inside map.with_options\n    @controller_block = false\n    @current_file = \"config/routes.rb\"\n  end\n\n  def process_routes exp\n    process Brakeman::AliasProcessor.new.process_safely(exp, nil, @current_file)\n  end\n\n  def process_call exp\n    case exp.method\n    when :resources\n      process_resources exp\n    when :resource\n      process_resource exp\n    when :root\n      process_root exp\n    when :member\n      process_default exp\n    when :get, :put, :post, :delete\n      process_verb exp\n    when :match\n      process_match exp\n    else\n      exp\n    end\n  end\n\n  def process_iter exp\n    case exp.block_call.method\n    when :namespace\n      process_namespace exp\n    when :resource\n      process_resource_block exp\n    when :resources\n      process_resources_block exp\n    when :scope\n      process_scope_block exp\n    when :controller\n      process_controller_block exp\n    else\n      process_default exp\n    end\n  end\n\n  def process_namespace exp\n    arg = exp.block_call.first_arg\n    return exp unless symbol? arg or string? arg \n\n    name = arg.value\n    block = exp.block\n\n    @prefix << camelize(name)\n\n    process block\n\n    @prefix.pop\n\n    exp\n  end\n\n  #TODO: Need test for this\n  def process_root exp\n    return exp unless hash? exp.first_arg\n\n    if value = hash_access(exp.first_arg, :to)\n      if string? value\n        add_route_from_string value\n      end\n    end\n\n    exp\n  end\n\n  def process_match exp\n    first_arg = exp.first_arg\n    second_arg = exp.second_arg\n    last_arg = exp.last_arg\n\n    if string? first_arg\n\n      matcher = first_arg.value\n      if matcher == ':controller(/:action(/:id(.:format)))' or\n        matcher.include? ':controller' and action_route?(matcher)  #Default routes\n        @tracker.routes[:allow_all_actions] = first_arg\n        return exp\n      elsif action_route?(first_arg)\n          if hash? second_arg and controller_name = hash_access(second_arg, :controller)\n            loose_action(controller_name, \"matched\") #TODO: Parse verbs\n          end\n      elsif second_arg.nil? and in_controller_block? and not matcher.include? \":\"\n        add_route matcher\n      end\n    end\n\n    if hash? last_arg\n      hash_iterate last_arg do |k, v|\n        if string? k\n          if string? v\n            add_route_from_string v\n          elsif in_controller_block? and symbol? v\n            add_route v\n          end\n        elsif symbol? k\n         case k.value\n         when :action\n          if string? v\n            add_route_from_string v\n          else\n            add_route v\n          end\n\n         when :to\n           if string? v\n             add_route_from_string v[1]\n           elsif in_controller_block? and symbol? v\n             add_route v\n           end\n         end\n        end\n      end\n    end\n\n    @current_controller = nil unless in_controller_block?\n    exp\n  end\n\n  def add_route_from_string value\n    value = value[1] if string? value\n\n    controller, action = extract_action value\n\n    if action\n      add_route action, controller\n    elsif in_controller_block?\n      add_route value\n    end\n  end\n\n  def process_verb exp\n    first_arg = exp.first_arg\n    second_arg = exp.second_arg\n\n    if symbol? first_arg and not hash? second_arg\n      add_route first_arg\n    elsif hash? second_arg\n      hash_iterate second_arg do |k, v|\n        if symbol? k and k.value == :to\n          if string? v\n            add_route_from_string v\n          elsif in_controller_block? and symbol? v\n            add_route v\n          end\n        elsif action_route?(first_arg)\n          if hash? second_arg and controller_name = hash_access(second_arg, :controller)\n            loose_action(controller_name, exp.method)\n          end\n        end\n      end\n    elsif string? first_arg\n      if first_arg.value.include? ':controller' and action_route?(first_arg) #Default routes\n        @tracker.routes[:allow_all_actions] = first_arg\n      end\n\n      route = first_arg.value.split \"/\"\n      if route.length != 2\n        add_route route[0]\n      else\n        add_route route[1], route[0]\n      end\n    else hash? first_arg\n      hash_iterate first_arg do |k, v|\n        if string? k\n          if string? v\n            add_route_from_string v\n          elsif in_controller_block?\n            add_route v\n          end\n        end\n      end\n    end\n\n    @current_controller = nil unless in_controller_block?\n    exp\n  end\n\n  def process_resources exp\n    first_arg = exp.first_arg\n    second_arg = exp.second_arg\n\n    return exp unless symbol? first_arg or string? first_arg\n\n    if second_arg and second_arg.node_type == :hash\n      self.current_controller = first_arg.value\n      #handle hash\n      add_resources_routes\n    elsif exp.args.all? { |s| symbol? s }\n      exp.each_arg do |s|\n        self.current_controller = s.value\n        add_resources_routes\n      end\n    end\n\n    @current_controller = nil unless in_controller_block?\n    exp\n  end\n\n  def process_resource exp\n    #Does resource even take more than one controller name?\n    exp.each_arg do |s|\n      if symbol? s\n        self.current_controller = pluralize(s.value.to_s)\n        add_resource_routes\n      else\n        #handle something else, like options\n        #or something?\n      end\n    end\n\n    @current_controller = nil unless in_controller_block?\n    exp\n  end\n\n  def process_resources_block exp\n    in_controller_block do\n      process_resources exp.block_call\n      process exp.block\n    end\n\n    @current_controller = nil\n    exp\n  end\n\n  def process_resource_block exp\n    in_controller_block do\n      process_resource exp.block_call\n      process exp.block\n    end\n\n    @current_controller = nil\n    exp\n  end\n\n  def process_scope_block exp\n    #How to deal with options?\n    process exp.block\n    exp\n  end\n\n  def process_controller_block exp\n    if string? exp or symbol? exp\n      self.current_controller = exp.block_call.first_arg.value\n\n      in_controller_block do\n        process exp[-1] if exp[-1]\n      end\n\n      @current_controller = nil\n    end\n\n    exp\n  end\n\n  def extract_action str\n    str.split \"#\"\n  end\n\n  def in_controller_block?\n    @controller_block\n  end\n\n  def in_controller_block\n    prev_block = @controller_block\n    @controller_block = true\n    yield\n    @controller_block = prev_block\n  end\n\n  def action_route? arg\n    if string? arg\n      arg = arg.value\n    end\n\n    arg.is_a? String and (arg.include? \":action\" or arg.include? \"*action\")\n  end\n\n  def loose_action controller_name, verb = \"any\"\n    self.current_controller = controller_name.value\n    @tracker.routes[@current_controller] = [:allow_all_actions, {:allow_verb => verb}]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/rails4_config_processor.rb",
    "content": "require 'brakeman/processors/lib/rails3_config_processor'\n\nclass Brakeman::Rails4ConfigProcessor < Brakeman::Rails3ConfigProcessor\n  APPLICATION_CONFIG = s(:call, s(:call, s(:const, :Rails), :application), :configure)\n  ALT_APPLICATION_CONFIG = s(:call, s(:call, s(:colon3, :Rails), :application), :configure)\n\n  # Look for Rails.application.configure do ... end\n  def process_iter exp\n    if exp.block_call == APPLICATION_CONFIG or exp.block_call == ALT_APPLICATION_CONFIG\n      @inside_config = true\n      process exp.block if sexp? exp.block\n      @inside_config = false\n    else\n      super\n    end\n\n    exp\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/render_helper.rb",
    "content": "require 'digest/sha1'\n\n#Processes a call to render() in a controller or template\nmodule Brakeman::RenderHelper\n\n  #Process s(:render, TYPE, OPTION?, OPTIONS)\n  def process_render exp\n    process_default exp\n    @rendered = true\n    case exp.render_type\n    when :action, :template, :inline\n      action = exp[2]\n      args = exp[3]\n\n      if string? action or symbol? action\n        process_action action.value, args, exp.line\n      else\n        process_model_action action, args\n      end\n    when :default\n      begin\n        process_template template_name, exp[3], nil, exp.line\n      rescue ArgumentError\n        Brakeman.debug \"Problem processing render: #{exp}\"\n      end\n    when :partial, :layout\n      process_partial exp[2], exp[3], exp.line\n    when :nothing\n    end\n    exp\n  end\n\n  #Processes layout\n  def process_layout name = nil\n    if name.nil? and defined? layout_name\n      name = layout_name\n    end\n\n    return unless name\n\n    process_template name, nil, nil, nil\n  end\n\n  #Determines file name for partial and then processes it\n  def process_partial name, args, line\n    if !(string? name or symbol? name) or name.value == \"\"\n      return\n    end\n\n    names = name.value.to_s.split(\"/\")\n    names[-1] = \"_\" + names[-1]\n    process_template template_name(names.join(\"/\")), args, nil, line\n  end\n\n  #Processes a given action\n  def process_action name, args, line\n    if name.is_a? String or name.is_a? Symbol\n      process_template template_name(name), args, nil, line\n    else\n      Brakeman.debug \"Not processing render #{name.inspect}\"\n    end\n  end\n\n  SINGLE_RECORD = [:first, :find, :last, :new]\n  COLLECTION = [:all, :where]\n\n  def process_model_action action, args\n    return unless call? action\n\n    method = action.method\n\n    klass = get_class_target(action) || Brakeman::Tracker::UNKNOWN_MODEL\n    name = Sexp.new(:lit, klass.downcase)\n\n    if SINGLE_RECORD.include? method\n      # Set a local variable with name based on class of model\n      # and value of the value passed to render\n      local_key = Sexp.new(:lit, :locals)\n      locals = hash_access(args, local_key) || Sexp.new(:hash)\n      hash_insert(locals, name, action)\n      hash_insert(args, local_key, locals)\n\n      process_partial name, args, action.line\n    elsif COLLECTION.include? method\n      collection_key = Sexp.new(:lit, :collection)\n      hash_insert(args, collection_key, action)\n\n      process_partial name, args, action.line\n    end\n  end\n\n  #Processes a template, adding any instance variables\n  #to its environment.\n  def process_template name, args, called_from = nil, *_\n\n    Brakeman.debug \"Rendering #{name} (#{called_from})\"\n    #Get scanned source for this template\n    name = name.to_s.gsub(/^\\//, \"\")\n    template = @tracker.templates[name.to_sym]\n    unless template\n      Brakeman.debug \"No such template: #{name}\"\n      return\n    end\n\n    if called_from\n      # Track actual template that was rendered\n      called_from.last_template = template\n    end\n\n    template_env = only_ivars(:include_request_vars)\n\n    #Hash the environment and the source of the template to avoid\n    #pointlessly processing templates, which can become prohibitively\n    #expensive in terms of time and memory.\n    digest = Digest::SHA1.new.update(template_env.instance_variable_get(:@env).to_a.sort.to_s << name).to_s.to_sym\n\n    if @tracker.template_cache.include? digest\n      #Already processed this template with identical environment\n      return\n    else\n      @tracker.template_cache << digest\n\n      options = get_options args\n\n      #Process layout\n      if string? options[:layout]\n        process_template \"layouts/#{options[:layout][1]}\", nil, nil, nil\n      elsif node_type? options[:layout], :false\n        #nothing\n      elsif not template.name.to_s.match(/[^\\/_][^\\/]+$/)\n        #Don't do this for partials\n\n        process_layout\n      end\n\n      if hash? options[:locals]\n        hash_iterate options[:locals] do |key, value|\n          if symbol? key\n            template_env[Sexp.new(:call, nil, key.value)] = value\n          end\n        end\n      end\n\n      if options[:collection]\n\n        #The collection name is the name of the partial without the leading\n        #underscore.\n        variable = template.name.to_s.match(/[^\\/_][^\\/]+$/)[0].to_sym\n\n        #Unless the :as => :variable_name option is used\n        if options[:as]\n          if string? options[:as] or symbol? options[:as]\n            variable = options[:as].value.to_sym\n          end\n        end\n\n        collection = get_class_target(options[:collection]) || Brakeman::Tracker::UNKNOWN_MODEL\n\n        template_env[Sexp.new(:call, nil, variable)] = Sexp.new(:call, Sexp.new(:const, collection), :new)\n      end\n\n      #Set original_line for values so it is clear\n      #that values came from another file\n      template_env.all.each do |_var, value|\n        unless value.original_line\n          #TODO: This has been broken for a while now and no one noticed\n          #so maybe we can skip it\n          value.original_line = value.line\n        end\n      end\n\n      #Run source through AliasProcessor with instance variables from the\n      #current environment.\n      #TODO: Add in :locals => { ... } to environment\n      src = Brakeman::TemplateAliasProcessor.new(@tracker, template, called_from).process_safely(template.src, template_env)\n\n      digest = Digest::SHA1.new.update(name + src.to_s).to_s.to_sym\n\n      if @tracker.template_cache.include? digest\n        return\n      else\n        @tracker.template_cache << digest\n      end\n\n      #Run alias-processed src through the template processor to pull out\n      #information and outputs.\n      #This information will be stored in tracker.templates, but with a name\n      #specifying this particular route. The original source should remain\n      #pristine (so it can be processed within other environments).\n      @tracker.processor.process_template name, src, template.type, called_from, template.file\n    end\n  end\n\n  #Override to process name, such as adding the controller name.\n  def template_name name\n    raise \"RenderHelper#template_name should be overridden.\"\n  end\n\n  #Turn options Sexp into hash\n  def get_options args\n    options = {}\n    return options unless hash? args\n\n    hash_iterate args do |key, value|\n      if symbol? key\n        options[key.value] = value\n      end\n    end\n\n    options\n  end\n\n  def get_class_target sexp\n    if call? sexp\n      get_class_target sexp.target\n    else\n      klass = class_name sexp\n      if klass.is_a? Symbol\n        klass\n      else\n        nil\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/render_path.rb",
    "content": "module Brakeman\n  class RenderPath\n    attr_reader :path\n\n    def initialize\n      @path = []\n    end\n\n    def add_controller_render controller_name, method_name, line, file\n      method_name ||= \"\"\n\n      @path << { :type => :controller,\n                 :class => controller_name.to_sym,\n                 :method => method_name.to_sym,\n                 :line => line,\n                 :file => file\n                }\n\n      self\n    end\n\n    def add_template_render template_name, line, file\n      @path << { :type => :template,\n                 :name => template_name.to_sym,\n                 :line => line,\n                 :file => file\n               }\n\n      self\n    end\n\n    def last_template= template\n      if @path.last\n        @path.last[:rendered] = {\n          name: template.name,\n          file: template.file,\n        }\n      else\n        Brakeman.debug \"No render path to add template information\"\n      end\n    end\n\n    def include_template? name\n      name = name.to_sym\n\n      @path.any? do |loc|\n        loc[:type] == :template and loc[:name] == name\n      end\n    end\n\n    def include_controller? klass\n      klass = klass.to_sym\n\n      @path.any? do |loc|\n        loc[:type] == :controller and loc[:class] == klass\n      end\n    end\n\n    def include_any_method? method_names\n      names = method_names.map(&:to_sym)\n\n      @path.any? do |loc|\n        loc[:type] == :controller and names.include? loc[:method]\n      end\n    end\n\n    def rendered_from_controller?\n      @path.any? do |loc|\n        loc[:type] == :controller\n      end\n    end\n\n    def each &block\n      @path.each(&block)\n    end\n\n    def join *args\n      self.to_a.join(*args)\n    end\n\n    def length\n      @path.length\n    end\n\n    def map &block\n      @path.map(&block)\n    end\n\n    def to_a\n      @path.map do |loc|\n        case loc[:type]\n        when :template\n          \"Template:#{loc[:name]}\"\n        when :controller\n          \"#{loc[:class]}##{loc[:method]}\"\n        end\n      end\n    end\n\n    def last\n      self.to_a.last\n    end\n\n    def to_s\n      self.to_a.to_s\n    end\n\n    def to_sym\n      self.to_s.to_sym\n    end\n\n    def to_json *args\n      require 'json'\n      JSON.generate(@path)\n    end\n\n    def with_relative_paths\n      @path.map do |loc|\n        r = loc.dup\n\n        if r[:file]\n          r[:file] = r[:file].relative\n        end\n\n        if r[:rendered] and r[:rendered][:file]\n          r[:rendered] = r[:rendered].dup\n          r[:rendered][:file] = r[:rendered][:file].relative\n        end\n\n        r\n      end\n    end\n\n    def initialize_copy original\n      @path = original.path.dup\n      self\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/route_helper.rb",
    "content": "module Brakeman::RouteHelper\n  #Manage Controller prefixes\n  #@prefix is an Array, but this method returns a string\n  #suitable for prefixing onto a controller name.\n  def prefix\n    if @prefix.length > 0\n      @prefix.join(\"::\") << \"::\"\n    else\n      ''\n    end\n  end\n\n  #Sets the controller name to a proper class name.\n  #For example\n  # self.current_controller = :session\n  # @controller == :SessionController #true\n  #\n  #Also prepends the prefix if there is one set.\n  def current_controller= name\n    @current_controller = (prefix + camelize(name) + \"Controller\").to_sym\n    @tracker.routes[@current_controller] ||= Set.new\n  end\n\n  #Add route to controller. If a controller is specified,\n  #the current controller will be set to that controller.\n  #If no controller is specified, uses current controller value.\n  def add_route route, controller = nil\n    if node_type? route, :str, :lit\n      route = route.value\n    end\n\n    return unless route.is_a? String or route.is_a? Symbol\n\n    if route.is_a? String and controller.nil? and route.include? \":controller\"\n      controller = \":controller\"\n    end\n\n    route = route.to_sym\n\n    if controller\n      self.current_controller = controller\n    end\n\n    routes = @tracker.routes[@current_controller]\n    \n    if routes and not routes.include? :allow_all_actions\n      routes << route\n    end\n  end\n\n  #Add default routes\n  def add_resources_routes\n    existing_routes = @tracker.routes[@current_controller]\n\n    unless existing_routes.is_a? Array and existing_routes.first == :allow_all_actions\n      existing_routes.merge [:index, :new, :create, :show, :edit, :update, :destroy]\n    end\n  end\n\n  #Add default routes minus :index\n  def add_resource_routes\n    existing_routes = @tracker.routes[@current_controller]\n\n    unless existing_routes.is_a? Array and existing_routes.first == :allow_all_actions\n      existing_routes.merge [:new, :create, :show, :edit, :update, :destroy]\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/lib/safe_call_helper.rb",
    "content": "module Brakeman\n  module SafeCallHelper\n    [[:process_safe_call, :process_call],\n     [:process_safe_attrasgn, :process_attrasgn],\n     [:process_safe_op_asgn, :process_op_asgn],\n     [:process_safe_op_asgn2, :process_op_asgn2]].each do |call, replacement|\n       define_method(call) do |exp|\n         if self.respond_to? replacement\n           self.send(replacement, exp)\n         else\n           process_default exp\n         end\n       end\n     end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/library_processor.rb",
    "content": "require 'brakeman/processors/base_processor'\nrequire 'brakeman/processors/alias_processor'\nrequire 'brakeman/processors/lib/module_helper'\nrequire 'brakeman/tracker/library'\n\n#Process generic library and stores it in Tracker.libs\nclass Brakeman::LibraryProcessor < Brakeman::BaseProcessor\n  include Brakeman::ModuleHelper\n\n  def initialize tracker\n    super\n    @current_file = nil\n    @alias_processor = Brakeman::AliasProcessor.new tracker\n    @current_module = nil\n    @current_class = nil\n    @initializer_env = nil\n  end\n\n  def process_library src, current_file = @current_file\n    @current_file = current_file\n    process src\n  end\n\n  def process_class exp\n    handle_class exp, @tracker.libs, Brakeman::Library\n  end\n\n  def process_module exp\n    handle_module exp, Brakeman::Library\n  end\n\n  def process_defn exp\n    # TODO: Why is this different from ModuleHelper?\n\n    if @inside_sclass\n      exp = make_defs(exp)\n    end\n\n    if exp.method_name == :initialize\n      @alias_processor.process_safely exp.body_list\n      @initializer_env = @alias_processor.only_ivars\n    elsif node_type? exp, :defn\n      exp = @alias_processor.process_safely exp, @initializer_env\n    else\n      exp = @alias_processor.process exp\n    end\n\n    if @current_class\n      exp.body = process_all! exp.body\n      @current_class.add_method :public, exp.method_name, exp, @current_file\n    elsif @current_module\n      exp.body = process_all! exp.body\n      @current_module.add_method :public, exp.method_name, exp, @current_file\n    end\n\n    exp\n  end\n\n  alias process_defs process_defn\n\n  def process_call exp\n    if process_call_defn? exp\n      exp\n    elsif @current_method.nil? and exp.target.nil? and (@current_class or @current_module)\n      # Methods called inside class / module\n      case exp.method\n      when :include\n        module_name = class_name(exp.first_arg)\n        (@current_class || @current_module).add_include module_name\n      end\n\n      exp\n    else\n      process_default exp\n    end\n  end\n\n  def process_iter exp\n    res = process_default exp\n\n    if node_type? res, :iter and call? exp.block_call # sometimes this changes after processing\n      if exp.block_call.method == :included and (@current_module or @current_class)\n        (@current_module || @current_class).options[:included] = res.block\n      end\n    end\n\n    res\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/model_processor.rb",
    "content": "require 'brakeman/processors/base_processor'\nrequire 'brakeman/processors/lib/module_helper'\nrequire 'brakeman/tracker/model'\n\n#Processes models. Puts results in tracker.models\nclass Brakeman::ModelProcessor < Brakeman::BaseProcessor\n  include Brakeman::ModuleHelper\n\n  def initialize tracker\n    super\n    @current_class = nil\n    @current_method = nil\n    @current_module = nil\n    @visibility = :public\n    @current_file = nil\n  end\n\n  #Process model source\n  def process_model src, current_file = @current_file\n    @current_file = current_file\n    process src\n  end\n\n  #s(:class, NAME, PARENT, BODY)\n  def process_class exp\n    name = class_name(exp.class_name)\n\n    #If inside an inner class we treat it as a library.\n    if @current_class\n      Brakeman.debug \"Treating inner class as library: #{name}\"\n      Brakeman::LibraryProcessor.new(@tracker).process_library exp, @current_file\n      return exp\n    end\n\n    handle_class exp, @tracker.models, Brakeman::Model\n  end\n\n  def process_module exp\n    handle_module exp, Brakeman::Model\n  end\n\n  #Handle calls outside of methods,\n  #such as include, attr_accessible, private, etc.\n  def process_call exp\n    return exp unless @current_class\n    return exp if process_call_defn? exp\n\n    target = exp.target\n    if sexp? target\n      target = process target\n    end\n\n    method = exp.method\n    first_arg = exp.first_arg\n\n    #Methods called inside class definition\n    #like attr_* and other settings\n    if @current_method.nil? and target.nil?\n      if first_arg.nil?\n        case method\n        when :private, :protected, :public\n          @visibility = method\n        when :attr_accessible\n          @current_class.set_attr_accessible\n        else\n          #??\n        end\n      else\n        case method\n        when :include\n          @current_class.add_include class_name(first_arg) if @current_class\n        when :attr_accessible\n          @current_class.set_attr_accessible exp\n        when :attr_protected\n          @current_class.set_attr_protected exp\n        when :enum\n          add_enum_method exp\n        else\n          if @current_class\n            @current_class.add_option method, exp\n          end\n        end\n      end\n\n      exp\n    else\n      call = make_call target, method, process_all!(exp.args)\n      call.line(exp.line)\n      call\n    end\n  end\n\n  def add_enum_method call\n    arg = call.first_arg\n    return unless hash? arg\n    return unless symbol? arg[1]\n\n    enum_name = arg[1].value # first key\n    enums = arg[2] # first value\n    enums_name = pluralize(enum_name.to_s).to_sym\n\n    call_line = call.line\n\n    if hash? enums\n      enum_values = enums\n    elsif array? enums\n      # Build hash for enum values like Rails does\n      enum_values = s(:hash).line(call_line)\n\n      enums.each_sexp.with_index do |v, index|\n        enum_values << v\n        enum_values << s(:lit, index).line(call_line)\n      end\n    end\n\n    enum_method = s(:defn, enum_name, s(:args), safe_literal(call_line))\n    enums_method = s(:defs, s(:self), enums_name, s(:args), enum_values)\n\n    @current_class.add_method :public, enum_name, enum_method, @current_file\n    @current_class.add_method :public, enums_name, enums_method, @current_file\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/output_processor.rb",
    "content": "Brakeman.load_brakeman_dependency 'ruby2ruby'\nrequire 'brakeman/util'\n\n#Produces formatted output strings from Sexps.\n#Recommended usage is\n#\n#  OutputProcessor.new.format(Sexp.new(:str, \"hello\"))\nclass Brakeman::OutputProcessor < Ruby2Ruby\n  include Brakeman::Util\n\n  def initialize *args\n    super\n    @user_input = nil\n  end\n\n  #Copies +exp+ and then formats it.\n  def format exp, user_input = nil, &block\n    @user_input = user_input\n    @user_input_block = block\n    process(exp.deep_clone) || \"[Format Error]\"\n  end\n\n  alias process_safely format\n\n  def process exp\n    begin\n      if @user_input and @user_input == exp\n        @user_input_block.call(exp, super(exp))\n      else\n        super exp if sexp? exp and not exp.empty?\n      end\n    rescue => e\n      Brakeman.debug \"While formatting #{exp}: #{e}\\n#{e.backtrace.join(\"\\n\")}\"\n    end\n  end\n\n  def process_ignore exp\n    \"[ignored]\"\n  end\n\n  def process_params exp\n    \"params\"\n  end\n\n  def process_session exp\n    \"session\"\n  end\n\n  def process_cookies exp\n    \"cookies\"\n  end\n\n  def process_rlist exp\n    out = exp.map do |e|\n      res = process e\n      if res == \"\"\n        nil\n      else\n        res\n      end\n    end.compact.join(\"\\n\")\n\n    out\n  end\n\n  def process_defn exp\n    # Copied from Ruby2Ruby except without the whole\n    # \"convert methods to attr_*\" stuff\n    exp = exp.deep_clone\n    exp.shift\n    name = exp.shift\n    args = process exp.shift\n    args = \"\" if args == \"()\"\n\n    exp.shift if exp == s(s(:nil)) # empty it out of a default nil expression\n\n    body = []\n    until exp.empty? do\n      body << indent(process(exp.shift))\n    end\n\n    body << indent(\"# do nothing\") if body.empty?\n\n    body = body.join(\"\\n\")\n\n    return \"def #{name}#{args}\\n#{body}\\nend\".gsub(/\\n\\s*\\n+/, \"\\n\")\n  end\n\n  def process_iter exp\n    call = process exp[1]\n    block = process_rlist exp.sexp_body(3)\n    out = \"#{call} do\\n #{block}\\n end\"\n\n    out\n  end\n\n  def process_output exp\n    output_format exp, \"Output\"\n  end\n\n  def process_escaped_output exp\n    output_format exp, \"Escaped Output\"\n  end\n\n\n  def process_format exp\n    output_format exp, \"Format\"\n  end\n\n  def process_format_escaped exp\n    output_format exp, \"Escaped\"\n  end\n\n  def output_format exp, tag\n    out = if exp[1].node_type == :str or exp[1].node_type == :ignore\n            \"\"\n          else\n            res = process exp[1]\n\n            if res == \"\"\n              \"\"\n            else\n              \"[#{tag}] #{res}\"\n            end\n          end\n\n    out\n  end\n\n  def process_const exp\n    if exp[1] == Brakeman::Tracker::UNKNOWN_MODEL\n      \"(Unresolved Model)\"\n    else\n      out = exp[1].to_s\n      out\n    end\n  end\n\n  def process_render exp\n    exp = exp.deep_clone\n    exp.shift\n\n    exp[1] = process exp[1] if sexp? exp[1]\n    exp[2] = process exp[2] if sexp? exp[2]\n    out = \"render(#{exp[0]} => #{exp[1]}, #{exp[2]})\"\n\n    out\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/route_processor.rb",
    "content": "require 'brakeman/processors/base_processor'\nrequire 'brakeman/processors/alias_processor'\nrequire 'brakeman/processors/lib/route_helper'\nrequire 'brakeman/util'\nrequire 'brakeman/processors/lib/rails3_route_processor.rb'\nrequire 'brakeman/processors/lib/rails2_route_processor.rb'\nrequire 'set'\n\nclass Brakeman::RoutesProcessor\n  def self.new tracker\n    if tracker.options[:rails3]\n      Brakeman::Rails3RoutesProcessor.new tracker\n    else\n      Brakeman::Rails2RoutesProcessor.new tracker\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/slim_template_processor.rb",
    "content": "require 'brakeman/processors/template_processor'\nrequire 'brakeman/processors/lib/render_helper'\n\nclass Brakeman::SlimTemplateProcessor < Brakeman::TemplateProcessor\n  include Brakeman::RenderHelper\n\n  SAFE_BUFFER = s(:call, s(:colon2, s(:const, :ActiveSupport), :SafeBuffer), :new)\n  OUTPUT_BUFFER = s(:ivar, :@output_buffer)\n  TEMPLE_UTILS = s(:colon2, s(:colon3, :Temple), :Utils)\n  ATTR_MERGE = s(:call, s(:call, s(:array), :reject, s(:block_pass, s(:lit, :empty?))), :join, s(:str, \" \"))\n  EMBEDDED_FILTER = s(:const, :BrakemanFilter)\n\n  def process_call exp\n    target = exp.target\n    method = exp.method\n\n    if method == :safe_concat and (target == SAFE_BUFFER or target == OUTPUT_BUFFER)\n      arg = normalize_output(exp.first_arg)\n\n      if is_escaped? arg\n        add_escaped_output arg.first_arg\n      elsif string? arg\n        ignore\n      elsif render? arg\n        add_output make_render_in_view arg\n      elsif string_interp? arg\n        process_inside_interp arg\n      elsif node_type? arg, :ignore\n        ignore\n      elsif internal_variable? arg\n        ignore\n      elsif arg == ATTR_MERGE\n        ignore\n      else\n        add_output arg\n      end\n    elsif is_escaped? exp\n      add_escaped_output arg\n    elsif target == nil and method == :render\n      exp.arglist = process exp.arglist\n      make_render_in_view exp\n    else\n      exp.arglist = process exp.arglist\n      exp\n    end\n  end\n\n  def normalize_output arg\n    arg = super(arg)\n\n    if embedded_filter? arg\n      super(arg.first_arg)\n    else\n      arg\n    end\n  end\n\n  # Handle our \"fake\" embedded filters\n  def embedded_filter? arg\n    call? arg and arg.method == :render and arg.target == EMBEDDED_FILTER\n  end\n\n  #Slim likes to interpolate output into strings then pass them to safe_concat.\n  #Better to pull those values out directly.\n  def process_inside_interp exp\n    exp.map! do |e|\n      if node_type? e, :evstr\n        e.value = process_interp_output e.value\n        e\n      else\n        e\n      end\n    end\n\n    exp\n  end\n\n  def process_interp_output exp\n    if sexp? exp\n      if node_type? exp, :if\n        process_interp_output exp.then_clause\n        process_interp_output exp.else_clause\n      elsif exp == SAFE_BUFFER\n        ignore\n      elsif render? exp\n        add_output make_render_in_view exp\n      elsif node_type? :output, :escaped_output\n        exp\n      elsif is_escaped? exp\n        add_escaped_output exp\n      else\n        add_output exp\n      end\n    end\n  end\n\n  def add_escaped_output exp\n    exp = normalize_output(exp)\n\n    return exp if string? exp or internal_variable? exp\n\n    super exp\n  end\n\n  def is_escaped? exp\n    call? exp and\n    exp.target == TEMPLE_UTILS and\n    (exp.method == :escape_html or exp.method == :escape_html_safe)\n  end\n\n  def internal_variable? exp\n    node_type? exp, :lvar and\n    exp.value =~ /^_(temple_|slim_)/\n  end\n\n  def render? exp\n    call? exp and\n    exp.target.nil? and\n    exp.method == :render\n  end\n\n  def process_render exp\n    #Still confused as to why this is not needed in other template processors\n    #but is needed here\n    exp\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/template_alias_processor.rb",
    "content": "require 'set'\nrequire 'brakeman/processors/alias_processor'\nrequire 'brakeman/processors/lib/render_helper'\nrequire 'brakeman/processors/lib/render_path'\nrequire 'brakeman/tracker'\n\n#Processes aliasing in templates.\n#Handles calls to +render+.\nclass Brakeman::TemplateAliasProcessor < Brakeman::AliasProcessor\n  include Brakeman::RenderHelper\n\n  FORM_METHODS = Set[:form_for, :remote_form_for, :form_remote_for]\n\n  def initialize tracker, template, called_from = nil\n    super tracker\n    @template = template\n    @current_file = template.file\n    @called_from = called_from\n  end\n\n  #Process template\n  def process_template name, args, _, line = nil\n    # Strip forward slash from beginning of template path.\n    # This also happens in RenderHelper#process_template but\n    # we need it here too to accurately avoid circular renders below.\n    name = name.to_s.gsub(/^\\//, \"\")\n\n    if @called_from\n      if @called_from.include_template? name\n        Brakeman.debug \"Skipping circular render from #{@template.name} to #{name}\"\n        return\n      end\n\n      super name, args, @called_from.dup.add_template_render(@template.name, line, @current_file), line\n    else\n      super name, args, Brakeman::RenderPath.new.add_template_render(@template.name, line, @current_file), line\n    end\n  end\n\n  def process_lasgn exp\n    if exp.lhs == :haml_temp or haml_capture? exp.rhs\n      exp.rhs = process exp.rhs\n\n      # Avoid propagating contents of block\n      if node_type? exp.rhs, :iter\n        new_exp = exp.dup\n        new_exp.rhs = exp.rhs.block_call\n\n        super new_exp\n\n        exp # Still save the original, though\n      else\n        super exp\n      end\n    else\n      super exp\n    end\n  end\n\n  HAML_CAPTURE = [:capture, :capture_haml]\n\n  def haml_capture? exp\n    node_type? exp, :iter and\n      call? exp.block_call and\n      HAML_CAPTURE.include? exp.block_call.method\n  end\n\n  #Determine template name\n  def template_name name\n    if !name.to_s.include?('/') && @template.name.to_s.include?('/')\n      name = \"#{@template.name.to_s.match(/^(.*\\/).*$/)[1]}#{name}\"\n    end\n    name\n  end\n\n  UNKNOWN_MODEL_CALL = Sexp.new(:call, Sexp.new(:const, Brakeman::Tracker::UNKNOWN_MODEL), :new)\n  FORM_BUILDER_CALL = Sexp.new(:call, Sexp.new(:const, :FormBuilder), :new)\n\n  #Looks for form methods and iterating over collections of Models\n  def process_iter exp\n    process_default exp\n\n    call = exp.block_call\n\n    if call? call\n      target = call.target\n      method = call.method\n      arg = exp.block_args.first_param\n      block = exp.block\n\n      #Check for e.g. Model.find.each do ... end\n      if method == :each and arg and block and model = get_model_target(target)\n        if arg.is_a? Symbol\n          if model == target.target\n            env[Sexp.new(:lvar, arg)] = Sexp.new(:call, model, :new)\n          else\n            env[Sexp.new(:lvar, arg)] = UNKNOWN_MODEL_CALL\n          end\n\n          process block if sexp? block\n        end\n      elsif FORM_METHODS.include? method\n        if arg.is_a? Symbol\n          env[Sexp.new(:lvar, arg)] = FORM_BUILDER_CALL\n\n          process block if sexp? block\n        end\n      end\n    end\n\n    exp\n  end\n\n  COLLECTION_METHODS = [:all, :find, :select, :where]\n\n  #Checks if +exp+ is a call to Model.all or Model.find*\n  def get_model_target exp\n    if call? exp\n      target = exp.target\n\n      if COLLECTION_METHODS.include? exp.method or exp.method.to_s[0,4] == \"find\"\n        models = Set.new @tracker.models.keys\n        name = class_name target\n        return target if models.include?(name)\n      end\n\n      return get_model_target(target)\n    end\n\n    false\n  end\n\n  #Ignore `<<` calls on template variables which are used by the templating\n  #library (HAML, ERB, etc.)\n  def find_push_target exp\n    if sexp? exp\n      if exp.node_type == :lvar and (exp.value == :_buf or exp.value == :_erbout)\n        return nil\n      elsif exp.node_type == :ivar and exp.value == :@output_buffer\n        return nil\n      elsif exp.node_type == :call and call? exp.target and\n        exp.target.method == :_hamlout and exp.method == :buffer\n\n        return nil\n      end\n    end\n\n    super\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/processors/template_processor.rb",
    "content": "require 'brakeman/processors/base_processor'\nrequire 'brakeman/tracker/template'\n\n#Base Processor for templates/views\nclass Brakeman::TemplateProcessor < Brakeman::BaseProcessor\n\n  #Initializes template information.\n  def initialize tracker, template_name, called_from = nil, current_file = nil\n    super(tracker)\n    @current_template = Brakeman::Template.new template_name, called_from, current_file, tracker\n    @current_file = @current_template.file\n\n    if called_from\n      template_name = (template_name.to_s + \".\" + called_from.to_s).to_sym\n    end\n\n    tracker.templates[template_name] = @current_template\n\n    @inside_concat = false\n  end\n\n  #Process the template Sexp.\n  def process exp\n    begin\n      super\n    rescue => e\n      except = e.exception(\"Error when processing #{@current_template.name}: #{e.message}\")\n      except.set_backtrace(e.backtrace)\n      raise except\n    end\n  end\n\n  #Ignore initial variable assignment\n  def process_lasgn exp\n    if exp.lhs == :_erbout and exp.rhs.node_type == :str  #ignore\n      ignore\n    elsif exp.lhs == :_buf and exp.rhs.node_type == :str\n      ignore\n    else\n      exp.rhs = process exp.rhs\n      exp\n    end\n  end\n\n  #Adds output to the list of outputs.\n  def process_output exp\n    exp.value = process exp.value\n    @current_template.add_output exp unless exp.original_line\n    exp\n  end\n\n  def process_escaped_output exp\n    process_output exp\n  end\n\n  # Pull out actual output value from template\n  def normalize_output arg\n    if call? arg and [:to_s, :html_safe!, :freeze].include? arg.method\n      normalize_output(arg.target) # sometimes it's foo.to_s.to_s\n    elsif node_type? arg, :if\n      branches = [arg.then_clause, arg.else_clause].compact\n\n      if branches.empty?\n        s(:nil).line(arg.line)\n      elsif branches.length == 2\n        Sexp.new(:or, *branches).line(arg.line)\n      else\n        branches.first\n      end\n    else\n      arg\n    end\n  end\n\n  def add_escaped_output output\n    add_output output, :escaped_output\n  end\n\n  def add_output output, type = :output\n    if node_type? output, :or\n      Sexp.new(:or, add_output(output.lhs, type), add_output(output.rhs, type)).line(output.line)\n    else\n      s = Sexp.new(type, output)\n      s.line(output.line)\n      @current_template.add_output s\n      s\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/config/remediation.yml",
    "content": "---\nbasic_auth_password: 300000\ncross_site_scripting: 300000\nxss_content_tag: 300000\nCVE_2014_3514_call: 600000\nall_default_routes: 2000000\nunsafe_deserialize: 2000000\nlocal_request_config: 100000\nCVE_2012_3424: 4000000\nCVE_2011_2932: 8000000\ncode_eval: 2000000\ncommand_injection: 2000000\nfile_access: 2000000\nCVE_2014_7829: 4000000\nCVE_2011_2929: 4000000\ncsrf_protection_disabled: 4000000\nCVE_2013_6414: 4000000\nCVE_2013_4491: 4000000\nCVE_2013_1856: 4000000\nCVE_2015_3226: 4000000\nCVE_2013_0333: 4000000\nxss_link_to: 300000\nxss_link_to_href: 300000\nCVE_2011_0446: 300000\nmass_assign_call: 2000000\ndangerous_attr_accessible: 2000000\nno_attr_accessible: 2000000\nCVE_2013_0277: 2000000\nCVE_2010_3933: 4000000\nCVE_2014_0081: 300000\nCVE_2011_2930: 600000\nopen_redirect: 300000\nregex_dos: 600000\ndynamic_render_path: 4000000\nCVE_2014_0082: 4000000\ncross_site_scripting_inline: 600000\nCVE_2011_3186: 2000000\nsafe_buffer_vuln: 4000000\nCVE_2013_1855: 4000000\nCVE_2013_1857: 4000000\nCVE_2012_3463: 600000\nselect_options_vuln: 4000000\ndangerous_send: 600000\nsession_key_manipulation: 600000\nhttp_cookies: 600000\nsession_secret: 600000\nsecure_cookies: 600000\nCVE_2013_6416: 600000\nCVE_2012_3464: 4000000\ncsrf_blacklist: 300000\nauth_blacklist: 300000\nsql_injection: 1200000\nCVE-2012-2660: 4000000\nCVE-2012-2661: 4000000\nCVE-2012-2695: 4000000\nCVE-2012-5664: 4000000\nCVE-2013-0155: 4000000\nCVE-2013-6417: 4000000\nCVE-2014-3482: 4000000\nCVE-2014-3483: 4000000\nssl_verification_bypass: 2500000\nCVE_2011_2931: 4000000\nunsafe_symbol_creation: 300000\ntranslate_vuln: 300000\nunsafe_constantize: 600000\nunscoped_find: 300000\nvalidation_regex: 300000\nmass_assign_without_protection: 600000\nCVE_2015_3227: 4000000\nCVE_2013_0156: 4000000\nweak_hash_digest: 800000\n"
  },
  {
    "path": "lib/brakeman/report/ignore/config.rb",
    "content": "require 'set'\nrequire 'json'\n\nmodule Brakeman\n  class IgnoreConfig\n    attr_reader :shown_warnings, :ignored_warnings\n    attr_accessor :file\n\n    def initialize file, new_warnings\n      @file = file\n      @new_warnings = new_warnings\n      @already_ignored = []\n      @ignored_fingerprints = Set.new\n      @used_fingerprints = Set.new\n      @notes = {}\n      @shown_warnings = @ignored_warnings = nil\n      @changed = false\n    end\n\n    # Populate ignored_warnings and shown_warnings based on ignore\n    # configuration\n    def filter_ignored\n      @shown_warnings = []\n      @ignored_warnings = []\n      @used_fingerprints = Set.new\n\n      @new_warnings.each do |w|\n        if ignored? w\n          @ignored_warnings << w\n        else\n          @shown_warnings << w\n        end\n      end\n\n      @shown_warnings\n    end\n\n    # Remove warning from ignored list\n    def unignore warning\n      @ignored_fingerprints.delete warning.fingerprint\n      if @already_ignored.reject! { |w|w[:fingerprint] == warning.fingerprint }\n        @changed = true\n      end\n    end\n\n    # Determine if warning should be ignored\n    def ignored? warning\n      @used_fingerprints << warning.fingerprint\n      @ignored_fingerprints.include? warning.fingerprint\n    end\n\n    def ignore warning\n      @changed = true unless ignored? warning\n      @ignored_fingerprints << warning.fingerprint\n    end\n\n    # Add note for warning\n    def add_note warning, note\n      @changed = true\n      @notes[warning.fingerprint] = note\n    end\n\n    # Retrieve note for warning if it exists. Returns nil if no\n    # note is found\n    def note_for warning\n      if warning.is_a? Warning\n        fingerprint = warning.fingerprint\n      else\n        fingerprint = warning[:fingerprint]\n      end\n\n      @already_ignored.each do |w|\n        if fingerprint == w[:fingerprint]\n          return w[:note]\n        end\n      end\n\n      nil\n    end\n\n    # The set of unused ignore entries\n    def obsolete_fingerprints\n      (@ignored_fingerprints - @used_fingerprints).to_a\n    end\n\n    def prune_obsolete\n      obsolete = obsolete_fingerprints.to_set\n      @ignored_fingerprints -= obsolete\n\n      @already_ignored.reject! do |w|\n        if obsolete.include? w[:fingerprint]\n          @changed = true\n        end\n      end\n    end\n\n    def already_ignored_entries_with_empty_notes\n      @already_ignored.select { |i| i if i[:note].strip.empty? }\n    end\n\n    # Read configuration to file\n    def read_from_file file = @file\n      if File.exist? file\n        begin\n          @already_ignored = JSON.parse(File.read(file), :symbolize_names => true)[:ignored_warnings]\n        rescue => e\n          raise e, \"\\nError[#{e.class}] while reading brakeman ignore file: #{file}\\n\"\n        end\n      else\n        Brakeman.alert \"Could not find ignore configuration in #{file} (no file)\"\n        @already_ignored = []\n      end\n\n      @already_ignored.each do |w|\n        @ignored_fingerprints << w[:fingerprint]\n        @notes[w[:fingerprint]] = w[:note]\n      end\n    end\n\n    # Save configuration to file\n    def save_to_file warnings, file = @file\n      warnings = warnings.map do |w|\n        if w.is_a? Warning\n          w = w.to_hash(absolute_paths: false)\n        end\n\n        w[:note] = @notes[w[:fingerprint]] || \"\"\n        w\n      end.sort_by { |w| [w[:fingerprint], w[:line] || 0] }\n\n      output = {\n        :ignored_warnings => warnings,\n        :brakeman_version => Brakeman::Version\n      }\n\n      File.open file, \"w\" do |f|\n        f.puts JSON.pretty_generate(output)\n      end\n    end\n\n    # Save old ignored warnings and newly ignored ones\n    def save_with_old\n      warnings = @ignored_warnings.dup\n\n      # Only add ignored warnings not already ignored\n      @already_ignored.each do |w|\n        fingerprint = w[:fingerprint]\n\n        unless @ignored_warnings.find { |ignored_warning| ignored_warning.fingerprint == fingerprint }\n          warnings << w\n        end\n      end\n\n      if @changed\n        save_to_file warnings\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/ignore/interactive.rb",
    "content": "Brakeman.load_brakeman_dependency 'highline'\n\nmodule Brakeman\n  class InteractiveIgnorer\n    def initialize file, warnings\n      @ignore_config = Brakeman::IgnoreConfig.new(file, warnings)\n      @new_warnings = warnings\n      @skip_ignored = false\n      @skip_rest = false\n      @ignore_rest = false\n      @quit = false\n      @restart = false\n    end\n\n    def start\n      file_menu\n      initial_menu\n\n      @ignore_config.filter_ignored\n\n      unless @quit\n        penultimate_menu\n        final_menu\n      end\n\n      if @restart\n        @restart = false\n        start\n      end\n\n      @ignore_config\n    end\n\n    private\n\n    def file_menu\n      loop do\n        @ignore_config.file = HighLine.new.ask \"Input file: \" do |q|\n          if @ignore_config.file and not @ignore_config.file.empty?\n            q.default = @ignore_config.file\n          else\n            q.default = \"config/brakeman.ignore\"\n          end\n        end\n\n        if File.exist? @ignore_config.file\n          @ignore_config.read_from_file\n          return\n        else\n          if yes_or_no \"No such file. Continue with empty config? \"\n            return\n          end\n        end\n      end\n    end\n\n    def initial_menu\n      HighLine.new.choose do |m|\n        m.choice \"Inspect all warnings\" do\n          @skip_ignored = false\n          pre_show_help\n          process_warnings\n        end\n\n        m.choice \"Inspect new warnings\" do\n          @skip_ignored = true\n          pre_show_help\n          process_warnings\n        end\n\n        m.choice \"Prune obsolete ignored warnings\" do\n          prune_obsolete\n        end\n\n        m.choice \"Skip - use current ignore configuration\" do\n          @quit = true\n          @ignore_config.filter_ignored\n        end\n      end\n    end\n\n    def warning_menu\n      HighLine.new.choose do |m|\n        m.prompt = \"Action: \"\n        m.layout = :one_line\n        m.list_option = \", \"\n        m.select_by = :name\n\n        m.choice \"i\"\n        m.choice \"n\"\n        m.choice \"s\"\n        m.choice \"u\"\n        m.choice \"a\"\n        m.choice \"k\"\n        m.choice \"q\"\n        m.choice \"?\" do\n          show_help\n          \"?\"\n        end\n      end\n    end\n\n    def pre_show_help\n      say \"-\" * 30\n      say \"Actions:\", :cyan\n      show_help\n    end\n\n    def show_help\n      say <<-HELP\ni - Add warning to ignore list\nn - Add warning to ignore list and add note\ns - Skip this warning (will remain ignored or shown)\nu - Remove this warning from ignore list\na - Ignore this warning and all remaining warnings\nk - Skip this warning and all remaining warnings\nq - Quit, do not update ignored warnings\n? - Display this help\n      HELP\n    end\n\n    def penultimate_menu\n      obsolete = @ignore_config.obsolete_fingerprints\n      return unless obsolete.any?\n\n      if obsolete.length > 1\n        plural = 's'\n        verb = 'are'\n      else\n        plural = ''\n        verb = 'is'\n      end\n\n      say \"\\n#{obsolete.length} fingerprint#{plural} #{verb} unused:\", :green\n      obsolete.each do |obs|\n        say obs\n      end\n\n      if yes_or_no \"\\nRemove fingerprint#{plural}?\"\n        @ignore_config.prune_obsolete\n      end\n    end\n\n    def prune_obsolete\n      @ignore_config.filter_ignored\n      obsolete = @ignore_config.obsolete_fingerprints\n      @ignore_config.prune_obsolete\n\n      say \"Removed #{obsolete.length} obsolete fingerprint#{'s' if obsolete.length > 1} from ignore config.\", :yellow\n    end\n\n    def final_menu\n      summarize_changes\n\n      HighLine.new.choose do |m|\n        m.choice \"Save changes\" do\n          save\n        end\n\n        m.choice \"Start over\" do\n          start_over\n        end\n\n        m.choice \"Quit, do not save changes\" do\n          quit\n        end\n      end\n    end\n\n    def save\n      @ignore_config.file = HighLine.new.ask \"Output file: \" do |q|\n        if @ignore_config.file and not @ignore_config.file.empty?\n          q.default = @ignore_config.file\n        else\n          q.default = \"config/brakeman.ignore\"\n        end\n      end\n\n      @ignore_config.save_with_old\n    end\n\n    def start_over\n      reset_config\n      @restart = true\n    end\n\n    def reset_config\n      @ignore_config = Brakeman::IgnoreConfig.new(@ignore_config.file, @new_warnings)\n    end\n\n    def process_warnings\n      @warning_count = @new_warnings.length\n\n      @new_warnings.each_with_index do |w, index|\n        @current_index = index\n\n        if skip_ignored? w or @skip_rest\n          next\n        elsif @ignore_rest\n          ignore w\n        elsif @quit or @restart\n          return\n        else\n          ask_about w\n        end\n      end\n    end\n\n    def ask_about warning\n      pretty_display warning\n      warning_action warning_menu, warning\n    end\n\n    def warning_action action, warning\n      case action\n      when \"i\"\n        ignore warning\n      when \"n\"\n        ignore_and_note warning\n      when \"s\"\n        # do nothing\n      when \"u\"\n        unignore warning\n      when \"a\"\n        ignore_rest warning\n      when \"k\"\n        skip_rest warning\n      when \"q\"\n        quit\n      when \"?\"\n        ask_about warning\n      else\n        raise \"Unexpected action\"\n      end\n    end\n\n    def ignore warning\n      @ignore_config.ignore warning\n    end\n\n    def ignore_and_note warning\n      note = HighLine.new.ask(\"Note: \")\n      @ignore_config.ignore warning\n      @ignore_config.add_note warning, note\n    end\n\n    def unignore warning\n      @ignore_config.unignore warning\n    end\n\n    def skip_rest warning\n      @skip_rest = true\n    end\n\n    def ignore_rest warning\n      ignore warning\n      @ignore_rest = true\n    end\n\n    def quit\n      reset_config\n      @ignore_config.read_from_file\n      @ignore_config.filter_ignored\n      @quit = true\n    end\n\n    def pretty_display warning\n      progress = \"#{@current_index + 1}/#{@warning_count}\"\n      say \"-------- #{progress} #{\"-\" * (20 - progress.length)}\", :cyan\n      show_confidence warning\n\n      label \"Category\"\n      say warning.warning_type\n\n      label \"Message\"\n      say warning.message\n\n      if warning.code\n        label \"Code\"\n        say warning.format_code\n      end\n\n      if warning.file\n        label \"File\"\n        say warning.file.relative\n      end\n\n      if warning.line\n        label \"Line\"\n        say warning.line\n      end\n\n      if already_ignored? warning\n        show_note warning\n        say \"Already ignored\", :red\n      end\n\n      say \"\"\n    end\n\n    def already_ignored? warning\n      @ignore_config.ignored? warning\n    end\n\n    def skip_ignored? warning\n      @skip_ignored and already_ignored? warning\n    end\n\n    def summarize_changes\n      say \"-\" * 30\n\n      say \"Ignoring #{@ignore_config.ignored_warnings.length} warnings\", :yellow\n      say \"Showing #{@ignore_config.shown_warnings.length} warnings\", :green\n    end\n\n    def label name\n      say \"#{name}: \", :green\n    end\n\n    def show_confidence warning\n      label \"Confidence\"\n\n      case warning.confidence\n      when 0\n        say \"High\", :red\n      when 1\n        say \"Medium\", :yellow\n      when 2\n        say \"Weak\", :cyan\n      else\n        say \"Unknown\"\n      end\n    end\n\n    def show_note warning\n      note = @ignore_config.note_for warning\n\n      if note\n        label \"Note\"\n        say note\n      end\n    end\n\n    def say text, color = nil\n      text = text.to_s\n\n      if color\n        HighLine.new.say HighLine.new.color(text, color)\n      else\n        HighLine.new.say text\n      end\n    end\n\n    def yes_or_no message\n      answer = HighLine.new.ask message do |q|\n        q.in = [\"y\", \"n\", \"yes\", \"no\"]\n      end\n\n      answer.match /^y/i\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/pager.rb",
    "content": "module Brakeman\n  class Pager\n    def initialize tracker, pager = :less, output = $stdout\n      @tracker = tracker\n      @pager = pager\n      @output = output\n      @less_available = @less_options = nil\n    end\n\n    def page_report report, format\n      if @pager == :less\n        set_color\n      end\n\n      text = report.format(format)\n\n      if in_ci?\n        no_pager text\n      else\n        page_output text\n      end\n    end\n\n    def page_output text\n      case @pager\n      when :none\n        no_pager text\n      when :highline\n        page_via_highline text\n      when :less\n        if less_available?\n          page_via_less text\n        else\n          page_via_highline text\n        end\n      else\n        no_pager text\n      end\n    end\n\n    def no_pager text\n      @output.puts text\n    end\n\n    def page_via_highline text\n      Brakeman.load_brakeman_dependency 'highline'\n      h = ::HighLine.new($stdin, @output)\n      h.page_at = :auto\n      h.say text\n    end\n\n    def page_via_less text\n      # Adapted from https://github.com/piotrmurach/tty-pager/\n\n      write_io = IO.popen(\"less #{less_options.join}\", 'w')\n      pid = write_io.pid\n\n      write_io.write(text)\n      write_io.close\n\n      Process.waitpid2(pid, Process::WNOHANG)\n    rescue Errno::ECHILD\n      # on jruby 9x waiting on pid raises (per tty-pager)\n      true\n    rescue => e\n      warn \"[Error] #{e}\"\n      warn \"[Error] Could not use pager. Set --no-pager to avoid this issue.\"\n      no_pager text\n    end\n\n    def in_ci?\n      ci = ENV[\"CI\"]\n\n      ci.is_a? String and ci.downcase == \"true\"\n    end\n\n    def less_available?\n      return @less_available unless @less_available.nil?\n\n      @less_available = system(\"which less > /dev/null\")\n    end\n\n    def less_options\n      # -R show colors\n      # -F exit if output fits on one screen\n      # -X do not clear screen after less exits\n\n      return @less_options if @less_options\n\n      @less_options = []\n\n      if system(\"which less > /dev/null\")\n        less_help = `less -?`\n\n        [\"-R \", \"-F \", \"-X \", \" --wordwrap\"].each do |opt|\n          if less_help.include? opt\n            @less_options << opt\n          end\n        end\n      end\n\n      @less_options\n    end\n\n    def set_color\n      return unless @tracker\n\n      unless less_options.include? \"-R \" or @tracker.options[:output_color] == :force\n        @tracker.options[:output_color] = false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/renderer.rb",
    "content": "require 'erb'\n\nclass Brakeman::Report\n  class Renderer\n    def initialize(template_file, hash = {})\n      hash[:locals] ||= {}\n      singleton = class << self; self end\n\n      hash[:locals].each do |attribute_name, attribute_value|\n        singleton.send(:define_method, attribute_name) { attribute_value }\n      end\n\n      # There are last, so as to make overwriting these using locals impossible.\n      singleton.send(:define_method, 'template_file') { template_file }\n      singleton.send(:define_method, 'template') {\n        File.read(File.expand_path(\"templates/#{template_file}.html.erb\", File.dirname(__FILE__)))\n      }\n    end\n\n    def render\n      ERB.new(template).result(binding)\n    end\n  end\nend"
  },
  {
    "path": "lib/brakeman/report/report_base.rb",
    "content": "require 'set'\nrequire 'brakeman/util'\nrequire 'brakeman/version'\nrequire 'brakeman/report/renderer'\nrequire 'brakeman/processors/output_processor'\nrequire 'brakeman/warning'\n\n# Base class for report formats\nclass Brakeman::Report::Base\n  include Brakeman::Util\n\n  attr_reader :tracker, :checks\n\n  def initialize tracker\n    @app_tree = tracker.app_tree\n    @tracker = tracker\n    @checks = tracker.checks\n    @ignore_filter = tracker.ignored_filter\n    @highlight_user_input = tracker.options[:highlight_user_input]\n    @warnings_summary = nil\n  end\n\n  #Return summary of warnings in hash and store in @warnings_summary\n  def warnings_summary\n    return @warnings_summary if @warnings_summary\n\n    summary = Hash.new(0)\n    high_confidence_warnings = 0\n\n    [all_warnings].each do |warnings|\n      warnings.each do |warning|\n        summary[warning.warning_type.to_s] += 1\n        high_confidence_warnings += 1 if warning.confidence == 0\n      end\n    end\n\n    summary[:high_confidence] = high_confidence_warnings\n    @warnings_summary = summary\n  end\n\n  def controller_information\n    controller_rows = []\n\n    tracker.controllers.keys.map{|k| k.to_s}.sort.each do |name|\n      name = name.to_sym\n      c = tracker.controllers[name]\n\n      if tracker.routes.include? :allow_all_actions or (tracker.routes[name] and tracker.routes[name].include? :allow_all_actions)\n        routes = c.methods_public.keys.map{|e| e.to_s}.sort.join(\", \")\n      elsif tracker.routes[name].nil?\n        #No routes defined for this controller.\n        #This can happen when it is only a parent class\n        #for other controllers, for example.\n        routes = \"[None]\"\n\n      else\n        routes = (Set.new(c.methods_public.keys) & tracker.routes[name.to_sym]).\n          to_a.\n          map {|e| e.to_s}.\n          sort.\n          join(\", \")\n      end\n\n      if routes == \"\"\n        routes = \"[None]\"\n      end\n\n      controller_rows << { \"Name\" => name.to_s,\n        \"Parent\" => c.parent.to_s,\n        \"Includes\" => c.includes.join(\", \"),\n        \"Routes\" => routes\n      }\n    end\n\n    controller_rows\n  end\n\n  def all_warnings\n    if @ignore_filter\n      @all_warnings ||= @ignore_filter.shown_warnings\n    else\n      @all_warnings ||= tracker.checks.all_warnings\n    end\n  end\n\n  def filter_warnings warnings\n    if @ignore_filter\n      warnings.reject do |w|\n        @ignore_filter.ignored? w\n      end\n    else\n      warnings\n    end\n  end\n\n  def generic_warnings\n    filter_warnings tracker.checks.warnings\n  end\n\n  def template_warnings\n    filter_warnings tracker.checks.template_warnings\n  end\n\n  def model_warnings\n    filter_warnings tracker.checks.model_warnings\n  end\n\n  def controller_warnings\n    filter_warnings tracker.checks.controller_warnings\n  end\n\n  def ignored_warnings\n    if @ignore_filter\n      @ignore_filter.ignored_warnings\n    else\n      []\n    end\n  end\n\n  def number_of_templates tracker\n    Set.new(tracker.templates.map {|k,v| v.name.to_s[/[^.]+/]}).length\n  end\n\n  def absolute_paths?\n    @tracker.options[:absolute_paths]\n  end\n\n  def warning_file warning\n    return nil if warning.file.nil?\n\n    if absolute_paths?\n      warning.file.absolute\n    else\n      warning.file.relative\n    end\n  end\n\n  #Return array of lines surrounding the warning location from the original\n  #file.\n  def context_for warning\n    file = warning.file\n    context = []\n    return context unless warning.line and file and file.exists?\n\n    current_line = 0\n    start_line = warning.line - 5\n    end_line = warning.line + 5\n\n    start_line = 1 if start_line < 0\n\n    File.open file do |f|\n      f.each_line do |line|\n        current_line += 1\n\n        next if line.strip == \"\"\n\n        if current_line > end_line\n          break\n        end\n\n        if current_line >= start_line\n          context << [current_line, line]\n        end\n      end\n    end\n\n    context\n  end\n\n  def rails_version\n    case\n    when tracker.config.rails_version\n      tracker.config.rails_version\n    when tracker.options[:rails4]\n      \"4.x\"\n    when tracker.options[:rails3]\n      \"3.x\"\n    else\n      \"Unknown\"\n    end\n  end\n\n  def github_url file, line=nil\n    if repo_url = @tracker.options[:github_url] and file\n      url = \"#{repo_url}/#{file.relative}\"\n      url << \"#L#{line}\" if line\n    else\n      nil\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_codeclimate.rb",
    "content": "require \"json\"\nrequire \"yaml\"\nrequire \"pathname\"\n\nclass Brakeman::Report::CodeClimate < Brakeman::Report::Base\n  DOCUMENTATION_PATH = File.expand_path(\"../../../../docs/warning_types\", __FILE__)\n  REMEDIATION_POINTS_CONFIG_PATH = File.expand_path(\"../config/remediation.yml\", __FILE__)\n  REMEDIATION_POINTS_DEFAULT = 300_000\n\n  def generate_report\n    all_warnings.map { |warning| issue_json(warning) }.join(\"\\0\")\n  end\n\n  private\n\n  def issue_json(warning)\n    warning_code_name = name_for(warning.warning_code)\n\n    {\n      type: \"Issue\",\n      check_name: warning_code_name,\n      description: warning.message,\n      fingerprint: warning.fingerprint,\n      categories: [\"Security\"],\n      severity: severity_level_for(warning.confidence),\n      remediation_points: remediation_points_for(warning_code_name),\n      location: {\n        path: file_path(warning),\n        lines: {\n          begin: warning.line || 1,\n          end: warning.line || 1,\n        }\n      },\n      content: {\n        body: content_for(warning.warning_code, warning.link)\n      }\n    }.to_json\n  end\n\n  def severity_level_for(confidence)\n    if confidence == 0\n      \"critical\"\n    else\n      \"normal\"\n    end\n  end\n\n  def remediation_points_for(warning_code)\n    @remediation_points ||= YAML.load_file(REMEDIATION_POINTS_CONFIG_PATH)\n    @remediation_points.fetch(name_for(warning_code), REMEDIATION_POINTS_DEFAULT)\n  end\n\n  def name_for(warning_code)\n    @warning_codes ||= Brakeman::WarningCodes::Codes.invert\n    @warning_codes[warning_code].to_s\n  end\n\n  def content_for(warning_code, link)\n    @contents ||= {}\n    unless link.nil?\n      @contents[warning_code] ||= local_content_for(link) || \"Read more: #{link}\"\n    end\n  end\n\n  def local_content_for(link)\n    directory = link.split(\"/\").last\n    filename = File.join(DOCUMENTATION_PATH, directory, \"index.markdown\")\n\n    File.read(filename) if File.exist?(filename)\n  end\n\n  def file_path(warning)\n    if tracker.options[:path_prefix]\n      (Pathname.new(tracker.options[:path_prefix]) + Pathname.new(warning.file.relative)).to_s\n    else\n      warning.relative_path\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_csv.rb",
    "content": "require 'csv'\n\nclass Brakeman::Report::CSV < Brakeman::Report::Base\n  def generate_report\n    headers = [\n      \"Confidence\",\n      \"Warning Type\",\n      \"CWE\",\n      \"File\",\n      \"Line\",\n      \"Message\",\n      \"Code\",\n      \"User Input\",\n      \"Check Name\",\n      \"Warning Code\",\n      \"Fingerprint\",\n      \"Link\"\n    ]\n\n    rows = tracker.filtered_warnings.sort_by do |w|\n      [w.confidence, w.warning_type, w.file, w.line || 0, w.fingerprint]\n    end.map do |warning|\n      generate_row(headers, warning)\n    end\n\n    table = CSV::Table.new(rows)\n\n    table.to_csv\n  end\n\n  def generate_row headers, warning\n    CSV::Row.new headers, warning_row(warning)\n  end\n\n  def warning_row warning\n    [\n      warning.confidence_name,\n      warning.warning_type,\n      warning.cwe_id.first,\n      warning_file(warning),\n      warning.line,\n      warning.message,\n      warning.code && warning.format_code(false),\n      warning.user_input && warning.format_user_input(false),\n      warning.check_name,\n      warning.warning_code,\n      warning.fingerprint,\n      warning.link,\n    ]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_github.rb",
    "content": "# GitHub Actions Formatter\n# Formats warnings as workflow commands to create annotations in GitHub UI\nclass Brakeman::Report::Github < Brakeman::Report::Base\n  def generate_report\n    # @see https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message\n    errors.concat(warnings).join(\"\\n\")\n  end\n\n  def warnings\n    all_warnings\n      .map { |warning| \"::warning file=#{warning_file(warning)},line=#{warning.line}::#{warning.message}\" }\n  end\n\n  def errors\n    tracker.errors.map do |error|\n      if error[:exception].is_a?(Racc::ParseError)\n        # app/services/balance.rb:4 :: parse error on value \"...\" (tDOT3)\n        file, line = error[:exception].message.split(':').map(&:strip)[0,2]\n        \"::error file=#{file},line=#{line}::#{clean_message(error[:error])}\"\n      else\n        \"::error ::#{clean_message(error[:error])}\"\n      end\n    end\n  end\n\n  private\n\n  def clean_message(msg)\n    msg.gsub('::','').squeeze(' ')\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_hash.rb",
    "content": "# Generates a hash table for use in Brakeman tests\nclass Brakeman::Report::Hash < Brakeman::Report::Base\n  def generate_report\n    report = { :errors => tracker.errors,\n               :controllers => tracker.controllers,\n               :models => tracker.models,\n               :templates => tracker.templates\n              }\n\n    [:generic_warnings, :controller_warnings, :model_warnings, :template_warnings].each do |meth|\n      report[meth] = self.send(meth)\n      report[meth].each do |w|\n        w.message = w.format_message\n        w.context = context_for(w).join(\"\\n\")\n      end\n    end\n\n    report[:config] = tracker.config\n    report[:checks_run] = tracker.checks.checks_run\n\n    report\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_html.rb",
    "content": "require 'cgi/escape'\nrequire 'brakeman/report/report_table.rb'\n\nclass Brakeman::Report::HTML < Brakeman::Report::Table\n  HTML_CONFIDENCE = [ \"<span class='high-confidence'>High</span>\",\n                      \"<span class='med-confidence'>Medium</span>\",\n                      \"<span class='weak-confidence'>Weak</span>\" ]\n\n  def initialize *args\n    super\n\n    @element_id = 0 #Used for HTML ids\n  end\n\n  def generate_report\n    out = html_header <<\n    generate_overview <<\n    generate_warning_overview.to_s\n\n    # Return early if only summarizing\n    return out if tracker.options[:summary_only]\n\n    out << generate_controllers.to_s if tracker.options[:report_routes] or tracker.options[:debug]\n    out << generate_templates.to_s if tracker.options[:debug]\n    out << generate_errors.to_s\n    out << generate_warnings.to_s\n    out << generate_controller_warnings.to_s\n    out << generate_model_warnings.to_s\n    out << generate_template_warnings.to_s\n    out << generate_ignored_warnings.to_s\n    out << \"</body></html>\"\n  end\n\n  def generate_overview\n      locals = {\n        :tracker => tracker,\n        :warnings => all_warnings.length,\n        :warnings_summary => warnings_summary,\n        :number_of_templates => number_of_templates(@tracker),\n        :ignored_warnings => ignored_warnings.length\n        }\n\n      Brakeman::Report::Renderer.new('overview', :locals => locals).render\n  end\n\n  #Generate listings of templates and their output\n  def generate_templates\n    out_processor = Brakeman::OutputProcessor.new\n    template_rows = {}\n    tracker.templates.each do |name, template|\n      template.each_output do |out|\n        out = CGI.escapeHTML(out_processor.format(out))\n        template_rows[name] ||= []\n        template_rows[name] << out.gsub(\"\\n\", \";\").gsub(/\\s+/, \" \")\n      end\n    end\n\n    template_rows = template_rows.sort_by{|name, value| name.to_s}\n\n      Brakeman::Report::Renderer.new('template_overview', :locals => {:template_rows => template_rows}).render\n  end\n\n  def render_array template, headings, value_array, locals\n    return if value_array.empty?\n\n    Brakeman::Report::Renderer.new(template, :locals => locals).render\n  end\n\n  def convert_warning warning, original\n    warning[\"Confidence\"] = HTML_CONFIDENCE[original.confidence]\n    warning[\"Message\"] = with_context original, warning[\"Message\"]\n    warning[\"Warning Type\"] = with_link original, warning[\"Warning Type\"]\n    warning\n  end\n\n  def with_link warning, message\n    \"<a rel=\\\"noreferrer\\\" href=\\\"#{warning.link}\\\">#{message}</a>\"\n  end\n\n  def convert_template_warning warning, original\n    warning = convert_warning warning, original\n    warning[\"Called From\"] = original.called_from\n    warning[\"Template Name\"] = original.template.name\n    warning\n  end\n\n  def convert_ignored_warning warning, original\n    warning = convert_warning(warning, original)\n    warning['File'] = original.file.relative\n    warning['Note'] = CGI.escapeHTML(@ignore_filter.note_for(original) || \"\")\n    warning\n  end\n\n  #Return header for HTML output. Uses CSS from tracker.options[:html_style]\n  def html_header\n    if File.exist? tracker.options[:html_style]\n      css = File.read tracker.options[:html_style]\n    else\n      raise \"Cannot find CSS stylesheet for HTML: #{tracker.options[:html_style]}\"\n    end\n\n    locals = {\n      :css => css,\n      :tracker => tracker,\n      :checks => checks,\n      :rails_version => rails_version,\n      :brakeman_version => Brakeman::Version\n      }\n\n    Brakeman::Report::Renderer.new('header', :locals => locals).render\n  end\n\n  #Generate HTML for warnings, including context show/hidden via Javascript\n  def with_context warning, message\n    @element_id += 1\n    context = context_for(warning)\n    message = html_message(warning, message)\n\n    code_id = \"context#@element_id\"\n    message_id = \"message#@element_id\"\n    full_message_id = \"full_message#@element_id\"\n    alt = false\n    output = \"<div class='warning_message' onClick=\\\"toggle('#{code_id}');toggle('#{message_id}');toggle('#{full_message_id}')\\\" >\" <<\n    message <<\n    \"<table id='#{code_id}' class='context' style='display:none'>\" <<\n    \"<caption>#{CGI.escapeHTML warning_file(warning) || ''}</caption>\"\n\n    output << <<-HTML\n      <thead style='display:none'>\n        <tr>\n          <th>line number</th>\n          <th>line content</th>\n        </tr>\n      </thead>\n      <tbody>\n    HTML\n\n    unless context.empty?\n      if warning.line - 1 == 1 or warning.line + 1 == 1\n        error = \" near_error\"\n      elsif 1 == warning.line\n        error = \" error\"\n      else\n        error = \"\"\n      end\n\n      output << <<-HTML\n        <tr class='context first#{error}'>\n          <td class='context_line'>\n            <pre class='context'>#{context.first[0]}</pre>\n          </td>\n          <td class='context'>\n            <pre class='context'>#{CGI.escapeHTML context.first[1].chomp}</pre>\n          </td>\n        </tr>\n      HTML\n\n      if context.length > 1\n        output << context[1..-1].map do |code|\n          alt = !alt\n          if code[0] == warning.line - 1 or code[0] == warning.line + 1\n            error = \" near_error\"\n          elsif code[0] == warning.line\n            error = \" error\"\n          else\n            error = \"\"\n          end\n\n          <<-HTML\n          <tr class='context#{alt ? ' alt' : ''}#{error}'>\n            <td class='context_line'>\n              <pre class='context'>#{code[0]}</pre>\n            </td>\n            <td class='context'>\n              <pre class='context'>#{CGI.escapeHTML code[1].chomp}</pre>\n            </td>\n          </tr>\n          HTML\n        end.join\n      end\n    end\n\n    output << \"</tbody></table></div>\"\n  end\n\n  #Escape warning message and highlight user input in HTML output\n  def html_message warning, message\n    message = message.to_html\n\n    if warning.file\n      if github_url = github_url(warning.file, warning.line)\n        message << \" <a href=\\\"#{github_url}\\\" target='_blank'>near line #{warning.line}</a>\"\n      elsif warning.line\n        message << \" near line #{warning.line}\"\n      end\n    end\n\n    if warning.code\n      code = warning.format_with_user_input do |_, user_input|\n        \"[BMP_UI]#{user_input}[/BMP_UI]\"\n      end\n\n      code = \"<span class=\\\"code\\\">#{CGI.escapeHTML(code).gsub(\"[BMP_UI]\", \"<span class=\\\"user_input\\\">\").gsub(\"[/BMP_UI]\", \"</span>\")}</span>\"\n      full_message = \"#{message}: #{code}\"\n\n      if warning.code.mass > 20\n        message_id = \"message#@element_id\"\n        full_message_id = \"full_message#@element_id\"\n\n        \"<span id='#{message_id}' style='display:block'>#{message}: ...</span>\" <<\n        \"<span id='#{full_message_id}' style='display:none'>#{full_message}</span>\"\n      else\n        full_message\n      end\n    else\n      message\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_json.rb",
    "content": "class Brakeman::Report::JSON < Brakeman::Report::Base\n  def generate_report\n    errors = tracker.errors.map{|e| { :error => e[:error], :location => e[:backtrace][0] }}\n\n    obsolete = tracker.unused_fingerprints\n\n    warnings = convert_to_hashes all_warnings\n\n    ignored = convert_to_hashes ignored_warnings\n\n    scan_info = {\n      :app_path => tracker.app_path,\n      :rails_version => rails_version,\n      :security_warnings => all_warnings.length,\n      :start_time => tracker.start_time.to_s,\n      :end_time => tracker.end_time.to_s,\n      :duration => tracker.duration,\n      :checks_performed => checks.checks_run.sort,\n      :number_of_controllers => tracker.controllers.length,\n      # ignore the \"fake\" model\n      :number_of_models => tracker.models.length - 1,\n      :number_of_templates => number_of_templates(@tracker),\n      :ruby_version => RUBY_VERSION,\n      :brakeman_version => Brakeman::Version\n    }\n\n    report_info = {\n      :scan_info => scan_info,\n      :warnings => warnings,\n      :ignored_warnings => ignored,\n      :errors => errors,\n      :obsolete => obsolete\n    }\n\n    JSON.pretty_generate report_info\n  end\n\n  def convert_to_hashes warnings\n    warnings.map do |w|\n      w.to_hash(absolute_paths: false)\n    end.sort_by { |w| \"#{w[:fingerprint]}#{w[:line]}\" }\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_junit.rb",
    "content": "require 'time'\nrequire 'stringio'\nBrakeman.load_brakeman_dependency 'rexml/document'\n\nclass Brakeman::Report::JUnit < Brakeman::Report::Base\n  def generate_report\n    io = StringIO.new\n    doc = REXML::Document.new\n    doc.add REXML::XMLDecl.new '1.0', 'UTF-8'\n\n    test_suites = REXML::Element.new 'testsuites'\n\n    i = 0\n    all_warnings\n      .map { |warning| [warning.file, [warning]] }\n      .reduce({}) { |entries, entry|\n        key, value = entry\n        entries[key] = entries[key] ? entries[key].concat(value) : value\n        entries\n      }\n      .each { |file, warnings|\n        i += 1\n        test_suite = test_suites.add_element 'testsuite'\n        test_suite.add_attribute 'id', i\n        test_suite.add_attribute 'package', 'brakeman'\n        test_suite.add_attribute 'file', file.relative\n        test_suite.add_attribute 'timestamp', tracker.start_time.strftime('%FT%T')\n        test_suite.add_attribute 'tests', checks.checks_run.length\n        test_suite.add_attribute 'failures', warnings.length\n        test_suite.add_attribute 'errors', '0'\n        test_suite.add_attribute 'time', '0'\n\n        warnings.each { |warning|\n          test_case = test_suite.add_element 'testcase'\n          test_case.add_attribute 'name', warning.check.sub(/^Brakeman::/, '')\n          test_case.add_attribute 'file', file.relative\n          test_case.add_attribute 'line', warning.line if warning.line\n          test_case.add_attribute 'time', '0'\n\n          failure = test_case.add_element 'failure'\n          failure.add_attribute 'message', warning.message\n          failure.add_attribute 'type', warning.warning_type\n          failure.add_text warning.to_s\n        }\n      }\n\n    doc.add test_suites\n    doc.write io\n    io.string\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_markdown.rb",
    "content": "require 'brakeman/report/report_table'\n\nclass Brakeman::Report::Markdown < Brakeman::Report::Table\n\n  class MarkdownTable < Terminal::Table\n\n    def initialize options = {}, &block\n      options[:style] ||= {}\n      options[:style].merge!({\n          :border_x => '-',\n          :border_y => '|',\n          :border_i => '|'\n      })\n      super options, &block\n    end\n\n    def render\n      super.split(\"\\n\")[1...-1].join(\"\\n\")\n    end\n    alias :to_s :render\n\n  end\n\n  def initialize *args\n    super\n    @table = MarkdownTable\n  end\n\n  def generate_report\n    out = +\"# BRAKEMAN REPORT\\n\\n\" <<\n    generate_metadata.to_s << \"\\n\\n\" <<\n    generate_checks.to_s << \"\\n\\n\" <<\n    \"### SUMMARY\\n\\n\" <<\n    generate_overview.to_s << \"\\n\\n\" <<\n    generate_warning_overview.to_s << \"\\n\\n\"\n\n    #Return output early if only summarizing\n    return out if tracker.options[:summary_only]\n\n    if tracker.options[:report_routes] or tracker.options[:debug]\n      out << \"### CONTROLLERS\"  << \"\\n\\n\" <<\n      generate_controllers.to_s << \"\\n\\n\"\n    end\n\n    if tracker.options[:debug]\n      out << \"### TEMPLATES\\n\\n\" <<\n      generate_templates.to_s << \"\\n\\n\"\n    end\n\n    output_table(\"Errors\", generate_errors, out)\n    output_table(\"SECURITY WARNINGS\", generate_warnings, out)\n    output_table(\"Controller Warnings:\", generate_controller_warnings, out)\n    output_table(\"Model Warnings:\", generate_model_warnings, out)\n    output_table(\"View Warnings:\", generate_template_warnings, out)\n\n    out\n  end\n\n  def output_table title, result, output\n    return unless result\n\n    output << \"### #{title}\\n\\n#{result.to_s}\\n\\n\"\n  end\n\n  def generate_metadata\n    MarkdownTable.new(\n      :headings =>\n        ['Application path', 'Rails version', 'Brakeman version', 'Started at', 'Duration']\n    ) do |t|\n      t.add_row([\n        tracker.app_path,\n        rails_version,\n        Brakeman::Version,\n        tracker.start_time,\n        \"#{tracker.duration} seconds\",\n      ])\n    end\n  end\n\n  def generate_checks\n    MarkdownTable.new(:headings => ['Checks performed']) do |t|\n      t.add_row([checks.checks_run.sort.join(\", \")])\n    end\n  end\n\n  def convert_warning warning, original\n    warning[\"Message\"] = markdown_message original, warning[\"Message\"]\n    warning[\"Warning Type\"] = \"[#{warning['Warning Type']}](#{original.link})\" if original.link\n    warning\n  end\n\n  # Escape and code format warning message\n  def markdown_message warning, message\n    message = message.to_s\n\n    if warning.file\n      github_url = github_url warning.file, warning.line\n\n      if github_url\n        message << \" near line [#{warning.line}](#{github_url})\"\n      elsif warning.line\n        message << \" near line #{warning.line}\"\n      end\n    end\n\n    if warning.code\n      code = warning.format_code.gsub('`','``').gsub(/\\A``|``\\z/, '` `')\n      message << \": `#{code}`\"\n    end\n\n    message\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_sarif.rb",
    "content": "require 'uri'\n\nclass Brakeman::Report::SARIF < Brakeman::Report::Base\n  def generate_report\n    sarif_log = {\n      :version => '2.1.0',\n      :$schema => 'https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json',\n      :runs => runs,\n    }\n    JSON.pretty_generate sarif_log\n  end\n\n  def runs\n    [\n      {\n        :tool => {\n          :driver => {\n            :name => 'Brakeman',\n            :informationUri => 'https://brakemanscanner.org',\n            :semanticVersion => Brakeman::Version,\n            :rules => rules,\n          },\n        },\n        :results => results,\n      }.merge(original_uri_base_ids)\n    ]\n  end\n\n  # Output base URIs\n  # based on what the user specified for the application path\n  # and whether or not --absolute-paths was set.\n  def original_uri_base_ids\n    if tracker.options[:app_path] == '.'\n      # Probably no app_path was specified, as that's the default\n\n      if absolute_paths?\n        # Set %SRCROOT% to absolute path\n        {\n          originalUriBaseIds: {\n            '%SRCROOT%' => {\n              uri: file_uri(tracker.app_tree.root),\n              description: {\n                text: 'Base path for application'\n              }\n            }\n          }\n        }\n      else\n        # Empty %SRCROOT%\n        # This avoids any paths appearing in the report\n        # that are not part of the application directory.\n        # Seems fine!\n        {\n          originalUriBaseIds: {\n            '%SRCROOT%' => {\n              description: {\n                text: 'Base path for application'\n              }\n            },\n          }\n        }\n\n      end\n    elsif tracker.options[:app_path] != tracker.app_tree.root\n      # Path was specified and it was relative\n\n      if absolute_paths?\n        # Include absolute root and relative application path\n        {\n          originalUriBaseIds: {\n            PROJECTROOT: {\n              uri: file_uri(tracker.app_tree.root),\n              description: {\n                text: 'Base path for all project files'\n              }\n            },\n            '%SRCROOT%' => {\n              # Technically should ensure this doesn't have any '..'\n              # but... TODO\n              uri: File.join(tracker.options[:app_path], '/'),\n              uriBaseId: 'PROJECTROOT',\n              description: {\n                text: 'Base path for application'\n              }\n            }\n          }\n        }\n      else\n        # Just include relative application path.\n        # Not clear this is 100% valid, but there is one example in the spec like this\n        {\n          originalUriBaseIds: {\n            PROJECTROOT: {\n              description: {\n                text: 'Base path for all project files'\n              }\n            },\n            '%SRCROOT%' => {\n              # Technically should ensure this doesn't have any '..'\n              # but... TODO\n              uri: File.join(tracker.options[:app_path], '/'),\n              uriBaseId: 'PROJECTROOT',\n              description: {\n                text: 'Base path for application'\n              }\n            }\n          }\n        }\n      end\n    else\n      # app_path was absolute\n\n      if absolute_paths?\n        # Set %SRCROOT% to absolute path\n        {\n          originalUriBaseIds: {\n            '%SRCROOT%' => {\n              uri: file_uri(tracker.app_tree.root),\n              description: {\n                text: 'Base path for application'\n              }\n            }\n          }\n        }\n      else\n        # Empty %SRCROOT%\n        # Seems fine!\n        {\n          originalUriBaseIds: {\n            '%SRCROOT%' => {\n              description: {\n                text: 'Base path for application'\n              }\n            },\n          }\n        }\n      end\n    end\n  end\n\n  def rules\n    @rules ||= unique_warnings_by_warning_code.map do |warning|\n      rule_id = render_id warning\n      check_name = warning.check_name\n      check_description = render_message check_descriptions[check_name]\n      {\n        :id => rule_id,\n        :name => \"#{check_name}/#{warning.warning_type}\",\n        :fullDescription => {\n          :text => check_description,\n        },\n        :helpUri => warning.link,\n        :help => {\n          :text => \"More info: #{warning.link}.\",\n          :markdown => \"[More info](#{warning.link}).\",\n        },\n        :properties => {\n          :tags => [check_name],\n        },\n      }\n    end\n  end\n\n  def results\n    @results ||= tracker.checks.all_warnings.map do |warning|\n      rule_id = render_id warning\n      result_level = infer_level warning\n      message_text = render_message warning.message.to_s\n      result = {\n        :ruleId => rule_id,\n        :ruleIndex => rules.index { |r| r[:id] == rule_id },\n        :level => result_level,\n        :message => {\n          :text => message_text,\n        },\n        :locations => [\n          :physicalLocation => {\n            :artifactLocation => {\n              :uri => warning.file.relative,\n              :uriBaseId => '%SRCROOT%',\n            },\n            :region => {\n              :startLine => warning.line.is_a?(Integer) ? warning.line : 1,\n            },\n          },\n        ],\n      }\n\n      if @ignore_filter && @ignore_filter.ignored?(warning)\n        result[:suppressions] = [\n          {\n            :kind => 'external',\n            :justification => @ignore_filter.note_for(warning),\n            :location => {\n              :physicalLocation => {\n                :artifactLocation => {\n                  :uri => Brakeman::FilePath.from_app_tree(@app_tree, @ignore_filter.file).relative,\n                  :uriBaseId => '%SRCROOT%',\n                },\n              },\n            },\n          }\n        ]\n      end\n\n      result\n    end\n  end\n\n  # Returns a hash of all check descriptions, keyed by check name\n  def check_descriptions\n    @check_descriptions ||= Brakeman::Checks.checks.map do |check|\n      [check.name.gsub(/^Check/, ''), check.description]\n    end.to_h\n  end\n\n  # Returns a de-duplicated set of warnings, used to generate rules\n  def unique_warnings_by_warning_code\n    @unique_warnings_by_warning_code ||= tracker.checks.all_warnings.uniq { |w| w.warning_code }\n  end\n\n  def render_id warning\n    # Include alpha prefix to provide 'compiler error' appearance\n    \"BRAKE#{'%04d' % warning.warning_code}\" # 46 becomes BRAKE0046, for example\n  end\n\n  def render_message message\n    return message if message.nil?\n\n    # Ensure message ends with a period\n    if message.end_with? \".\"\n      message\n    else\n      \"#{message}.\"\n    end\n  end\n\n  def infer_level warning\n    # Infer result level from warning confidence\n    @@levels_from_confidence ||= Hash.new('warning').update({\n      0 => 'error',    # 0 represents 'high confidence', which we infer as 'error'\n      1 => 'warning',  # 1 represents 'medium confidence' which we infer as 'warning'\n      2 => 'note',     # 2 represents 'weak, or low, confidence', which we infer as 'note'\n    })\n    @@levels_from_confidence[warning.confidence]\n  end\n\n  # File URI as a string with trailing forward-slash\n  # as required by SARIF standard\n  def file_uri(path)\n    URI::File.build(path: File.join(path, '/')).to_s\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_sonar.rb",
    "content": "class Brakeman::Report::Sonar < Brakeman::Report::Base\n  def generate_report\n    report_object = {\n      issues: all_warnings.map { |warning| issue_json(warning) }\n    }\n    return JSON.pretty_generate report_object\n  end\n  \n  private\n  \n  def issue_json(warning)\n    {\n      engineId: \"Brakeman\",\n      ruleId: warning.warning_code,\n      type: \"VULNERABILITY\",\n      severity: severity_level_for(warning.confidence),\n      primaryLocation: {\n        message: warning.message,\n        filePath: warning.file.relative,\n        textRange: {\n          \"startLine\": warning.line || 1,\n          \"endLine\": warning.line || 1,\n        }\n      },\n      effortMinutes: (4 - warning.confidence) * 15\n    }\n  end\n\n  def severity_level_for(confidence)\n    if confidence == 0\n      \"CRITICAL\"\n    elsif confidence == 1\n      \"MAJOR\"\n    else\n      \"MINOR\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_table.rb",
    "content": "Brakeman.load_brakeman_dependency 'terminal-table'\n\nclass Brakeman::Report::Table < Brakeman::Report::Base\n  def initialize *args\n    super\n    @table = Terminal::Table\n  end\n\n  def generate_report\n    summary_option = tracker.options[:summary_only]\n    out = +\"\"\n\n    unless summary_option == :no_summary\n      out << text_header <<\n        \"\\n\\n+SUMMARY+\\n\\n\" <<\n        truncate_table(generate_overview.to_s) << \"\\n\\n\" <<\n        truncate_table(generate_warning_overview.to_s) << \"\\n\"\n    end\n\n    #Return output early if only summarizing\n    if summary_option == :summary_only or summary_option == true\n      return out\n    end\n\n    if tracker.options[:report_routes] or tracker.options[:debug]\n      out << \"\\n+CONTROLLERS+\\n\" <<\n      truncate_table(generate_controllers.to_s) << \"\\n\"\n    end\n\n    if tracker.options[:debug]\n      out << \"\\n+TEMPLATES+\\n\\n\" <<\n      truncate_table(generate_templates.to_s) << \"\\n\"\n    end\n\n    output_table(\"+Obsolete Ignore Entries+\", generate_obsolete, out)\n    output_table(\"+Errors+\", generate_errors, out)\n    output_table(\"+SECURITY WARNINGS+\", generate_warnings, out)\n    output_table(\"Controller Warnings:\", generate_controller_warnings, out)\n    output_table(\"Model Warnings:\", generate_model_warnings, out)\n    output_table(\"View Warnings:\", generate_template_warnings, out)\n\n    out << \"\\n\"\n    out\n  end\n\n  def output_table title, result, output\n    return unless result\n\n    output << \"\\n\\n#{title}\\n\\n#{truncate_table(result.to_s)}\"\n  end\n\n  def generate_overview\n    num_warnings = all_warnings.length\n\n    @table.new(:headings => ['Scanned/Reported', 'Total']) do |t|\n      t.add_row ['Controllers', tracker.controllers.length]\n      t.add_row ['Models', tracker.models.length - 1]\n      t.add_row ['Templates', number_of_templates(@tracker)]\n      t.add_row ['Errors', tracker.errors.length]\n      t.add_row ['Security Warnings', \"#{num_warnings} (#{warnings_summary[:high_confidence]})\"]\n      t.add_row ['Ignored Warnings', ignored_warnings.length] unless ignored_warnings.empty?\n    end\n  end\n\n  #Generate table of how many warnings of each warning type were reported\n  def generate_warning_overview\n    types = warnings_summary.keys\n    types.delete :high_confidence\n    values = types.sort.collect{|warning_type| [warning_type, warnings_summary[warning_type]] }\n    locals = {:types => types, :warnings_summary => warnings_summary}\n\n    render_array('warning_overview', ['Warning Type', 'Total'], values, locals)\n  end\n\n  #Generate table of controllers and routes found for those controllers\n  def generate_controllers\n    controller_rows = controller_information\n\n    cols = ['Name', 'Parent', 'Includes', 'Routes']\n\n    locals = {:controller_rows => controller_rows}\n    values = controller_rows.collect{|row| row.values_at(*cols) }\n    render_array('controller_overview', cols, values, locals)\n  end\n\n  #Generate table of errors or return nil if no errors\n  def generate_errors\n    values = tracker.errors.collect{|error| [error[:error], error[:backtrace][0]]}\n    render_array('error_overview', ['Error', 'Location'], values, {:tracker => tracker})\n  end\n\n  def generate_obsolete\n    values = tracker.unused_fingerprints.collect{|fingerprint| [fingerprint] }\n    render_array('obsolete_ignore_entries', ['fingerprint'], values, {:tracker => tracker})\n  end\n\n  def generate_warnings\n    render_warnings generic_warnings,\n                    :warning,\n                    'security_warnings',\n                    [\"Confidence\", \"Class\", \"Method\", \"Warning Type\", \"CWE ID\", \"Message\"],\n                    'Class'\n  end\n\n  #Generate table of template warnings or return nil if no warnings\n  def generate_template_warnings\n    render_warnings template_warnings,\n                    :template,\n                    'view_warnings',\n                    ['Confidence', 'Template', 'Warning Type', \"CWE ID\", 'Message'],\n                    'Template'\n\n  end\n\n  #Generate table of model warnings or return nil if no warnings\n  def generate_model_warnings\n    render_warnings model_warnings,\n                    :model,\n                    'model_warnings',\n                    ['Confidence', 'Model', 'Warning Type', \"CWE ID\", 'Message'],\n                    'Model'\n  end\n\n  #Generate table of controller warnings or nil if no warnings\n  def generate_controller_warnings\n    render_warnings controller_warnings,\n                    :controller,\n                    'controller_warnings',\n                    ['Confidence', 'Controller', 'Warning Type', \"CWE ID\", 'Message'],\n                    'Controller'\n  end\n\n  def generate_ignored_warnings\n    render_warnings ignored_warnings,\n                    :ignored,\n                    'ignored_warnings',\n                    ['Confidence', 'Warning Type', \"CWE ID\", 'File', 'Message'],\n                    'Warning Type'\n  end\n\n  def render_warnings warnings, type, template, cols, sort_col\n    unless warnings.empty?\n      rows = sort(convert_to_rows(warnings, type), sort_col)\n\n      values = rows.collect { |row| row.values_at(*cols) }\n\n      locals = { :warnings => rows }\n\n      render_array(template, cols, values, locals)\n    else\n      nil\n    end\n  end\n\n  #Generate listings of templates and their output\n  def generate_templates\n    out_processor = Brakeman::OutputProcessor.new\n    template_rows = {}\n    tracker.templates.each do |name, template|\n      template.each_output do |out|\n        out = out_processor.format out\n        template_rows[name] ||= []\n        template_rows[name] << out.gsub(\"\\n\", \";\").gsub(/\\s+/, \" \")\n      end\n    end\n\n    template_rows = template_rows.sort_by{|name, value| name.to_s}\n\n    output = +''\n    template_rows.each do |template|\n      output << template.first.to_s << \"\\n\\n\"\n      table = @table.new(:headings => ['Output']) do |t|\n        # template[1] is an array of calls\n        template[1].each do |v|\n          t.add_row [v]\n        end\n      end\n\n      output << table.to_s << \"\\n\\n\"\n    end\n\n    output\n  end\n\n  def convert_to_rows warnings, type = :warning\n    warnings.map do |warning|\n      w = warning.to_row type\n\n      case type\n      when :warning\n        convert_warning w, warning\n      when :ignored\n        convert_ignored_warning w, warning\n      when :template\n        convert_template_warning w, warning\n      else\n        convert_warning w, warning\n      end\n    end\n  end\n\n  def convert_ignored_warning warning, original\n    convert_warning warning, original\n  end\n\n  def convert_template_warning warning, original\n    convert_warning warning, original\n  end\n\n  def sort rows, sort_col\n    stabilizer = 0\n    rows.sort_by do |row|\n      stabilizer += 1\n\n      row.values_at(\"Confidence\", \"Warning Type\", sort_col) << stabilizer\n    end\n  end\n\n  def render_array template, headings, value_array, locals\n    return if value_array.empty?\n\n    @table.new(:headings => headings) do |t|\n      value_array.each { |value_row| t.add_row value_row }\n    end\n  end\n\n  def convert_warning warning, original\n    warning[\"Message\"] = text_message original, warning[\"Message\"]\n\n    warning\n  end\n\n  #Escape warning message and highlight user input in text output\n  def text_message warning, message\n    message = message.to_s\n\n    if warning.line\n      message << \" near line #{warning.line}\"\n    end\n\n    if warning.code\n      if @highlight_user_input and warning.user_input\n        code = warning.format_with_user_input do |user_input, user_input_string|\n          \"+#{user_input_string}+\"\n        end\n      else\n        code = warning.format_code\n      end\n\n      message << \": #{code}\"\n    end\n\n    message\n  end\n\n  #Generate header for text output\n  def text_header\n    <<-HEADER\n\n+BRAKEMAN REPORT+\n\nApplication path: #{tracker.app_path}\nRails version: #{rails_version}\nBrakeman version: #{Brakeman::Version}\nStarted at #{tracker.start_time}\nDuration: #{tracker.duration} seconds\nChecks run: #{checks.checks_run.sort.join(\", \")}\nHEADER\n  end\n\n  def truncate_table str\n    @terminal_width ||= if @tracker.options[:table_width]\n                          @tracker.options[:table_width]\n                        elsif $stdin && $stdin.tty?\n                          Brakeman.load_brakeman_dependency 'highline'\n                          ::HighLine.default_instance.terminal.terminal_size[0]\n                        else\n                          80\n                        end\n    lines = str.lines\n\n    lines.map do |line|\n      if line.chomp.length > @terminal_width\n        line[0..(@terminal_width - 3)] + \">>\\n\"\n      else\n        line\n      end\n    end.join\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_tabs.rb",
    "content": "require 'brakeman/report/report_table'\n\n#Generated tab-separated output suitable for the Jenkins Brakeman Plugin:\n#https://github.com/presidentbeef/brakeman-jenkins-plugin\nclass Brakeman::Report::Tabs < Brakeman::Report::Table\n  def generate_report\n    [[:generic_warnings, \"General\"], [:controller_warnings, \"Controller\"],\n      [:model_warnings, \"Model\"], [:template_warnings, \"Template\"]].map do |meth, category|\n\n      self.send(meth).map do |w|\n        line = w.line || 0\n        \"#{(w.file.absolute)}\\t#{line}\\t#{w.warning_type}\\t#{category}\\t#{w.format_message}\\t#{w.confidence_name}\"\n      end.join \"\\n\"\n\n    end.join \"\\n\"\n\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/report_text.rb",
    "content": "Brakeman.load_brakeman_dependency 'highline'\n\nclass Brakeman::Report::Text < Brakeman::Report::Base\n  def generate_report\n    HighLine.use_color = !!tracker.options[:output_color]\n    summary_option = tracker.options[:summary_only]\n    @output_string = +\"\\n\"\n\n    unless summary_option == :no_summary\n      add_chunk generate_header\n      add_chunk generate_overview\n      add_chunk generate_warning_overview\n    end\n\n    if summary_option == :summary_only or summary_option == true\n      return @output_string\n    end\n\n    add_chunk generate_controllers if tracker.options[:debug] or tracker.options[:report_routes]\n    add_chunk generate_templates if tracker.options[:debug]\n    add_chunk generate_obsolete\n    add_chunk generate_errors\n    add_chunk generate_warnings\n    add_chunk generate_show_ignored_overview if tracker.options[:show_ignored] && ignored_warnings.any?\n\n    @output_string\n  end\n\n  def add_chunk chunk, out = @output_string\n    if chunk and not chunk.empty?\n      if chunk.is_a? Array\n        chunk = chunk.join(\"\\n\")\n      end\n\n      out << chunk << \"\\n\\n\"\n    end\n  end\n\n  def generate_controllers\n    double_space \"Controller Overview\", controller_information.map { |ci|\n      controller = [\n        label(\"Controller\", ci[\"Name\"]),\n        label(\"Parent\", ci[\"Parent\"]),\n        label(\"Routes\", ci[\"Routes\"])\n      ]\n\n      if ci[\"Includes\"] and not ci[\"Includes\"].empty?\n        controller.insert(2, label(\"Includes\", ci[\"Includes\"]))\n      end\n\n      controller\n    }\n  end\n\n  def generate_header\n    [\n      header(\"Brakeman Report\"),\n      label(\"Application Path\", tracker.app_path),\n      label(\"Rails Version\", rails_version),\n      label(\"Brakeman Version\", Brakeman::Version),\n      label(\"Scan Date\", tracker.start_time),\n      label(\"Duration\", \"#{tracker.duration} seconds\"),\n      label(\"Checks Run\", checks.checks_run.sort.join(\", \"))\n    ]\n  end\n\n  def generate_overview\n    overview = [\n      header(\"Overview\"),\n      label('Controllers', tracker.controllers.length),\n      label('Models', tracker.models.length - 1),\n      label('Templates', number_of_templates(@tracker)),\n      label('Errors', tracker.errors.length),\n      label('Security Warnings', all_warnings.length)\n    ]\n\n    unless ignored_warnings.empty?\n      overview << label('Ignored Warnings', ignored_warnings.length)\n    end\n\n    overview\n  end\n\n  def generate_warning_overview\n    warning_types = warnings_summary\n    warning_types.delete :high_confidence\n\n    warning_types.sort_by { |t, c| t }.map do |type, count|\n      label(type, count)\n    end.unshift(header('Warning Types'))\n  end\n\n  def generate_warnings\n    if tracker.filtered_warnings.empty?\n      HighLine.color(\"No warnings found\", :bold, :green)\n    else\n      warnings = tracker.filtered_warnings.sort_by do |w|\n        [w.confidence, w.warning_type, w.file, w.line || 0, w.fingerprint]\n      end.map do |w|\n        output_warning w\n      end\n\n      double_space \"Warnings\", warnings\n    end\n  end\n\n  def generate_show_ignored_overview\n    double_space(\"Ignored Warnings\", ignored_warnings.map {|w| output_warning w})\n  end\n\n  def generate_errors\n    return if tracker.errors.empty?\n    full_trace = tracker.options[:debug]\n\n    errors = tracker.errors.map do |e|\n      trace = if full_trace\n        e[:backtrace].join(\"\\n\")\n      else\n        e[:backtrace][0]\n      end\n\n      [\n        label(\"Error\", e[:error]),\n        label(\"Location\", trace)\n      ]\n    end\n\n    double_space \"Errors\", errors\n  end\n\n  def generate_obsolete\n    return if tracker.unused_fingerprints.empty?\n\n    [header(\"Obsolete Ignore Entries\")] + tracker.unused_fingerprints\n  end\n\n  def generate_templates\n    out_processor = Brakeman::OutputProcessor.new\n\n    template_rows = {}\n    tracker.templates.each do |name, template|\n      template.each_output do |out|\n        out = out_processor.format out\n        template_rows[name] ||= []\n        template_rows[name] << out.gsub(\"\\n\", \";\").gsub(/\\s+/, \" \")\n      end\n    end\n\n    double_space \"Template Output\", template_rows.sort_by { |name, value| name.to_s }.map { |template|\n      [HighLine.new.color(\"#{template.first}\\n\", :cyan)] + template[1]\n    }.compact\n  end\n\n  def output_warning w\n    text_format = tracker.options[:text_fields] ||\n      [:confidence, :category, :check, :message, :code, :file, :line]\n\n    text_format.map do |option|\n      format_line(w, option)\n    end.compact\n  end\n\n  def format_line w, option\n    case option\n    when :confidence\n      label('Confidence', confidence(w.confidence))\n    when :category\n      label('Category', w.warning_type.to_s)\n    when :cwe\n      label('CWE', w.cwe_id.join(', '))\n    when :check\n      label('Check', w.check_name)\n    when :message\n      label('Message', w.message)\n    when :code\n      if w.code\n        label('Code', format_code(w))\n      end\n    when :file\n      label('File', warning_file(w))\n    when :line\n      if w.line\n        label('Line', w.line)\n      end\n    when :link\n      label('Link', w.link)\n    when :fingerprint\n      label('Fingerprint', w.fingerprint)\n    when :category_id\n      label('Category ID', w.warning_code)\n    when :render_path\n      if w.called_from\n        label('Render Path', w.called_from.join(\" > \"))\n      end\n    end\n  end\n\n  def double_space title, values\n    values = values.map { |v| v.join(\"\\n\") }.join(\"\\n\\n\")\n    [header(title), values]\n  end\n\n  def format_code w\n    if @highlight_user_input and w.user_input\n      w.format_with_user_input do |exp, text|\n        HighLine.new.color(text, :yellow)\n      end\n    else\n      w.format_code\n    end\n  end\n\n  def confidence c\n    case c\n    when 0\n      HighLine.new.color(\"High\", :red)\n    when 1\n      HighLine.new.color(\"Medium\", :yellow)\n    when 2\n      HighLine.new.color(\"Weak\", :none)\n    end\n  end\n\n  def label l, value, color = :green\n    \"#{HighLine.new.color(l, color)}: #{value}\"\n  end\n\n  def header text\n    HighLine.new.color(\"== #{text} ==\\n\", :bold, :magenta)\n  end\n\n  # ONLY used for generate_controllers to avoid duplication\n  def render_array name, cols, values, locals\n    controllers = values.map do |controller_name, parent, includes, routes|\n      c = [ label(\"Controller\", controller_name) ]\n      c << label(\"Parent\", parent) unless parent.empty?\n      c << label(\"Includes\", includes) unless includes.empty?\n      c << label(\"Routes\", routes)\n    end\n\n    double_space \"Controller Overview\", controllers\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/report/templates/controller_overview.html.erb",
    "content": "<h2>Controllers</h2>\n\n<table>\n  <thead>\n    <tr>\n      <th>Name</th>\n      <th>Parent</th>\n      <th>Includes</th>\n      <th>Routes</th>\n    </tr>\n  </thead>\n  <tbody>\n  <% controller_rows.each do |row| %>\n    <tr>\n      <td><%= row['Name'] %></td>\n      <td><%= row['Parent'] %></td>\n      <td><%= row['Includes'] %></td>\n      <td><%= row['Routes'] %></td>\n    </tr>\n  <% end %>\n  </tbody>\n</table>"
  },
  {
    "path": "lib/brakeman/report/templates/controller_warnings.html.erb",
    "content": "<p>Controller Warnings</p>\n<table>\n  <thead>\n    <tr>\n      <th>Confidence</th>\n      <th>Controller</th>\n      <th>Warning Type</th>\n      <th>CWE ID</th>\n      <th>Message</th>\n    </tr>\n  </thead>\n  <tbody>\n  <% warnings.each do |warning| %>\n    <tr>\n      <td><%= warning['Confidence']%></td>\n      <td><%= warning['Controller']%></td>\n      <td><%= warning['Warning Type']%></td>\n      <td><%= warning['CWE ID']%></td>\n      <td><%= warning['Message']%></td>\n    </tr>\n  <% end %>\n  </tbody>\n</table>"
  },
  {
    "path": "lib/brakeman/report/templates/error_overview.html.erb",
    "content": "<div onClick=\"toggle('errors_table');\">  <h2>Exceptions raised during the analysis (click to see them)</h2 ></div>\n  <div>\n    <div id='errors_table' style='display:none'> \n      <table>\n        <thead>\n          <tr>\n            <th>Error</th>\n            <th>Location</th>\n          </tr>\n        </thead>\n        <tbody>\n        <% tracker.errors.each do |warning| %>\n          <tr>\n            <td><%= CGI.escapeHTML warning[:error] %></td>\n            <td>\n              <% if tracker.options[:debug] %>\n                <% warning[:backtrace].each do |line| %>\n                  <%= line %><br/>\n                <% end %>\n              <% else %>\n                <%= warning[:backtrace][0] %>\n              <% end %>\n            </td>\n          </tr>\n        <% end %>\n        </tbody>\n      </table>\n    </div>\n  </div>\n"
  },
  {
    "path": "lib/brakeman/report/templates/header.html.erb",
    "content": "<!DOCTYPE HTML SYSTEM>\n<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<title>Brakeman Report</title>\n  <script type=\"text/javascript\" src=\"https://code.jquery.com/jquery-2.1.4.min.js\"></script>\n  <script type=\"text/javascript\" src=\"https://cdn.datatables.net/1.10.9/js/jquery.dataTables.min.js\"></script>\n  <script type=\"text/javascript\">\n    function toggle(context) {\n      var elem = document.getElementById(context);\n\n      if (elem.style.display != \"block\") {\n        elem.style.display = \"block\";\n\n        elem.querySelectorAll(\"table\").forEach(function(table) {\n          $(table).DataTable().columns.adjust();\n        });\n      } else {\n        elem.style.display = \"none\";\n      }\n\n      elem.parentNode.scrollIntoView();\n    }\n\n    $(document).ready(function() {\n      $('table').DataTable({\n        searching: false,\n        paging:    false,\n        info:      false\n      });\n    });\n  </script>\n  <style>\n    <%= css %>\n  </style>\n</head>\n<body>\n\n<h1>Brakeman Report</h1>\n<table>\n  <thead>\n    <tr>\n      <th>Application Path</th>\n      <th>Rails Version</th>\n      <th>Brakeman Version</th>\n      <th>Report Time</th>\n      <th>Checks Performed</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td><%= tracker.app_path %></td>\n      <td><%= rails_version %></td>\n      <td><%= brakeman_version %></td>\n      <td>\n        <%= tracker.start_time %><br><br>\n        <%= tracker.duration %> seconds\n      </td>\n      <td><%= checks.checks_run.sort.join(\", \") %></td>\n    </tr>\n  </tbody>\n</table>\n<br>\n"
  },
  {
    "path": "lib/brakeman/report/templates/ignored_warnings.html.erb",
    "content": "<div onClick=\"toggle('ignored_table');\">  <h2><%= warnings.length %> Ignored Warnings (click to see them)</h2 ></div>\n<div style=\"display:none; width:100%\" id=\"ignored_table\">\n  <table>\n    <thead>\n      <tr>\n        <th>Confidence</th>\n        <th>File</th>\n        <th>Warning Type</th>\n        <th>CWE ID</th>\n        <th>Message</th>\n        <th width=\"auto\">Note</th>\n      </tr>\n    </thead>\n    <tbody>\n    <% warnings.each do |warning| %>\n      <tr>\n        <td><%= warning['Confidence']%></td>\n        <td><%= warning['File']%></td>\n        <td><%= warning['Warning Type']%></td>\n        <td><%= warning['CWE ID']%></td>\n        <td><%= warning['Message']%></td>\n        <td><%= warning['Note']%></td>\n      </tr>\n    <% end %>\n    </tbody>\n  </table>\n</div>\n"
  },
  {
    "path": "lib/brakeman/report/templates/model_warnings.html.erb",
    "content": "<p>Model Warnings</p>\n<table>\n  <thead>\n    <tr>\n      <th>Confidence</th>\n      <th>Model</th>\n      <th>Warning Type</th>\n      <th>CWE ID</th>\n      <th>Message</th>\n    </tr>\n  </thead>\n  <tbody>\n  <% warnings.each do |warning| %>\n    <tr>\n      <td><%= warning['Confidence']%></td>\n      <td><%= warning['Model']%></td>\n      <td><%= warning['Warning Type']%></td>\n      <td><%= warning['CWE ID']%></td>\n      <td><%= warning['Message']%></td>\n    </tr>\n  <% end %>\n  </tbody>\n</table>"
  },
  {
    "path": "lib/brakeman/report/templates/overview.html.erb",
    "content": "<h2 id='summary'>Summary</h2>\n<table>\n  <thead>\n    <tr>\n      <th>Scanned/Reported</th>\n      <th>Total</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>Controllers</td>\n      <td><%= tracker.controllers.length %></td>\n    </tr>\n    <tr>\n      <td>Models</td>\n      <td><%= tracker.models.length - 1 %></td>\n    </tr>\n    <tr>\n      <td>Templates</td>\n      <td><%= number_of_templates %></td>\n    </tr>\n    <tr>\n      <td>Errors</td>\n      <td><%= tracker.errors.length %></td>\n    </tr>\n    <tr>\n      <td>Security Warnings</td>\n      <td><%= warnings %> <span class='high-confidence'>(<%= warnings_summary[:high_confidence] %>)</span></td>\n    </tr>\n  <% if warnings_summary['Ignored Warnings'] %>\n    <tr>\n      <td>Ignored Warnings</td>\n      <td><%= ignored_warnings %></td>\n    </tr>\n  <% end %>\n  </tbody>\n</table>\n<br>\n"
  },
  {
    "path": "lib/brakeman/report/templates/security_warnings.html.erb",
    "content": "<h2>Security Warnings</h2>  \n<table>\n  <thead>\n    <tr>\n      <th>Confidence</th>\n      <th>Class</th>\n      <th>Method</th>\n      <th>Warning Type</th>\n      <th>CWE ID</th>\n      <th>Message</th>\n    </tr>\n  </thead>\n  <tbody>\n  <% warnings.each do |warning| %>\n    <tr>\n      <td><%= warning['Confidence']%></td>\n      <td><%= warning['Class']%></td>\n      <td><%= warning['Method']%></td>\n      <td><%= warning['Warning Type']%></td>\n      <td><%= warning['CWE ID']%></td>\n      <td><%= warning['Message']%></td>\n    </tr>\n  <% end %>\n  </tbody>\n</table>\n"
  },
  {
    "path": "lib/brakeman/report/templates/template_overview.html.erb",
    "content": "<h2>Templates</h2>\n\n<% template_rows.each do |template| %>\n\n<p><%= template[0] %></p>\n<table>\n  <thead>\n    <tr>\n      <th>Output</th>\n    </tr>\n  </thead>\n  <tbody>\n  <% template[1].each do |call| %>\n    <tr>\n      <td><%= call %></td>\n    </tr>\n  <% end %>\n  </tbody>\n</table>\n\n<% end %>"
  },
  {
    "path": "lib/brakeman/report/templates/view_warnings.html.erb",
    "content": "<p>View Warnings</p>\n<table>\n  <thead>\n    <tr>\n      <th>Confidence</th>\n      <th>Template</th>\n      <th>Warning Type</th>\n      <th>CWE ID</th>\n      <th>Message</th>\n    </tr>\n  </thead>\n  <tbody>\n  <% warnings.each_with_index do |warning, i| %>\n    <tr>\n      <td><%= warning['Confidence']%></td>\n      <td>\n        <% if warning['Called From'] and warning['Called From'].length > 1 %>\n          <div class=\"template_name\" onClick=\"toggle('callers<%= i %>')\" >\n            <div>\n              <%= warning['Template'] %>\n            </div>\n            <div class=\"render_path\" id=\"callers<%= i %>\" >\n              <%= warning['Called From'].join(' &rarr; ') %> &rarr; <%= warning['Template Name'] %>\n            </div>\n          </div>\n        <% else %>\n          <%= warning['Template']%>\n        <% end %>\n      </td>\n      <td><%= warning['Warning Type']%></td>\n      <td><%= warning['CWE ID']%></td>\n      <td><%= warning['Message']%></td>\n    </tr>\n  <% end %>\n  </tbody>\n</table>\n"
  },
  {
    "path": "lib/brakeman/report/templates/warning_overview.html.erb",
    "content": "<table>\n  <thead>\n    <tr>\n      <th>Warning Type</th>\n      <th>Total</th>\n    </tr>\n  </thead>\n  <tbody>\n  <% types.sort.each do |warning_type| %>\n    <tr>\n      <td><%= warning_type %></td>\n      <td><%= warnings_summary[warning_type] %></td>\n    </tr>\n  <% end %>\n  </tbody>\n</table>\n<br>\n"
  },
  {
    "path": "lib/brakeman/report.rb",
    "content": "require 'brakeman/report/report_base'\n\n#Generates a report based on the Tracker and the results of\n#Tracker#run_checks. Be sure to +run_checks+ before generating\n#a report.\nclass Brakeman::Report\n  attr_reader :tracker\n\n  VALID_FORMATS = [:to_html, :to_pdf, :to_csv, :to_json, :to_tabs, :to_hash, :to_s, :to_markdown, :to_codeclimate, :to_plain, :to_text, :to_junit, :to_github]\n\n  def initialize tracker\n    @app_tree = tracker.app_tree\n    @tracker = tracker\n  end\n\n  def format format\n    reporter = case format\n    when :to_codeclimate\n      require_report 'codeclimate'\n      Brakeman::Report::CodeClimate\n    when :to_csv\n      require_report 'csv'\n      Brakeman::Report::CSV\n    when :to_html\n      require_report 'html'\n      Brakeman::Report::HTML\n    when :to_json\n      return self.to_json\n    when :to_tabs\n      require_report 'tabs'\n      Brakeman::Report::Tabs\n    when :to_hash\n      require_report 'hash'\n      Brakeman::Report::Hash\n    when :to_markdown\n      return self.to_markdown\n    when :to_plain, :to_text, :to_s\n      return self.to_plain\n    when :to_table\n      return self.to_table\n    when :to_pdf\n      raise \"PDF output is not yet supported.\"\n    when :to_junit\n      require_report 'junit'\n      Brakeman::Report::JUnit\n    when :to_sarif\n      return self.to_sarif\n    when :to_sonar\n      require_report 'sonar'\n      Brakeman::Report::Sonar\n    when :to_github\n      require_report 'github'\n      Brakeman::Report::Github\n    else\n      raise \"Invalid format: #{format}. Should be one of #{VALID_FORMATS.inspect}\"\n    end\n\n    generate(reporter)\n  end\n\n  def method_missing method, *args\n    if VALID_FORMATS.include? method\n      format method\n    else\n      super\n    end\n  end\n\n  def require_report type\n    require \"brakeman/report/report_#{type}\"\n  end\n\n  def to_json\n    require_report 'json'\n    generate Brakeman::Report::JSON\n  end\n\n  def to_sonar\n    require_report 'sonar'\n    generate Brakeman::Report::Sonar\n  end\n\n  def to_table\n    require_report 'table'\n    generate Brakeman::Report::Table\n  end\n\n  def to_markdown\n    require_report 'markdown'\n    generate Brakeman::Report::Markdown\n  end\n\n  def to_text\n    require_report 'text'\n    generate Brakeman::Report::Text\n  end\n\n  alias to_plain to_text\n  alias to_s to_text\n\n  def to_sarif\n    require_report 'sarif'\n    generate Brakeman::Report::SARIF\n  end\n\n  def generate reporter\n    reporter.new(@tracker).generate_report\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/rescanner.rb",
    "content": "require 'brakeman/scanner'\nrequire 'brakeman/util'\nrequire 'brakeman/differ'\n\n#Class for rescanning changed files after an initial scan\nclass Brakeman::Rescanner < Brakeman::Scanner\n include Brakeman::Util\n  KNOWN_TEMPLATE_EXTENSIONS = Brakeman::TemplateParser::KNOWN_TEMPLATE_EXTENSIONS\n\n  #Create new Rescanner to scan changed files\n  def initialize options, processor, changed_files\n    super(options)\n\n    @old_tracker = processor.tracked_events\n\n    @paths = changed_files.map {|f| tracker.app_tree.file_path(f) }\n    @old_results = @old_tracker.filtered_warnings.dup  #Old warnings from previous scan\n    @changes = nil                 #True if files had to be rescanned\n    @reindex = Set.new\n  end\n\n  #Runs checks.\n  #Will rescan files if they have not already been scanned\n  def recheck\n    rescan if @changes.nil?\n\n    if @changes\n      tracker.run_checks\n      Brakeman.filter_warnings(tracker, options) # Actually sets ignored_filter\n      Brakeman::RescanReport.new @old_results, tracker\n    else\n      # No changes, fake no new results\n      Brakeman::RescanReport.new @old_results, @old_tracker\n    end\n  end\n\n  #Rescans changed files\n  def rescan\n    raise \"Cannot rescan: set `support_rescanning: true`\" unless @old_tracker.options[:support_rescanning]\n\n    tracker.file_cache = @old_tracker.pristine_file_cache\n\n    template_paths = []\n    ruby_paths = []\n\n    # Remove changed files from the cache.\n    # Collect files to re-parse.\n    @paths.each do |path|\n      file_cache.delete path\n\n      if path.exists?\n        if path.relative.match? KNOWN_TEMPLATE_EXTENSIONS\n          template_paths << path\n        elsif path.relative.end_with? '.rb'\n          ruby_paths << path\n        end\n      end\n    end\n\n    # Try to skip rescanning files that do not impact\n    # Brakeman results\n    if @paths.all? { |path| ignorable? path }\n      @changes = false\n    else\n      @changes = true\n      process(ruby_paths:, template_paths:)\n    end\n\n    self\n  end\n\n  IGNORE_PATTERN = /\\.(md|txt|js|ts|tsx|json|scss|css|xml|ru|png|jpg|pdf|gif|svg|webm|ttf|sql)$/\n\n  def ignorable? path\n    path.relative.match? IGNORE_PATTERN\n  end\nend\n\n#Class to make reporting of rescan results simpler to deal with\nclass Brakeman::RescanReport\n  include Brakeman::Util\n  attr_reader :old_results, :new_results\n\n  def initialize old_results, tracker\n    @tracker = tracker\n    @old_results = old_results\n    @all_warnings = nil\n    @diff = nil\n  end\n\n  #Returns true if any warnings were found (new or old)\n  def any_warnings?\n    not all_warnings.empty?\n  end\n\n  #Returns an array of all warnings found\n  def all_warnings\n    @all_warnings ||= @tracker.filtered_warnings\n  end\n\n  #Returns an array of warnings which were in the old report but are not in the\n  #new report after rescanning\n  def fixed_warnings\n    diff[:fixed]\n  end\n\n  #Returns an array of warnings which were in the new report but were not in\n  #the old report\n  def new_warnings\n    diff[:new]\n  end\n\n  #Returns true if there are any new or fixed warnings\n  def warnings_changed?\n    not (diff[:new].empty? and diff[:fixed].empty?)\n  end\n\n  #Returns a hash of arrays for :new and :fixed warnings\n  def diff\n    @diff ||= Brakeman::Differ.new(all_warnings, @old_results).diff\n  end\n\n  #Returns an array of warnings which were in the old report and the new report\n  def existing_warnings\n    @old ||= all_warnings.select do |w|\n      not new_warnings.include? w\n    end\n  end\n\n  #Output total, fixed, and new warnings\n  def to_s\n    <<~OUTPUT\n      Total warnings: #{all_warnings.length}\n      Fixed warnings: #{fixed_warnings.length}\n      New warnings: #{new_warnings.length}\n    OUTPUT\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/scanner.rb",
    "content": "begin\n  Brakeman.load_brakeman_dependency 'ruby_parser'\n  require 'ruby_parser/bm_sexp.rb'\n  require 'ruby_parser/bm_sexp_processor.rb'\n  require 'brakeman/processor'\n  require 'brakeman/app_tree'\n  require 'brakeman/file_parser'\n  require 'brakeman/parsers/template_parser'\n  require 'brakeman/processors/lib/file_type_detector'\n  require 'brakeman/tracker/file_cache'\nrescue LoadError => e\n  $stderr.puts e.message\n  $stderr.puts \"Please install the appropriate dependency.\"\n  exit(-1)\nend\n\n#Scans the Rails application.\nclass Brakeman::Scanner\n  attr_reader :options\n\n  #Pass in path to the root of the Rails application\n  def initialize options, processor = nil\n    @options = options\n    @app_tree = Brakeman::AppTree.from_options(options)\n\n    if (!@app_tree.root || !@app_tree.exists?(\"app\")) && !options[:force_scan]\n      message = \"Please supply the path to a Rails application (looking in #{@app_tree.root}).\\n\" <<\n                \"  Use `--force` to run a scan anyway.\"\n\n      raise Brakeman::NoApplication, message\n    end\n\n    @processor = processor || Brakeman::Processor.new(@app_tree, options)\n  end\n\n  #Returns the Tracker generated from the scan\n  def tracker\n    @processor.tracked_events\n  end\n\n  def file_cache\n    tracker.file_cache\n  end\n\n  def process_step(description, &)\n    Brakeman.process_step(description, &)\n  end\n\n  def process_step_file(description, &)\n    Brakeman.logger.single_context(description, &)\n  end\n\n  #Process everything in the Rails application\n  def process(ruby_paths: nil, template_paths: nil)\n    process_step 'Processing gems' do\n      process_gems\n    end\n\n    process_step 'Processing configuration' do\n      guess_rails_version\n      process_config\n    end\n\n    # -\n    # If ruby_paths or template_paths are set,\n    # only parse those files. The rest will be fetched\n    # from the file cache.\n    #\n    # Otherwise, parse everything normally.\n    #\n    astfiles = nil\n    process_step 'Finding files' do\n      ruby_paths ||= tracker.app_tree.ruby_file_paths\n      template_paths ||= tracker.app_tree.template_paths\n    end\n\n    process_step 'Parsing files' do\n      astfiles = parse_files(ruby_paths: ruby_paths, template_paths: template_paths)\n    end\n\n    process_step 'Detecting file types' do\n      detect_file_types(astfiles)\n    end\n\n    tracker.save_file_cache! if support_rescanning?\n    # -\n\n    process_step 'Processing initializers' do\n      process_initializers\n    end\n\n    process_step 'Processing libraries' do\n      process_libs\n    end\n\n    process_step 'Processing routes' do\n      process_routes\n    end\n\n    process_step 'Processing templates' do\n      process_templates\n    end\n\n    process_step 'Processing data flow' do\n      process_template_data_flows\n    end\n\n    process_step 'Processing models' do\n      process_models\n    end\n\n    process_step 'Processing controllers' do\n      process_controllers\n    end\n\n    process_step 'Processing data flow' do\n      process_controller_data_flows\n    end\n\n    process_step 'Indexing method calls' do\n      index_call_sites\n    end\n\n    tracker\n  end\n\n  def parse_files(ruby_paths:, template_paths:)\n    fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout], tracker.options[:parallel_checks], tracker.options[:use_prism])\n\n    fp.parse_files ruby_paths\n\n    template_parser = Brakeman::TemplateParser.new(tracker, fp)\n\n    fp.read_files(template_paths) do |path, contents|\n      Brakeman.logger.spin\n      template_parser.parse_template(path, contents)\n    end\n\n    # Collect errors raised during parsing\n    tracker.add_errors(fp.errors)\n\n    fp.file_list\n  end\n\n  def detect_file_types(astfiles)\n    detector = Brakeman::FileTypeDetector.new\n\n    astfiles.each do |file|\n      Brakeman.logger.spin\n\n      if file.is_a? Brakeman::TemplateParser::TemplateFile\n        file_cache.add_file file, :template\n      else\n        type = detector.detect_type(file)\n\n        unless type == :skip\n          if file_cache.valid_type? type\n            file_cache.add_file(file, type)\n          else\n            raise \"Unexpected file type: #{type.inspect}\"\n          end\n        end\n      end\n    end\n  end\n\n  #Process config/environment.rb and config/gems.rb\n  #\n  #Stores parsed information in tracker.config\n  def process_config\n    # Sometimes folks like to put constants in environment.rb\n    # so let's always process it even for newer Rails versions\n    process_config_file \"environment.rb\"\n\n    if options[:rails3] or options[:rails4] or options[:rails5] or options[:rails6]\n      process_config_file \"application.rb\"\n      process_config_file \"environments/production.rb\"\n    else\n      process_config_file \"gems.rb\"\n    end\n\n    if @app_tree.exists?(\"vendor/plugins/rails_xss\") or\n      options[:rails3] or options[:escape_html]\n\n      tracker.config.escape_html = true\n      Brakeman.debug 'Escaping HTML by default'\n    end\n\n    if @app_tree.exists? \".ruby-version\"\n      if version = @app_tree.file_path(\".ruby-version\").read[/(\\d\\.\\d.\\d+)/]\n        tracker.config.set_ruby_version version, @app_tree.file_path(\".ruby-version\"), 1\n      end\n    end\n\n    tracker.config.load_rails_defaults\n  end\n\n  def process_config_file file\n    path = @app_tree.file_path(\"config/#{file}\")\n\n    if path.exists?\n      @processor.process_config(parse_ruby_file(path), path)\n    end\n\n  rescue => e\n    Brakeman.alert \"Error while processing #{path}\"\n    tracker.error e.exception(e.message + \"\\nwhile processing #{path}\"), e.backtrace\n  end\n\n  private :process_config_file\n\n  #Process Gemfile\n  def process_gems\n    gem_files = {}\n    gem_file_names = ['Gemfile', 'gems.rb']\n    lock_file_names = ['Gemfile.lock', 'gems.locked']\n\n    if tracker.options[:gemfile]\n      name = tracker.options[:gemfile]\n      gem_file_names.unshift name\n      lock_file_names.unshift \"#{name}.lock\"\n    end\n\n    gem_file_names.each do |name|\n      if @app_tree.exists? name\n        file = @app_tree.file_path(name)\n        gem_files[:gemfile] = { :src => parse_ruby_file(file), :file => file }\n        break\n      end\n    end\n\n    lock_file_names.each do |name|\n      if @app_tree.exists? name\n        file = @app_tree.file_path(name)\n        gem_files[:gemlock] = { :src => file.read, :file => file }\n        break\n      end\n    end\n\n    if @app_tree.gemspec\n      gem_files[:gemspec] = { :src => parse_ruby_file(@app_tree.gemspec), :file => @app_tree.gemspec }\n    end\n\n    if not gem_files.empty?\n      @processor.process_gems gem_files\n    end\n  rescue => e\n    Brakeman.alert 'Error while processing Gemfile'\n    tracker.error e.exception(e.message + \"\\nWhile processing Gemfile\"), e.backtrace\n  end\n\n  #Set :rails3/:rails4 option if version was not determined from Gemfile\n  def guess_rails_version\n    unless tracker.options[:rails3] or tracker.options[:rails4]\n      if @app_tree.exists?(\"script/rails\")\n        tracker.options[:rails3] = true\n        Brakeman.debug 'Detected Rails 3 application'\n      elsif @app_tree.exists?(\"app/channels\")\n        tracker.options[:rails3] = true\n        tracker.options[:rails4] = true\n        tracker.options[:rails5] = true\n        Brakeman.debug 'Detected Rails 5 application'\n      elsif not @app_tree.exists?(\"script\")\n        tracker.options[:rails3] = true\n        tracker.options[:rails4] = true\n        Brakeman.debug 'Detected Rails 4 application'\n      end\n    end\n  end\n\n  #Process all the .rb files in config/initializers/\n  #\n  #Adds parsed information to tracker.initializers\n  def process_initializers\n    track_progress file_cache.initializers do |path, init|\n      process_step_file path do\n        process_initializer init\n      end\n    end\n  end\n\n  #Process an initializer\n  def process_initializer init\n    @processor.process_initializer(init.path, init.ast)\n  end\n\n  # Adds parsed information to tracker.libs.\n  # This is a catch-all for any Ruby files that weren't determined\n  # to be a specific type of file (like a controller).\n  def process_libs\n    libs = file_cache.libs.sort_by { |path, _| path }\n\n    track_progress libs do |path, lib|\n      process_step_file path do\n        process_lib lib\n      end\n    end\n  end\n\n  #Process a library\n  def process_lib lib\n    @processor.process_lib lib.ast, lib.path\n  end\n\n  #Process config/routes.rb\n  #\n  #Adds parsed information to tracker.routes\n  def process_routes\n    if @app_tree.exists?(\"config/routes.rb\")\n      file = @app_tree.file_path(\"config/routes.rb\")\n      if routes_sexp = parse_ruby_file(file)\n        @processor.process_routes routes_sexp\n      else\n        Brakeman.alert 'Error while processing routes - assuming all public controller methods are actions.'\n        options[:assume_all_routes] = true\n      end\n    else\n      Brakeman.alert 'No route information found'\n    end\n  end\n\n  #Process all .rb files in controllers/\n  #\n  #Adds processed controllers to tracker.controllers\n  def process_controllers\n    controllers = file_cache.controllers.sort_by { |path, _| path }\n\n    track_progress controllers do |path, controller|\n      process_step_file path do\n        process_controller controller\n      end\n    end\n  end\n\n  def process_controller_data_flows\n    controllers = tracker.controllers.sort_by { |name, _| name }\n\n    track_progress controllers, \"controllers\" do |name, controller|\n      process_step_file name do\n        controller.src.each do |file, src|\n          @processor.process_controller_alias name, src, nil, file\n        end\n      end\n    end\n\n    #No longer need these processed filter methods\n    tracker.filter_cache.clear\n  end\n\n  def process_controller astfile\n    begin\n      @processor.process_controller(astfile.ast, astfile.path)\n    rescue => e\n      tracker.error e.exception(e.message + \"\\nWhile processing #{astfile.path}\"), e.backtrace\n    end\n  end\n\n  #Process all views and partials in views/\n  #\n  #Adds processed views to tracker.views\n  def process_templates\n    templates = file_cache.templates.sort_by { |path, _| path }\n\n    track_progress templates, \"templates\" do |path, template|\n      process_step_file path do\n        process_template template\n      end\n    end\n  end\n\n  def process_template template\n    @processor.process_template(template.name, template.ast, template.type, nil, template.path)\n  end\n\n  def process_template_data_flows\n    templates = tracker.templates.sort_by { |name, _| name }\n\n    track_progress templates, \"templates\" do |name, template|\n      process_step_file name do\n        @processor.process_template_alias template\n      end\n    end\n  end\n\n  #Process all the .rb files in models/\n  #\n  #Adds the processed models to tracker.models\n  def process_models\n    models = file_cache.models.sort_by { |path, _| path }\n\n    track_progress models do |path, model|\n      process_step_file path do\n        process_model model\n      end\n    end\n  end\n\n  def process_model astfile\n    @processor.process_model(astfile.ast, astfile.path)\n  end\n\n  def track_progress list, type = \"files\"\n    total = list.length\n    current = 0\n    list.each do |item|\n      report_progress current, total\n      current += 1\n      yield item\n    end\n  end\n\n  def report_progress(current, total)\n    return unless @options[:report_progress]\n    Brakeman.logger.update_progress(current, total)\n  end\n\n  def index_call_sites\n    tracker.index_call_sites\n  end\n\n  def parse_ruby_file file\n    fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout], false, tracker.options[:use_prism])\n    fp.parse_ruby(file.read, file)\n  rescue Exception => e\n    tracker.error(e)\n    nil\n  end\n\n  def support_rescanning?\n    tracker.options[:support_rescanning]\n  end\nend\n\n# This is to allow operation without loading the Haml library\nmodule Haml; class Error < StandardError; end; end\n"
  },
  {
    "path": "lib/brakeman/tracker/collection.rb",
    "content": "require 'brakeman/util'\nrequire 'brakeman/tracker/method_info'\n\nmodule Brakeman\n  class Collection\n    include Brakeman::Util\n\n    attr_reader :collection, :files, :includes, :name, :options, :parent, :src, :tracker\n\n    def initialize name, parent, file_name, src, tracker\n      @name = name\n      @parent = parent\n      @files = []\n      @src = {}\n      @includes = []\n      @methods = { :public => {}, :private => {}, :protected => {} }\n      @class_methods = {}\n      @simple_methods = { :class => {}, instance: {} }\n      @options = {}\n      @tracker = tracker\n\n      add_file file_name, src\n    end\n\n    def ancestor? parent, seen={}\n      seen[self.name] = true\n\n      if self.parent == parent or self.name == parent or seen[self.parent]\n        true\n      elsif parent_model = collection[self.parent]\n        parent_model.ancestor? parent, seen\n      else\n        false\n      end\n    end\n\n    def add_file file_name, src\n      @files << file_name unless @files.include? file_name\n      @src[file_name] = src\n    end\n\n    def add_include class_name\n      @includes << class_name unless ancestor?(class_name)\n    end\n\n    def add_option name, exp\n      @options[name] ||= []\n      @options[name] << exp\n    end\n\n    def add_method visibility, name, src, file_name\n      meth_info = Brakeman::MethodInfo.new(name, src, self, file_name)\n      add_simple_method_maybe meth_info\n\n      if src.node_type == :defs\n        @class_methods[name] = meth_info\n\n        name = :\"#{method_definition_receiver(src[1])}.#{name}\"\n      end\n\n      @methods[visibility][name] = meth_info\n    end\n\n    def method_definition_receiver(receiver)\n      return receiver if receiver.is_a?(Symbol)\n\n      case receiver.sexp_type\n      when :self\n        \"self\"\n      else\n        receiver[1].to_s\n      end\n    end\n\n    def each_method\n      @methods.each do |_vis, meths|\n        meths.each do |name, info|\n          yield name, info\n        end\n      end\n    end\n\n    def get_method name, type = :instance\n      case type\n      when :class\n        get_class_method name\n      when :instance\n        get_instance_method name\n      else\n        raise \"Unexpected method type: #{type.inspect}\"\n      end\n    end\n\n    def get_instance_method name\n      @methods.each do |_vis, meths|\n        if meths[name]\n          return meths[name]\n        end\n      end\n\n      nil\n    end\n\n    def get_class_method name\n      @class_methods[name]\n    end\n\n    def file\n      @files.first\n    end\n\n    def top_line\n      if sexp? @src[file]\n        @src[file].line\n      else\n        @src.each_value do |source|\n          if sexp? source\n            return source.line\n          end\n        end\n      end\n    end\n\n    def methods_public\n      @methods[:public]\n    end\n\n    def get_simple_method_return_value type, name\n      @simple_methods[type][name]\n    end\n\n    private\n\n    def add_simple_method_maybe meth_info\n      if meth_info.very_simple_method?\n        add_simple_method meth_info\n      end\n    end\n\n    def add_simple_method meth_info\n      name = meth_info.name\n      value = meth_info.return_value\n\n      case meth_info.src.node_type\n      when :defn\n        @simple_methods[:instance][name] = value\n      when :defs\n        @simple_methods[:class][name] = value\n      else\n        raise \"Expected sexp type: #{src.node_type}\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/tracker/config.rb",
    "content": "require 'brakeman/util'\n\nmodule Brakeman\n  class Config\n    include Util\n\n    attr_reader :gems, :rails, :ruby_version, :tracker\n    attr_writer :erubi, :escape_html\n\n    def initialize tracker\n      @tracker = tracker\n      @rails = {}\n      @gems = {}\n      @settings = {}\n      @escape_html = nil\n      @erubi = nil\n      @ruby_version = nil\n      @rails_version = nil\n    end\n\n    def default_protect_from_forgery?\n      if version_between? \"5.2.0.beta1\", \"9.9.9\"\n        if @rails.dig(:action_controller, :default_protect_from_forgery) == Sexp.new(:true)\n          return true\n        end\n      end\n\n      false\n    end\n\n    def erubi?\n      @erubi\n    end\n\n    def escape_html?\n      @escape_html\n    end\n\n    def escape_html_entities_in_json?\n      #TODO add version-specific information here\n      true? @rails.dig(:active_support, :escape_html_entities_in_json)\n    end\n\n    def escape_filter_interpolations?\n      # TODO see if app is actually turning this off itself\n      has_gem?(:haml) and\n        version_between? \"5.0.0\", \"5.99\", gem_version(:haml)\n    end\n\n    def whitelist_attributes?\n      @rails.dig(:active_record, :whitelist_attributes) == Sexp.new(:true)\n    end\n\n    def gem_version name\n      extract_version @gems.dig(name.to_sym, :version)\n    end\n\n    def add_gem name, version, file, line\n      name = name.to_sym\n      @gems[name] = {\n        :version => version,\n        :file => file,\n        :line => line\n      }\n    end\n\n    def has_gem? name\n      !!@gems[name.to_sym]\n    end\n\n    def get_gem name\n      @gems[name.to_sym]\n    end\n\n    def set_rails_version version = nil\n      version = if version\n                  # Only used by Rails2ConfigProcessor right now\n                  extract_version(version)\n                else\n                  gem_version(:rails) ||\n                    gem_version(:railties) ||\n                    gem_version(:activerecord)\n                end\n\n      if version\n        @rails_version = version\n\n        if tracker.options[:rails3].nil? and tracker.options[:rails4].nil?\n          if @rails_version.start_with? \"3\"\n            tracker.options[:rails3] = true\n            notify_version 3\n          elsif @rails_version.start_with? \"4\"\n            tracker.options[:rails3] = true\n            tracker.options[:rails4] = true\n            notify_version 4\n          elsif @rails_version.start_with? \"5\"\n            tracker.options[:rails3] = true\n            tracker.options[:rails4] = true\n            tracker.options[:rails5] = true\n            notify_version 5\n          elsif @rails_version.start_with? \"6\"\n            tracker.options[:rails3] = true\n            tracker.options[:rails4] = true\n            tracker.options[:rails5] = true\n            tracker.options[:rails6] = true\n            notify_version 6\n          elsif @rails_version.start_with? \"7\"\n            tracker.options[:rails3] = true\n            tracker.options[:rails4] = true\n            tracker.options[:rails5] = true\n            tracker.options[:rails6] = true\n            tracker.options[:rails7] = true\n            notify_version 7\n          elsif @rails_version.start_with? \"8\"\n            tracker.options[:rails3] = true\n            tracker.options[:rails4] = true\n            tracker.options[:rails5] = true\n            tracker.options[:rails6] = true\n            tracker.options[:rails7] = true\n            tracker.options[:rails8] = true\n            notify_version 8\n          end\n        end\n      end\n\n      if get_gem :rails_xss\n        @escape_html = true\n        Brakeman.debug \"Escaping HTML by default\"\n      end\n    end\n\n    def rails_version\n      # This needs to be here because Util#rails_version calls Tracker::Config#rails_version\n      # but Tracker::Config includes Util...\n      @rails_version\n    end\n\n    def set_ruby_version version, file, line\n      @ruby_version = extract_version(version)\n      add_gem :ruby, @ruby_version, file, line\n    end\n\n    def extract_version version\n      return unless version.is_a? String\n\n      version[/\\d+\\.\\d+(\\.\\d+.*)?/]\n    end\n\n    #Returns true if low_version <= RAILS_VERSION <= high_version\n    #\n    #If the Rails version is unknown, returns false.\n    def version_between? low_version, high_version, current_version = nil\n      current_version ||= rails_version\n      return false unless current_version\n\n      low = Gem::Version.new(low_version)\n      high = Gem::Version.new(high_version)\n      current = Gem::Version.new(current_version)\n\n      current.between?(low, high)\n    end\n\n    def session_settings\n      @rails.dig(:action_controller, :session)\n    end\n\n\n    # Set Rails config option value\n    # where path is an array of attributes, e.g.\n    #\n    #   :action_controller, :perform_caching\n    #\n    # then this will set\n    #\n    #   rails[:action_controller][:perform_caching] = value\n    def set_rails_config value:, path:, overwrite: false\n      config = self.rails\n\n      path[0..-2].each do |o|\n        config[o] ||= {}\n\n        option = config[o]\n\n        if not option.is_a? Hash\n          Brakeman.debug \"Skipping config setting: #{path.map(&:to_s).join(\".\")}\"\n          return\n        end\n\n        config = option\n      end\n\n      if overwrite || config[path.last].nil?\n        config[path.last] = value\n      end\n    end\n\n    # Load defaults based on config.load_defaults value\n    # as documented here: https://guides.rubyonrails.org/configuring.html#results-of-config-load-defaults\n    def load_rails_defaults\n      return unless node_type? tracker.config.rails[:load_defaults], :lit, :str\n\n      version = tracker.config.rails[:load_defaults].value.to_s\n\n      unless version.match?(/^\\d+\\.\\d+$/)\n        Brakeman.alert \"Unknown version: #{tracker.config.rails[:load_defaults]}\"\n        return\n      end\n\n      true_value = Sexp.new(:true)\n      false_value = Sexp.new(:false)\n\n      if version >= '5.0'\n        set_rails_config(value: true_value, path: [:action_controller, :per_form_csrf_tokens])\n        set_rails_config(value: true_value, path: [:action_controller, :forgery_protection_origin_check])\n        set_rails_config(value: true_value, path: [:active_record, :belongs_to_required_by_default])\n        # Note: this may need to be changed, because ssl_options is a Hash\n        set_rails_config(value: true_value, path: [:ssl_options, :hsts, :subdomains])\n      end\n\n      if version >= '5.1'\n        set_rails_config(value: false_value, path: [:assets, :unknown_asset_fallback])\n        set_rails_config(value: true_value, path: [:action_view, :form_with_generates_remote_forms])\n      end\n\n      if version >= '5.2'\n        set_rails_config(value: true_value, path: [:active_record, :cache_versioning])\n        set_rails_config(value: true_value, path: [:action_dispatch, :use_authenticated_cookie_encryption])\n        set_rails_config(value: true_value, path: [:active_support, :use_authenticated_message_encryption])\n        set_rails_config(value: true_value, path: [:active_support, :use_sha1_digests])\n        set_rails_config(value: true_value, path: [:action_controller, :default_protect_from_forgery])\n        set_rails_config(value: true_value, path: [:action_view, :form_with_generates_ids])\n      end\n\n      if version >= '6.0'\n        set_rails_config(value: Sexp.new(:lit, :zeitwerk), path: [:autoloader])\n        set_rails_config(value: false_value, path: [:action_view, :default_enforce_utf8])\n        set_rails_config(value: true_value, path: [:action_dispatch, :use_cookies_with_metadata])\n        set_rails_config(value: false_value, path: [:action_dispatch, :return_only_media_type_on_content_type])\n        set_rails_config(value: Sexp.new(:str, 'ActionMailer::MailDeliveryJob'), path: [:action_mailer, :delivery_job])\n        set_rails_config(value: true_value, path: [:active_job, :return_false_on_aborted_enqueue])\n        set_rails_config(value: Sexp.new(:lit, :active_storage_analysis), path: [:active_storage, :queues, :analysis])\n        set_rails_config(value: Sexp.new(:lit, :active_storage_purge), path: [:active_storage, :queues, :purge])\n        set_rails_config(value: true_value, path: [:active_storage, :replace_on_assign_to_many])\n        set_rails_config(value: true_value, path: [:active_record, :collection_cache_versioning])\n      end\n\n      if version >= '6.1'\n        set_rails_config(value: true_value, path: [:action_controller, :urlsafe_csrf_tokens])\n        set_rails_config(value: Sexp.new(:lit, :lax), path: [:action_dispatch, :cookies_same_site_protection])\n        set_rails_config(value: Sexp.new(:lit, 308), path: [:action_dispatch, :ssl_default_redirect_status])\n        set_rails_config(value: false_value, path: [:action_view, :form_with_generates_remote_forms])\n        set_rails_config(value: true_value, path: [:action_view, :preload_links_header])\n        set_rails_config(value: Sexp.new(:lit, 0.15), path: [:active_job, :retry_jitter])\n        set_rails_config(value: true_value, path: [:active_record, :has_many_inversing])\n        set_rails_config(value: false_value, path: [:active_record, :legacy_connection_handling])\n        set_rails_config(value: true_value, path: [:active_storage, :track_variants])\n      end\n\n      if version >= '7.0'\n        video_args =\n          Sexp.new(:str, \"-vf 'select=eq(n\\\\,0)+eq(key\\\\,1)+gt(scene\\\\,0.015),loop=loop=-1:size=2,trim=start_frame=1' -frames:v 1 -f image2\")\n        hash_class = s(:colon2, s(:colon2, s(:const, :OpenSSL), :Digest), :SHA256)\n\n        set_rails_config(value: true_value, path: [:action_controller, :raise_on_open_redirects])\n        set_rails_config(value: true_value, path: [:action_controller, :wrap_parameters_by_default])\n        set_rails_config(value: Sexp.new(:lit, :json), path: [:action_dispatch, :cookies_serializer])\n        set_rails_config(value: false_value, path: [:action_dispatch, :return_only_request_media_type_on_content_type])\n        set_rails_config(value: Sexp.new(:lit, 5), path: [:action_mailer, :smtp_timeout])\n        set_rails_config(value: false_value, path: [:action_view, :apply_stylesheet_media_default])\n        set_rails_config(value: true_value, path: [:ction_view, :button_to_generates_button_tag])\n        set_rails_config(value: true_value, path: [:active_record, :automatic_scope_inversing])\n        set_rails_config(value: false_value, path: [:active_record, :partial_inserts])\n        set_rails_config(value: true_value, path: [:active_record, :verify_foreign_keys_for_fixtures])\n        set_rails_config(value: true_value, path: [:active_storage, :multiple_file_field_include_hidden])\n        set_rails_config(value: Sexp.new(:lit, :vips), path: [:active_storage, :variant_processor])\n        set_rails_config(value: video_args, path: [:active_storage, :video_preview_arguments])\n        set_rails_config(value: Sexp.new(:lit, 7.0), path: [:active_support, :cache_format_version])\n        set_rails_config(value: true_value, path: [:active_support, :disable_to_s_conversion])\n        set_rails_config(value: true_value, path: [:active_support, :executor_around_test_case])\n        set_rails_config(value: hash_class, path: [:active_support, :hash_digest_class])\n        set_rails_config(value: Sexp.new(:lit, :thread), path: [:active_support, :isolation_level])\n        set_rails_config(value: hash_class, path: [:active_support, :key_generator_hash_digest_class])\n        set_rails_config(value: true_value, path: [:active_support, :remove_deprecated_time_with_zone_name])\n        set_rails_config(value: true_value, path: [:active_support, :use_rfc4122_namespaced_uuids])\n      end\n    end\n\n    private def notify_version version\n      Brakeman.debug \"Detected Rails #{version} application\"\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/tracker/constants.rb",
    "content": "require 'brakeman/processors/output_processor'\nrequire 'brakeman/util'\n\nmodule Brakeman\n  class Constant\n    include Brakeman::Util\n\n    attr_reader :name, :name_array, :file, :value, :context\n\n    def initialize name, value, context = {}\n      set_name name, context\n      @value = value\n      @context = context\n\n      if @context\n        if @context[:class].is_a? Brakeman::Controller\n          @context[:class] = @context[:class].name\n        end\n\n        @file = @context[:file]\n      end\n    end\n\n    def line\n      if @value.is_a? Sexp\n        @value.line\n      end\n    end\n\n    def set_name name, context\n      @name = name\n      @name_array = Constants.constant_as_array(name, context)\n    end\n\n    def match? name\n      if name == @name\n        return true\n      elsif name.is_a? Sexp and name.node_type == :const and name.value == @name\n        return true\n      elsif name.is_a? Symbol and name.value == @name\n        return true\n      elsif name.class == Array\n        name == @name_array or\n          @name_array.reverse.zip(name.reverse).reduce(true) { |m, a| a[1] ? a[0] == a[1] && m : m }\n      else\n        false\n      end\n    end\n  end\n\n  class Constants\n    include Brakeman::Util\n\n    def initialize\n      @constants = {}\n    end\n\n    def size\n      @constants.length\n    end\n\n    def [] exp\n      return unless constant? exp\n      match = find_constant exp\n\n      if match\n        match.value\n      else\n        nil\n      end\n    end\n\n    def find_constant exp\n      base_name = Constants.get_constant_base_name(exp)\n\n      if @constants.key? base_name\n        @constants[base_name].find do |c|\n          if c.match? exp\n            return c\n          end\n        end\n\n        name_array = Constants.constant_as_array(exp)\n\n        # Avoid losing info about dynamic constant values\n        return unless name_array.all? { |n| constant? n or n.is_a? Symbol }\n\n        @constants[base_name].find do |c|\n          if c.match? name_array\n            return c\n          end\n        end\n      end\n\n      nil\n    end\n\n    def find_all exp\n      base_name = Constants.get_constant_base_name(exp)\n      @constants[base_name]\n    end\n\n    def add name, value, context = nil\n      if call? value and value.method == :freeze\n        value = value.target\n      end\n\n      base_name = Constants.get_constant_base_name(name)\n      @constants[base_name] ||= []\n      @constants[base_name] << Constant.new(name, value, context)\n    end\n\n    # Returns constant values that are not too complicated.\n    # Right now that means literal values (string, array, etc.)\n    # or calls on Dir.glob(..).whatever.\n    def get_simple_value name\n      if x = self[name] and (literal? x or dir_glob? x)\n        x\n      else\n        nil\n      end\n    end\n\n    def each\n      @constants.each do |name, values|\n        values.each do |constant|\n          yield constant\n        end\n      end\n    end\n\n    def self.constant_as_array exp, context = nil\n      # Only prepend context for simple (unqualified) constants\n      if context && (exp.is_a?(Symbol) || (exp.is_a?(Sexp) && exp.node_type == :const))\n        context_name = context[:module] || context[:class]\n        context_name = context_name.name if context_name.respond_to?(:name)\n        if context_name\n          # Build colon2 chain: A::B becomes s(:colon2, s(:const, :A), :B)\n          parts = context_name.to_s.split(\"::\")\n          base = Sexp.new(:const, parts.first.to_sym)\n          parts[1..].each do |part|\n            base = Sexp.new(:colon2, base, part.to_sym)\n          end\n          exp = Sexp.new(:colon2, base, exp)\n        end\n      end\n\n      res = []\n      while exp\n        if exp.is_a? Sexp\n          case exp.node_type\n          when :const\n            res << exp.value\n            exp = nil\n          when :colon3\n            res << exp.value << :\"\"\n            exp = nil\n          when :colon2\n            res << exp.last\n            exp = exp[1]\n          else\n            res << exp\n            exp = nil\n          end\n        else\n          res << exp\n          exp = nil\n        end\n      end\n\n      res.reverse!\n      res\n    end\n\n    def self.get_constant_base_name exp\n      return exp unless exp.is_a? Sexp\n\n      case exp.node_type\n      when :const, :colon3\n        exp.value\n      when :colon2\n        exp.last\n      else\n        exp\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/tracker/controller.rb",
    "content": "require 'brakeman/tracker/collection'\n\nmodule Brakeman\n  module ControllerMethods\n    attr_accessor :layout\n\n    def initialize_controller\n      @options[:before_filters] = []\n      @options[:skip_filters] = []\n      @layout = nil\n      @skip_filter_cache = nil\n      @before_filter_cache = nil\n    end\n\n    def protect_from_forgery?\n      @options[:protect_from_forgery]\n    end\n\n    def add_before_filter exp\n      @options[:before_filters] << exp\n    end\n\n    def prepend_before_filter exp\n      @options[:before_filters].unshift exp\n    end\n\n    def before_filters\n      @options[:before_filters]\n    end\n\n    def skip_filter exp\n      @options[:skip_filters] << exp\n    end\n\n    def skip_filters\n      @options[:skip_filters]\n    end\n\n    def before_filter_list processor, method\n      controller = self\n      filters = []\n\n      while controller\n        filters = controller.get_before_filters(processor, method) + filters\n\n        controller = tracker.controllers[controller.parent] ||\n          tracker.libs[controller.parent]\n      end\n\n      remove_skipped_filters processor, filters, method\n    end\n\n    def get_skipped_filters processor, method\n      filters = []\n\n      if @skip_filter_cache.nil?\n        @skip_filter_cache = skip_filters.map do |filter|\n          before_filter_to_hash(processor, filter.args)\n        end\n      end\n\n      @skip_filter_cache.each do |f|\n        if filter_includes_method? f, method\n          filters.concat f[:methods]\n        else\n        end\n      end\n\n      filters\n    end\n\n\n    def remove_skipped_filters processor, filters, method\n      controller = self\n\n      while controller\n        filters = filters - controller.get_skipped_filters(processor, method)\n\n        controller = tracker.controllers[controller.parent] ||\n          tracker.libs[controller.parent]\n      end\n\n      filters\n    end\n\n    def get_before_filters processor, method\n      filters = []\n\n      if @before_filter_cache.nil?\n        @before_filter_cache = []\n\n        before_filters.each do |filter|\n          @before_filter_cache << before_filter_to_hash(processor, filter.args)\n        end\n      end\n\n      @before_filter_cache.each do |f|\n        if filter_includes_method? f, method\n          filters.concat f[:methods]\n        end\n      end\n\n      filters\n    end\n\n    def before_filter_to_hash processor, args\n      filter = {}\n\n      #Process args for the uncommon but possible situation\n      #in which some variables are used in the filter.\n      args.each do |a|\n        if sexp? a\n          a = processor.process_default a\n        end\n      end\n\n      filter[:methods] = []\n\n      args.each do |a|\n        filter[:methods] << a[1] if a.node_type == :lit\n      end\n\n      options = args.last\n\n      if hash? options\n        # Probably only one option,\n        # but this also avoids issues with kwsplats\n        hash_iterate(options) do |option, value|\n          case value.node_type\n          when :array\n            filter[option.value] = value.sexp_body.map {|v| v[1] }\n          when :lit, :str\n            filter[option.value] = value[1]\n          else\n            Brakeman.debug \"Unknown before_filter value: #{option} => #{value}\"\n          end\n        end\n      else\n        filter[:all] = true\n      end\n\n      filter\n    end\n\n    private\n\n    def filter_includes_method? filter_rule, method_name\n       filter_rule[:all] or\n       (filter_rule[:only] == method_name) or\n       (filter_rule[:only].is_a? Array and filter_rule[:only].include? method_name) or\n       (filter_rule[:except].is_a? Symbol and filter_rule[:except] != method_name) or\n       (filter_rule[:except].is_a? Array and not filter_rule[:except].include? method_name)\n    end\n  end\n\n  class Controller < Brakeman::Collection\n    include ControllerMethods\n\n    def initialize name, parent, file_name, src, tracker\n      super\n      initialize_controller\n      @collection = tracker.controllers\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/tracker/file_cache.rb",
    "content": "module Brakeman\n  class FileCache\n    def initialize(file_list = nil)\n      @file_list = file_list || {\n        controller: {},\n        initializer: {},\n        lib: {},\n        model: {},\n        template: {},\n      }\n    end\n\n    def controllers\n      @file_list[:controller]\n    end\n\n    def initializers\n      @file_list[:initializer]\n    end\n\n    def libs\n      @file_list[:lib]\n    end\n\n    def models\n      @file_list[:model]\n    end\n\n    def templates\n      @file_list[:template]\n    end\n\n    def add_file(astfile, type)\n      raise \"Unknown type: #{type}\" unless valid_type? type\n      @file_list[type][astfile.path] = astfile\n    end\n\n    def valid_type?(type)\n      @file_list.key? type\n    end\n\n    def cached? path\n      @file_list.any? do |name, list|\n        list[path]\n      end\n    end\n\n    def delete path\n      @file_list.each do |name, list|\n        list.delete path\n      end\n    end\n\n    def diff other\n      @file_list.each do |name, list|\n        other_list = other.send(:\"#{name}s\")\n\n        if list == other_list\n          next\n        else\n          puts \"-- #{name} --\"\n          puts \"Old: #{other_list.keys - list.keys}\"\n          puts \"New: #{list.keys - other_list.keys}\"\n        end\n      end\n    end\n\n    def dup\n      copy_file_list = @file_list.map do |name, list|\n        copy_list = list.map do |path, astfile|\n          copy_astfile = astfile.dup\n          copy_astfile.ast = copy_astfile.ast.deep_clone\n\n          [path, copy_astfile]\n        end.to_h\n\n        [name, copy_list]\n      end.to_h\n\n      FileCache.new(copy_file_list)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/tracker/library.rb",
    "content": "require 'brakeman/tracker/collection'\nrequire 'brakeman/tracker/controller'\nrequire 'brakeman/tracker/model'\n\nmodule Brakeman\n  class Library < Brakeman::Collection\n    include ControllerMethods\n    include ModelMethods\n\n    def initialize name, parent, file_name, src, tracker\n      super\n      initialize_controller\n      initialize_model\n      @collection = tracker.libs\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/tracker/method_info.rb",
    "content": "require 'brakeman/util'\n\nmodule Brakeman\n  class MethodInfo\n    include Brakeman::Util\n\n    attr_reader :name, :src, :owner, :file, :type\n\n    def initialize name, src, owner, file\n      @name = name\n      @src = src\n      @owner = owner\n      @file = file\n      @type = case src.node_type\n              when :defn\n                :instance\n              when :defs\n                :class\n              else\n                raise \"Expected sexp type: #{src.node_type}\"\n              end\n\n      @simple_method = nil\n    end\n\n    # To support legacy code that expected a Hash\n    def [] attr\n      self.send(attr)\n    end\n\n    def very_simple_method?\n      return @simple_method == :very unless @simple_method.nil?\n\n      # Very simple methods have one (simple) expression in the body and\n      # no arguments\n      if src.formal_args.length == 1 # no args\n        if src.method_length == 1 # Single expression in body\n          value = first_body # First expression in body\n\n          if simple_literal? value or\n              (array? value and all_literals? value) or\n              (hash? value and all_literals? value, :hash)\n\n            @return_value = value\n            @simple_method = :very\n          end\n        end\n      end\n\n      @simple_method ||= false\n    end\n\n    def return_value env = nil\n      if very_simple_method?\n        return @return_value\n      else\n        nil\n      end\n    end\n\n    def first_body\n      case @type\n      when :class\n        src[4]\n      when :instance\n        src[3]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/tracker/model.rb",
    "content": "require 'brakeman/tracker/collection'\n\nmodule Brakeman\n  module ModelMethods\n    attr_reader :associations, :attr_accessible, :role_accessible\n\n    def initialize_model\n      @associations = {}\n      @role_accessible = []\n      @attr_accessible = nil\n    end\n\n    def association? method_name\n      @associations.each do |name, args|\n        args.each do |arg|\n          if symbol? arg and arg.value == method_name\n            return true\n          end\n        end\n      end\n\n      false\n    end\n\n    def unprotected_model?\n      @attr_accessible.nil? and !parent_classes_protected? and ancestor?(:\"ActiveRecord::Base\")\n    end\n\n    # go up the chain of parent classes to see if any have attr_accessible\n    def parent_classes_protected? seen={}\n      seen[self.name] = true\n\n      if @attr_accessible or self.includes.include? :\"ActiveModel::ForbiddenAttributesProtection\"\n        true\n      elsif parent = tracker.models[self.parent] and !seen[self.parent]\n        parent.parent_classes_protected? seen\n      else\n        false\n      end\n    end\n\n    def set_attr_accessible exp = nil\n      if exp\n        args = []\n\n        exp.each_arg do |e|\n          if node_type? e, :lit\n            args << e.value\n          elsif hash? e\n            @role_accessible.concat args\n          end\n        end\n\n        @attr_accessible ||= []\n        @attr_accessible.concat args\n      else\n        @attr_accessible ||= []\n      end\n    end\n\n    def set_attr_protected exp\n      add_option :attr_protected, exp\n    end\n\n    def attr_protected\n      @options[:attr_protected]\n    end\n  end\n\n  class Model < Brakeman::Collection\n    include ModelMethods\n\n    ASSOCIATIONS = Set[:belongs_to, :has_one, :has_many, :has_and_belongs_to_many]\n\n    def initialize name, parent, file_name, src, tracker\n      super\n      initialize_model\n      @collection = tracker.models\n    end\n\n    def add_option name, exp\n      if ASSOCIATIONS.include? name\n        @associations[name] ||= []\n        @associations[name].concat exp.args\n      else\n        super name, exp.arglist.line(exp.line)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/tracker/template.rb",
    "content": "require 'brakeman/tracker/collection'\n\nmodule Brakeman\n  class Template < Brakeman::Collection\n    attr_accessor :type\n    attr_reader :render_path\n    attr_writer :src\n\n    def initialize name, called_from, file_name, tracker\n      super name, nil, file_name, nil, tracker\n      @render_path = called_from\n      @outputs = []\n    end\n\n    def add_output exp\n      @outputs << exp\n    end\n\n    def each_output\n      @outputs.each do |o|\n        yield o\n      end\n    end\n\n    def rendered_from_controller?\n      if @render_path\n        @render_path.rendered_from_controller?\n      else\n        false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/tracker.rb",
    "content": "require 'set'\nrequire 'brakeman/call_index'\nrequire 'brakeman/checks'\nrequire 'brakeman/report'\nrequire 'brakeman/processors/lib/find_call'\nrequire 'brakeman/processors/lib/find_all_calls'\nrequire 'brakeman/tracker/config'\nrequire 'brakeman/tracker/constants'\n\n#The Tracker keeps track of all the processed information.\nclass Brakeman::Tracker\n  attr_accessor :controllers, :constants, :templates, :models, :errors,\n    :checks, :initializers, :config, :routes, :processor, :libs,\n    :template_cache, :options, :filter_cache, :start_time, :end_time,\n    :duration, :ignored_filter, :app_tree, :file_cache, :pristine_file_cache\n\n  #Place holder when there should be a model, but it is not\n  #clear what model it will be.\n  UNKNOWN_MODEL = :BrakemanUnresolvedModel\n\n  #Creates a new Tracker.\n  #\n  #The Processor argument is only used by other Processors\n  #that might need to access it.\n  def initialize(app_tree, processor = nil, options = {})\n    @app_tree = app_tree\n    @processor = processor\n    @options = options\n    @file_cache = Brakeman::FileCache.new\n    @pristine_file_cache = nil\n\n    reset_all\n  end\n\n  def reset_all\n    @templates = {}\n    @controllers = {}\n\n    #Initialize models with the unknown model so\n    #we can match models later without knowing precisely what\n    #class they are.\n    @models = {}\n    @models[UNKNOWN_MODEL] = Brakeman::Model.new(UNKNOWN_MODEL, nil, @app_tree.file_path(\"NOT_REAL.rb\"), nil, self)\n\n    @method_cache = {}\n    @routes = {}\n    @initializers = {}\n    @errors = []\n    @libs = {}\n    @constants = Brakeman::Constants.new\n    @checks = nil\n    @processed = nil\n    @template_cache = Set.new\n    @filter_cache = {}\n    @call_index = nil\n    @config = Brakeman::Config.new(self)\n    @start_time = Time.now\n    @end_time = nil\n    @duration = nil\n  end\n\n  def save_file_cache!\n    @pristine_file_cache = @file_cache.dup\n  end\n\n  #Add an error to the list. If no backtrace is given,\n  #the one from the exception will be used.\n  def error exception, backtrace = nil\n    backtrace ||= exception.backtrace\n    unless backtrace.is_a? Array\n      backtrace = [ backtrace ]\n    end\n\n    Brakeman.debug exception\n    Brakeman.debug backtrace\n\n    @errors << {\n      :exception => exception,\n      :error => exception.to_s.gsub(\"\\n\", \" \"),\n      :backtrace => backtrace\n    }\n  end\n\n  def add_errors exceptions\n    exceptions.each do |e|\n      error(e)\n    end\n  end\n\n  #Run a set of checks on the current information. Results will be stored\n  #in Tracker#checks.\n  def run_checks\n    @checks = Brakeman::Checks.run_checks(self)\n\n    @end_time = Time.now\n    @duration = @end_time - @start_time\n    @checks\n  end\n\n  def app_path\n    @app_path ||= File.expand_path @options[:app_path]\n  end\n\n  #Iterate over all methods\n  def each_method\n    [self.controllers, self.models, self.libs].each do |set|\n      set.each do |set_name, collection|\n        collection.each_method do |method_name, definition|\n          src = definition.src\n          yield src, set_name, method_name, definition.file\n        end\n      end\n    end\n  end\n\n  #Iterates over each template, yielding the name and the template.\n  #Prioritizes templates which have been rendered.\n  def each_template\n    if @processed.nil?\n      @processed, @rest = templates.keys.sort_by{|template| template.to_s}.partition { |k| k.to_s.include? \".\" }\n    end\n\n    @processed.each do |k|\n      yield k, templates[k]\n    end\n\n    @rest.each do |k|\n      yield k, templates[k]\n    end\n  end\n\n\n  def each_class\n    [self.controllers, self.models, self.libs].each do |set|\n      set.each do |set_name, collection|\n        collection.src.each do |file, src|\n          yield src, set_name, file\n        end\n      end\n    end\n  end\n\n  #Find a method call.\n  #\n  #Options:\n  #  * :target => target name(s)\n  #  * :method => method name(s)\n  #  * :chained => search in method chains\n  #\n  #If :target => false or :target => nil, searches for methods without a target.\n  #Targets and methods can be specified as a symbol, an array of symbols,\n  #or a regular expression.\n  #\n  #If :chained => true, matches target at head of method chain and method at end.\n  #\n  #For example:\n  #\n  #    find_call :target => User, :method => :all, :chained => true\n  #\n  #could match\n  #\n  #    User.human.active.all(...)\n  #\n  def find_call options\n    index_call_sites unless @call_index\n    @call_index.find_calls options\n  end\n\n  #Searches the initializers for a method call\n  def check_initializers target, method\n    finder = Brakeman::FindCall.new target, method, self\n\n    initializers.sort.each do |name, initializer|\n      finder.process_source initializer\n    end\n\n    finder.matches\n  end\n\n  #Returns a Report with this Tracker's information\n  def report\n    Brakeman::Report.new(self)\n  end\n\n  def warnings\n    self.checks.all_warnings\n  end\n\n  def filtered_warnings\n    if self.ignored_filter\n      self.warnings.reject do |w|\n        self.ignored_filter.ignored? w\n      end\n    else\n      self.warnings\n    end\n  end\n\n  def unused_fingerprints\n    return [] unless self.ignored_filter\n    self.ignored_filter.obsolete_fingerprints\n  end\n\n  def add_constant name, value, context = nil\n    @constants.add name, value, context unless @options[:disable_constant_tracking]\n  end\n\n  # This method does not return all constants at this time,\n  # just ones with \"simple\" values.\n  def constant_lookup name\n    @constants.get_simple_value name unless @options[:disable_constant_tracking]\n  end\n\n  def find_class name\n    [@controllers, @models, @libs].each do |collection|\n      if c = collection[name]\n        return c\n      end\n    end\n\n    nil\n  end\n\n  def find_method method_name, class_name, method_type = :instance\n    return nil unless method_name.is_a? Symbol\n\n    klass = find_class(class_name)\n    return nil unless klass\n\n    cache_key = [klass, method_name, method_type]\n\n    if method = @method_cache[cache_key]\n      return method\n    end\n\n    if method = klass.get_method(method_name, method_type)\n      return method\n    else\n      # Check modules included for method definition\n      # TODO: only for instance methods, otherwise check extends!\n      klass.includes.each do |included_name|\n        if method = find_method(method_name, included_name, method_type)\n          return (@method_cache[cache_key] = method)\n        end\n      end\n\n      # Not in any included modules, check the parent\n      @method_cache[cache_key] = find_method(method_name, klass.parent, method_type)\n    end\n  end\n\n  def index_call_sites\n    finder = Brakeman::FindAllCalls.new self\n\n    self.each_method do |definition, set_name, method_name, file|\n      finder.process_source definition, :class => set_name, :method => method_name, :file => file\n    end\n\n    self.each_class do |definition, set_name, file|\n      finder.process_source definition, :class => set_name, :file => file\n    end\n\n    self.each_template do |_name, template|\n      finder.process_source template.src, :template => template, :file => template.file\n    end\n\n    self.initializers.each do |file_name, src|\n      finder.process_all_source src, :file => file_name\n    end\n\n    @call_index = Brakeman::CallIndex.new finder.calls\n  end\n\n  #Reindex call sites\n  #\n  #Takes a set of symbols which can include :templates, :models,\n  #or :controllers\n  #\n  #This will limit reindexing to the given sets\n  def reindex_call_sites locations\n    #If reindexing templates, models, controllers,\n    #just redo everything.\n    if locations.length == 3\n      return index_call_sites\n    end\n\n    if locations.include? :templates\n      @call_index.remove_template_indexes\n    end\n\n    classes_to_reindex = Set.new\n    method_sets = []\n\n    if locations.include? :models\n      classes_to_reindex.merge self.models.keys\n      method_sets << self.models\n    end\n\n    if locations.include? :controllers\n      classes_to_reindex.merge self.controllers.keys\n      method_sets << self.controllers\n    end\n\n    if locations.include? :libs\n      classes_to_reindex.merge self.libs.keys\n      method_sets << self.libs\n    end\n\n    if locations.include? :initializers\n      self.initializers.each do |file_name, src|\n        @call_index.remove_indexes_by_file file_name\n      end\n    end\n\n    @call_index.remove_indexes_by_class classes_to_reindex\n\n    finder = Brakeman::FindAllCalls.new self\n\n    method_sets.each do |set|\n      Brakeman.logger.spin\n\n      set.each do |set_name, info|\n        info.each_method do |method_name, definition|\n          src = definition.src\n          finder.process_source src, :class => set_name, :method => method_name, :file => definition.file\n        end\n      end\n    end\n\n    if locations.include? :templates\n      self.each_template do |_name, template|\n        Brakeman.logger.spin\n        finder.process_source template.src, :template => template, :file => template.file\n      end\n    end\n\n    if locations.include? :initializers\n      self.initializers.each do |file_name, src|\n        Brakeman.logger.spin\n        finder.process_all_source src, :file => file_name\n      end\n    end\n\n    @call_index.index_calls finder.calls\n  end\n\n  #Clear information related to templates.\n  #If :only_rendered => true, will delete templates rendered from\n  #controllers (but not those rendered from other templates)\n  def reset_templates options = { :only_rendered => false }\n    if options[:only_rendered]\n      @templates.delete_if do |_name, template|\n        template.rendered_from_controller?\n      end\n    else\n      @templates = {}\n    end\n    @processed = nil\n    @rest = nil\n    @template_cache.clear\n  end\n\n  #Clear information related to template\n  def reset_template name\n    name = name.to_sym\n    @templates.delete name\n    @processed = nil\n    @rest = nil\n    @template_cache.clear\n  end\n\n  #Clear information related to model\n  def reset_model path\n    model_name = nil\n\n    @models.each do |name, model|\n      if model.files.include?(path)\n        model_name = name\n        break\n      end\n    end\n\n    @models.delete(model_name)\n  end\n\n  #Clear information related to model\n  def reset_lib path\n    lib_name = nil\n\n    @libs.each do |name, lib|\n      if lib.files.include?(path)\n        lib_name = name\n        break\n      end\n    end\n\n    @libs.delete lib_name\n  end\n\n  def reset_controller path\n    controller_name = nil\n\n    #Remove from controller\n    @controllers.each do |name, controller|\n      if controller.files.include?(path)\n        controller_name = name\n\n        #Remove templates rendered from this controller\n        @templates.each do |template_name, template|\n          if template.render_path and template.render_path.include_controller? name\n            reset_template template_name\n            @call_index.remove_template_indexes template_name\n          end\n        end\n\n        #Remove calls indexed from this controller\n        @call_index.remove_indexes_by_class [name]\n        break\n      end\n    end\n    @controllers.delete controller_name\n  end\n\n  #Clear information about routes\n  def reset_routes\n    @routes = {}\n  end\n\n  def reset_initializer path\n    @initializers.delete_if do |file, src|\n      path.relative.include? file\n    end\n\n    @call_index.remove_indexes_by_file path\n  end\n\n  # Call this to be able to marshal the Tracker\n  def marshallable\n    @app_tree.marshallable\n    self\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/util.rb",
    "content": "require 'set'\nrequire 'pathname'\n\n#This is a mixin containing utility methods.\nmodule Brakeman::Util\n\n  QUERY_PARAMETERS = Sexp.new(:call, Sexp.new(:call, nil, :request), :query_parameters)\n\n  PATH_PARAMETERS = Sexp.new(:call, Sexp.new(:call, nil, :request), :path_parameters)\n\n  REQUEST_REQUEST_PARAMETERS = Sexp.new(:call, Sexp.new(:call, nil, :request), :request_parameters)\n\n  REQUEST_PARAMETERS = Sexp.new(:call, Sexp.new(:call, nil, :request), :parameters)\n\n  REQUEST_PARAMS = Sexp.new(:call, Sexp.new(:call, nil, :request), :params)\n\n  REQUEST_ENV = Sexp.new(:call, Sexp.new(:call, nil, :request), :env)\n\n  PARAMETERS = Sexp.new(:call, nil, :params)\n\n  COOKIES = Sexp.new(:call, nil, :cookies)\n\n  REQUEST_COOKIES = s(:call, s(:call, nil, :request), :cookies)\n\n  SESSION = Sexp.new(:call, nil, :session)\n\n  ALL_PARAMETERS = Set[PARAMETERS, QUERY_PARAMETERS, PATH_PARAMETERS, REQUEST_REQUEST_PARAMETERS, REQUEST_PARAMETERS, REQUEST_PARAMS]\n\n  ALL_COOKIES = Set[COOKIES, REQUEST_COOKIES]\n\n  SAFE_LITERAL = s(:lit, :BRAKEMAN_SAFE_LITERAL)\n\n  #Convert a string from \"something_like_this\" to \"SomethingLikeThis\"\n  #\n  #Taken from ActiveSupport.\n  def camelize lower_case_and_underscored_word\n    lower_case_and_underscored_word.to_s.gsub(/\\/(.?)/) { \"::#{$1.upcase}\" }.gsub(/(?:^|_)(.)/) { $1.upcase }\n  end\n\n  #Convert a string from \"Something::LikeThis\" to \"something/like_this\"\n  #\n  #Taken from ActiveSupport.\n  def underscore camel_cased_word\n    camel_cased_word.to_s.gsub(/::/, '/').\n      gsub(/([A-Z]+)([A-Z][a-z])/,'\\1_\\2').\n      gsub(/([a-z\\d])([A-Z])/,'\\1_\\2').\n      tr(\"-\", \"_\").\n      downcase\n  end\n\n  # stupid simple, used to delegate to ActiveSupport\n  def pluralize word\n    if word.end_with? 's'\n      word + 'es'\n    else\n      word + 's'\n    end\n  end\n\n  #Returns a class name as a Symbol.\n  #If class name cannot be determined, returns _exp_.\n  def class_name exp\n    case exp\n    when Sexp\n      case exp.node_type\n      when :const, :colon3\n        exp.value\n      when :lvar\n        exp.value.to_sym\n      when :colon2\n        \"#{class_name(exp.lhs)}::#{exp.rhs}\".to_sym\n      when :self\n        @current_class || @current_module || nil\n      else\n        exp\n      end\n    when Symbol\n      exp\n    when nil\n      nil\n    else\n      exp\n    end\n  end\n\n  #Takes an Sexp like\n  # (:hash, (:lit, :key), (:str, \"value\"))\n  #and yields the key and value pairs to the given block.\n  #\n  #For example:\n  #\n  # h = Sexp.new(:hash, (:lit, :name), (:str, \"bob\"), (:lit, :name), (:str, \"jane\"))\n  # names = []\n  # hash_iterate(h) do |key, value|\n  #   if symbol? key and key[1] == :name\n  #     names << value[1]\n  #   end\n  # end\n  # names #[\"bob\"]\n  def hash_iterate hash\n    hash = remove_kwsplat(hash)\n\n    1.step(hash.length - 1, 2) do |i|\n      yield hash[i], hash[i + 1]\n    end\n  end\n\n  def remove_kwsplat exp\n    if exp.any? { |e| node_type? e, :kwsplat }\n      exp.reject { |e| node_type? e, :kwsplat }\n    else\n      exp\n    end\n  end\n\n  #Insert value into Hash Sexp\n  def hash_insert hash, key, value\n    index = 1\n    hash_iterate hash.dup do |k,v|\n      if k == key\n        hash[index + 1] = value\n        return hash\n      end\n      index += 2\n    end\n\n    hash << key << value\n\n    hash\n  end\n\n  #Get value from hash using key.\n  #\n  #If _key_ is a Symbol, it will be converted to a Sexp(:lit, key).\n  def hash_access hash, key\n    if key.is_a? Symbol\n      key = Sexp.new(:lit, key)\n    end\n\n    if index = hash.find_index(key) and index > 0\n      return hash[index + 1]\n    end\n\n    nil\n  end\n\n  def hash_values hash\n    values = hash.each_sexp.each_slice(2).map do |_, value|\n      value\n    end\n\n    Sexp.new(:array).concat(values).line(hash.line)\n  end\n\n  #These are never modified\n  PARAMS_SEXP = Sexp.new(:params)\n  SESSION_SEXP = Sexp.new(:session)\n  COOKIES_SEXP = Sexp.new(:cookies)\n\n  #Adds params, session, and cookies to environment\n  #so they can be replaced by their respective Sexps.\n  def set_env_defaults\n    @env[PARAMETERS] = PARAMS_SEXP\n    @env[SESSION] = SESSION_SEXP\n    @env[COOKIES] = COOKIES_SEXP\n  end\n\n  #Check if _exp_ represents a hash: s(:hash, {...})\n  #This also includes pseudo hashes params, session, and cookies.\n  def hash? exp\n    exp.is_a? Sexp and (exp.node_type == :hash or\n                        exp.node_type == :params or\n                        exp.node_type == :session or\n                        exp.node_type == :cookies)\n  end\n\n  #Check if _exp_ represents an array: s(:array, [...])\n  def array? exp\n    exp.is_a? Sexp and exp.node_type == :array\n  end\n\n  #Check if _exp_ represents a String: s(:str, \"...\")\n  def string? exp\n    exp.is_a? Sexp and exp.node_type == :str\n  end\n\n  def string_interp? exp\n    exp.is_a? Sexp and exp.node_type == :dstr\n  end\n\n  #Check if _exp_ represents a Symbol: s(:lit, :...)\n  def symbol? exp\n    exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Symbol\n  end\n\n  #Check if _exp_ represents a method call: s(:call, ...)\n  def call? exp\n    exp.is_a? Sexp and\n      (exp.node_type == :call or exp.node_type == :safe_call)\n  end\n\n  #Check if _exp_ represents a Regexp: s(:lit, /.../)\n  def regexp? exp\n    exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Regexp\n  end\n\n  #Check if _exp_ represents an Integer: s(:lit, ...)\n  def integer? exp\n    exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Integer\n  end\n\n  #Check if _exp_ represents a number: s(:lit, ...)\n  def number? exp\n    exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Numeric\n  end\n\n  #Check if _exp_ represents a result: s(:result, ...)\n  def result? exp\n    exp.is_a? Sexp and exp.node_type == :result\n  end\n\n  #Check if _exp_ represents a :true, :lit, or :string node\n  def true? exp\n    exp.is_a? Sexp and (exp.node_type == :true or\n                        exp.node_type == :lit or\n                        exp.node_type == :string)\n  end\n\n  #Check if _exp_ represents a :false or :nil node\n  def false? exp\n    exp.is_a? Sexp and (exp.node_type == :false or\n                        exp.node_type == :nil)\n  end\n\n  #Check if _exp_ represents a block of code\n  def block? exp\n    exp.is_a? Sexp and (exp.node_type == :block or\n                        exp.node_type == :rlist)\n  end\n\n  #Check if _exp_ is a params hash\n  def params? exp\n    recurse_check?(exp) { |child| child.node_type == :params or ALL_PARAMETERS.include? child }\n  end\n\n  def cookies? exp\n    recurse_check?(exp) { |child| child.node_type == :cookies or ALL_COOKIES.include? child }\n  end\n\n  def recurse_check? exp, &check\n    if exp.is_a? Sexp\n      return true if yield(exp)\n\n      if call? exp\n        if recurse_check? exp[1], &check\n          return true\n        elsif exp[2] == :[]\n          return recurse_check? exp[1], &check\n        end\n      end\n    end\n\n    false\n  end\n\n  # Only return true when accessing request headers via request.env[...]\n  def request_headers? exp\n    return unless sexp? exp\n\n    if exp[1] == REQUEST_ENV\n      if exp.method == :[]\n        if string? exp.first_arg\n          # Only care about HTTP headers, which are prefixed by 'HTTP_'\n          exp.first_arg.value.start_with?('HTTP_'.freeze)\n        else\n          true # request.env[something]\n        end\n      else\n        false # request.env.something\n      end\n    else\n      false\n    end\n  end\n\n  #Check if exp is params, cookies, or request_headers\n  def request_value? exp\n    params? exp or\n    cookies? exp or\n    request_headers? exp\n  end\n\n  def constant? exp\n    node_type? exp, :const, :colon2, :colon3\n  end\n\n  def kwsplat? exp\n    exp.is_a? Sexp and\n      exp.node_type == :hash and\n      exp[1].is_a? Sexp and\n      exp[1].node_type == :kwsplat\n  end\n\n  #Check if _exp_ is a Sexp.\n  def sexp? exp\n    exp.is_a? Sexp\n  end\n\n  #Check if _exp_ is a Sexp and the node type matches one of the given types.\n  def node_type? exp, *types\n    exp.is_a? Sexp and types.include? exp.node_type\n  end\n\n  SIMPLE_LITERALS = [:lit, :false, :str, :true]\n\n  def simple_literal? exp\n    exp.is_a? Sexp and SIMPLE_LITERALS.include? exp.node_type\n  end\n\n  LITERALS = [*SIMPLE_LITERALS, :array, :hash]\n\n  def literal? exp\n    exp.is_a? Sexp and LITERALS.include? exp.node_type\n  end\n\n  def all_literals? exp, expected_type = :array\n    node_type? exp, expected_type and\n      exp.length > 1 and\n      exp.all? { |e| e.is_a? Symbol or node_type? e, :lit, :str }\n  end\n\n  DIR_CONST = s(:const, :Dir)\n\n  # Dir.glob(...).whatever\n  def dir_glob? exp\n    exp = exp.block_call if node_type? exp, :iter\n    return unless call? exp\n\n    (exp.target == DIR_CONST and exp.method == :glob) or dir_glob? exp.target\n  end\n\n  #Returns true if the given _exp_ contains a :class node.\n  #\n  #Useful for checking if a module is just a module or if it is a namespace.\n  def contains_class? exp\n    todo = [exp]\n\n    until todo.empty?\n      current = todo.shift\n\n      if node_type? current, :class\n        return true\n      elsif sexp? current\n        todo = current.sexp_body.concat todo\n      end\n    end\n\n    false\n  end\n\n  def make_call target, method, *args\n    call = Sexp.new(:call, target, method)\n\n    if args.empty? or args.first.empty?\n      #nothing to do\n    elsif node_type? args.first, :arglist\n      call.concat args.first.sexp_body\n    elsif args.first.node_type.is_a? Sexp #just a list of args\n      call.concat args.first\n    else\n      call.concat args\n    end\n\n    call\n  end\n\n  def safe_literal line = nil\n    s(:lit, :BRAKEMAN_SAFE_LITERAL).line(line || 0)\n  end\n\n  def safe_literal? exp\n    exp == SAFE_LITERAL\n  end\n\n  def safe_literal_target? exp\n    if call? exp\n      safe_literal_target? exp.target\n    else\n      safe_literal? exp\n    end\n  end\n\n  def rails_version\n    @tracker.config.rails_version\n  end\n\n  #Convert path/filename to view name\n  #\n  # views/test/something.html.erb -> test/something\n  def template_path_to_name path\n    names = path.relative.split('/')\n    names.last.gsub!(/(\\.(html|js)\\..*|\\.(rhtml|haml|erb|slim))$/, '')\n\n    if names.include? 'views'\n      names[(names.index('views') + 1)..-1]\n    else\n      names\n    end.join('/').to_sym\n  end\nend\n"
  },
  {
    "path": "lib/brakeman/version.rb",
    "content": "module Brakeman\n  Version = \"8.0.4\"\nend\n"
  },
  {
    "path": "lib/brakeman/warning.rb",
    "content": "require 'json'\nrequire 'digest/sha2'\nrequire 'brakeman/warning_codes'\nrequire 'brakeman/messages'\n\n#The Warning class stores information about warnings\nclass Brakeman::Warning\n  attr_reader :called_from, :check, :class, :confidence, :controller, :cwe_id,\n    :line, :method, :model, :template, :user_input, :user_input_type,\n    :warning_code, :warning_set, :warning_type\n\n  attr_accessor :code, :context, :file, :message\n\n  TEXT_CONFIDENCE = {\n    0 => \"High\",\n    1 => \"Medium\",\n    2 => \"Weak\",\n  }\n\n  CONFIDENCE = {\n    :high => 0,\n    :med => 1,\n    :medium => 1,\n    :low => 2,\n    :weak => 2,\n  }\n\n  OPTIONS = {\n    :called_from => :@called_from,\n    :check => :@check,\n    :class => :@class,\n    :code => :@code,\n    :controller => :@controller,\n    :cwe_id => :@cwe_id,\n    :file => :@file,\n    :gem_info => :@gem_info,\n    :line => :@line,\n    :link => :@link,\n    :link_path => :@link_path,\n    :message => :@message,\n    :method => :@method,\n    :model => :@model,\n    :template => :@template,\n    :user_input => :@user_input,\n    :warning_set => :@warning_set,\n    :warning_type => :@warning_type,\n  }\n\n  #+options[:result]+ can be a result from Tracker#find_call. Otherwise, it can be +nil+.\n  def initialize options = {}\n    @view_name = nil\n\n    OPTIONS.each do |key, var|\n      self.instance_variable_set(var, options[key])\n    end\n\n    self.confidence = options[:confidence]\n\n    result = options[:result]\n    if result\n      @code ||= result[:call]\n      @file ||= result[:location][:file]\n\n      if result[:location][:type] == :template #template result\n        @template ||= result[:location][:template]\n      else\n        @class ||= result[:location][:class]\n        @method ||= result[:location][:method]\n      end\n    end\n\n    if @method.to_s =~ /^fake_filter\\d+/\n      @method = :before_filter\n    end\n\n    if @user_input.is_a? Brakeman::BaseCheck::Match\n      @user_input_type = @user_input.type\n      @user_input = @user_input.match\n    elsif @user_input == false\n      @user_input = nil\n    end\n\n    if not @line\n      if @user_input and @user_input.respond_to? :line\n        @line = @user_input.line\n      elsif @code and @code.respond_to? :line\n        @line = @code.line\n      end\n    end\n\n    if @gem_info\n      if @gem_info.is_a? Hash\n        @line ||= @gem_info[:line]\n        @file ||= @gem_info[:file]\n      else\n        # Fallback behavior returns just a string for the file name\n        @file ||= @gem_info\n      end\n    end\n\n    unless @warning_set\n      if self.model\n        @warning_set = :model\n        @file ||= self.model.file\n      elsif self.template\n        @warning_set = :template\n        @called_from = self.template.render_path\n        @file ||= self.template.file\n      elsif self.controller\n        @warning_set = :controller\n      else\n        @warning_set = :warning\n      end\n    end\n\n    if options[:warning_code]\n      @warning_code = Brakeman::WarningCodes.code options[:warning_code]\n    else\n      @warning_code = nil\n    end\n\n    Brakeman.debug(\"Warning created without warning code: #{options[:warning_code]}\") unless @warning_code\n\n    if options[:message].is_a? String\n      @message = Brakeman::Messages::Message.new(options[:message])\n    end\n\n    @format_message = nil\n    @row = nil\n  end\n\n  def hash\n    self.to_s.hash\n  end\n\n  def eql? other_warning\n    self.hash == other_warning.hash\n  end\n\n  def confidence= conf\n    @confidence = case conf\n                  when Integer\n                    conf\n                  when Symbol\n                    CONFIDENCE[conf]\n                  else\n                    raise \"Could not set confidence to `#{conf}`\"\n                  end\n\n    raise \"Could not set confidence to `#{conf}`\" unless @confidence\n    raise \"Invalid confidence: `#{@confidence}`\" unless TEXT_CONFIDENCE[@confidence]\n  end\n\n  #Returns name of a view, including where it was rendered from\n  def view_name(include_renderer = true)\n    if called_from and include_renderer\n      @view_name = \"#{template.name} (#{called_from.last})\"\n    else\n      @view_name = template.name\n    end\n  end\n\n  #Return String of the code output from the OutputProcessor and\n  #stripped of newlines and tabs.\n  def format_code strip = true\n    format_ruby self.code, strip\n  end\n\n  #Return String of the user input formatted and\n  #stripped of newlines and tabs.\n  def format_user_input strip = true\n    format_ruby self.user_input, strip\n  end\n\n  def format_with_user_input strip = true, &block\n    if self.user_input\n      formatted = Brakeman::OutputProcessor.new.format(code, self.user_input, &block)\n      formatted.gsub!(/(\\t|\\r|\\n)+/, \" \") if strip\n      formatted\n    else\n      format_code\n    end\n  end\n\n  #Return formatted warning message\n  def format_message\n    return @format_message if @format_message\n\n    @format_message = self.message.to_s.dup\n\n    if self.line\n      @format_message << \" near line #{self.line}\"\n    end\n\n    if self.code\n      @format_message << \": #{format_code}\"\n    end\n\n    @format_message\n  end\n\n  def link\n    return @link if @link\n\n    if @link_path\n      if @link_path.start_with? \"http\"\n        @link = @link_path\n      else\n        @link = \"https://brakemanscanner.org/docs/warning_types/#{@link_path}\"\n      end\n    else\n      warning_path = self.warning_type.to_s.downcase.gsub(/\\s+/, '_') + \"/\"\n      @link = \"https://brakemanscanner.org/docs/warning_types/#{warning_path}\"\n    end\n\n    @link\n  end\n\n  #Generates a hash suitable for inserting into a table\n  def to_row type = :warning\n    @row = { \"Confidence\" => TEXT_CONFIDENCE[self.confidence],\n      \"Warning Type\" => self.warning_type.to_s,\n      \"CWE ID\" => self.cwe_id,\n      \"Message\" => self.message }\n\n    case type\n    when :template\n      @row[\"Template\"] = self.view_name.to_s\n    when :model\n      @row[\"Model\"] = self.model.name.to_s\n    when :controller\n      @row[\"Controller\"] = self.controller.to_s\n    when :warning\n      @row[\"Class\"] = self.class.to_s\n      @row[\"Method\"] = self.method.to_s\n    end\n\n    @row\n  end\n\n  def to_s\n   output =  \"(#{TEXT_CONFIDENCE[self.confidence]}) #{self.warning_type} - #{self.message}\"\n   output << \" near line #{self.line}\" if self.line\n   output << \" in #{self.file.relative}\" if self.file\n   output << \": #{self.format_code}\" if self.code\n\n   output\n  end\n\n  def fingerprint\n    loc = self.location\n    location_string = loc && loc.sort_by { |k, v| k.to_s }.inspect\n    warning_code_string = sprintf(\"%03d\", @warning_code)\n    code_string = @code.inspect\n\n    Digest::SHA2.new(256).update(\"#{warning_code_string}#{code_string}#{location_string}#{self.file.relative}#{self.confidence}\").to_s\n  end\n\n  def location include_renderer = true\n    case @warning_set\n    when :template\n      { :type => :template, :template => self.view_name(include_renderer) }\n    when :model\n      { :type => :model, :model => self.model.name }\n    when :controller\n      { :type => :controller, :controller => self.controller }\n    when :warning\n      if self.class\n        { :type => :method, :class => self.class, :method => self.method }\n      else\n        nil\n      end\n    end\n  end\n\n  def relative_path\n    self.file.relative\n  end\n\n  def check_name\n    @check_name ||= self.check.sub(/^Brakeman::Check/, '')\n  end\n\n  def confidence_name\n    TEXT_CONFIDENCE[self.confidence]\n  end\n\n  def to_hash absolute_paths: true\n    if self.called_from and not absolute_paths\n      render_path = self.called_from.with_relative_paths\n    else\n      render_path = self.called_from\n    end\n\n    { :warning_type => self.warning_type,\n      :warning_code => @warning_code,\n      :fingerprint => self.fingerprint,\n      :check_name => self.check_name,\n      :message => self.message.to_s,\n      :file => (absolute_paths ? self.file.absolute : self.file.relative),\n      :line => self.line,\n      :link => self.link,\n      :code => (@code && self.format_code(false)),\n      :render_path => render_path,\n      :location => self.location(false),\n      :user_input => (@user_input && self.format_user_input(false)),\n      :confidence => self.confidence_name,\n      :cwe_id => cwe_id\n    }\n  end\n\n  def to_json\n    JSON.generate self.to_hash\n  end\n\n  private\n\n  def format_ruby code, strip\n    formatted = Brakeman::OutputProcessor.new.format(code)\n    formatted = formatted.gsub(/(\\t|\\r|\\n)+/, \" \") if strip\n    formatted\n  end\nend\n\n"
  },
  {
    "path": "lib/brakeman/warning_codes.rb",
    "content": "module Brakeman::WarningCodes\n  Codes = {\n    :sql_injection => 0,\n    :sql_injection_limit_offset => 1,\n    :cross_site_scripting => 2,\n    :xss_link_to => 3,\n    :xss_link_to_href => 4,\n    :xss_to_json => 5,\n    :csrf_protection_disabled => 6,\n    :csrf_protection_missing => 7,\n    :csrf_blacklist => 8,\n    :basic_auth_password => 9,\n    :auth_blacklist => 10,\n    :all_default_routes => 11,\n    :controller_default_routes => 12,\n    :code_eval => 13,\n    :command_injection => 14,\n    :dynamic_render_path => 15,\n    :file_access => 16,\n    :mass_assign_call => 17,\n    :open_redirect => 18,\n    :no_attr_accessible => 19,\n    :attr_protected_used => 20,\n    :safe_buffer_vuln => 21,\n    :select_options_vuln => 22,\n    :dangerous_send => 23,\n    :unsafe_constantize => 24,\n    :unsafe_deserialize => 25,\n    :http_cookies => 26,\n    :secure_cookies => 27,\n    :translate_vuln => 28,\n    :session_secret => 29,\n    :validation_regex => 30,\n    :CVE_2010_3933 => 31,\n    :CVE_2011_0446 => 32,\n    :CVE_2011_0447 => 33,\n    :CVE_2011_2929 => 34,\n    :CVE_2011_2930 => 35,\n    :CVE_2011_2931 => 36,\n    :CVE_2011_3186 => 37,\n    :CVE_2012_2660 => 38,\n    :CVE_2012_2661 => 39,\n    :CVE_2012_2695 => 40,\n    #:CVE_2012_2931 => 41,\n    :CVE_2012_3424 => 42,\n    :CVE_2012_3463 => 43,\n    :CVE_2012_3464 => 44,\n    :CVE_2012_3465 => 45,\n    :CVE_2012_5664 => 46,\n    :CVE_2013_0155 => 47,\n    :CVE_2013_0156 => 48,\n    :CVE_2013_0269 => 49,\n    :CVE_2013_0277 => 50,\n    :CVE_2013_0276 => 51,\n    :CVE_2013_0333 => 52,\n    :xss_content_tag => 53,\n    :mass_assign_without_protection => 54,\n    :CVE_2013_1854 => 55,\n    :CVE_2013_1855 => 56,\n    :CVE_2013_1856 => 57,\n    :CVE_2013_1857 => 58,\n    :unsafe_symbol_creation => 59,\n    :dangerous_attr_accessible => 60,\n    :local_request_config => 61,\n    :detailed_exceptions => 62,\n    :CVE_2013_4491 => 63,\n    :CVE_2013_6414 => 64,\n    # Replaced by CVE_2014_0081\n    #:CVE_2013_6415 => 65,\n    #:CVE_2013_6415_call => 66,\n    :CVE_2013_6416 => 67,\n    :CVE_2013_6416_call => 68,\n    :CVE_2013_6417 => 69,\n    :mass_assign_permit! => 70,\n    :ssl_verification_bypass => 71,\n    :CVE_2014_0080 => 72,\n    :CVE_2014_0081 => 73,\n    :CVE_2014_0081_call => 74,\n    :CVE_2014_0082 => 75,\n    :regex_dos => 76,\n    :CVE_2014_0130 => 77,\n    :CVE_2014_3482 => 78,\n    :CVE_2014_3483 => 79,\n    :CVE_2014_3514 => 80,\n    :CVE_2014_3514_call => 81,\n    :unscoped_find => 82,\n    :CVE_2011_2932 => 83,\n    :cross_site_scripting_inline => 84,\n    :CVE_2014_7829 => 85,\n    :csrf_not_protected_by_raising_exception => 86,\n    :CVE_2015_3226 => 87,\n    :CVE_2015_3227 => 88,\n    :session_key_manipulation => 89,\n    :weak_hash_digest => 90,\n    :weak_hash_hmac => 91,\n    :sql_injection_dynamic_finder => 92,\n    :CVE_2015_7576 => 93,\n    :CVE_2016_0751 => 94,\n    :CVE_2015_7577 => 95,\n    :CVE_2015_7578 => 96,\n    :CVE_2015_7580 => 97,\n    :CVE_2015_7579 => 98,\n    :dynamic_render_path_rce => 99,\n    :CVE_2015_7581 => 100,\n    :secret_in_source => 101,\n    :CVE_2016_6316 => 102,\n    :CVE_2016_6317 => 103,\n    :divide_by_zero => 104,\n    :dangerous_permit_key => 105,\n    :CVE_2018_8048 => 106,\n    :CVE_2018_3741 => 107,\n    :CVE_2018_3760 => 108,\n    :force_ssl_disabled => 109,\n    :unsafe_cookie_serialization => 110,\n    :reverse_tabnabbing => 111,\n    :mass_assign_permit_all => 112,\n    :json_html_escape_config => 113,\n    :json_html_escape_module => 114,\n    :CVE_2020_8159 => 115,\n    :CVE_2020_8166 => 116,\n    :erb_template_injection => 117,\n    :http_verb_confusion => 118,\n    :unsafe_method_reflection => 119,\n    :eol_rails => 120,\n    :eol_ruby => 121,\n    :pending_eol_rails => 122,\n    :pending_eol_ruby => 123,\n    :CVE_2022_32209 => 124,\n    :pathname_traversal => 125,\n    :insecure_rsa_padding_mode => 126,\n    :missing_rsa_padding_mode => 127,\n    :small_rsa_key_size => 128,\n    :ransack_search => 129,\n\n    :custom_check => 9090,\n  }\n\n  def self.code name\n    Codes[name]\n  end\nend\n"
  },
  {
    "path": "lib/brakeman.rb",
    "content": "require 'set'\nrequire 'brakeman/logger'\nrequire 'brakeman/version'\n\nmodule Brakeman\n\n  #This exit code is used when warnings are found and the --exit-on-warn\n  #option is set\n  Warnings_Found_Exit_Code = 3\n\n  #Exit code returned when no Rails application is detected\n  No_App_Found_Exit_Code = 4\n\n  #Exit code returned when brakeman was outdated\n  Not_Latest_Version_Exit_Code = 5\n\n  #Exit code returned when user requests non-existent checks\n  Missing_Checks_Exit_Code = 6\n\n  #Exit code returned when errors were found and the --exit-on-error\n  #option is set\n  Errors_Found_Exit_Code = 7\n\n  #Exit code returned when an ignored warning has no note and\n  #--ensure-ignore-notes is set\n  Empty_Ignore_Note_Exit_Code = 8\n\n  # Exit code returned when at least one obsolete ignore entry is present\n  # and `--ensure-no-obsolete-ignore-entries` is set.\n  Obsolete_Ignore_Entries_Exit_Code = 9\n\n  @debug = false\n  @quiet = false\n  @loaded_dependencies = []\n  @vendored_paths = false\n  @logger = nil\n\n  #Run Brakeman scan. Returns Tracker object.\n  #\n  #Options:\n  #\n  #  * :app_path - path to root of Rails app (required)\n  #  * :additional_checks_path - array of additional directories containing additional out-of-tree checks to run\n  #  * :additional_libs_path - array of additional application relative lib directories (ex. app/mailers) to process\n  #  * :assume_all_routes - assume all methods are routes (default: true)\n  #  * :check_arguments - check arguments of methods (default: true)\n  #  * :collapse_mass_assignment - report unprotected models in single warning (default: false)\n  #  * :combine_locations - combine warning locations (default: true)\n  #  * :config_file - configuration file\n  #  * :escape_html - escape HTML by default (automatic)\n  #  * :exit_on_error - only affects Commandline module (default: true)\n  #  * :exit_on_warn - only affects Commandline module (default: true)\n  #  * :github_repo - github repo to use for file links (user/repo[/path][@ref])\n  #  * :highlight_user_input - highlight user input in reported warnings (default: true)\n  #  * :html_style - path to CSS file\n  #  * :ignore_model_output - consider models safe (default: false)\n  #  * :interprocedural - limited interprocedural processing of method calls (default: false)\n  #  * :message_limit - limit length of messages\n  #  * :min_confidence - minimum confidence (0-2, 0 is highest)\n  #  * :output_files - files for output\n  #  * :output_formats - formats for output (:to_s, :to_tabs, :to_csv, :to_html)\n  #  * :parallel_checks - run checks in parallel (default: true)\n  #  * :parser_timeout - set timeout for parsing an individual file (default: 10 seconds)\n  #  * :print_report - if no output file specified, print to stdout (default: false)\n  #  * :quiet - suppress most messages (default: true)\n  #  * :rails3 - force Rails 3 mode (automatic)\n  #  * :rails4 - force Rails 4 mode (automatic)\n  #  * :rails5 - force Rails 5 mode (automatic)\n  #  * :rails6 - force Rails 6 mode (automatic)\n  #  * :report_routes - show found routes on controllers (default: false)\n  #  * :run_checks - array of checks to run (run all if not specified)\n  #  * :safe_methods - array of methods to consider safe\n  #  * :show_ignored - Display warnings that are usually ignored\n  #  * :sql_safe_methods - array of sql sanitization methods to consider safe\n  #  * :skip_vendor - do not process vendor/ directory (default: true)\n  #  * :skip_checks - checks not to run (run all if not specified)\n  #  * :absolute_paths - show absolute path of each file (default: false)\n  #  * :summary_only - only output summary section of report for plain/table (:summary_only, :no_summary, true)\n  #\n  #Alternatively, just supply a path as a string.\n  def self.run options\n    if not $stderr.tty? and options[:report_progress].nil?\n      options[:report_progress] = false\n    end\n\n    options = set_options options\n\n    @quiet = !!options[:quiet]\n    @debug = !!options[:debug]\n\n    if @quiet\n      options[:report_progress] = false\n    end\n\n    @logger = options[:logger] || set_default_logger(options)\n\n    if options[:use_prism]\n      begin\n        require 'prism'\n      rescue LoadError => e\n        Brakeman.alert \"Asked to use Prism, but failed to load: #{e}\"\n      end\n    end\n\n    Brakeman.announce \"Brakeman v#{Brakeman::Version}\"\n\n    scan options\n  end\n\n  def self.logger\n    @logger\n  end\n\n  def self.logger= log\n    @logger = log\n  end\n\n  def self.set_default_logger(options = {})\n    @logger = Brakeman::Logger.get_logger(options)\n  end\n\n  def self.cleanup(newline = true)\n    @logger.cleanup(newline) if @logger\n  end\n\n  #Sets up options for run, checks given application path\n  def self.set_options options\n    if options.is_a? String\n      options = { :app_path => options }\n    end\n\n    if options[:quiet] == :command_line\n      command_line = true\n      options.delete :quiet\n    end\n\n    options = default_options.merge(load_options(options)).merge(options)\n\n    if options[:quiet].nil? and not command_line\n      options[:quiet] = true\n    end\n\n    if options[:rails4]\n      options[:rails3] = true\n    elsif options[:rails5]\n      options[:rails3] = true\n      options[:rails4] = true\n    elsif options[:rails6]\n      options[:rails3] = true\n      options[:rails4] = true\n      options[:rails5] = true\n    end\n\n    options[:output_formats] = get_output_formats options\n    options[:github_url] = get_github_url options\n\n\n    # Use ENV value only if option was not already explicitly set\n    # (i.e. prefer commandline option over environment variable).\n    if options[:gemfile].nil? and ENV['BUNDLE_GEMFILE'] and not ENV['BUNDLE_GEMFILE'].empty?\n      options[:gemfile] = ENV['BUNDLE_GEMFILE']\n    end\n\n    options\n  end\n\n  #Load options from YAML file\n  def self.load_options line_options\n    custom_location = line_options[:config_file]\n    app_path = line_options[:app_path]\n\n    #Load configuration file\n    if config = config_file(custom_location, app_path)\n      require 'yaml'\n      options = YAML.safe_load_file config, permitted_classes: [Symbol], symbolize_names: true\n\n      if options\n        options.each { |k, v| options[k] = Set.new v if v.is_a? Array }\n\n        # After parsing the yaml config file for options, convert any string keys into symbols.\n        options.keys.select {|k| k.is_a? String}.map {|k| k.to_sym }.each {|k| options[k] = options[k.to_s]; options.delete(k.to_s) }\n\n        # Brakeman.logger is probably not set yet\n        logger = Brakeman::Logger.get_logger(options.merge(line_options))\n\n        unless line_options[:allow_check_paths_in_config]\n          if options.include? :additional_checks_path\n            options.delete :additional_checks_path\n\n            logger.alert 'Ignoring additional check paths in config file. Use --allow-check-paths-in-config to allow'\n          end\n        end\n\n        logger.alert \"Using configuration in #{config}\"\n        options\n      else\n        logger = Brakeman::Logger.get_logger(line_options)\n        logger.alert \"Empty configuration file: #{config}\"\n        {}\n      end\n    else\n      {}\n    end\n  end\n\n  CONFIG_FILES = begin\n                   [\n                     File.expand_path(\"~/.brakeman/config.yml\"),\n                     File.expand_path(\"/etc/brakeman/config.yml\")\n                   ]\n                 rescue ArgumentError\n                   # In case $HOME or $USER aren't defined for use of `~`\n                   [\n                     File.expand_path(\"/etc/brakeman/config.yml\")\n                   ]\n                 end\n\n  def self.config_file custom_location, app_path\n    app_config = File.expand_path(File.join(app_path, \"config\", \"brakeman.yml\"))\n    supported_locations = [File.expand_path(custom_location || \"\"), app_config] + CONFIG_FILES\n    supported_locations.detect {|f| File.file?(f) }\n  end\n\n  #Default set of options\n  def self.default_options\n    { :assume_all_routes => true,\n      :check_arguments => true,\n      :collapse_mass_assignment => false,\n      :combine_locations => true,\n      :engine_paths => [\"engines/*\"],\n      :exit_on_error => true,\n      :exit_on_warn => true,\n      :highlight_user_input => true,\n      :html_style => \"#{File.expand_path(File.dirname(__FILE__))}/brakeman/format/style.css\",\n      :ignore_model_output => false,\n      :ignore_redirect_to_model => true,\n      :message_limit => 100,\n      :min_confidence => 2,\n      :output_color => true,\n      :pager => true,\n      :parallel_checks => true,\n      :parser_timeout => 10,\n      :use_prism => true,\n      :relative_path => false,\n      :report_progress => true,\n      :safe_methods => Set.new,\n      :show_ignored => false,\n      :sql_safe_methods => Set.new,\n      :skip_checks => Set.new,\n      :skip_vendor => true,\n    }\n  end\n\n  #Determine output formats based on options[:output_formats]\n  #or options[:output_files]\n  def self.get_output_formats options\n    #Set output format\n    if options[:output_format] && options[:output_files] && options[:output_files].size > 1\n      raise ArgumentError, \"Cannot specify output format if multiple output files specified\"\n    end\n    if options[:output_format]\n      get_formats_from_output_format options[:output_format]\n    elsif options[:output_files]\n      get_formats_from_output_files options[:output_files]\n    else\n      begin\n        self.load_brakeman_dependency 'terminal-table', :allow_fail\n        return [:to_s]\n      rescue LoadError\n        return [:to_json]\n      end\n    end\n  end\n\n  def self.get_formats_from_output_format output_format\n    case output_format\n    when :html, :to_html\n      [:to_html]\n    when :csv, :to_csv\n      [:to_csv]\n    when :pdf, :to_pdf\n      [:to_pdf]\n    when :tabs, :to_tabs\n      [:to_tabs]\n    when :json, :to_json\n      [:to_json]\n    when :markdown, :to_markdown\n      [:to_markdown]\n    when :cc, :to_cc, :codeclimate, :to_codeclimate\n      [:to_codeclimate]\n    when :plain ,:to_plain, :text, :to_text, :to_s\n      [:to_text]\n    when :table, :to_table\n      [:to_table]\n    when :junit, :to_junit\n      [:to_junit]\n    when :sarif, :to_sarif\n      [:to_sarif]\n    when :sonar, :to_sonar\n      [:to_sonar]\n    when :github, :to_github\n      [:to_github]\n    else\n      [:to_text]\n    end\n  end\n  private_class_method :get_formats_from_output_format\n\n  def self.get_formats_from_output_files output_files\n    output_files.map do |output_file|\n      case output_file\n      when /\\.html$/i\n        :to_html\n      when /\\.csv$/i\n        :to_csv\n      when /\\.pdf$/i\n        :to_pdf\n      when /\\.tabs$/i\n        :to_tabs\n      when /\\.json$/i\n        :to_json\n      when /\\.md$/i\n        :to_markdown\n      when /(\\.cc|\\.codeclimate)$/i\n        :to_codeclimate\n      when /\\.plain$/i\n        :to_text\n      when /\\.table$/i\n        :to_table\n      when /\\.junit$/i\n        :to_junit\n      when /\\.sarif$/i\n        :to_sarif\n      when /\\.sonar$/i\n        :to_sonar\n      when /\\.github$/i\n        :to_github\n      else\n        :to_text\n      end\n    end\n  end\n  private_class_method :get_formats_from_output_files\n\n  def self.get_github_url options\n    if github_repo = options[:github_repo]\n      full_repo, ref = github_repo.split '@', 2\n      name, repo, path = full_repo.split '/', 3\n      unless name && repo && !(name.empty? || repo.empty?)\n        raise ArgumentError, \"Invalid GitHub repository format\"\n      end\n      path.chomp '/' if path\n      ref ||= 'master'\n      ['https://github.com', name, repo, 'blob', ref, path].compact.join '/'\n    else\n      nil\n    end\n  end\n  private_class_method :get_github_url\n\n  #Output list of checks (for `-k` option)\n  def self.list_checks options\n    require 'brakeman/scanner'\n\n    add_external_checks options\n\n    if options[:list_optional_checks]\n      $stderr.puts \"Optional Checks:\"\n      checks = Checks.optional_checks\n    else\n      $stderr.puts \"Available Checks:\"\n      checks = Checks.checks\n    end\n\n    format_length = 30\n\n    $stderr.puts \"-\" * format_length\n    checks.each do |check|\n      $stderr.printf(\"%-#{format_length}s%s\\n\", check.name, check.description)\n    end\n  end\n\n  #Output configuration to YAML\n  def self.dump_config options\n    require 'yaml'\n    if options[:create_config].is_a? String\n      file = options[:create_config]\n    else\n      file = nil\n    end\n\n    options.delete :create_config\n\n    if options[:logger]\n      @logger = options.delete(:logger)\n    else\n      set_default_logger(options)\n    end\n\n    options.each do |k,v|\n      if v.is_a? Set\n        options[k] = v.to_a\n      end\n    end\n\n    if file\n      File.open file, \"w\" do |f|\n        YAML.dump options, f\n      end\n\n      announce \"Output configuration to #{file}\"\n    else\n      $stdout.puts YAML.dump(options)\n    end\n  end\n\n  # Returns quit message unless the latest version\n  # of Brakeman matches the current version.\n  #\n  # Optionally checks that the latest version is at least\n  # the specified number of days old.\n  def self.ensure_latest(days_old: 0)\n    require 'date'\n\n    current = Brakeman::Version\n    latest = Gem.latest_spec_for('brakeman')\n    release_date = latest.date.to_date\n    latest_version = latest.version.to_s\n\n    if (Date.today - latest.date.to_date) >= days_old\n      if current != latest_version\n        return \"Brakeman #{current} is not the latest version #{latest_version}\"\n      else\n        false\n      end\n    else\n      false\n    end\n  end\n\n  #Run a scan. Generally called from Brakeman.run instead of directly.\n  def self.scan options\n    #Load scanner\n    scanner, tracker = nil\n\n    process_step 'Loading scanner' do\n      begin\n        require 'brakeman/scanner'\n      rescue LoadError\n        raise NoBrakemanError, 'Cannot find lib/ directory.'\n      end\n\n      add_external_checks options\n\n      #Start scanning\n      scanner = Scanner.new options\n      tracker = scanner.tracker\n\n      check_for_missing_checks options[:run_checks], options[:skip_checks], options[:enable_checks]\n    end\n\n    logger.announce \"Scanning #{tracker.app_path}\"\n    scanner.process\n\n    tracker.run_checks\n\n    self.filter_warnings tracker, options\n\n    if options[:output_files]\n      process_step 'Generating report' do\n        write_report_to_files tracker, options[:output_files]\n      end\n    elsif options[:print_report]\n      process_step 'Generating report' do\n        write_report_to_formats tracker, options[:output_formats]\n      end\n    end\n\n    tracker\n  end\n\n  def self.write_report_to_files tracker, output_files\n    require 'fileutils'\n    tracker.options[:output_color] = false unless tracker.options[:output_color] == :force\n\n    output_files.each_with_index do |output_file, idx|\n      dir = File.dirname(output_file)\n      unless Dir.exist? dir\n        FileUtils.mkdir_p(dir)\n      end\n\n      File.open output_file, \"w\" do |f|\n        f.write tracker.report.format(tracker.options[:output_formats][idx])\n      end\n\n      logger.announce \"Report saved in '#{output_file}'\"\n    end\n  end\n  private_class_method :write_report_to_files\n\n  def self.write_report_to_formats tracker, output_formats\n    unless $stdout.tty? or tracker.options[:output_color] == :force\n      tracker.options[:output_color] = false\n    end\n\n    if not $stdout.tty? or not tracker.options[:pager] or output_formats.length > 1 # does this ever happen??\n      output_formats.each do |output_format|\n        puts tracker.report.format(output_format)\n      end\n    else\n      require \"brakeman/report/pager\"\n\n      Brakeman::Pager.new(tracker).page_report(tracker.report, output_formats.first)\n    end\n  end\n  private_class_method :write_report_to_formats\n\n  #Rescan a subset of files in a Rails application.\n  #\n  #A full scan must have been run already to use this method.\n  #The returned Tracker object from Brakeman.run is used as a starting point\n  #for the rescan.\n  #\n  #Options may be given as a hash with the same values as Brakeman.run.\n  #Note that these options will be merged into the Tracker.\n  #\n  #This method returns a RescanReport object with information about the scan.\n  #However, the Tracker object will also be modified as the scan is run.\n  def self.rescan tracker, files, options = {}\n    require 'brakeman/rescanner'\n\n    options = tracker.options.merge options\n\n    @quiet = !!tracker.options[:quiet]\n    @debug = !!tracker.options[:debug]\n\n    Rescanner.new(options, tracker.processor, files).recheck\n  end\n\n  def self.announce message\n    logger.announce message\n  end\n\n  def self.alert message\n    logger.alert message\n  end\n\n  def self.debug message\n    logger.debug message\n  end\n\n  # Compare JSON output from a previous scan and return the diff of the two scans\n  def self.compare options\n    require 'json'\n    require 'brakeman/differ'\n    raise ArgumentError.new(\"Comparison file doesn't exist\") unless File.exist? options[:previous_results_json]\n\n    begin\n      previous_results = JSON.parse(File.read(options[:previous_results_json]), :symbolize_names => true)[:warnings]\n    rescue JSON::ParserError\n      self.alert \"Error parsing comparison file: #{options[:previous_results_json]}\"\n      exit!\n    end\n\n    tracker = run(options)\n    new_report = JSON.parse(tracker.report.to_json, symbolize_names: true)\n\n    new_results = new_report[:warnings]\n    obsolete_ignored = tracker.unused_fingerprints\n\n    Brakeman::Differ.new(new_results, previous_results).diff.tap do |diff|\n      diff[:obsolete] = obsolete_ignored\n    end\n  end\n\n  def self.load_brakeman_dependency name, allow_fail = false\n    return if @loaded_dependencies.include? name\n\n    unless @vendored_paths\n      path_load = \"#{File.expand_path(File.dirname(__FILE__))}/../bundle/load.rb\"\n\n      if File.exist? path_load\n        require path_load\n      end\n\n      @vendored_paths = true\n    end\n\n    begin\n      require name\n    rescue LoadError => e\n      if allow_fail\n        raise e\n      else\n        $stderr.puts e.message\n        $stderr.puts \"Please install the appropriate dependency: #{name}.\"\n        exit!(-1)\n      end\n    end\n  end\n\n  # Returns an array of alert fingerprints for any ignored warnings without\n  # notes found in the specified ignore file (if it exists).\n  def self.ignore_file_entries_with_empty_notes file\n    return [] unless file\n\n    require 'brakeman/report/ignore/config'\n\n    config = IgnoreConfig.new(file, nil)\n    config.read_from_file\n    config.already_ignored_entries_with_empty_notes.map { |i| i[:fingerprint] }\n  end\n\n  def self.filter_warnings tracker, options\n    require 'brakeman/report/ignore/config'\n    config = nil\n\n    app_tree = Brakeman::AppTree.from_options(options)\n\n    if options[:ignore_file]\n      file = options[:ignore_file]\n    elsif app_tree.exists? \"config/brakeman.ignore\"\n      file = app_tree.expand_path(\"config/brakeman.ignore\")\n    elsif not options[:interactive_ignore]\n      return\n    end\n\n    process_step \"Filtering warnings...\" do\n      if options[:interactive_ignore]\n        require 'brakeman/report/ignore/interactive'\n        logger.cleanup\n        config = InteractiveIgnorer.new(file, tracker.warnings).start\n      else\n        logger.announce \"Using '#{file}' to filter warnings\"\n        config = IgnoreConfig.new(file, tracker.warnings)\n        config.read_from_file\n        config.filter_ignored\n      end\n    end\n\n    tracker.ignored_filter = config\n  end\n\n  def self.add_external_checks options\n    options[:additional_checks_path].each do |path|\n      Brakeman::Checks.initialize_checks path\n    end if options[:additional_checks_path]\n  end\n\n  def self.check_for_missing_checks included_checks, excluded_checks, enabled_checks\n    checks = included_checks.to_a + excluded_checks.to_a + enabled_checks.to_a\n\n    missing = Brakeman::Checks.missing_checks(checks)\n\n    unless missing.empty?\n      raise MissingChecksError, \"Could not find specified check#{missing.length > 1 ? 's' : ''}: #{missing.map {|c| \"`#{c}`\"}.join(', ')}\"\n    end\n  end\n\n  def self.debug= val\n    @debug = val\n  end\n\n  def self.quiet= val\n    @quiet = val\n  end\n\n  def self.process_step(description, &)\n    logger.context(description, &)\n  end\n\n  class DependencyError < RuntimeError; end\n  class NoBrakemanError < RuntimeError; end\n  class NoApplication < RuntimeError; end\n  class MissingChecksError < RuntimeError; end\nend\n"
  },
  {
    "path": "lib/ruby_parser/bm_sexp.rb",
    "content": "#Sexp changes from ruby_parser\n#and some changes for caching hash value and tracking 'original' line number\n#of a Sexp.\nclass Sexp\n  attr_accessor :original_line, :or_depth\n  ASSIGNMENT_BOOL = [:gasgn, :iasgn, :lasgn, :cvdecl, :cvasgn, :cdecl, :or, :and, :colon2, :op_asgn_or]\n  CALLS = [:call, :attrasgn, :safe_call, :safe_attrasgn]\n\n  alias_method :method_missing, :method_missing # silence redefined method warning\n  def method_missing name, *args\n    #Brakeman does not use this functionality,\n    #so overriding it to raise a NoMethodError.\n    #\n    #The original functionality calls find_node and optionally\n    #deletes the node if found.\n    #\n    #Defining a method named \"return\" seems like a bad idea, so we have to\n    #check for it here instead\n    if name == :return\n      find_node name, *args\n    else\n      raise NoMethodError.new(\"No method '#{name}' for Sexp\", name, args)\n    end\n  end\n\n  #Create clone of Sexp and nested Sexps but not their non-Sexp contents.\n  #If a line number is provided, also sets line/original_line on all Sexps.\n  def deep_clone line = nil\n    s = Sexp.new\n\n    self.each do |e|\n      if e.is_a? Sexp\n        s << e.deep_clone(line)\n      else\n        s << e\n      end\n    end\n\n    if line\n      s.original_line = self.original_line || self.line\n      s.line(line)\n    else\n      s.original_line = self.original_line\n      s.line(self.line) if self.line\n    end\n\n    s\n  end\n\n  alias_method :paren, :paren # silence redefined method warning\n  def paren\n    @paren ||= false\n  end\n\n  alias_method :value, :value # silence redefined method warning\n  def value\n    raise WrongSexpError, \"Sexp#value called on multi-item Sexp: `#{self.inspect}`\" if size > 2\n    self[1]\n  end\n\n  def value= exp\n    raise WrongSexpError, \"Sexp#value= called on multi-item Sexp: `#{self.inspect}`\" if size > 2\n    @my_hash_value = nil\n    self[1] = exp\n  end\n\n  def second\n    self[1]\n  end\n\n  def to_sym\n    self.value.to_sym\n  end\n\n  def node_type= type\n    @my_hash_value = nil\n    self[0] = type\n  end\n\n  #Join self and exp into an :or Sexp.\n  #Sets or_depth.\n  #Used for combining \"branched\" values in AliasProcessor.\n  def combine exp, line = nil\n    combined = Sexp.new(:or, self, exp).line(line || -2)\n\n    combined.or_depth = [self.or_depth, exp.or_depth].compact.reduce(0, :+) + 1\n\n    combined\n  end\n\n  alias :node_type :sexp_type\n  alias :values :sexp_body # TODO: retire\n\n  alias :old_push :<<\n  alias :old_compact :compact\n  alias :old_fara :find_and_replace_all\n  alias :old_find_node :find_node\n\n  def << arg\n    @my_hash_value = nil\n    old_push arg\n  end\n\n  alias_method :hash, :hash # silence redefined method warning\n  def hash\n    #There still seems to be some instances in which the hash of the\n    #Sexp changes, but I have not found what method call is doing it.\n    #Of course, Sexp is subclasses from Array, so who knows what might\n    #be going on.\n    @my_hash_value ||= super\n  end\n\n  def compact\n    @my_hash_value = nil\n    old_compact\n  end\n\n  def find_and_replace_all *args\n    @my_hash_value = nil\n    old_fara(*args)\n  end\n\n  def find_node *args\n    @my_hash_value = nil\n    old_find_node(*args)\n  end\n\n  #Raise a WrongSexpError if the nodes type does not match one of the expected\n  #types.\n  def expect *types\n    unless types.include? self.node_type\n      raise WrongSexpError, \"Expected #{types.join ' or '} but given #{self.inspect}\", caller[1..-1]\n    end\n  end\n\n  #Returns target of a method call:\n  #\n  #s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1)))\n  #         ^-----------target-----------^\n  def target\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn\n    self[1]\n  end\n\n  #Sets the target of a method call:\n  def target= exp\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn\n    @my_hash_value = nil\n    self[1] = exp\n  end\n\n  #Returns method of a method call:\n  #\n  #s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1)))\n  #                        ^- method\n  def method\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper, :result\n\n    case self.node_type\n    when :call, :attrasgn, :safe_call, :safe_attrasgn\n      self[2]\n    when :super, :zsuper\n      :super\n    when :result\n      self.last\n    end\n  end\n\n  def method= name\n    expect :call, :safe_call\n\n    self[2] = name\n  end\n\n  # Number of arguments in a method call.\n  def num_args\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper\n\n    case self.node_type\n    when :call, :attrasgn, :safe_call, :safe_attrasgn\n      self.length - 3\n    when :super\n      self.length - 1\n    when :zsuper\n      0\n    end\n  end\n\n  #Sets the arglist in a method call.\n  def arglist= exp\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn\n    @my_hash_value = nil\n    start_index = 3\n\n    if exp.is_a? Sexp and exp.node_type == :arglist\n      exp = exp.sexp_body\n    end\n\n    exp.each_with_index do |e, i|\n      self[start_index + i] = e\n    end\n  end\n\n  def set_args *exp\n    self.arglist = exp\n  end\n\n  #Returns arglist for method call. This differs from Sexp#args, as Sexp#args\n  #does not return a 'real' Sexp (it does not have a node type) but\n  #Sexp#arglist returns a s(:arglist, ...)\n  #\n  #    s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1), s(:lit, 2)))\n  #                                                 ^------------ arglist ------------^\n  def arglist\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper\n\n    case self.node_type\n    when :call, :attrasgn, :safe_call, :safe_attrasgn\n      self.sexp_body(3).unshift :arglist\n    when :super, :zsuper\n      if self[1]\n        self.sexp_body.unshift :arglist\n      else\n        Sexp.new(:arglist)\n      end\n    end\n  end\n\n  #Returns arguments of a method call. This will be an 'untyped' Sexp.\n  #\n  #    s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1), s(:lit, 2)))\n  #                                                             ^--------args--------^\n  def args\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper\n\n    case self.node_type\n    when :call, :attrasgn, :safe_call, :safe_attrasgn\n      if self[3]\n        self.sexp_body(3)\n      else\n        Sexp.new\n      end\n    when :super, :zsuper\n      if self[1]\n        self.sexp_body\n      else\n        Sexp.new\n      end\n    end\n  end\n\n  def each_arg replace = false\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn, :super, :zsuper\n    range = nil\n\n    case self.node_type\n    when :call, :attrasgn, :safe_call, :safe_attrasgn\n      if self[3]\n        range = (3...self.length)\n      end\n    when :super, :zsuper\n      if self[1]\n        range = (1...self.length)\n      end\n    end\n\n    if range\n      range.each do |i|\n        res = yield self[i]\n        self[i] = res if replace\n      end\n    end\n\n    self\n  end\n\n  def each_arg! &block\n    @my_hash_value = nil\n    self.each_arg true, &block\n  end\n\n  #Returns first argument of a method call.\n  def first_arg\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn\n    self[3]\n  end\n\n  #Sets first argument of a method call.\n  def first_arg= exp\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn\n    @my_hash_value = nil\n    self[3] = exp\n  end\n\n  #Returns second argument of a method call.\n  def second_arg\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn\n    self[4]\n  end\n\n  #Sets second argument of a method call.\n  def second_arg= exp\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn\n    @my_hash_value = nil\n    self[4] = exp\n  end\n\n  def third_arg\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn\n    self[5]\n  end\n\n  def third_arg= exp\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn\n    @my_hash_value = nil\n    self[5] = exp\n  end\n\n  def last_arg\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn\n\n    if self[3]\n      self[-1]\n    else\n      nil\n    end\n  end\n\n  def call_chain\n    expect :call, :attrasgn, :safe_call, :safe_attrasgn\n\n    chain = []\n    call = self\n\n    while call.class == Sexp and CALLS.include? call.first \n      chain << call.method\n      call = call.target\n    end\n\n    chain.reverse!\n    chain\n  end\n\n  #Returns condition of an if expression:\n  #\n  #    s(:if,\n  #     s(:lvar, :condition), <-- condition\n  #     s(:lvar, :then_val),\n  #     s(:lvar, :else_val)))\n  def condition\n    expect :if\n    self[1]\n  end\n\n  def condition= exp\n    expect :if\n    self[1] = exp\n  end\n\n\n  #Returns 'then' clause of an if expression:\n  #\n  #    s(:if,\n  #     s(:lvar, :condition),\n  #     s(:lvar, :then_val), <-- then clause\n  #     s(:lvar, :else_val)))\n  def then_clause\n    expect :if\n    self[2]\n  end\n\n  #Returns 'else' clause of an if expression:\n  #\n  #    s(:if,\n  #     s(:lvar, :condition),\n  #     s(:lvar, :then_val),\n  #     s(:lvar, :else_val)))\n  #     ^---else caluse---^\n  def else_clause\n    expect :if\n    self[3]\n  end\n\n  #Method call associated with a block:\n  #\n  #    s(:iter,\n  #     s(:call, nil, :x, s(:arglist)), <- block_call\n  #      s(:lasgn, :y),\n  #       s(:block, s(:lvar, :y), s(:call, nil, :z, s(:arglist))))\n  def block_call\n    expect :iter\n\n    if self[1].node_type == :lambda\n      s(:call, nil, :lambda).line(self.line)\n    else\n      self[1]\n    end\n  end\n\n  #Returns block of a call with a block.\n  #Could be a single expression or a block:\n  #\n  #    s(:iter,\n  #     s(:call, nil, :x, s(:arglist)),\n  #      s(:lasgn, :y),\n  #       s(:block, s(:lvar, :y), s(:call, nil, :z, s(:arglist))))\n  #       ^-------------------- block --------------------------^\n  def block delete = nil\n    unless delete.nil? #this is from RubyParser\n      return find_node :block, delete\n    end\n\n    expect :iter, :scope, :resbody\n\n    case self.node_type\n    when :iter\n      self[3]\n    when :scope\n      self[1]\n    when :resbody\n      #This is for Ruby2Ruby ONLY\n      find_node :block\n    end\n  end\n\n  #Returns parameters for a block\n  #\n  #    s(:iter,\n  #     s(:call, nil, :x, s(:arglist)),\n  #      s(:lasgn, :y), <- block_args\n  #       s(:call, nil, :p, s(:arglist, s(:lvar, :y))))\n  def block_args\n    expect :iter\n    if self[2] == 0 # ?! See https://github.com/presidentbeef/brakeman/issues/331\n      return Sexp.new(:args)\n    else\n      self[2]\n    end\n  end\n\n  def first_param\n    expect :args\n    self[1]\n  end\n\n  #Returns the left hand side of assignment or boolean:\n  #\n  #    s(:lasgn, :x, s(:lit, 1))\n  #               ^--lhs\n  def lhs\n    expect(*ASSIGNMENT_BOOL)\n    self[1]\n  end\n\n  #Sets the left hand side of assignment or boolean.\n  def lhs= exp\n    expect(*ASSIGNMENT_BOOL)\n    @my_hash_value = nil\n    self[1] = exp\n  end\n\n  #Returns right side (value) of assignment or boolean:\n  #\n  #    s(:lasgn, :x, s(:lit, 1))\n  #                  ^--rhs---^\n  def rhs\n    expect :attrasgn, :safe_attrasgn, *ASSIGNMENT_BOOL\n\n    if self.node_type == :attrasgn or self.node_type == :safe_attrasgn\n      if self[2] == :[]=\n        self[4]\n      else\n        self[3]\n      end\n    else\n      self[2]\n    end\n  end\n\n  #Sets the right hand side of assignment or boolean.\n  def rhs= exp\n    expect :attrasgn, :safe_attrasgn, *ASSIGNMENT_BOOL\n    @my_hash_value = nil\n\n    if self.node_type == :attrasgn or self.node_type == :safe_attrasgn\n      self[3] = exp\n    else\n      self[2] = exp\n    end\n  end\n\n  #Returns name of method being defined in a method definition.\n  def method_name\n    expect :defn, :defs\n\n    case self.node_type\n    when :defn\n      self[1]\n    when :defs\n      self[2]\n    end\n  end\n\n  def formal_args\n    expect :defn, :defs\n\n    case self.node_type\n    when :defn\n      self[2]\n    when :defs\n      self[3]\n    end\n  end\n\n  #Sets body, which is now a complicated process because the body is no longer\n  #a separate Sexp, but just a list of Sexps.\n  def body= exp\n    expect :defn, :defs, :class, :module\n    @my_hash_value = nil\n\n    case self.node_type\n    when :defn, :class\n      index = 3\n    when :defs\n      index = 4\n    when :module\n      index = 2\n    end\n\n    self.slice!(index..-1) #Remove old body\n\n    if exp.first == :rlist\n      exp = exp.sexp_body\n    end\n\n    #Insert new body\n    exp.each do |e|\n      self[index] = e\n      index += 1\n    end\n  end\n\n  #Returns body of a method definition, class, or module.\n  #This will be an untyped Sexp containing a list of Sexps from the body.\n  def body\n    expect :defn, :defs, :class, :module\n\n    case self.node_type\n    when :defn, :class\n      self.sexp_body(3)\n    when :defs\n      self.sexp_body(4)\n    when :module\n      self.sexp_body(2)\n    end\n  end\n\n  #Like Sexp#body, except the returned Sexp is of type :rlist\n  #instead of untyped.\n  def body_list\n    self.body.unshift :rlist\n  end\n\n  # Number of \"statements\" in a method.\n  # This is more efficient than `Sexp#body.length`\n  # because `Sexp#body` creates a new Sexp.\n  def method_length\n    expect :defn, :defs\n\n    case self.node_type\n    when :defn\n      self.length - 3\n    when :defs\n      self.length - 4\n    end\n  end\n\n  def render_type\n    expect :render\n    self[1]\n  end\n\n  def class_name\n    expect :class, :module\n    self[1]\n  end\n\n  alias module_name class_name\n\n  def parent_name\n    expect :class\n    self[2]\n  end\n\n  #Returns the call Sexp in a result returned from FindCall\n  def call\n    expect :result\n\n    self.last\n  end\n\n  #Returns the module the call is inside\n  def module\n    expect :result\n\n    self[1]\n  end\n\n  #Return the class the call is inside\n  def result_class\n    expect :result\n\n    self[2]\n  end\n\n  require 'set'\n  def inspect seen = Set.new\n    if seen.include? self.object_id\n      's(...)'\n    else\n      seen << self.object_id\n      sexp_str = self.map do |x|\n        if x.is_a? Sexp\n          x.inspect seen\n        else\n          x.inspect\n        end\n      end.join(', ')\n\n      \"s(#{sexp_str})\"\n    end\n  end\nend\n\n#Invalidate hash cache if the Sexp changes\n[:[]=, :clear, :collect!, :compact!, :concat, :delete, :delete_at,\n  :delete_if, :drop, :drop_while, :fill, :flatten!, :insert,\n  :keep_if, :map!, :pop, :push, :reject!, :replace, :reverse!, :rotate!,\n  :select!, :shift, :shuffle!, :slice!, :sort!, :sort_by!, :transpose,\n  :uniq!, :unshift].each do |method|\n\n  Sexp.class_eval <<-RUBY\n    def #{method} *args\n      @my_hash_value = nil\n      super\n    end\n    RUBY\nend\n\n#Methods used by RubyParser which would normally go through method_missing but\n#we don't want that to happen because it hides Brakeman errors\n[:resbody, :lasgn, :iasgn, :splat].each do |method|\n  Sexp.class_eval <<-RUBY\n    def #{method} delete = false\n      if delete\n        @my_hash_value = false\n      end\n      find_node :#{method}, delete\n    end\n  RUBY\nend\n\nclass String\n  ##\n  # This is a hack used by the lexer to sneak in line numbers at the\n  # identifier level. This should be MUCH smaller than making\n  # process_token return [value, lineno] and modifying EVERYTHING that\n  # reduces tIDENTIFIER.\n\n  attr_accessor :lineno\nend\n\nclass WrongSexpError < RuntimeError; end\n"
  },
  {
    "path": "lib/ruby_parser/bm_sexp_processor.rb",
    "content": "##\n# SexpProcessor provides a uniform interface to process Sexps.\n#\n# In order to create your own SexpProcessor subclass you'll need\n# to call super in the initialize method, then set any of the\n# Sexp flags you want to be different from the defaults.\n#\n# SexpProcessor uses a Sexp's type to determine which process method\n# to call in the subclass.  For Sexp <code>s(:lit, 1)</code>\n# SexpProcessor will call #process_lit, if it is defined.\n#\n\nclass Brakeman::SexpProcessor\n\n  VERSION = 'CUSTOM'\n\n  ##\n  # Return a stack of contexts. Most recent node is first.\n\n  attr_reader :context\n\n  ##\n  # Expected result class\n\n  attr_accessor :expected\n\n  ##\n  # A scoped environment to make you happy.\n\n  attr_reader :env\n\n  # Cache process methods per class\n\n  def self.processors\n    @processors ||= {}\n  end\n\n  ##\n  # Creates a new SexpProcessor.  Use super to invoke this\n  # initializer from SexpProcessor subclasses, then use the\n  # attributes above to customize the functionality of the\n  # SexpProcessor\n\n  def initialize\n    @expected            = Sexp\n    @processors = self.class.processors\n    @context    = []\n    @current_class = @current_module = @current_method = @visibility = nil\n\n    if @processors.empty?\n      public_methods.each do |name|\n        if name.to_s.start_with? \"process_\" then\n          @processors[name[8..-1].to_sym] = name.to_sym\n        end\n      end\n    end\n  end\n\n  ##\n  # Default Sexp processor.  Invokes process_<type> methods matching\n  # the Sexp type given.  Performs additional checks as specified by\n  # the initializer.\n\n  def process(exp)\n    return nil if exp.nil?\n\n    result = nil\n\n    type = exp.first\n    raise \"Type should be a Symbol, not: #{exp.first.inspect} in #{exp.inspect}\" unless Symbol === type\n\n    in_context type do\n      # now do a pass with the real processor (or generic)\n      meth = @processors[type]\n      if meth then\n        result = self.send(meth, exp)\n      else\n        result = self.process_default(exp)\n      end\n    end\n    \n    raise SexpTypeError, \"Result must be a #{@expected}, was #{result.class}:#{result.inspect}\" unless @expected === result\n    \n    result\n  end\n\n  ##\n  # Add a scope level to the current env. Eg:\n  #\n  #   def process_defn exp\n  #     name = exp.shift\n  #     args = process(exp.shift)\n  #     scope do\n  #       body = process(exp.shift)\n  #       # ...\n  #     end\n  #   end\n  #\n  #   env[:x] = 42\n  #   scope do\n  #     env[:x]       # => 42\n  #     env[:y] = 24\n  #   end\n  #   env[:y]         # => nil\n\n  def scope &block\n    env.scope(&block)\n  end\n\n  def in_context type\n    self.context.unshift type\n\n    yield\n\n    self.context.shift\n  end\nend\n"
  },
  {
    "path": "test/README.md",
    "content": "## Testing\n\nRun `bundle` then `rake` or if you want to avoid bundler `ruby test/test.rb`.\n\nThis runs Brakeman against full apps in the `apps` directory and checks the results against what is expected.\n\n## Test Generation\n\nRun `cd test && ruby to_test.rb apps/some_app > tests/some_app.rb` to generate a test suite with tests for each warning reported.\n\n## Single File\n\nRun `ruby test/tests/some_file.rb` to run a single file of tests.\n\n## Single Test\n\nRuby `ruby test/test.rb --name test_something` to run a single test.\n"
  },
  {
    "path": "test/apps/active_record_only/Gemfile",
    "content": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\n\ngem \"activerecord\", \"~> 5.2.4.3\"\n"
  },
  {
    "path": "test/apps/active_record_only/app/models/book.rb",
    "content": "# frozen_string_literal: true\n\nrequire \"active_record\"\n\nclass Book < ActiveRecord::Base\nend\n"
  },
  {
    "path": "test/apps/active_record_only/script/.gitkeep",
    "content": "Kept only to help guessing this is a rails 2 unless activerecord version is detected\n"
  },
  {
    "path": "test/apps/rails2/README",
    "content": "== Welcome to Rails\n\nRails is a web-application framework that includes everything needed to create \ndatabase-backed web applications according to the Model-View-Control pattern. \n\nThis pattern splits the view (also called the presentation) into \"dumb\" templates\nthat are primarily responsible for inserting pre-built data in between HTML tags.\nThe model contains the \"smart\" domain objects (such as Account, Product, Person,\nPost) that holds all the business logic and knows how to persist themselves to\na database. The controller handles the incoming requests (such as Save New Account,\nUpdate Product, Show Post) by manipulating the model and directing data to the view.\n\nIn Rails, the model is handled by what's called an object-relational mapping\nlayer entitled Active Record. This layer allows you to present the data from\ndatabase rows as objects and embellish these data objects with business logic\nmethods. You can read more about Active Record in\nlink:files/vendor/rails/activerecord/README.html.\n\nThe controller and view are handled by the Action Pack, which handles both\nlayers by its two parts: Action View and Action Controller. These two layers\nare bundled in a single package due to their heavy interdependence. This is\nunlike the relationship between the Active Record and Action Pack that is much\nmore separate. Each of these packages can be used independently outside of\nRails.  You can read more about Action Pack in\nlink:files/vendor/rails/actionpack/README.html.\n\n\n== Getting Started\n\n1. At the command prompt, start a new Rails application using the <tt>rails</tt> command\n   and your application name. Ex: rails myapp\n2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options)\n3. Go to http://localhost:3000/ and get \"Welcome aboard: You're riding the Rails!\"\n4. Follow the guidelines to start developing your application\n\n\n== Web Servers\n\nBy default, Rails will try to use Mongrel if it's are installed when started with script/server, otherwise Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails\nwith a variety of other web servers.\n\nMongrel is a Ruby-based webserver with a C component (which requires compilation) that is\nsuitable for development and deployment of Rails applications. If you have Ruby Gems installed,\ngetting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.\nMore info at: http://mongrel.rubyforge.org\n\nSay other Ruby web servers like Thin and Ebb or regular web servers like Apache or LiteSpeed or\nLighttpd or IIS. The Ruby web servers are run through Rack and the latter can either be setup to use\nFCGI or proxy to a pack of Mongrels/Thin/Ebb servers.\n\n== Apache .htaccess example for FCGI/CGI\n\n# General Apache options\nAddHandler fastcgi-script .fcgi\nAddHandler cgi-script .cgi\nOptions +FollowSymLinks +ExecCGI\n\n# If you don't want Rails to look in certain directories,\n# use the following rewrite rules so that Apache won't rewrite certain requests\n# \n# Example:\n#   RewriteCond %{REQUEST_URI} ^/notrails.*\n#   RewriteRule .* - [L]\n\n# Redirect all requests not available on the filesystem to Rails\n# By default the cgi dispatcher is used which is very slow\n# \n# For better performance replace the dispatcher with the fastcgi one\n#\n# Example:\n#   RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]\nRewriteEngine On\n\n# If your Rails application is accessed via an Alias directive,\n# then you MUST also set the RewriteBase in this htaccess file.\n#\n# Example:\n#   Alias /myrailsapp /path/to/myrailsapp/public\n#   RewriteBase /myrailsapp\n\nRewriteRule ^$ index.html [QSA]\nRewriteRule ^([^.]+)$ $1.html [QSA]\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteRule ^(.*)$ dispatch.cgi [QSA,L]\n\n# In case Rails experiences terminal errors\n# Instead of displaying this message you can supply a file here which will be rendered instead\n# \n# Example:\n#   ErrorDocument 500 /500.html\n\nErrorDocument 500 \"<h2>Application error</h2>Rails application failed to start properly\"\n\n\n== Debugging Rails\n\nSometimes your application goes wrong.  Fortunately there are a lot of tools that\nwill help you debug it and get it back on the rails.\n\nFirst area to check is the application log files.  Have \"tail -f\" commands running\non the server.log and development.log. Rails will automatically display debugging\nand runtime information to these files. Debugging info will also be shown in the\nbrowser on requests from 127.0.0.1.\n\nYou can also log your own messages directly into the log file from your code using\nthe Ruby logger class from inside your controllers. Example:\n\n  class WeblogController < ActionController::Base\n    def destroy\n      @weblog = Weblog.find(params[:id])\n      @weblog.destroy\n      logger.info(\"#{Time.now} Destroyed Weblog ID ##{@weblog.id}!\")\n    end\n  end\n\nThe result will be a message in your log file along the lines of:\n\n  Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1\n\nMore information on how to use the logger is at http://www.ruby-doc.org/core/\n\nAlso, Ruby documentation can be found at http://www.ruby-lang.org/ including:\n\n* The Learning Ruby (Pickaxe) Book: http://www.ruby-doc.org/docs/ProgrammingRuby/\n* Learn to Program: http://pine.fm/LearnToProgram/  (a beginners guide)\n\nThese two online (and free) books will bring you up to speed on the Ruby language\nand also on programming in general.\n\n\n== Debugger\n\nDebugger support is available through the debugger command when you start your Mongrel or\nWebrick server with --debugger. This means that you can break out of execution at any point\nin the code, investigate and change the model, AND then resume execution! \nYou need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'\nExample:\n\n  class WeblogController < ActionController::Base\n    def index\n      @posts = Post.find(:all)\n      debugger\n    end\n  end\n\nSo the controller will accept the action, run the first line, then present you\nwith a IRB prompt in the server window. Here you can do things like:\n\n  >> @posts.inspect\n  => \"[#<Post:0x14a6be8 @attributes={\\\"title\\\"=>nil, \\\"body\\\"=>nil, \\\"id\\\"=>\\\"1\\\"}>,\n       #<Post:0x14a6620 @attributes={\\\"title\\\"=>\\\"Rails you know!\\\", \\\"body\\\"=>\\\"Only ten..\\\", \\\"id\\\"=>\\\"2\\\"}>]\"\n  >> @posts.first.title = \"hello from a debugger\"\n  => \"hello from a debugger\"\n\n...and even better is that you can examine how your runtime objects actually work:\n\n  >> f = @posts.first\n  => #<Post:0x13630c4 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>\n  >> f.\n  Display all 152 possibilities? (y or n)\n\nFinally, when you're ready to resume execution, you enter \"cont\"\n\n\n== Console\n\nYou can interact with the domain model by starting the console through <tt>script/console</tt>.\nHere you'll have all parts of the application configured, just like it is when the\napplication is running. You can inspect domain models, change values, and save to the\ndatabase. Starting the script without arguments will launch it in the development environment.\nPassing an argument will specify a different environment, like <tt>script/console production</tt>.\n\nTo reload your controllers and models after launching the console run <tt>reload!</tt>\n\n== dbconsole\n\nYou can go to the command line of your database directly through <tt>script/dbconsole</tt>.\nYou would be connected to the database with the credentials defined in database.yml.\nStarting the script without arguments will connect you to the development database. Passing an\nargument will connect you to a different database, like <tt>script/dbconsole production</tt>.\nCurrently works for mysql, postgresql and sqlite.\n\n== Description of Contents\n\napp\n  Holds all the code that's specific to this particular application.\n\napp/controllers\n  Holds controllers that should be named like weblogs_controller.rb for\n  automated URL mapping. All controllers should descend from ApplicationController\n  which itself descends from ActionController::Base.\n\napp/models\n  Holds models that should be named like post.rb.\n  Most models will descend from ActiveRecord::Base.\n\napp/views\n  Holds the template files for the view that should be named like\n  weblogs/index.html.erb for the WeblogsController#index action. All views use eRuby\n  syntax.\n\napp/views/layouts\n  Holds the template files for layouts to be used with views. This models the common\n  header/footer method of wrapping views. In your views, define a layout using the\n  <tt>layout :default</tt> and create a file named default.html.erb. Inside default.html.erb,\n  call <% yield %> to render the view using this layout.\n\napp/helpers\n  Holds view helpers that should be named like weblogs_helper.rb. These are generated\n  for you automatically when using script/generate for controllers. Helpers can be used to\n  wrap functionality for your views into methods.\n\nconfig\n  Configuration files for the Rails environment, the routing map, the database, and other dependencies.\n\ndb\n  Contains the database schema in schema.rb.  db/migrate contains all\n  the sequence of Migrations for your schema.\n\ndoc\n  This directory is where your application documentation will be stored when generated\n  using <tt>rake doc:app</tt>\n\nlib\n  Application specific libraries. Basically, any kind of custom code that doesn't\n  belong under controllers, models, or helpers. This directory is in the load path.\n\npublic\n  The directory available for the web server. Contains subdirectories for images, stylesheets,\n  and javascripts. Also contains the dispatchers and the default HTML files. This should be\n  set as the DOCUMENT_ROOT of your web server.\n\nscript\n  Helper scripts for automation and generation.\n\ntest\n  Unit and functional tests along with fixtures. When using the script/generate scripts, template\n  test files will be generated for you and placed in this directory.\n\nvendor\n  External libraries that the application depends on. Also includes the plugins subdirectory.\n  If the app has frozen rails, those gems also go here, under vendor/rails/.\n  This directory is in the load path.\n"
  },
  {
    "path": "test/apps/rails2/Rakefile",
    "content": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire(File.join(File.dirname(__FILE__), 'config', 'boot'))\n\nrequire 'rake'\nrequire 'rake/testtask'\nrequire 'rake/rdoctask'\n\nrequire 'tasks/rails'\n"
  },
  {
    "path": "test/apps/rails2/app/controllers/application_controller.rb",
    "content": "# Filters added to this controller apply to all controllers in the application.\n# Likewise, all the methods added will be available for all controllers.\n\nclass ApplicationController < ActionController::Base\n  helper :all # include all helpers, all the time\n  #protect_from_forgery # See ActionController::RequestForgeryProtection for details\n\n  # Scrub sensitive parameters from your log\n  # filter_parameter_logging :password\n  before_filter :awesome\n\n  def funky_panda\n  end\n\n  def awesome\n    something = if params[:thang]\n                  params[:thang]\n                elsif somevar = \"monkeypanda\"\n                  somevar = somevar.split(\",\").map { |s|\n                    s += 'stuff' unless s =~ /regex/\n                      s.split('things')\n                  }.first\n                  somevar.first.downcase\n                end\n\n    if (some_var = SomeClass.things, something)\n      AnotherClass.thang = @thang = some_var.to_sym\n    elsif (some_var = find_thang(AppConfig.stuff, something))\n      AnotherClass.thang = @thang = some_var.to_sym\n    end\n\n    if beta_override && cookies['yummy'] != @thang.to_s\n      cookies['yummy'] = { :value => @thang.to_s }\n    end\n\n    return true\n  end\n\n  def decent\n    p = params\n    if params[:thang] && self.respond_to?(params[:thang].to_sym)\n      :\"really_#{params[:thang]}\"\n    end\n    p.symbolize_keys[:custom]\n  end\n\nend\n"
  },
  {
    "path": "test/apps/rails2/app/controllers/emails_controller.rb",
    "content": "class EmailsController < ApplicationController\n  def show\n    @email = Email.find params[:email_id]\n  end\n\n  def show_email_1\n    @email = Email.find 1\n  end\nend\n"
  },
  {
    "path": "test/apps/rails2/app/controllers/home_controller.rb",
    "content": "class HomeController < ApplicationController\n  before_filter :filter_it, :only => :test_filter\n  before_filter :or_equals, :only => :test_mass_assign_with_or_equals\n\n  def index; end\n\n  def test_params\n    @name = params[:name]\n    @indirect = indirect_method(params[:input])\n  end\n\n  def test_model\n    @name = User.first.name\n  end\n\n  def test_cookie\n    @name = cookies[:name]\n  end\n\n  def test_filter\n  end\n\n  def test_file_access\n    File.open RAILS_ROOT + \"/\" + params[:file]\n  end\n\n  def test_sql some_var = \"hello\"\n    User.find_by_sql \"select * from users where something = '#{some_var}'\"\n    User.all(:conditions => \"status => '#{happy}'\")\n    @user = User.first(:conditions => \"name = '#{params[:name]}'\")\n  end\n\n  def test_command\n    `ls #{params[:file_name]}`\n\n    system params[:user_input]\n  end\n\n  def test_eval\n    eval params[:dangerous_input]\n  end\n\n  def test_redirect\n    params[:action] = :index\n    redirect_to params\n  end\n\n  def test_render\n    @some_variable = params[:unsafe_input]\n    render :index\n  end\n\n  def test_mass_assignment\n    User.new(params[:user])\n  end\n\n  def test_dynamic_render\n    page = params[:page]\n    render :file => \"/some/path/#{page}\"\n  end\n\n  def test_load_params\n    load params[:file]\n    RandomClass.load params[:file]\n  end\n\n  def test_redirect_with_url_for\n    url = url_for(params)\n    redirect_to url\n  end\n\n  def test_sql_nested\n    User.humans.alive.find(:all, :conditions => \"age > #{params[:age]}\")\n  end\n\n  def test_another_dynamic_render\n    render :action => params[:my_action]\n  end\n\n  # not safe\n  def test_send_first_param\n    method = params[\"method\"]\n    @result = User.send(method.to_sym)\n    do_something_with @result # don't warn on this line\n  end\n\n  def test_send_target # not that safe\n    table = params[\"table\"]\n    model = table.classify.constantize\n    @result = model.send(:method)\n  end\n\n  # safe\n  def test_send_second_param\n    args = params[\"args\"] || []\n    @result = User.send(:method, *args)\n  end\n\n  # safe\n  def test_send_second_param\n    method = params[\"method\"] == 1 ? :method_a : :method_b\n    @result = User.send(method, *args)\n  end\n\n  # safe\n  def test_send_second_param\n    target = params[\"target\"] == 1 ? Account : User\n    @result = target.send(:method, *args)\n  end\n\n  def test_sanitized_param\n    params[\"something\"] = h(params[\"something\"])\n  end\n\n  def test_safe_find_by\n    User.find_or_create_by_name(params[:name], :code => (params[:x] + \"code\"))\n  end\n\n  def test_user_input_on_multiline\n    User.find_by_sql \"select * from users where something = 'something safe' AND \" +\n      \"something_not_safe = #{params[:unsafe]} AND \" +\n      \"something_else_that_is_safe = 'something else safe'\"\n    SQL\n  end\n\n  def test_mass_assign_with_or_equals\n    User.new(params[:still_bad])\n  end\n\n  def test_xss_with_or\n    @params_or_something = params[:x] || something\n\n    if some_condition\n      @user_input = true\n    else\n      @user_input = params[:y]\n    end\n\n    @more_user_input = x || params[:z] || z\n\n    @user = User.find(current_user)\n  end\n\n  def test_to_json\n    @model_json = User.find(current_user).to_json\n    @not_json = {:thing => params[:thing]}\n    @json = {:json_thing => params[:json_thing]}.to_json\n  end\n\n  def test_content_tag\n    @user = User.find(current_user)\n  end\n\n  def test_more_send_methods\n    User.try(params[:meth])\n    self.__send__(params[:meth])\n    Account.public_send(params[:meth])\n\n    table = params[\"table\"]\n    table.classify.constantize.try(:meth)\n  end\n\n  private\n\n  def filter_it\n    @filtered = params[:evil_input]\n  end\n\n  def or_equals\n    params[:still_bad] ||= {}\n  end\n\n  def test_safe_model_redirect\n    redirect_to User.find(1)\n  end\n\n  def test_safe_mode_array_redirect\n    redirect_to [User.find(1), User.find(2)]\n  end\n\n  def test_model_attributes_badness\n    redirect_to User.new.donkey\n  end\nend\n"
  },
  {
    "path": "test/apps/rails2/app/controllers/other_controller.rb",
    "content": "class OtherController < ApplicationController\n  def test_locals\n    render :locals => { :input => params[:user_input] }\n  end\n\n  def test_object\n    render :partial => \"account\", :object => Account.first\n  end\n\n  def test_collection\n    users = User.all\n    partial = \"user\"\n    render :partial => partial, :collection => users\n  end\n\n  def test_iteration\n    @users = User.all\n  end\n\n  def test_send_file\n    send_file params[:file]\n  end\n\n  def test_update_attribute\n    @user = User.first\n    @user.update_attribute(:attr, params[:attr])\n  end\n\n  def test_render_template\n    @something_bad = params[:bad]\n\n    render :template => 'home/test_render_template'\n  end\n\n  def test_render_update\n    render :update do |page|\n      do_something\n    end\n  end\n\n  def test_to_i\n    @x = params[:x].to_i\n    @id = cookies[:id].to_i\n  end\n\n  def test_to_sym\n    :\"#{hello!}\"\n\n    x = params[:x].to_sym\n\n    #Checking that the code below does not warn about to_sym again\n    call_something_with x\n\n    x.cool_thing?\n  end\n\n  def test_xss_duplicates1\n    @thing = params[:thing]\n\n    render :xss_dupes, :layout => 'thing'\n  end\n\n  def test_xss_duplicates2\n    @thing = blah(params[:other_thing])\n\n    render :xss_dupes, :layout => 'thing'\n  end\n\n  def test_haml_stuff\n    render :locals => { :user => User.first }\n  end\n\n  def test_regex_dos\n    /#{params[:regex]}/\n  end\n\n  def test_escaped_regex\n    /#{Regexp.escape(params[:regex])}/\n  end\n\n  def test_unescaped_regex\n    /#{Rack::Utils.escape_html(params[:regex])}/\n  end\n\n  def test_intern\n    x = params[:x].intern\n\n    params.symbolize_keys!\n\n    #Checking that the code below does not warn about to_sym again\n    call_something_with x\n\n    x.cool_thing?\n  end\nend\n"
  },
  {
    "path": "test/apps/rails2/app/helpers/application_helper.rb",
    "content": "# Methods added to this helper will be available to all templates in the application.\nmodule ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails2/app/helpers/home_helper.rb",
    "content": "module HomeHelper\nend\n"
  },
  {
    "path": "test/apps/rails2/app/helpers/other_helper.rb",
    "content": "module OtherHelper\nend\n"
  },
  {
    "path": "test/apps/rails2/app/models/account.rb",
    "content": "class Account < ActiveRecord::Base\n  validates_format_of :name, :with => /^[a-zA-Z]+$/\n\n  named_scope :all\nend\n"
  },
  {
    "path": "test/apps/rails2/app/models/email.rb",
    "content": "class Email < ActiveRecord::Base\n  attr_accessible :email\n\n  belongs_to :user\nend\n"
  },
  {
    "path": "test/apps/rails2/app/models/protected.rb",
    "content": "class Protected < ActiveRecord::Base\n  attr_accessible nil\nend\n\n"
  },
  {
    "path": "test/apps/rails2/app/models/unprotected.rb",
    "content": "class Unprotected < Protected\n  serialize :unsafe_stuff\nend\n"
  },
  {
    "path": "test/apps/rails2/app/models/user.rb",
    "content": "class User < ActiveRecord::Base\n  named_scope :dah, lambda {|*args| { :conditions => \"dah = '#{args[1]}'\"}}\n  \n  named_scope :phooey, :conditions => \"phoeey = '#{User.phooey}'\"\n\n  named_scope :with_state, lambda {|state| state.present? ? {:conditions => \"state_name = '#{state}'\"} : {}}\n\n  named_scope :safe_phooey, :conditions => [\"phoeey = ?\", \"#{User.phooey}\"]\n\n  named_scope :safe_dah, lambda {|*args| { :conditions => [\"dah = ?\", \"#{args[1]}\"]}}\n\n  named_scope :with_state, lambda {|state| state.present? ? {:conditions => [\"state_name = ?\", \"#{state}\"]} : {}}\n\n  def get_something x\n    self.find(:all, :conditions => \"where blah = #{x}\")\n  end\n\n  def test_merge_conditions\n    #Should not warn\n    User.find(:all, :conditions => merge_conditions(some_conditions))\n    User.find(:all, :conditions => self.merge_conditions(some_conditions))\n    find(:all, :conditions => User.merge_conditions(some_conditions))\n  end\n\n  def self.some_method(value)\n    results = ActiveRecord::Base.connection.execute(%Q(SELECT\n        id\n      FROM\n        table t\n      WHERE\n        t.something = '#{value}'))\n  end\n\n  def self.test_sanitized_sql input\n    self.connection.select_all(\"select * from cool_table where stuff = \" + self.sanitize_sql(input))\n  end\n\n  def more_sanitized_sql\n    self.connection.execute(\"DELETE FROM cool_table WHERE cool_id=\" + quote_value(self.cool_id) + \"  AND my_id=\" + quote_value(self.id))\n  end\n\n  has_many :emails\nend\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/_models.html.erb",
    "content": "<%= model.id %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/index.html.erb",
    "content": "<h1>Home#index</h1>\n<p>Find me in app/views/home/index.html.erb</p>\n<%= params[:user_input] %>\n\n<%= @some_variable %>\n\n<%= escape_once params[:some_cookie] %>\n\n<%= x = []; x << 1 %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_command.html.erb",
    "content": "<h1>Home#test_command</h1>\n<p>Find me in app/views/home/test_command.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_content_tag.html.erb",
    "content": "Should not warn\n<%= content_tag :p, h(params[:something]) %>\n\nShould warn\n<%= content_tag :span, @user.name %>\n\nShould not warn\n<%= content_tag :div, \"Blah!\", { :class => params[:class] }, true %>\n\nShould warn\n<%= content_tag :div, \"Blah!\", { cookies[:weird] => \"bad idea\" } %>\n\nShould not warn\n<%= content_tag :h1, params[:x] == 1 ? \"totally\" : \"safe\" %>\n\nShould still warn\n<%= content_tag :div, \"Blah!\", { @user.something => \"bad idea\"}, true %>\n\nShould not warn\n<%= content_tag :div, \"Blah!\", { :class => params[:class] } %>\n\nShould warn\n<%= content_tag :div, \"Blah!\", { :id => @user.name }, false %>\n\nShould warn, medium confidence\n<%= content_tag :div, x(params[:maybe_bad]) %>\n\nShould not warn\n<%= content_tag :span, u(params[:url]) %>"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_cookie.html.erb",
    "content": "<h1>Home#test_cookie</h1>\n<p>Find me in app/views/home/test_cookie.html.erb</p>\nHello, cookie named <%= @name %>!\n\n<%= indirect cookies[:oreo] %>\n\nAnd: <%= cookies[:user][:name] %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_dynamic_render.html.erb",
    "content": "<h1>Home#test_dynamic_render</h1>\n<p>Find me in app/views/home/test_dynamic_render.html.erb</p>\n\nThis is not a problem, because this page is not rendered: <%= @page %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_eval.html.erb",
    "content": "<h1>Home#test_eval</h1>\n<p>Find me in app/views/home/test_eval.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_filter.html.erb",
    "content": "<h1>Home#test_filter</h1>\n<p>Find me in app/views/home/test_filter.html.erb</p>\nValue from filter: <%= @filtered %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_link_to.html.erb",
    "content": "<%= link_to :action => \"should_not_warn\", :q => params[:q] %>\n\n<% link_to(params[:evil_url]) do %>\n  <b>Something!</b>\n<% end %>\n\n<%= link_to params[:evil], \"http://brakemanscanner.org\" %>\n\n<%= link_to make_awesome(User.find(1).name), \"http://google.com\" %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_mass_assignment.html.erb",
    "content": "<h1>Home#test_mass_assignment</h1>\n<p>Find me in app/views/home/test_mass_assignment.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_model.html.erb",
    "content": "<h1>Home#test_model</h1>\n<p>Find me in app/views/home/test_model.html.erb</p>\nHello, <%= @name %>!\n\nVery likely bad: <%= truncate User.profile %>\n\nBad for 2.x: <%= link_to User.first.name, \"some_url\" %>\n\nIt's just a model <%= link_to \"Hipster ipsum\", User.first %>\n\nIt's just a couple of models <%= link_to \"Hipster ipsum\", [Account.first, User.last] %>\n\n<%= render :partial => 'models', :collection => all_the_things, :as => :model %>\n\nSafe link_to herf fun: <%= link_to \"test\", u(params[:user_id]) %>"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_params.html.erb",
    "content": "<h1>Home#test_params</h1>\n<p>Find me in app/views/home/test_params.html.erb</p>\n\nJello, <%= @name %>\n\nMore: <%= @indirect %>\n\nAnd: <%= params[:x][:y] %>\n\nShould not warn: <%= link_to h(params[:blah]), \"some_url\" %>\n\nNot-so-dangerous href: <%= link_to \"some text\", ensure_valid_proto!(params[:not_so_bad], :js) %>\n\nDangerous href: <%= link_to \"more text\", params[:dangerous] %>\n\nNot going to warn: <%= link_to \"donkey\", not_safe(params[:bad_robot]) %>\n\nNot completely safe: <%= link_to \"Helvetica hoodie bushwick\", h(params[:js_xss]) %>\n\nShould not warn <%= u(params[:w00t]) %>\n\nShould not warn <%= link_to u(params[:w00t]), \"some_url\" %>"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_redirect.html.erb",
    "content": "<h1>Home#test_redirect</h1>\n<p>Find me in app/views/home/test_redirect.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_render.html.erb",
    "content": "Should not raise a warning:\n<%= render :partial => (params[:awesome] ? 'awesome' : 'not_awesome') %>\n\nAlso should not raise a warning:\n<%= render :partial => User.find(params[:user][:id]) %>\n\nShould raise a warning:\n<%= render :file => \"/tmp/#{params[:file]}\" %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_render_template.html.haml",
    "content": "= @something_bad\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_sanitized_param.html.erb",
    "content": "<%= params[\"something\"] %>\n\n<% x = params[\"something\"] %>\n\n<%= x %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_send_target.html.erb",
    "content": "<%= h @result %> should not warn about send() because it warns in controller where it happens.\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_sql.html.erb",
    "content": "<h1>Home#test_sql</h1>\n<p>Find me in app/views/home/test_sql.html.erb</p>\n\n<%= @user %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_strip_tags.html.erb",
    "content": "<%= h strip_tags(params[:name]) %>\n\n<%= strip_tags(params[:body]) %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_to_json.html.erb",
    "content": "Detection of to_json\n\n<%= @model_json %>\n\nIn the view\n\n<%= @not_json.to_json %>\n\nIn the controller\n\n<%= @json %>\n\nYou would break the json formatting by doing this, but it's technically safe...\n<%= h(@json) %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/home/test_xss_with_or.html.erb",
    "content": "<%= params[:x] || nil %>\n\n<%= @params_or_something %>\n\n<%= @user_input %>\n\n<%= @more_user_input %>\n\n<%= @user.name || 'nothing dangerous' %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/layouts/thing.html.erb",
    "content": "<%= @thing %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/_account.html.haml",
    "content": "%p Name:\n= account.name\n= account.type\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/_user.html.erb",
    "content": "Name: <%= user.first_name %> <%= user.last_name %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/ignore_me.html.erb",
    "content": "Going to ignore the warning below\n<%= User.first(:conditions => \"x = #{params[:x]}\").bio %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/not_used.html.erb",
    "content": "<%= params[:blah] %>\n\n<%= select('post', 'author_id', \"<option value='#{params[:id]}'>#{params[:name]}</option>\") %> \n\n<%= sanitize params[:id] %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/test_collection.html.erb",
    "content": "<h1>Other#test_collection</h1>\n<p>Find me in app/views/other/test_collection.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/test_env.html.erb",
    "content": "<%= request.env[\"HTTP_USER_AGENT\"] %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/test_haml_stuff.html.haml",
    "content": "%tr\n  %td= user.age.to_i\n  %td= user.stuff\n  %td= user.status\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/test_iteration.html.erb",
    "content": "\n<% @users.each do |user| %>\n  <%= user.name %>\n  <%= user.email %>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/test_locals.html.erb",
    "content": "<h1>Other#test_locals</h1>\n<p>Find me in app/views/other/test_locals.html.erb</p>\n\nThis is user input: <%= input %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/test_object.html.erb",
    "content": "<h1>Other#test_object</h1>\n<p>Find me in app/views/other/test_object.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/test_to_i.html.erb",
    "content": "<%= @x %>\n\n<%= request.env[:QUERY_STRING].to_i %>\n\n<%= out @id %>\n\n<%= User.current.age.to_i %>\n\n<%= out Account.current.number.to_i %>\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/test_trim_mode.html.erb",
    "content": "<%= blah -%>\n"
  },
  {
    "path": "test/apps/rails2/app/views/other/xss_dupes.html.erb",
    "content": ""
  },
  {
    "path": "test/apps/rails2/config/boot.rb",
    "content": "# Don't change this file!\n# Configure your app in config/environment.rb and config/environments/*.rb\n\nRAILS_ROOT = \"#{File.dirname(__FILE__)}/..\" unless defined?(RAILS_ROOT)\n\nmodule Rails\n  class << self\n    def boot!\n      unless booted?\n        preinitialize\n        pick_boot.run\n      end\n    end\n\n    def booted?\n      defined? Rails::Initializer\n    end\n\n    def pick_boot\n      (vendor_rails? ? VendorBoot : GemBoot).new\n    end\n\n    def vendor_rails?\n      File.exist?(\"#{RAILS_ROOT}/vendor/rails\")\n    end\n\n    def preinitialize\n      load(preinitializer_path) if File.exist?(preinitializer_path)\n    end\n\n    def preinitializer_path\n      \"#{RAILS_ROOT}/config/preinitializer.rb\"\n    end\n  end\n\n  class Boot\n    def run\n      load_initializer\n      Rails::Initializer.run(:set_load_path)\n    end\n  end\n\n  class VendorBoot < Boot\n    def load_initializer\n      require \"#{RAILS_ROOT}/vendor/rails/railties/lib/initializer\"\n      Rails::Initializer.run(:install_gem_spec_stubs)\n      Rails::GemDependency.add_frozen_gem_path\n    end\n  end\n\n  class GemBoot < Boot\n    def load_initializer\n      self.class.load_rubygems\n      load_rails_gem\n      require 'initializer'\n    end\n\n    def load_rails_gem\n      if version = self.class.gem_version\n        gem 'rails', version\n      else\n        gem 'rails'\n      end\n    rescue Gem::LoadError => load_error\n      if load_error.message =~ /Could not find RubyGem rails/\n        STDERR.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)\n        exit 1\n      else\n        raise\n      end\n    end\n\n    class << self\n      def rubygems_version\n        Gem::RubyGemsVersion rescue nil\n      end\n\n      def gem_version\n        if defined? RAILS_GEM_VERSION\n          RAILS_GEM_VERSION\n        elsif ENV.include?('RAILS_GEM_VERSION')\n          ENV['RAILS_GEM_VERSION']\n        else\n          parse_gem_version(read_environment_rb)\n        end\n      end\n\n      def load_rubygems\n        min_version = '1.3.2'\n        require 'rubygems'\n        unless rubygems_version >= min_version\n          $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)\n          exit 1\n        end\n\n      rescue LoadError\n        $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)\n        exit 1\n      end\n\n      def parse_gem_version(text)\n        $1 if text =~ /^[^#]*RAILS_GEM_VERSION\\s*=\\s*[\"']([!~<>=]*\\s*[\\d.]+)[\"']/\n      end\n\n      private\n        def read_environment_rb\n          File.read(\"#{RAILS_ROOT}/config/environment.rb\")\n        end\n    end\n  end\nend\n\n# All that for this:\nRails.boot!\n"
  },
  {
    "path": "test/apps/rails2/config/brakeman.ignore",
    "content": "{\n  \"ignored_warnings\": [\n    {\n      \"warning_type\": \"Cross-Site Scripting\",\n      \"warning_code\": 2,\n      \"fingerprint\": \"e53a25ddc8ca61f7c4b81602bae04cd6746cf6a7432e89407fbeb362a66f4e8f\",\n      \"message\": \"Unescaped model attribute\",\n      \"file\": \"app/views/other/ignore_me.html.erb\",\n      \"line\": 2,\n      \"link\": \"http://brakemanscanner.org/docs/warning_types/cross_site_scripting\",\n      \"code\": \"User.first(:conditions => (\\\"x = #{params[:x]}\\\")).bio\",\n      \"render_path\": null,\n      \"location\": {\n        \"type\": \"template\",\n        \"template\": \"other/ignore_me\"\n      },\n      \"user_input\": null,\n      \"confidence\": \"High\",\n      \"note\": \"\"\n    },\n    {\n      \"warning_type\": \"SQL Injection\",\n      \"warning_code\": 0,\n      \"fingerprint\": \"5313b025804b1cbf885f2862078391086c6d5cba892f47ed31506cb5b23df24a\",\n      \"message\": \"Possible SQL injection\",\n      \"file\": \"app/views/other/ignore_me.html.erb\",\n      \"line\": 2,\n      \"link\": \"http://brakemanscanner.org/docs/warning_types/sql_injection/\",\n      \"code\": \"User.first(:conditions => (\\\"x = #{params[:x]}\\\"))\",\n      \"render_path\": null,\n      \"location\": {\n        \"type\": \"template\",\n        \"template\": \"other/ignore_me\"\n      },\n      \"user_input\": \"params[:x]\",\n      \"confidence\": \"High\",\n      \"note\": \"\"\n    }\n  ],\n  \"brakeman_version\": \"3.0.5\"\n}\n"
  },
  {
    "path": "test/apps/rails2/config/database.yml",
    "content": "# SQLite version 3.x\n#   gem install sqlite3-ruby (not necessary on OS X Leopard)\ndevelopment:\n  adapter: sqlite3\n  database: db/development.sqlite3\n  pool: 5\n  timeout: 5000\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  adapter: sqlite3\n  database: db/test.sqlite3\n  pool: 5\n  timeout: 5000\n\nproduction:\n  adapter: sqlite3\n  database: db/production.sqlite3\n  pool: 5\n  timeout: 5000\n"
  },
  {
    "path": "test/apps/rails2/config/environment.rb",
    "content": "# Be sure to restart your server when you modify this file\n\n# Specifies gem version of Rails to use when vendor/rails is not present\nRAILS_GEM_VERSION = '2.3.11' unless defined? RAILS_GEM_VERSION\n\n# Bootstrap the Rails environment, frameworks, and default configuration\nrequire File.join(File.dirname(__FILE__), 'boot')\n\nRails::Initializer.run do |config|\n  # Settings in config/environments/* take precedence over those specified here.\n  # Application configuration should go into files in config/initializers\n  # -- all .rb files in that directory are automatically loaded.\n\n  # Add additional load paths for your own custom dirs\n  # config.autoload_paths += %W( #{RAILS_ROOT}/extras )\n\n  # Specify gems that this application depends on and have them installed with rake gems:install\n  # config.gem \"bj\"\n  # config.gem \"hpricot\", :version => '0.6', :source => \"http://code.whytheluckystiff.net\"\n  # config.gem \"sqlite3-ruby\", :lib => \"sqlite3\"\n  # config.gem \"aws-s3\", :lib => \"aws/s3\"\n\n  # Only load the plugins named here, in the order given (default is alphabetical).\n  # :all can be used as a placeholder for all plugins not explicitly named\n  # config.plugins = [ :exception_notification, :ssl_requirement, :all ]\n\n  # Skip frameworks you're not going to use. To use Rails without a database,\n  # you must remove the Active Record framework.\n  # config.frameworks -= [ :active_record, :active_resource, :action_mailer ]\n\n  # Activate observers that should always be running\n  # config.active_record.observers = :cacher, :garbage_collector, :forum_observer\n\n  # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.\n  # Run \"rake -D time\" for a list of tasks for finding time zone names.\n  config.time_zone = 'UTC'\n\n  # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.\n  # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]\n  # config.i18n.default_locale = :de\nend"
  },
  {
    "path": "test/apps/rails2/config/environments/development.rb",
    "content": "# Settings specified here will take precedence over those in config/environment.rb\n\n# In the development environment your application's code is reloaded on\n# every request.  This slows down response time but is perfect for development\n# since you don't have to restart the webserver when you make code changes.\nconfig.cache_classes = false\n\n# Log error messages when you accidentally call methods on nil.\nconfig.whiny_nils = true\n\n# Show full error reports and disable caching\nconfig.action_controller.consider_all_requests_local = true\nconfig.action_view.debug_rjs                         = true\nconfig.action_controller.perform_caching             = false\n\n# Don't care if the mailer can't send\nconfig.action_mailer.raise_delivery_errors = false"
  },
  {
    "path": "test/apps/rails2/config/environments/production.rb",
    "content": "# Settings specified here will take precedence over those in config/environment.rb\n\n# The production environment is meant for finished, \"live\" apps.\n# Code is not reloaded between requests\nconfig.cache_classes = true\n\n# Full error reports are disabled and caching is turned on\nconfig.action_controller.consider_all_requests_local = false\nconfig.action_controller.perform_caching             = true\nconfig.action_view.cache_template_loading            = true\n\n# See everything in the log (default is :info)\n# config.log_level = :debug\n\n# Use a different logger for distributed setups\n# config.logger = SyslogLogger.new\n\n# Use a different cache store in production\n# config.cache_store = :mem_cache_store\n\n# Enable serving of images, stylesheets, and javascripts from an asset server\n# config.action_controller.asset_host = \"http://assets.example.com\"\n\n# Disable delivery errors, bad email addresses will be ignored\n# config.action_mailer.raise_delivery_errors = false\n\n# Enable threaded mode\n# config.threadsafe!"
  },
  {
    "path": "test/apps/rails2/config/environments/test.rb",
    "content": "# Settings specified here will take precedence over those in config/environment.rb\n\n# The test environment is used exclusively to run your application's\n# test suite.  You never need to work with it otherwise.  Remember that\n# your test database is \"scratch space\" for the test suite and is wiped\n# and recreated between test runs.  Don't rely on the data there!\nconfig.cache_classes = true\n\n# Log error messages when you accidentally call methods on nil.\nconfig.whiny_nils = true\n\n# Show full error reports and disable caching\nconfig.action_controller.consider_all_requests_local = true\nconfig.action_controller.perform_caching             = false\nconfig.action_view.cache_template_loading            = true\n\n# Disable request forgery protection in test environment\nconfig.action_controller.allow_forgery_protection    = false\n\n# Tell Action Mailer not to deliver emails to the real world.\n# The :test delivery method accumulates sent emails in the\n# ActionMailer::Base.deliveries array.\nconfig.action_mailer.delivery_method = :test\n\n# Use SQL instead of Active Record's schema dumper when creating the test database.\n# This is necessary if your schema can't be completely dumped by the schema dumper,\n# like if you have constraints or database-specific column types\n# config.active_record.schema_format = :sql"
  },
  {
    "path": "test/apps/rails2/config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying do debug a problem that might steem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!"
  },
  {
    "path": "test/apps/rails2/config/initializers/cookie_verification_secret.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n# Make sure the secret is at least 30 characters and all random, \n# no regular words or you'll be exposed to dictionary attacks.\nActionController::Base.cookie_verifier_secret = '4cdaca76a832bfacec3a57ef202842febb9c973facbbd472756d14eba6653e9ed5db8532ed4c089b16ed7ce44e8865cf676054fc2a7a8f5f42816894ddb2f3a4';\n"
  },
  {
    "path": "test/apps/rails2/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format \n# (all these examples are active by default):\n# ActiveSupport::Inflector.inflections do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n"
  },
  {
    "path": "test/apps/rails2/config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n# Mime::Type.register_alias \"text/html\", :iphone\n"
  },
  {
    "path": "test/apps/rails2/config/initializers/new_rails_defaults.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# These settings change the behavior of Rails 2 apps and will be defaults\n# for Rails 3. You can remove this initializer when Rails 3 is released.\n\nif defined?(ActiveRecord)\n  # Include Active Record class name as root for JSON serialized output.\n  ActiveRecord::Base.include_root_in_json = true\n\n  # Store the full class name (including module namespace) in STI type column.\n  ActiveRecord::Base.store_full_sti_class = true\nend\n\nActionController::Routing.generate_best_match = false\n\n# Use ISO 8601 format for JSON serialized times and dates.\nActiveSupport.use_standard_json_time_format = true\n\n# Don't escape HTML entities in JSON, leave that for the #json_escape helper.\n# if you're including raw json in an HTML page.\nActiveSupport.escape_html_entities_in_json = false"
  },
  {
    "path": "test/apps/rails2/config/initializers/security_defaults.rb",
    "content": "#ActiveRecord::Base.send(:attr_accessible, nil)\n"
  },
  {
    "path": "test/apps/rails2/config/initializers/session_store.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key for verifying cookie session data integrity.\n# If you change this key, all old sessions will become invalid!\n# Make sure the secret is at least 30 characters and all random, \n# no regular words or you'll be exposed to dictionary attacks.\nActionController::Base.session = {\n  :key         => '_rails2_session',\n  :secret      => 'secret!',\n  :session_http_only   => false\n}\n\n# Use the database for sessions instead of the cookie-based default,\n# which shouldn't be used to store highly confidential information\n# (create the session table with \"rake db:sessions:create\")\nActionController::Base.session_store = :active_record_store\n"
  },
  {
    "path": "test/apps/rails2/config/locales/en.yml",
    "content": "# Sample localization file for English. Add more files in this directory for other locales.\n# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.\n\nen:\n  hello: \"Hello world\""
  },
  {
    "path": "test/apps/rails2/config/routes.rb",
    "content": "ActionController::Routing::Routes.draw do |map|\n  #Default routes are set for everything,\n  #so these routes are testing for specific problems\n  map.with_options :controller => 'other', :action => 'nothing' do |r|\n    r.connect 'blah'\n  end\n\n  map.connect 'something', :controller => \"something#{dynamic}\"\n\n  map.resource :users\n\n  map.resources :stuff do |r|; end\n\n  namespace :without_block_arg do;end\n  # The priority is based upon order of creation: first created -> highest priority.\n\n  # Sample of regular route:\n  #   map.connect 'products/:id', :controller => 'catalog', :action => 'view'\n  # Keep in mind you can assign values other than :controller and :action\n\n  # Sample of named route:\n  #   map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'\n  # This route can be invoked with purchase_url(:id => product.id)\n\n  # Sample resource route (maps HTTP verbs to controller actions automatically):\n  #   map.resources :products\n\n  # Sample resource route with options:\n  #   map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get }\n\n  # Sample resource route with sub-resources:\n  #   map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller\n  \n  # Sample resource route with more complex sub-resources\n  #   map.resources :products do |products|\n  #     products.resources :comments\n  #     products.resources :sales, :collection => { :recent => :get }\n  #   end\n\n  # Sample resource route within a namespace:\n  #   map.namespace :admin do |admin|\n  #     # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb)\n  #     admin.resources :products\n  #   end\n\n  # You can have the root of your site routed with map.root -- just remember to delete public/index.html.\n  # map.root :controller => \"welcome\"\n\n  # See how all your routes lay out with \"rake routes\"\n\n  # Install the default routes as the lowest priority.\n  # Note: These default routes make all actions in every controller accessible via GET requests. You should\n  # consider removing or commenting them out if you're using named routes and resources.\n  map.connect ':controller/:action/:id'\n  map.connect ':controller/:action/:id.:format'\n  map.some_routes\n\n  map.things \"/things\", :controller => \"home\", :action => \"index_#{random_dynamic_thing}\" \nend\n"
  },
  {
    "path": "test/apps/rails2/db/migrate/20110520193611_create_users.rb",
    "content": "class CreateUsers < ActiveRecord::Migration\n  def self.up\n    create_table :users do |t|\n\n      t.timestamps\n    end\n  end\n\n  def self.down\n    drop_table :users\n  end\nend\n"
  },
  {
    "path": "test/apps/rails2/db/migrate/20110523184125_create_accounts.rb",
    "content": "class CreateAccounts < ActiveRecord::Migration\n  def self.up\n    create_table :accounts do |t|\n\n      t.timestamps\n    end\n  end\n\n  def self.down\n    drop_table :accounts\n  end\nend\n"
  },
  {
    "path": "test/apps/rails2/db/seeds.rb",
    "content": "# This file should contain all the record creation needed to seed the database with its default values.\n# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).\n#\n# Examples:\n#   \n#   cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])\n#   Major.create(:name => 'Daley', :city => cities.first)\n"
  },
  {
    "path": "test/apps/rails2/doc/README_FOR_APP",
    "content": "Use this README file to introduce your application and point to useful places in the API for learning more.\nRun \"rake doc:app\" to generate API documentation for your models, controllers, helpers, and libraries.\n"
  },
  {
    "path": "test/apps/rails2/lib/generators/test_generator/templates/model.rb",
    "content": "class <%= file_name.camelize %> < ActiveRecord::Base\nend\n"
  },
  {
    "path": "test/apps/rails2/log/development.log",
    "content": ""
  },
  {
    "path": "test/apps/rails2/log/production.log",
    "content": ""
  },
  {
    "path": "test/apps/rails2/log/server.log",
    "content": ""
  },
  {
    "path": "test/apps/rails2/log/test.log",
    "content": ""
  },
  {
    "path": "test/apps/rails2/public/404.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n       \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n\n<head>\n  <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />\n  <title>The page you were looking for doesn't exist (404)</title>\n\t<style type=\"text/css\">\n\t\tbody { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n\t\tdiv.dialog {\n\t\t\twidth: 25em;\n\t\t\tpadding: 0 4em;\n\t\t\tmargin: 4em auto 0 auto;\n\t\t\tborder: 1px solid #ccc;\n\t\t\tborder-right-color: #999;\n\t\t\tborder-bottom-color: #999;\n\t\t}\n\t\th1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n\t</style>\n</head>\n\n<body>\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <h1>The page you were looking for doesn't exist.</h1>\n    <p>You may have mistyped the address or the page may have moved.</p>\n  </div>\n</body>\n</html>"
  },
  {
    "path": "test/apps/rails2/public/422.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n       \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n\n<head>\n  <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />\n  <title>The change you wanted was rejected (422)</title>\n\t<style type=\"text/css\">\n\t\tbody { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n\t\tdiv.dialog {\n\t\t\twidth: 25em;\n\t\t\tpadding: 0 4em;\n\t\t\tmargin: 4em auto 0 auto;\n\t\t\tborder: 1px solid #ccc;\n\t\t\tborder-right-color: #999;\n\t\t\tborder-bottom-color: #999;\n\t\t}\n\t\th1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n\t</style>\n</head>\n\n<body>\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <h1>The change you wanted was rejected.</h1>\n    <p>Maybe you tried to change something you didn't have access to.</p>\n  </div>\n</body>\n</html>"
  },
  {
    "path": "test/apps/rails2/public/500.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n       \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n\n<head>\n  <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />\n  <title>We're sorry, but something went wrong (500)</title>\n\t<style type=\"text/css\">\n\t\tbody { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n\t\tdiv.dialog {\n\t\t\twidth: 25em;\n\t\t\tpadding: 0 4em;\n\t\t\tmargin: 4em auto 0 auto;\n\t\t\tborder: 1px solid #ccc;\n\t\t\tborder-right-color: #999;\n\t\t\tborder-bottom-color: #999;\n\t\t}\n\t\th1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n\t</style>\n</head>\n\n<body>\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <h1>We're sorry, but something went wrong.</h1>\n    <p>We've been notified about this issue and we'll take a look at it shortly.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails2/public/index.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n        \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html>\n  <head>\n    <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n    <title>Ruby on Rails: Welcome aboard</title>\n    <style type=\"text/css\" media=\"screen\">\n      body {\n        margin: 0;\n        margin-bottom: 25px;\n        padding: 0;\n        background-color: #f0f0f0;\n        font-family: \"Lucida Grande\", \"Bitstream Vera Sans\", \"Verdana\";\n        font-size: 13px;\n        color: #333;\n      }\n      \n      h1 {\n        font-size: 28px;\n        color: #000;\n      }\n      \n      a  {color: #03c}\n      a:hover {\n        background-color: #03c;\n        color: white;\n        text-decoration: none;\n      }\n      \n      \n      #page {\n        background-color: #f0f0f0;\n        width: 750px;\n        margin: 0;\n        margin-left: auto;\n        margin-right: auto;\n      }\n      \n      #content {\n        float: left;\n        background-color: white;\n        border: 3px solid #aaa;\n        border-top: none;\n        padding: 25px;\n        width: 500px;\n      }\n      \n      #sidebar {\n        float: right;\n        width: 175px;\n      }\n\n      #footer {\n        clear: both;\n      }\n      \n\n      #header, #about, #getting-started {\n        padding-left: 75px;\n        padding-right: 30px;\n      }\n\n\n      #header {\n        background-image: url(\"images/rails.png\");\n        background-repeat: no-repeat;\n        background-position: top left;\n        height: 64px;\n      }\n      #header h1, #header h2 {margin: 0}\n      #header h2 {\n        color: #888;\n        font-weight: normal;\n        font-size: 16px;\n      }\n      \n      \n      #about h3 {\n        margin: 0;\n        margin-bottom: 10px;\n        font-size: 14px;\n      }\n      \n      #about-content {\n        background-color: #ffd;\n        border: 1px solid #fc0;\n        margin-left: -11px;\n      }\n      #about-content table {\n        margin-top: 10px;\n        margin-bottom: 10px;\n        font-size: 11px;\n        border-collapse: collapse;\n      }\n      #about-content td {\n        padding: 10px;\n        padding-top: 3px;\n        padding-bottom: 3px;\n      }\n      #about-content td.name  {color: #555}\n      #about-content td.value {color: #000}\n      \n      #about-content.failure {\n        background-color: #fcc;\n        border: 1px solid #f00;\n      }\n      #about-content.failure p {\n        margin: 0;\n        padding: 10px;\n      }\n      \n      \n      #getting-started {\n        border-top: 1px solid #ccc;\n        margin-top: 25px;\n        padding-top: 15px;\n      }\n      #getting-started h1 {\n        margin: 0;\n        font-size: 20px;\n      }\n      #getting-started h2 {\n        margin: 0;\n        font-size: 14px;\n        font-weight: normal;\n        color: #333;\n        margin-bottom: 25px;\n      }\n      #getting-started ol {\n        margin-left: 0;\n        padding-left: 0;\n      }\n      #getting-started li {\n        font-size: 18px;\n        color: #888;\n        margin-bottom: 25px;\n      }\n      #getting-started li h2 {\n        margin: 0;\n        font-weight: normal;\n        font-size: 18px;\n        color: #333;\n      }\n      #getting-started li p {\n        color: #555;\n        font-size: 13px;\n      }\n      \n      \n      #search {\n        margin: 0;\n        padding-top: 10px;\n        padding-bottom: 10px;\n        font-size: 11px;\n      }\n      #search input {\n        font-size: 11px;\n        margin: 2px;\n      }\n      #search-text {width: 170px}\n      \n      \n      #sidebar ul {\n        margin-left: 0;\n        padding-left: 0;\n      }\n      #sidebar ul h3 {\n        margin-top: 25px;\n        font-size: 16px;\n        padding-bottom: 10px;\n        border-bottom: 1px solid #ccc;\n      }\n      #sidebar li {\n        list-style-type: none;\n      }\n      #sidebar ul.links li {\n        margin-bottom: 5px;\n      }\n      \n    </style>\n    <script type=\"text/javascript\" src=\"javascripts/prototype.js\"></script>\n    <script type=\"text/javascript\" src=\"javascripts/effects.js\"></script>\n    <script type=\"text/javascript\">\n      function about() {\n        if (Element.empty('about-content')) {\n          new Ajax.Updater('about-content', 'rails/info/properties', {\n            method:     'get',\n            onFailure:  function() {Element.classNames('about-content').add('failure')},\n            onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})}\n          });\n        } else {\n          new Effect[Element.visible('about-content') ? \n            'BlindUp' : 'BlindDown']('about-content', {duration: 0.25});\n        }\n      }\n      \n      window.onload = function() {\n        $('search-text').value = '';\n        $('search').onsubmit = function() {\n          $('search-text').value = 'site:rubyonrails.org ' + $F('search-text');\n        }\n      }\n    </script>\n  </head>\n  <body>\n    <div id=\"page\">\n      <div id=\"sidebar\">\n        <ul id=\"sidebar-items\">\n          <li>\n            <form id=\"search\" action=\"http://www.google.com/search\" method=\"get\">\n              <input type=\"hidden\" name=\"hl\" value=\"en\" />\n              <input type=\"text\" id=\"search-text\" name=\"q\" value=\"site:rubyonrails.org \" />\n              <input type=\"submit\" value=\"Search\" /> the Rails site\n            </form>\n          </li>\n        \n          <li>\n            <h3>Join the community</h3>\n            <ul class=\"links\">\n              <li><a href=\"http://www.rubyonrails.org/\">Ruby on Rails</a></li>\n              <li><a href=\"http://weblog.rubyonrails.org/\">Official weblog</a></li>\n              <li><a href=\"http://wiki.rubyonrails.org/\">Wiki</a></li>\n            </ul>\n          </li>\n          \n          <li>\n            <h3>Browse the documentation</h3>\n            <ul class=\"links\">\n              <li><a href=\"http://api.rubyonrails.org/\">Rails API</a></li>\n              <li><a href=\"http://stdlib.rubyonrails.org/\">Ruby standard library</a></li>\n              <li><a href=\"http://corelib.rubyonrails.org/\">Ruby core</a></li>\n              <li><a href=\"http://guides.rubyonrails.org/\">Rails Guides</a></li>\n            </ul>\n          </li>\n        </ul>\n      </div>\n\n      <div id=\"content\">\n        <div id=\"header\">\n          <h1>Welcome aboard</h1>\n          <h2>You&rsquo;re riding Ruby on Rails!</h2>\n        </div>\n\n        <div id=\"about\">\n          <h3><a href=\"rails/info/properties\" onclick=\"about(); return false\">About your application&rsquo;s environment</a></h3>\n          <div id=\"about-content\" style=\"display: none\"></div>\n        </div>\n        \n        <div id=\"getting-started\">\n          <h1>Getting started</h1>\n          <h2>Here&rsquo;s how to get rolling:</h2>\n          \n          <ol>          \n            <li>\n              <h2>Use <tt>script/generate</tt> to create your models and controllers</h2>\n              <p>To see all available options, run it without parameters.</p>\n            </li>\n            \n            <li>\n              <h2>Set up a default route and remove or rename this file</h2>\n              <p>Routes are set up in config/routes.rb.</p>\n            </li>\n\n            <li>\n              <h2>Create your database</h2>\n              <p>Run <tt>rake db:migrate</tt> to create your database. If you're not using SQLite (the default), edit <tt>config/database.yml</tt> with your username and password.</p>\n            </li>\n          </ol>\n        </div>\n      </div>\n      \n      <div id=\"footer\">&nbsp;</div>\n    </div>\n  </body>\n</html>"
  },
  {
    "path": "test/apps/rails2/public/javascripts/application.js",
    "content": "// Place your application-specific JavaScript functions and classes here\n// This file is automatically included by javascript_include_tag :defaults\n"
  },
  {
    "path": "test/apps/rails2/public/javascripts/controls.js",
    "content": "// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n//           (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)\n//           (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)\n// Contributors:\n//  Richard Livsey\n//  Rahul Bhargava\n//  Rob Wills\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\n// Autocompleter.Base handles all the autocompletion functionality\n// that's independent of the data source for autocompletion. This\n// includes drawing the autocompletion menu, observing keyboard\n// and mouse events, and similar.\n//\n// Specific autocompleters need to provide, at the very least,\n// a getUpdatedChoices function that will be invoked every time\n// the text inside the monitored textbox changes. This method\n// should get the text for which to provide autocompletion by\n// invoking this.getToken(), NOT by directly accessing\n// this.element.value. This is to allow incremental tokenized\n// autocompletion. Specific auto-completion logic (AJAX, etc)\n// belongs in getUpdatedChoices.\n//\n// Tokenized incremental autocompletion is enabled automatically\n// when an autocompleter is instantiated with the 'tokens' option\n// in the options parameter, e.g.:\n// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });\n// will incrementally autocomplete with a comma as the token.\n// Additionally, ',' in the above example can be replaced with\n// a token array, e.g. { tokens: [',', '\\n'] } which\n// enables autocompletion on multiple tokens. This is most\n// useful when one of the tokens is \\n (a newline), as it\n// allows smart autocompletion after linebreaks.\n\nif(typeof Effect == 'undefined')\n  throw(\"controls.js requires including script.aculo.us' effects.js library\");\n\nvar Autocompleter = { };\nAutocompleter.Base = Class.create({\n  baseInitialize: function(element, update, options) {\n    element          = $(element);\n    this.element     = element;\n    this.update      = $(update);\n    this.hasFocus    = false;\n    this.changed     = false;\n    this.active      = false;\n    this.index       = 0;\n    this.entryCount  = 0;\n    this.oldElementValue = this.element.value;\n\n    if(this.setOptions)\n      this.setOptions(options);\n    else\n      this.options = options || { };\n\n    this.options.paramName    = this.options.paramName || this.element.name;\n    this.options.tokens       = this.options.tokens || [];\n    this.options.frequency    = this.options.frequency || 0.4;\n    this.options.minChars     = this.options.minChars || 1;\n    this.options.onShow       = this.options.onShow ||\n      function(element, update){\n        if(!update.style.position || update.style.position=='absolute') {\n          update.style.position = 'absolute';\n          Position.clone(element, update, {\n            setHeight: false,\n            offsetTop: element.offsetHeight\n          });\n        }\n        Effect.Appear(update,{duration:0.15});\n      };\n    this.options.onHide = this.options.onHide ||\n      function(element, update){ new Effect.Fade(update,{duration:0.15}) };\n\n    if(typeof(this.options.tokens) == 'string')\n      this.options.tokens = new Array(this.options.tokens);\n    // Force carriage returns as token delimiters anyway\n    if (!this.options.tokens.include('\\n'))\n      this.options.tokens.push('\\n');\n\n    this.observer = null;\n\n    this.element.setAttribute('autocomplete','off');\n\n    Element.hide(this.update);\n\n    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));\n    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));\n  },\n\n  show: function() {\n    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);\n    if(!this.iefix &&\n      (Prototype.Browser.IE) &&\n      (Element.getStyle(this.update, 'position')=='absolute')) {\n      new Insertion.After(this.update,\n       '<iframe id=\"' + this.update.id + '_iefix\" '+\n       'style=\"display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);\" ' +\n       'src=\"javascript:false;\" frameborder=\"0\" scrolling=\"no\"></iframe>');\n      this.iefix = $(this.update.id+'_iefix');\n    }\n    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);\n  },\n\n  fixIEOverlapping: function() {\n    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});\n    this.iefix.style.zIndex = 1;\n    this.update.style.zIndex = 2;\n    Element.show(this.iefix);\n  },\n\n  hide: function() {\n    this.stopIndicator();\n    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);\n    if(this.iefix) Element.hide(this.iefix);\n  },\n\n  startIndicator: function() {\n    if(this.options.indicator) Element.show(this.options.indicator);\n  },\n\n  stopIndicator: function() {\n    if(this.options.indicator) Element.hide(this.options.indicator);\n  },\n\n  onKeyPress: function(event) {\n    if(this.active)\n      switch(event.keyCode) {\n       case Event.KEY_TAB:\n       case Event.KEY_RETURN:\n         this.selectEntry();\n         Event.stop(event);\n       case Event.KEY_ESC:\n         this.hide();\n         this.active = false;\n         Event.stop(event);\n         return;\n       case Event.KEY_LEFT:\n       case Event.KEY_RIGHT:\n         return;\n       case Event.KEY_UP:\n         this.markPrevious();\n         this.render();\n         Event.stop(event);\n         return;\n       case Event.KEY_DOWN:\n         this.markNext();\n         this.render();\n         Event.stop(event);\n         return;\n      }\n     else\n       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||\n         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;\n\n    this.changed = true;\n    this.hasFocus = true;\n\n    if(this.observer) clearTimeout(this.observer);\n      this.observer =\n        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);\n  },\n\n  activate: function() {\n    this.changed = false;\n    this.hasFocus = true;\n    this.getUpdatedChoices();\n  },\n\n  onHover: function(event) {\n    var element = Event.findElement(event, 'LI');\n    if(this.index != element.autocompleteIndex)\n    {\n        this.index = element.autocompleteIndex;\n        this.render();\n    }\n    Event.stop(event);\n  },\n\n  onClick: function(event) {\n    var element = Event.findElement(event, 'LI');\n    this.index = element.autocompleteIndex;\n    this.selectEntry();\n    this.hide();\n  },\n\n  onBlur: function(event) {\n    // needed to make click events working\n    setTimeout(this.hide.bind(this), 250);\n    this.hasFocus = false;\n    this.active = false;\n  },\n\n  render: function() {\n    if(this.entryCount > 0) {\n      for (var i = 0; i < this.entryCount; i++)\n        this.index==i ?\n          Element.addClassName(this.getEntry(i),\"selected\") :\n          Element.removeClassName(this.getEntry(i),\"selected\");\n      if(this.hasFocus) {\n        this.show();\n        this.active = true;\n      }\n    } else {\n      this.active = false;\n      this.hide();\n    }\n  },\n\n  markPrevious: function() {\n    if(this.index > 0) this.index--;\n      else this.index = this.entryCount-1;\n    this.getEntry(this.index).scrollIntoView(true);\n  },\n\n  markNext: function() {\n    if(this.index < this.entryCount-1) this.index++;\n      else this.index = 0;\n    this.getEntry(this.index).scrollIntoView(false);\n  },\n\n  getEntry: function(index) {\n    return this.update.firstChild.childNodes[index];\n  },\n\n  getCurrentEntry: function() {\n    return this.getEntry(this.index);\n  },\n\n  selectEntry: function() {\n    this.active = false;\n    this.updateElement(this.getCurrentEntry());\n  },\n\n  updateElement: function(selectedElement) {\n    if (this.options.updateElement) {\n      this.options.updateElement(selectedElement);\n      return;\n    }\n    var value = '';\n    if (this.options.select) {\n      var nodes = $(selectedElement).select('.' + this.options.select) || [];\n      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);\n    } else\n      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');\n\n    var bounds = this.getTokenBounds();\n    if (bounds[0] != -1) {\n      var newValue = this.element.value.substr(0, bounds[0]);\n      var whitespace = this.element.value.substr(bounds[0]).match(/^\\s+/);\n      if (whitespace)\n        newValue += whitespace[0];\n      this.element.value = newValue + value + this.element.value.substr(bounds[1]);\n    } else {\n      this.element.value = value;\n    }\n    this.oldElementValue = this.element.value;\n    this.element.focus();\n\n    if (this.options.afterUpdateElement)\n      this.options.afterUpdateElement(this.element, selectedElement);\n  },\n\n  updateChoices: function(choices) {\n    if(!this.changed && this.hasFocus) {\n      this.update.innerHTML = choices;\n      Element.cleanWhitespace(this.update);\n      Element.cleanWhitespace(this.update.down());\n\n      if(this.update.firstChild && this.update.down().childNodes) {\n        this.entryCount =\n          this.update.down().childNodes.length;\n        for (var i = 0; i < this.entryCount; i++) {\n          var entry = this.getEntry(i);\n          entry.autocompleteIndex = i;\n          this.addObservers(entry);\n        }\n      } else {\n        this.entryCount = 0;\n      }\n\n      this.stopIndicator();\n      this.index = 0;\n\n      if(this.entryCount==1 && this.options.autoSelect) {\n        this.selectEntry();\n        this.hide();\n      } else {\n        this.render();\n      }\n    }\n  },\n\n  addObservers: function(element) {\n    Event.observe(element, \"mouseover\", this.onHover.bindAsEventListener(this));\n    Event.observe(element, \"click\", this.onClick.bindAsEventListener(this));\n  },\n\n  onObserverEvent: function() {\n    this.changed = false;\n    this.tokenBounds = null;\n    if(this.getToken().length>=this.options.minChars) {\n      this.getUpdatedChoices();\n    } else {\n      this.active = false;\n      this.hide();\n    }\n    this.oldElementValue = this.element.value;\n  },\n\n  getToken: function() {\n    var bounds = this.getTokenBounds();\n    return this.element.value.substring(bounds[0], bounds[1]).strip();\n  },\n\n  getTokenBounds: function() {\n    if (null != this.tokenBounds) return this.tokenBounds;\n    var value = this.element.value;\n    if (value.strip().empty()) return [-1, 0];\n    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);\n    var offset = (diff == this.oldElementValue.length ? 1 : 0);\n    var prevTokenPos = -1, nextTokenPos = value.length;\n    var tp;\n    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {\n      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);\n      if (tp > prevTokenPos) prevTokenPos = tp;\n      tp = value.indexOf(this.options.tokens[index], diff + offset);\n      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;\n    }\n    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);\n  }\n});\n\nAutocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {\n  var boundary = Math.min(newS.length, oldS.length);\n  for (var index = 0; index < boundary; ++index)\n    if (newS[index] != oldS[index])\n      return index;\n  return boundary;\n};\n\nAjax.Autocompleter = Class.create(Autocompleter.Base, {\n  initialize: function(element, update, url, options) {\n    this.baseInitialize(element, update, options);\n    this.options.asynchronous  = true;\n    this.options.onComplete    = this.onComplete.bind(this);\n    this.options.defaultParams = this.options.parameters || null;\n    this.url                   = url;\n  },\n\n  getUpdatedChoices: function() {\n    this.startIndicator();\n\n    var entry = encodeURIComponent(this.options.paramName) + '=' +\n      encodeURIComponent(this.getToken());\n\n    this.options.parameters = this.options.callback ?\n      this.options.callback(this.element, entry) : entry;\n\n    if(this.options.defaultParams)\n      this.options.parameters += '&' + this.options.defaultParams;\n\n    new Ajax.Request(this.url, this.options);\n  },\n\n  onComplete: function(request) {\n    this.updateChoices(request.responseText);\n  }\n});\n\n// The local array autocompleter. Used when you'd prefer to\n// inject an array of autocompletion options into the page, rather\n// than sending out Ajax queries, which can be quite slow sometimes.\n//\n// The constructor takes four parameters. The first two are, as usual,\n// the id of the monitored textbox, and id of the autocompletion menu.\n// The third is the array you want to autocomplete from, and the fourth\n// is the options block.\n//\n// Extra local autocompletion options:\n// - choices - How many autocompletion choices to offer\n//\n// - partialSearch - If false, the autocompleter will match entered\n//                    text only at the beginning of strings in the\n//                    autocomplete array. Defaults to true, which will\n//                    match text at the beginning of any *word* in the\n//                    strings in the autocomplete array. If you want to\n//                    search anywhere in the string, additionally set\n//                    the option fullSearch to true (default: off).\n//\n// - fullSsearch - Search anywhere in autocomplete array strings.\n//\n// - partialChars - How many characters to enter before triggering\n//                   a partial match (unlike minChars, which defines\n//                   how many characters are required to do any match\n//                   at all). Defaults to 2.\n//\n// - ignoreCase - Whether to ignore case when autocompleting.\n//                 Defaults to true.\n//\n// It's possible to pass in a custom function as the 'selector'\n// option, if you prefer to write your own autocompletion logic.\n// In that case, the other options above will not apply unless\n// you support them.\n\nAutocompleter.Local = Class.create(Autocompleter.Base, {\n  initialize: function(element, update, array, options) {\n    this.baseInitialize(element, update, options);\n    this.options.array = array;\n  },\n\n  getUpdatedChoices: function() {\n    this.updateChoices(this.options.selector(this));\n  },\n\n  setOptions: function(options) {\n    this.options = Object.extend({\n      choices: 10,\n      partialSearch: true,\n      partialChars: 2,\n      ignoreCase: true,\n      fullSearch: false,\n      selector: function(instance) {\n        var ret       = []; // Beginning matches\n        var partial   = []; // Inside matches\n        var entry     = instance.getToken();\n        var count     = 0;\n\n        for (var i = 0; i < instance.options.array.length &&\n          ret.length < instance.options.choices ; i++) {\n\n          var elem = instance.options.array[i];\n          var foundPos = instance.options.ignoreCase ?\n            elem.toLowerCase().indexOf(entry.toLowerCase()) :\n            elem.indexOf(entry);\n\n          while (foundPos != -1) {\n            if (foundPos == 0 && elem.length != entry.length) {\n              ret.push(\"<li><strong>\" + elem.substr(0, entry.length) + \"</strong>\" +\n                elem.substr(entry.length) + \"</li>\");\n              break;\n            } else if (entry.length >= instance.options.partialChars &&\n              instance.options.partialSearch && foundPos != -1) {\n              if (instance.options.fullSearch || /\\s/.test(elem.substr(foundPos-1,1))) {\n                partial.push(\"<li>\" + elem.substr(0, foundPos) + \"<strong>\" +\n                  elem.substr(foundPos, entry.length) + \"</strong>\" + elem.substr(\n                  foundPos + entry.length) + \"</li>\");\n                break;\n              }\n            }\n\n            foundPos = instance.options.ignoreCase ?\n              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :\n              elem.indexOf(entry, foundPos + 1);\n\n          }\n        }\n        if (partial.length)\n          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));\n        return \"<ul>\" + ret.join('') + \"</ul>\";\n      }\n    }, options || { });\n  }\n});\n\n// AJAX in-place editor and collection editor\n// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).\n\n// Use this if you notice weird scrolling problems on some browsers,\n// the DOM might be a bit confused when this gets called so do this\n// waits 1 ms (with setTimeout) until it does the activation\nField.scrollFreeActivate = function(field) {\n  setTimeout(function() {\n    Field.activate(field);\n  }, 1);\n};\n\nAjax.InPlaceEditor = Class.create({\n  initialize: function(element, url, options) {\n    this.url = url;\n    this.element = element = $(element);\n    this.prepareOptions();\n    this._controls = { };\n    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!\n    Object.extend(this.options, options || { });\n    if (!this.options.formId && this.element.id) {\n      this.options.formId = this.element.id + '-inplaceeditor';\n      if ($(this.options.formId))\n        this.options.formId = '';\n    }\n    if (this.options.externalControl)\n      this.options.externalControl = $(this.options.externalControl);\n    if (!this.options.externalControl)\n      this.options.externalControlOnly = false;\n    this._originalBackground = this.element.getStyle('background-color') || 'transparent';\n    this.element.title = this.options.clickToEditText;\n    this._boundCancelHandler = this.handleFormCancellation.bind(this);\n    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);\n    this._boundFailureHandler = this.handleAJAXFailure.bind(this);\n    this._boundSubmitHandler = this.handleFormSubmission.bind(this);\n    this._boundWrapperHandler = this.wrapUp.bind(this);\n    this.registerListeners();\n  },\n  checkForEscapeOrReturn: function(e) {\n    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;\n    if (Event.KEY_ESC == e.keyCode)\n      this.handleFormCancellation(e);\n    else if (Event.KEY_RETURN == e.keyCode)\n      this.handleFormSubmission(e);\n  },\n  createControl: function(mode, handler, extraClasses) {\n    var control = this.options[mode + 'Control'];\n    var text = this.options[mode + 'Text'];\n    if ('button' == control) {\n      var btn = document.createElement('input');\n      btn.type = 'submit';\n      btn.value = text;\n      btn.className = 'editor_' + mode + '_button';\n      if ('cancel' == mode)\n        btn.onclick = this._boundCancelHandler;\n      this._form.appendChild(btn);\n      this._controls[mode] = btn;\n    } else if ('link' == control) {\n      var link = document.createElement('a');\n      link.href = '#';\n      link.appendChild(document.createTextNode(text));\n      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;\n      link.className = 'editor_' + mode + '_link';\n      if (extraClasses)\n        link.className += ' ' + extraClasses;\n      this._form.appendChild(link);\n      this._controls[mode] = link;\n    }\n  },\n  createEditField: function() {\n    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());\n    var fld;\n    if (1 >= this.options.rows && !/\\r|\\n/.test(this.getText())) {\n      fld = document.createElement('input');\n      fld.type = 'text';\n      var size = this.options.size || this.options.cols || 0;\n      if (0 < size) fld.size = size;\n    } else {\n      fld = document.createElement('textarea');\n      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);\n      fld.cols = this.options.cols || 40;\n    }\n    fld.name = this.options.paramName;\n    fld.value = text; // No HTML breaks conversion anymore\n    fld.className = 'editor_field';\n    if (this.options.submitOnBlur)\n      fld.onblur = this._boundSubmitHandler;\n    this._controls.editor = fld;\n    if (this.options.loadTextURL)\n      this.loadExternalText();\n    this._form.appendChild(this._controls.editor);\n  },\n  createForm: function() {\n    var ipe = this;\n    function addText(mode, condition) {\n      var text = ipe.options['text' + mode + 'Controls'];\n      if (!text || condition === false) return;\n      ipe._form.appendChild(document.createTextNode(text));\n    };\n    this._form = $(document.createElement('form'));\n    this._form.id = this.options.formId;\n    this._form.addClassName(this.options.formClassName);\n    this._form.onsubmit = this._boundSubmitHandler;\n    this.createEditField();\n    if ('textarea' == this._controls.editor.tagName.toLowerCase())\n      this._form.appendChild(document.createElement('br'));\n    if (this.options.onFormCustomization)\n      this.options.onFormCustomization(this, this._form);\n    addText('Before', this.options.okControl || this.options.cancelControl);\n    this.createControl('ok', this._boundSubmitHandler);\n    addText('Between', this.options.okControl && this.options.cancelControl);\n    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');\n    addText('After', this.options.okControl || this.options.cancelControl);\n  },\n  destroy: function() {\n    if (this._oldInnerHTML)\n      this.element.innerHTML = this._oldInnerHTML;\n    this.leaveEditMode();\n    this.unregisterListeners();\n  },\n  enterEditMode: function(e) {\n    if (this._saving || this._editing) return;\n    this._editing = true;\n    this.triggerCallback('onEnterEditMode');\n    if (this.options.externalControl)\n      this.options.externalControl.hide();\n    this.element.hide();\n    this.createForm();\n    this.element.parentNode.insertBefore(this._form, this.element);\n    if (!this.options.loadTextURL)\n      this.postProcessEditField();\n    if (e) Event.stop(e);\n  },\n  enterHover: function(e) {\n    if (this.options.hoverClassName)\n      this.element.addClassName(this.options.hoverClassName);\n    if (this._saving) return;\n    this.triggerCallback('onEnterHover');\n  },\n  getText: function() {\n    return this.element.innerHTML.unescapeHTML();\n  },\n  handleAJAXFailure: function(transport) {\n    this.triggerCallback('onFailure', transport);\n    if (this._oldInnerHTML) {\n      this.element.innerHTML = this._oldInnerHTML;\n      this._oldInnerHTML = null;\n    }\n  },\n  handleFormCancellation: function(e) {\n    this.wrapUp();\n    if (e) Event.stop(e);\n  },\n  handleFormSubmission: function(e) {\n    var form = this._form;\n    var value = $F(this._controls.editor);\n    this.prepareSubmission();\n    var params = this.options.callback(form, value) || '';\n    if (Object.isString(params))\n      params = params.toQueryParams();\n    params.editorId = this.element.id;\n    if (this.options.htmlResponse) {\n      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);\n      Object.extend(options, {\n        parameters: params,\n        onComplete: this._boundWrapperHandler,\n        onFailure: this._boundFailureHandler\n      });\n      new Ajax.Updater({ success: this.element }, this.url, options);\n    } else {\n      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n      Object.extend(options, {\n        parameters: params,\n        onComplete: this._boundWrapperHandler,\n        onFailure: this._boundFailureHandler\n      });\n      new Ajax.Request(this.url, options);\n    }\n    if (e) Event.stop(e);\n  },\n  leaveEditMode: function() {\n    this.element.removeClassName(this.options.savingClassName);\n    this.removeForm();\n    this.leaveHover();\n    this.element.style.backgroundColor = this._originalBackground;\n    this.element.show();\n    if (this.options.externalControl)\n      this.options.externalControl.show();\n    this._saving = false;\n    this._editing = false;\n    this._oldInnerHTML = null;\n    this.triggerCallback('onLeaveEditMode');\n  },\n  leaveHover: function(e) {\n    if (this.options.hoverClassName)\n      this.element.removeClassName(this.options.hoverClassName);\n    if (this._saving) return;\n    this.triggerCallback('onLeaveHover');\n  },\n  loadExternalText: function() {\n    this._form.addClassName(this.options.loadingClassName);\n    this._controls.editor.disabled = true;\n    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n    Object.extend(options, {\n      parameters: 'editorId=' + encodeURIComponent(this.element.id),\n      onComplete: Prototype.emptyFunction,\n      onSuccess: function(transport) {\n        this._form.removeClassName(this.options.loadingClassName);\n        var text = transport.responseText;\n        if (this.options.stripLoadedTextTags)\n          text = text.stripTags();\n        this._controls.editor.value = text;\n        this._controls.editor.disabled = false;\n        this.postProcessEditField();\n      }.bind(this),\n      onFailure: this._boundFailureHandler\n    });\n    new Ajax.Request(this.options.loadTextURL, options);\n  },\n  postProcessEditField: function() {\n    var fpc = this.options.fieldPostCreation;\n    if (fpc)\n      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();\n  },\n  prepareOptions: function() {\n    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);\n    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);\n    [this._extraDefaultOptions].flatten().compact().each(function(defs) {\n      Object.extend(this.options, defs);\n    }.bind(this));\n  },\n  prepareSubmission: function() {\n    this._saving = true;\n    this.removeForm();\n    this.leaveHover();\n    this.showSaving();\n  },\n  registerListeners: function() {\n    this._listeners = { };\n    var listener;\n    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {\n      listener = this[pair.value].bind(this);\n      this._listeners[pair.key] = listener;\n      if (!this.options.externalControlOnly)\n        this.element.observe(pair.key, listener);\n      if (this.options.externalControl)\n        this.options.externalControl.observe(pair.key, listener);\n    }.bind(this));\n  },\n  removeForm: function() {\n    if (!this._form) return;\n    this._form.remove();\n    this._form = null;\n    this._controls = { };\n  },\n  showSaving: function() {\n    this._oldInnerHTML = this.element.innerHTML;\n    this.element.innerHTML = this.options.savingText;\n    this.element.addClassName(this.options.savingClassName);\n    this.element.style.backgroundColor = this._originalBackground;\n    this.element.show();\n  },\n  triggerCallback: function(cbName, arg) {\n    if ('function' == typeof this.options[cbName]) {\n      this.options[cbName](this, arg);\n    }\n  },\n  unregisterListeners: function() {\n    $H(this._listeners).each(function(pair) {\n      if (!this.options.externalControlOnly)\n        this.element.stopObserving(pair.key, pair.value);\n      if (this.options.externalControl)\n        this.options.externalControl.stopObserving(pair.key, pair.value);\n    }.bind(this));\n  },\n  wrapUp: function(transport) {\n    this.leaveEditMode();\n    // Can't use triggerCallback due to backward compatibility: requires\n    // binding + direct element\n    this._boundComplete(transport, this.element);\n  }\n});\n\nObject.extend(Ajax.InPlaceEditor.prototype, {\n  dispose: Ajax.InPlaceEditor.prototype.destroy\n});\n\nAjax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {\n  initialize: function($super, element, url, options) {\n    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;\n    $super(element, url, options);\n  },\n\n  createEditField: function() {\n    var list = document.createElement('select');\n    list.name = this.options.paramName;\n    list.size = 1;\n    this._controls.editor = list;\n    this._collection = this.options.collection || [];\n    if (this.options.loadCollectionURL)\n      this.loadCollection();\n    else\n      this.checkForExternalText();\n    this._form.appendChild(this._controls.editor);\n  },\n\n  loadCollection: function() {\n    this._form.addClassName(this.options.loadingClassName);\n    this.showLoadingText(this.options.loadingCollectionText);\n    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n    Object.extend(options, {\n      parameters: 'editorId=' + encodeURIComponent(this.element.id),\n      onComplete: Prototype.emptyFunction,\n      onSuccess: function(transport) {\n        var js = transport.responseText.strip();\n        if (!/^\\[.*\\]$/.test(js)) // TODO: improve sanity check\n          throw('Server returned an invalid collection representation.');\n        this._collection = eval(js);\n        this.checkForExternalText();\n      }.bind(this),\n      onFailure: this.onFailure\n    });\n    new Ajax.Request(this.options.loadCollectionURL, options);\n  },\n\n  showLoadingText: function(text) {\n    this._controls.editor.disabled = true;\n    var tempOption = this._controls.editor.firstChild;\n    if (!tempOption) {\n      tempOption = document.createElement('option');\n      tempOption.value = '';\n      this._controls.editor.appendChild(tempOption);\n      tempOption.selected = true;\n    }\n    tempOption.update((text || '').stripScripts().stripTags());\n  },\n\n  checkForExternalText: function() {\n    this._text = this.getText();\n    if (this.options.loadTextURL)\n      this.loadExternalText();\n    else\n      this.buildOptionList();\n  },\n\n  loadExternalText: function() {\n    this.showLoadingText(this.options.loadingText);\n    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n    Object.extend(options, {\n      parameters: 'editorId=' + encodeURIComponent(this.element.id),\n      onComplete: Prototype.emptyFunction,\n      onSuccess: function(transport) {\n        this._text = transport.responseText.strip();\n        this.buildOptionList();\n      }.bind(this),\n      onFailure: this.onFailure\n    });\n    new Ajax.Request(this.options.loadTextURL, options);\n  },\n\n  buildOptionList: function() {\n    this._form.removeClassName(this.options.loadingClassName);\n    this._collection = this._collection.map(function(entry) {\n      return 2 === entry.length ? entry : [entry, entry].flatten();\n    });\n    var marker = ('value' in this.options) ? this.options.value : this._text;\n    var textFound = this._collection.any(function(entry) {\n      return entry[0] == marker;\n    }.bind(this));\n    this._controls.editor.update('');\n    var option;\n    this._collection.each(function(entry, index) {\n      option = document.createElement('option');\n      option.value = entry[0];\n      option.selected = textFound ? entry[0] == marker : 0 == index;\n      option.appendChild(document.createTextNode(entry[1]));\n      this._controls.editor.appendChild(option);\n    }.bind(this));\n    this._controls.editor.disabled = false;\n    Field.scrollFreeActivate(this._controls.editor);\n  }\n});\n\n//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****\n//**** This only  exists for a while,  in order to  let ****\n//**** users adapt to  the new API.  Read up on the new ****\n//**** API and convert your code to it ASAP!            ****\n\nAjax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {\n  if (!options) return;\n  function fallback(name, expr) {\n    if (name in options || expr === undefined) return;\n    options[name] = expr;\n  };\n  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :\n    options.cancelLink == options.cancelButton == false ? false : undefined)));\n  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :\n    options.okLink == options.okButton == false ? false : undefined)));\n  fallback('highlightColor', options.highlightcolor);\n  fallback('highlightEndColor', options.highlightendcolor);\n};\n\nObject.extend(Ajax.InPlaceEditor, {\n  DefaultOptions: {\n    ajaxOptions: { },\n    autoRows: 3,                                // Use when multi-line w/ rows == 1\n    cancelControl: 'link',                      // 'link'|'button'|false\n    cancelText: 'cancel',\n    clickToEditText: 'Click to edit',\n    externalControl: null,                      // id|elt\n    externalControlOnly: false,\n    fieldPostCreation: 'activate',              // 'activate'|'focus'|false\n    formClassName: 'inplaceeditor-form',\n    formId: null,                               // id|elt\n    highlightColor: '#ffff99',\n    highlightEndColor: '#ffffff',\n    hoverClassName: '',\n    htmlResponse: true,\n    loadingClassName: 'inplaceeditor-loading',\n    loadingText: 'Loading...',\n    okControl: 'button',                        // 'link'|'button'|false\n    okText: 'ok',\n    paramName: 'value',\n    rows: 1,                                    // If 1 and multi-line, uses autoRows\n    savingClassName: 'inplaceeditor-saving',\n    savingText: 'Saving...',\n    size: 0,\n    stripLoadedTextTags: false,\n    submitOnBlur: false,\n    textAfterControls: '',\n    textBeforeControls: '',\n    textBetweenControls: ''\n  },\n  DefaultCallbacks: {\n    callback: function(form) {\n      return Form.serialize(form);\n    },\n    onComplete: function(transport, element) {\n      // For backward compatibility, this one is bound to the IPE, and passes\n      // the element directly.  It was too often customized, so we don't break it.\n      new Effect.Highlight(element, {\n        startcolor: this.options.highlightColor, keepBackgroundImage: true });\n    },\n    onEnterEditMode: null,\n    onEnterHover: function(ipe) {\n      ipe.element.style.backgroundColor = ipe.options.highlightColor;\n      if (ipe._effect)\n        ipe._effect.cancel();\n    },\n    onFailure: function(transport, ipe) {\n      alert('Error communication with the server: ' + transport.responseText.stripTags());\n    },\n    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.\n    onLeaveEditMode: null,\n    onLeaveHover: function(ipe) {\n      ipe._effect = new Effect.Highlight(ipe.element, {\n        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,\n        restorecolor: ipe._originalBackground, keepBackgroundImage: true\n      });\n    }\n  },\n  Listeners: {\n    click: 'enterEditMode',\n    keydown: 'checkForEscapeOrReturn',\n    mouseover: 'enterHover',\n    mouseout: 'leaveHover'\n  }\n});\n\nAjax.InPlaceCollectionEditor.DefaultOptions = {\n  loadingCollectionText: 'Loading options...'\n};\n\n// Delayed observer, like Form.Element.Observer,\n// but waits for delay after last key input\n// Ideal for live-search fields\n\nForm.Element.DelayedObserver = Class.create({\n  initialize: function(element, delay, callback) {\n    this.delay     = delay || 0.5;\n    this.element   = $(element);\n    this.callback  = callback;\n    this.timer     = null;\n    this.lastValue = $F(this.element);\n    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));\n  },\n  delayedListener: function(event) {\n    if(this.lastValue == $F(this.element)) return;\n    if(this.timer) clearTimeout(this.timer);\n    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);\n    this.lastValue = $F(this.element);\n  },\n  onTimerEvent: function() {\n    this.timer = null;\n    this.callback(this.element, $F(this.element));\n  }\n});"
  },
  {
    "path": "test/apps/rails2/public/javascripts/dragdrop.js",
    "content": "// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n//           (c) 2005-2008 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\nif(Object.isUndefined(Effect))\n  throw(\"dragdrop.js requires including script.aculo.us' effects.js library\");\n\nvar Droppables = {\n  drops: [],\n\n  remove: function(element) {\n    this.drops = this.drops.reject(function(d) { return d.element==$(element) });\n  },\n\n  add: function(element) {\n    element = $(element);\n    var options = Object.extend({\n      greedy:     true,\n      hoverclass: null,\n      tree:       false\n    }, arguments[1] || { });\n\n    // cache containers\n    if(options.containment) {\n      options._containers = [];\n      var containment = options.containment;\n      if(Object.isArray(containment)) {\n        containment.each( function(c) { options._containers.push($(c)) });\n      } else {\n        options._containers.push($(containment));\n      }\n    }\n\n    if(options.accept) options.accept = [options.accept].flatten();\n\n    Element.makePositioned(element); // fix IE\n    options.element = element;\n\n    this.drops.push(options);\n  },\n\n  findDeepestChild: function(drops) {\n    deepest = drops[0];\n\n    for (i = 1; i < drops.length; ++i)\n      if (Element.isParent(drops[i].element, deepest.element))\n        deepest = drops[i];\n\n    return deepest;\n  },\n\n  isContained: function(element, drop) {\n    var containmentNode;\n    if(drop.tree) {\n      containmentNode = element.treeNode;\n    } else {\n      containmentNode = element.parentNode;\n    }\n    return drop._containers.detect(function(c) { return containmentNode == c });\n  },\n\n  isAffected: function(point, element, drop) {\n    return (\n      (drop.element!=element) &&\n      ((!drop._containers) ||\n        this.isContained(element, drop)) &&\n      ((!drop.accept) ||\n        (Element.classNames(element).detect(\n          function(v) { return drop.accept.include(v) } ) )) &&\n      Position.within(drop.element, point[0], point[1]) );\n  },\n\n  deactivate: function(drop) {\n    if(drop.hoverclass)\n      Element.removeClassName(drop.element, drop.hoverclass);\n    this.last_active = null;\n  },\n\n  activate: function(drop) {\n    if(drop.hoverclass)\n      Element.addClassName(drop.element, drop.hoverclass);\n    this.last_active = drop;\n  },\n\n  show: function(point, element) {\n    if(!this.drops.length) return;\n    var drop, affected = [];\n\n    this.drops.each( function(drop) {\n      if(Droppables.isAffected(point, element, drop))\n        affected.push(drop);\n    });\n\n    if(affected.length>0)\n      drop = Droppables.findDeepestChild(affected);\n\n    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);\n    if (drop) {\n      Position.within(drop.element, point[0], point[1]);\n      if(drop.onHover)\n        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));\n\n      if (drop != this.last_active) Droppables.activate(drop);\n    }\n  },\n\n  fire: function(event, element) {\n    if(!this.last_active) return;\n    Position.prepare();\n\n    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))\n      if (this.last_active.onDrop) {\n        this.last_active.onDrop(element, this.last_active.element, event);\n        return true;\n      }\n  },\n\n  reset: function() {\n    if(this.last_active)\n      this.deactivate(this.last_active);\n  }\n};\n\nvar Draggables = {\n  drags: [],\n  observers: [],\n\n  register: function(draggable) {\n    if(this.drags.length == 0) {\n      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);\n      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);\n      this.eventKeypress  = this.keyPress.bindAsEventListener(this);\n\n      Event.observe(document, \"mouseup\", this.eventMouseUp);\n      Event.observe(document, \"mousemove\", this.eventMouseMove);\n      Event.observe(document, \"keypress\", this.eventKeypress);\n    }\n    this.drags.push(draggable);\n  },\n\n  unregister: function(draggable) {\n    this.drags = this.drags.reject(function(d) { return d==draggable });\n    if(this.drags.length == 0) {\n      Event.stopObserving(document, \"mouseup\", this.eventMouseUp);\n      Event.stopObserving(document, \"mousemove\", this.eventMouseMove);\n      Event.stopObserving(document, \"keypress\", this.eventKeypress);\n    }\n  },\n\n  activate: function(draggable) {\n    if(draggable.options.delay) {\n      this._timeout = setTimeout(function() {\n        Draggables._timeout = null;\n        window.focus();\n        Draggables.activeDraggable = draggable;\n      }.bind(this), draggable.options.delay);\n    } else {\n      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari\n      this.activeDraggable = draggable;\n    }\n  },\n\n  deactivate: function() {\n    this.activeDraggable = null;\n  },\n\n  updateDrag: function(event) {\n    if(!this.activeDraggable) return;\n    var pointer = [Event.pointerX(event), Event.pointerY(event)];\n    // Mozilla-based browsers fire successive mousemove events with\n    // the same coordinates, prevent needless redrawing (moz bug?)\n    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;\n    this._lastPointer = pointer;\n\n    this.activeDraggable.updateDrag(event, pointer);\n  },\n\n  endDrag: function(event) {\n    if(this._timeout) {\n      clearTimeout(this._timeout);\n      this._timeout = null;\n    }\n    if(!this.activeDraggable) return;\n    this._lastPointer = null;\n    this.activeDraggable.endDrag(event);\n    this.activeDraggable = null;\n  },\n\n  keyPress: function(event) {\n    if(this.activeDraggable)\n      this.activeDraggable.keyPress(event);\n  },\n\n  addObserver: function(observer) {\n    this.observers.push(observer);\n    this._cacheObserverCallbacks();\n  },\n\n  removeObserver: function(element) {  // element instead of observer fixes mem leaks\n    this.observers = this.observers.reject( function(o) { return o.element==element });\n    this._cacheObserverCallbacks();\n  },\n\n  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'\n    if(this[eventName+'Count'] > 0)\n      this.observers.each( function(o) {\n        if(o[eventName]) o[eventName](eventName, draggable, event);\n      });\n    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);\n  },\n\n  _cacheObserverCallbacks: function() {\n    ['onStart','onEnd','onDrag'].each( function(eventName) {\n      Draggables[eventName+'Count'] = Draggables.observers.select(\n        function(o) { return o[eventName]; }\n      ).length;\n    });\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Draggable = Class.create({\n  initialize: function(element) {\n    var defaults = {\n      handle: false,\n      reverteffect: function(element, top_offset, left_offset) {\n        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;\n        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,\n          queue: {scope:'_draggable', position:'end'}\n        });\n      },\n      endeffect: function(element) {\n        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;\n        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,\n          queue: {scope:'_draggable', position:'end'},\n          afterFinish: function(){\n            Draggable._dragging[element] = false\n          }\n        });\n      },\n      zindex: 1000,\n      revert: false,\n      quiet: false,\n      scroll: false,\n      scrollSensitivity: 20,\n      scrollSpeed: 15,\n      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }\n      delay: 0\n    };\n\n    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))\n      Object.extend(defaults, {\n        starteffect: function(element) {\n          element._opacity = Element.getOpacity(element);\n          Draggable._dragging[element] = true;\n          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});\n        }\n      });\n\n    var options = Object.extend(defaults, arguments[1] || { });\n\n    this.element = $(element);\n\n    if(options.handle && Object.isString(options.handle))\n      this.handle = this.element.down('.'+options.handle, 0);\n\n    if(!this.handle) this.handle = $(options.handle);\n    if(!this.handle) this.handle = this.element;\n\n    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {\n      options.scroll = $(options.scroll);\n      this._isScrollChild = Element.childOf(this.element, options.scroll);\n    }\n\n    Element.makePositioned(this.element); // fix IE\n\n    this.options  = options;\n    this.dragging = false;\n\n    this.eventMouseDown = this.initDrag.bindAsEventListener(this);\n    Event.observe(this.handle, \"mousedown\", this.eventMouseDown);\n\n    Draggables.register(this);\n  },\n\n  destroy: function() {\n    Event.stopObserving(this.handle, \"mousedown\", this.eventMouseDown);\n    Draggables.unregister(this);\n  },\n\n  currentDelta: function() {\n    return([\n      parseInt(Element.getStyle(this.element,'left') || '0'),\n      parseInt(Element.getStyle(this.element,'top') || '0')]);\n  },\n\n  initDrag: function(event) {\n    if(!Object.isUndefined(Draggable._dragging[this.element]) &&\n      Draggable._dragging[this.element]) return;\n    if(Event.isLeftClick(event)) {\n      // abort on form elements, fixes a Firefox issue\n      var src = Event.element(event);\n      if((tag_name = src.tagName.toUpperCase()) && (\n        tag_name=='INPUT' ||\n        tag_name=='SELECT' ||\n        tag_name=='OPTION' ||\n        tag_name=='BUTTON' ||\n        tag_name=='TEXTAREA')) return;\n\n      var pointer = [Event.pointerX(event), Event.pointerY(event)];\n      var pos     = Position.cumulativeOffset(this.element);\n      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });\n\n      Draggables.activate(this);\n      Event.stop(event);\n    }\n  },\n\n  startDrag: function(event) {\n    this.dragging = true;\n    if(!this.delta)\n      this.delta = this.currentDelta();\n\n    if(this.options.zindex) {\n      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);\n      this.element.style.zIndex = this.options.zindex;\n    }\n\n    if(this.options.ghosting) {\n      this._clone = this.element.cloneNode(true);\n      this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');\n      if (!this._originallyAbsolute)\n        Position.absolutize(this.element);\n      this.element.parentNode.insertBefore(this._clone, this.element);\n    }\n\n    if(this.options.scroll) {\n      if (this.options.scroll == window) {\n        var where = this._getWindowScroll(this.options.scroll);\n        this.originalScrollLeft = where.left;\n        this.originalScrollTop = where.top;\n      } else {\n        this.originalScrollLeft = this.options.scroll.scrollLeft;\n        this.originalScrollTop = this.options.scroll.scrollTop;\n      }\n    }\n\n    Draggables.notify('onStart', this, event);\n\n    if(this.options.starteffect) this.options.starteffect(this.element);\n  },\n\n  updateDrag: function(event, pointer) {\n    if(!this.dragging) this.startDrag(event);\n\n    if(!this.options.quiet){\n      Position.prepare();\n      Droppables.show(pointer, this.element);\n    }\n\n    Draggables.notify('onDrag', this, event);\n\n    this.draw(pointer);\n    if(this.options.change) this.options.change(this);\n\n    if(this.options.scroll) {\n      this.stopScrolling();\n\n      var p;\n      if (this.options.scroll == window) {\n        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }\n      } else {\n        p = Position.page(this.options.scroll);\n        p[0] += this.options.scroll.scrollLeft + Position.deltaX;\n        p[1] += this.options.scroll.scrollTop + Position.deltaY;\n        p.push(p[0]+this.options.scroll.offsetWidth);\n        p.push(p[1]+this.options.scroll.offsetHeight);\n      }\n      var speed = [0,0];\n      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);\n      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);\n      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);\n      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);\n      this.startScrolling(speed);\n    }\n\n    // fix AppleWebKit rendering\n    if(Prototype.Browser.WebKit) window.scrollBy(0,0);\n\n    Event.stop(event);\n  },\n\n  finishDrag: function(event, success) {\n    this.dragging = false;\n\n    if(this.options.quiet){\n      Position.prepare();\n      var pointer = [Event.pointerX(event), Event.pointerY(event)];\n      Droppables.show(pointer, this.element);\n    }\n\n    if(this.options.ghosting) {\n      if (!this._originallyAbsolute)\n        Position.relativize(this.element);\n      delete this._originallyAbsolute;\n      Element.remove(this._clone);\n      this._clone = null;\n    }\n\n    var dropped = false;\n    if(success) {\n      dropped = Droppables.fire(event, this.element);\n      if (!dropped) dropped = false;\n    }\n    if(dropped && this.options.onDropped) this.options.onDropped(this.element);\n    Draggables.notify('onEnd', this, event);\n\n    var revert = this.options.revert;\n    if(revert && Object.isFunction(revert)) revert = revert(this.element);\n\n    var d = this.currentDelta();\n    if(revert && this.options.reverteffect) {\n      if (dropped == 0 || revert != 'failure')\n        this.options.reverteffect(this.element,\n          d[1]-this.delta[1], d[0]-this.delta[0]);\n    } else {\n      this.delta = d;\n    }\n\n    if(this.options.zindex)\n      this.element.style.zIndex = this.originalZ;\n\n    if(this.options.endeffect)\n      this.options.endeffect(this.element);\n\n    Draggables.deactivate(this);\n    Droppables.reset();\n  },\n\n  keyPress: function(event) {\n    if(event.keyCode!=Event.KEY_ESC) return;\n    this.finishDrag(event, false);\n    Event.stop(event);\n  },\n\n  endDrag: function(event) {\n    if(!this.dragging) return;\n    this.stopScrolling();\n    this.finishDrag(event, true);\n    Event.stop(event);\n  },\n\n  draw: function(point) {\n    var pos = Position.cumulativeOffset(this.element);\n    if(this.options.ghosting) {\n      var r   = Position.realOffset(this.element);\n      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;\n    }\n\n    var d = this.currentDelta();\n    pos[0] -= d[0]; pos[1] -= d[1];\n\n    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {\n      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;\n      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;\n    }\n\n    var p = [0,1].map(function(i){\n      return (point[i]-pos[i]-this.offset[i])\n    }.bind(this));\n\n    if(this.options.snap) {\n      if(Object.isFunction(this.options.snap)) {\n        p = this.options.snap(p[0],p[1],this);\n      } else {\n      if(Object.isArray(this.options.snap)) {\n        p = p.map( function(v, i) {\n          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));\n      } else {\n        p = p.map( function(v) {\n          return (v/this.options.snap).round()*this.options.snap }.bind(this));\n      }\n    }}\n\n    var style = this.element.style;\n    if((!this.options.constraint) || (this.options.constraint=='horizontal'))\n      style.left = p[0] + \"px\";\n    if((!this.options.constraint) || (this.options.constraint=='vertical'))\n      style.top  = p[1] + \"px\";\n\n    if(style.visibility==\"hidden\") style.visibility = \"\"; // fix gecko rendering\n  },\n\n  stopScrolling: function() {\n    if(this.scrollInterval) {\n      clearInterval(this.scrollInterval);\n      this.scrollInterval = null;\n      Draggables._lastScrollPointer = null;\n    }\n  },\n\n  startScrolling: function(speed) {\n    if(!(speed[0] || speed[1])) return;\n    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];\n    this.lastScrolled = new Date();\n    this.scrollInterval = setInterval(this.scroll.bind(this), 10);\n  },\n\n  scroll: function() {\n    var current = new Date();\n    var delta = current - this.lastScrolled;\n    this.lastScrolled = current;\n    if(this.options.scroll == window) {\n      with (this._getWindowScroll(this.options.scroll)) {\n        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {\n          var d = delta / 1000;\n          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );\n        }\n      }\n    } else {\n      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;\n      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;\n    }\n\n    Position.prepare();\n    Droppables.show(Draggables._lastPointer, this.element);\n    Draggables.notify('onDrag', this);\n    if (this._isScrollChild) {\n      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);\n      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;\n      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;\n      if (Draggables._lastScrollPointer[0] < 0)\n        Draggables._lastScrollPointer[0] = 0;\n      if (Draggables._lastScrollPointer[1] < 0)\n        Draggables._lastScrollPointer[1] = 0;\n      this.draw(Draggables._lastScrollPointer);\n    }\n\n    if(this.options.change) this.options.change(this);\n  },\n\n  _getWindowScroll: function(w) {\n    var T, L, W, H;\n    with (w.document) {\n      if (w.document.documentElement && documentElement.scrollTop) {\n        T = documentElement.scrollTop;\n        L = documentElement.scrollLeft;\n      } else if (w.document.body) {\n        T = body.scrollTop;\n        L = body.scrollLeft;\n      }\n      if (w.innerWidth) {\n        W = w.innerWidth;\n        H = w.innerHeight;\n      } else if (w.document.documentElement && documentElement.clientWidth) {\n        W = documentElement.clientWidth;\n        H = documentElement.clientHeight;\n      } else {\n        W = body.offsetWidth;\n        H = body.offsetHeight;\n      }\n    }\n    return { top: T, left: L, width: W, height: H };\n  }\n});\n\nDraggable._dragging = { };\n\n/*--------------------------------------------------------------------------*/\n\nvar SortableObserver = Class.create({\n  initialize: function(element, observer) {\n    this.element   = $(element);\n    this.observer  = observer;\n    this.lastValue = Sortable.serialize(this.element);\n  },\n\n  onStart: function() {\n    this.lastValue = Sortable.serialize(this.element);\n  },\n\n  onEnd: function() {\n    Sortable.unmark();\n    if(this.lastValue != Sortable.serialize(this.element))\n      this.observer(this.element)\n  }\n});\n\nvar Sortable = {\n  SERIALIZE_RULE: /^[^_\\-](?:[A-Za-z0-9\\-\\_]*)[_](.*)$/,\n\n  sortables: { },\n\n  _findRootElement: function(element) {\n    while (element.tagName.toUpperCase() != \"BODY\") {\n      if(element.id && Sortable.sortables[element.id]) return element;\n      element = element.parentNode;\n    }\n  },\n\n  options: function(element) {\n    element = Sortable._findRootElement($(element));\n    if(!element) return;\n    return Sortable.sortables[element.id];\n  },\n\n  destroy: function(element){\n    element = $(element);\n    var s = Sortable.sortables[element.id];\n\n    if(s) {\n      Draggables.removeObserver(s.element);\n      s.droppables.each(function(d){ Droppables.remove(d) });\n      s.draggables.invoke('destroy');\n\n      delete Sortable.sortables[s.element.id];\n    }\n  },\n\n  create: function(element) {\n    element = $(element);\n    var options = Object.extend({\n      element:     element,\n      tag:         'li',       // assumes li children, override with tag: 'tagname'\n      dropOnEmpty: false,\n      tree:        false,\n      treeTag:     'ul',\n      overlap:     'vertical', // one of 'vertical', 'horizontal'\n      constraint:  'vertical', // one of 'vertical', 'horizontal', false\n      containment: element,    // also takes array of elements (or id's); or false\n      handle:      false,      // or a CSS class\n      only:        false,\n      delay:       0,\n      hoverclass:  null,\n      ghosting:    false,\n      quiet:       false,\n      scroll:      false,\n      scrollSensitivity: 20,\n      scrollSpeed: 15,\n      format:      this.SERIALIZE_RULE,\n\n      // these take arrays of elements or ids and can be\n      // used for better initialization performance\n      elements:    false,\n      handles:     false,\n\n      onChange:    Prototype.emptyFunction,\n      onUpdate:    Prototype.emptyFunction\n    }, arguments[1] || { });\n\n    // clear any old sortable with same element\n    this.destroy(element);\n\n    // build options for the draggables\n    var options_for_draggable = {\n      revert:      true,\n      quiet:       options.quiet,\n      scroll:      options.scroll,\n      scrollSpeed: options.scrollSpeed,\n      scrollSensitivity: options.scrollSensitivity,\n      delay:       options.delay,\n      ghosting:    options.ghosting,\n      constraint:  options.constraint,\n      handle:      options.handle };\n\n    if(options.starteffect)\n      options_for_draggable.starteffect = options.starteffect;\n\n    if(options.reverteffect)\n      options_for_draggable.reverteffect = options.reverteffect;\n    else\n      if(options.ghosting) options_for_draggable.reverteffect = function(element) {\n        element.style.top  = 0;\n        element.style.left = 0;\n      };\n\n    if(options.endeffect)\n      options_for_draggable.endeffect = options.endeffect;\n\n    if(options.zindex)\n      options_for_draggable.zindex = options.zindex;\n\n    // build options for the droppables\n    var options_for_droppable = {\n      overlap:     options.overlap,\n      containment: options.containment,\n      tree:        options.tree,\n      hoverclass:  options.hoverclass,\n      onHover:     Sortable.onHover\n    };\n\n    var options_for_tree = {\n      onHover:      Sortable.onEmptyHover,\n      overlap:      options.overlap,\n      containment:  options.containment,\n      hoverclass:   options.hoverclass\n    };\n\n    // fix for gecko engine\n    Element.cleanWhitespace(element);\n\n    options.draggables = [];\n    options.droppables = [];\n\n    // drop on empty handling\n    if(options.dropOnEmpty || options.tree) {\n      Droppables.add(element, options_for_tree);\n      options.droppables.push(element);\n    }\n\n    (options.elements || this.findElements(element, options) || []).each( function(e,i) {\n      var handle = options.handles ? $(options.handles[i]) :\n        (options.handle ? $(e).select('.' + options.handle)[0] : e);\n      options.draggables.push(\n        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));\n      Droppables.add(e, options_for_droppable);\n      if(options.tree) e.treeNode = element;\n      options.droppables.push(e);\n    });\n\n    if(options.tree) {\n      (Sortable.findTreeElements(element, options) || []).each( function(e) {\n        Droppables.add(e, options_for_tree);\n        e.treeNode = element;\n        options.droppables.push(e);\n      });\n    }\n\n    // keep reference\n    this.sortables[element.id] = options;\n\n    // for onupdate\n    Draggables.addObserver(new SortableObserver(element, options.onUpdate));\n\n  },\n\n  // return all suitable-for-sortable elements in a guaranteed order\n  findElements: function(element, options) {\n    return Element.findChildren(\n      element, options.only, options.tree ? true : false, options.tag);\n  },\n\n  findTreeElements: function(element, options) {\n    return Element.findChildren(\n      element, options.only, options.tree ? true : false, options.treeTag);\n  },\n\n  onHover: function(element, dropon, overlap) {\n    if(Element.isParent(dropon, element)) return;\n\n    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {\n      return;\n    } else if(overlap>0.5) {\n      Sortable.mark(dropon, 'before');\n      if(dropon.previousSibling != element) {\n        var oldParentNode = element.parentNode;\n        element.style.visibility = \"hidden\"; // fix gecko rendering\n        dropon.parentNode.insertBefore(element, dropon);\n        if(dropon.parentNode!=oldParentNode)\n          Sortable.options(oldParentNode).onChange(element);\n        Sortable.options(dropon.parentNode).onChange(element);\n      }\n    } else {\n      Sortable.mark(dropon, 'after');\n      var nextElement = dropon.nextSibling || null;\n      if(nextElement != element) {\n        var oldParentNode = element.parentNode;\n        element.style.visibility = \"hidden\"; // fix gecko rendering\n        dropon.parentNode.insertBefore(element, nextElement);\n        if(dropon.parentNode!=oldParentNode)\n          Sortable.options(oldParentNode).onChange(element);\n        Sortable.options(dropon.parentNode).onChange(element);\n      }\n    }\n  },\n\n  onEmptyHover: function(element, dropon, overlap) {\n    var oldParentNode = element.parentNode;\n    var droponOptions = Sortable.options(dropon);\n\n    if(!Element.isParent(dropon, element)) {\n      var index;\n\n      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});\n      var child = null;\n\n      if(children) {\n        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);\n\n        for (index = 0; index < children.length; index += 1) {\n          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {\n            offset -= Element.offsetSize (children[index], droponOptions.overlap);\n          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {\n            child = index + 1 < children.length ? children[index + 1] : null;\n            break;\n          } else {\n            child = children[index];\n            break;\n          }\n        }\n      }\n\n      dropon.insertBefore(element, child);\n\n      Sortable.options(oldParentNode).onChange(element);\n      droponOptions.onChange(element);\n    }\n  },\n\n  unmark: function() {\n    if(Sortable._marker) Sortable._marker.hide();\n  },\n\n  mark: function(dropon, position) {\n    // mark on ghosting only\n    var sortable = Sortable.options(dropon.parentNode);\n    if(sortable && !sortable.ghosting) return;\n\n    if(!Sortable._marker) {\n      Sortable._marker =\n        ($('dropmarker') || Element.extend(document.createElement('DIV'))).\n          hide().addClassName('dropmarker').setStyle({position:'absolute'});\n      document.getElementsByTagName(\"body\").item(0).appendChild(Sortable._marker);\n    }\n    var offsets = Position.cumulativeOffset(dropon);\n    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});\n\n    if(position=='after')\n      if(sortable.overlap == 'horizontal')\n        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});\n      else\n        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});\n\n    Sortable._marker.show();\n  },\n\n  _tree: function(element, options, parent) {\n    var children = Sortable.findElements(element, options) || [];\n\n    for (var i = 0; i < children.length; ++i) {\n      var match = children[i].id.match(options.format);\n\n      if (!match) continue;\n\n      var child = {\n        id: encodeURIComponent(match ? match[1] : null),\n        element: element,\n        parent: parent,\n        children: [],\n        position: parent.children.length,\n        container: $(children[i]).down(options.treeTag)\n      };\n\n      /* Get the element containing the children and recurse over it */\n      if (child.container)\n        this._tree(child.container, options, child);\n\n      parent.children.push (child);\n    }\n\n    return parent;\n  },\n\n  tree: function(element) {\n    element = $(element);\n    var sortableOptions = this.options(element);\n    var options = Object.extend({\n      tag: sortableOptions.tag,\n      treeTag: sortableOptions.treeTag,\n      only: sortableOptions.only,\n      name: element.id,\n      format: sortableOptions.format\n    }, arguments[1] || { });\n\n    var root = {\n      id: null,\n      parent: null,\n      children: [],\n      container: element,\n      position: 0\n    };\n\n    return Sortable._tree(element, options, root);\n  },\n\n  /* Construct a [i] index for a particular node */\n  _constructIndex: function(node) {\n    var index = '';\n    do {\n      if (node.id) index = '[' + node.position + ']' + index;\n    } while ((node = node.parent) != null);\n    return index;\n  },\n\n  sequence: function(element) {\n    element = $(element);\n    var options = Object.extend(this.options(element), arguments[1] || { });\n\n    return $(this.findElements(element, options) || []).map( function(item) {\n      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';\n    });\n  },\n\n  setSequence: function(element, new_sequence) {\n    element = $(element);\n    var options = Object.extend(this.options(element), arguments[2] || { });\n\n    var nodeMap = { };\n    this.findElements(element, options).each( function(n) {\n        if (n.id.match(options.format))\n            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];\n        n.parentNode.removeChild(n);\n    });\n\n    new_sequence.each(function(ident) {\n      var n = nodeMap[ident];\n      if (n) {\n        n[1].appendChild(n[0]);\n        delete nodeMap[ident];\n      }\n    });\n  },\n\n  serialize: function(element) {\n    element = $(element);\n    var options = Object.extend(Sortable.options(element), arguments[1] || { });\n    var name = encodeURIComponent(\n      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);\n\n    if (options.tree) {\n      return Sortable.tree(element, arguments[1]).children.map( function (item) {\n        return [name + Sortable._constructIndex(item) + \"[id]=\" +\n                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));\n      }).flatten().join('&');\n    } else {\n      return Sortable.sequence(element, arguments[1]).map( function(item) {\n        return name + \"[]=\" + encodeURIComponent(item);\n      }).join('&');\n    }\n  }\n};\n\n// Returns true if child is contained within element\nElement.isParent = function(child, element) {\n  if (!child.parentNode || child == element) return false;\n  if (child.parentNode == element) return true;\n  return Element.isParent(child.parentNode, element);\n};\n\nElement.findChildren = function(element, only, recursive, tagName) {\n  if(!element.hasChildNodes()) return null;\n  tagName = tagName.toUpperCase();\n  if(only) only = [only].flatten();\n  var elements = [];\n  $A(element.childNodes).each( function(e) {\n    if(e.tagName && e.tagName.toUpperCase()==tagName &&\n      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))\n        elements.push(e);\n    if(recursive) {\n      var grandchildren = Element.findChildren(e, only, recursive, tagName);\n      if(grandchildren) elements.push(grandchildren);\n    }\n  });\n\n  return (elements.length>0 ? elements.flatten() : []);\n};\n\nElement.offsetSize = function (element, type) {\n  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];\n};"
  },
  {
    "path": "test/apps/rails2/public/javascripts/effects.js",
    "content": "// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n// Contributors:\n//  Justin Palmer (http://encytemedia.com/)\n//  Mark Pilgrim (http://diveintomark.org/)\n//  Martin Bialasinki\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\n// converts rgb() and #xxx to #xxxxxx format,\n// returns self (or first argument) if not convertable\nString.prototype.parseColor = function() {\n  var color = '#';\n  if (this.slice(0,4) == 'rgb(') {\n    var cols = this.slice(4,this.length-1).split(',');\n    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);\n  } else {\n    if (this.slice(0,1) == '#') {\n      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();\n      if (this.length==7) color = this.toLowerCase();\n    }\n  }\n  return (color.length==7 ? color : (arguments[0] || this));\n};\n\n/*--------------------------------------------------------------------------*/\n\nElement.collectTextNodes = function(element) {\n  return $A($(element).childNodes).collect( function(node) {\n    return (node.nodeType==3 ? node.nodeValue :\n      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));\n  }).flatten().join('');\n};\n\nElement.collectTextNodesIgnoreClass = function(element, className) {\n  return $A($(element).childNodes).collect( function(node) {\n    return (node.nodeType==3 ? node.nodeValue :\n      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?\n        Element.collectTextNodesIgnoreClass(node, className) : ''));\n  }).flatten().join('');\n};\n\nElement.setContentZoom = function(element, percent) {\n  element = $(element);\n  element.setStyle({fontSize: (percent/100) + 'em'});\n  if (Prototype.Browser.WebKit) window.scrollBy(0,0);\n  return element;\n};\n\nElement.getInlineOpacity = function(element){\n  return $(element).style.opacity || '';\n};\n\nElement.forceRerendering = function(element) {\n  try {\n    element = $(element);\n    var n = document.createTextNode(' ');\n    element.appendChild(n);\n    element.removeChild(n);\n  } catch(e) { }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Effect = {\n  _elementDoesNotExistError: {\n    name: 'ElementDoesNotExistError',\n    message: 'The specified DOM element does not exist, but is required for this effect to operate'\n  },\n  Transitions: {\n    linear: Prototype.K,\n    sinoidal: function(pos) {\n      return (-Math.cos(pos*Math.PI)/2) + .5;\n    },\n    reverse: function(pos) {\n      return 1-pos;\n    },\n    flicker: function(pos) {\n      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;\n      return pos > 1 ? 1 : pos;\n    },\n    wobble: function(pos) {\n      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;\n    },\n    pulse: function(pos, pulses) {\n      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;\n    },\n    spring: function(pos) {\n      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));\n    },\n    none: function(pos) {\n      return 0;\n    },\n    full: function(pos) {\n      return 1;\n    }\n  },\n  DefaultOptions: {\n    duration:   1.0,   // seconds\n    fps:        100,   // 100= assume 66fps max.\n    sync:       false, // true for combining\n    from:       0.0,\n    to:         1.0,\n    delay:      0.0,\n    queue:      'parallel'\n  },\n  tagifyText: function(element) {\n    var tagifyStyle = 'position:relative';\n    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';\n\n    element = $(element);\n    $A(element.childNodes).each( function(child) {\n      if (child.nodeType==3) {\n        child.nodeValue.toArray().each( function(character) {\n          element.insertBefore(\n            new Element('span', {style: tagifyStyle}).update(\n              character == ' ' ? String.fromCharCode(160) : character),\n              child);\n        });\n        Element.remove(child);\n      }\n    });\n  },\n  multiple: function(element, effect) {\n    var elements;\n    if (((typeof element == 'object') ||\n        Object.isFunction(element)) &&\n       (element.length))\n      elements = element;\n    else\n      elements = $(element).childNodes;\n\n    var options = Object.extend({\n      speed: 0.1,\n      delay: 0.0\n    }, arguments[2] || { });\n    var masterDelay = options.delay;\n\n    $A(elements).each( function(element, index) {\n      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));\n    });\n  },\n  PAIRS: {\n    'slide':  ['SlideDown','SlideUp'],\n    'blind':  ['BlindDown','BlindUp'],\n    'appear': ['Appear','Fade']\n  },\n  toggle: function(element, effect) {\n    element = $(element);\n    effect = (effect || 'appear').toLowerCase();\n    var options = Object.extend({\n      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }\n    }, arguments[2] || { });\n    Effect[element.visible() ?\n      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);\n  }\n};\n\nEffect.DefaultOptions.transition = Effect.Transitions.sinoidal;\n\n/* ------------- core effects ------------- */\n\nEffect.ScopedQueue = Class.create(Enumerable, {\n  initialize: function() {\n    this.effects  = [];\n    this.interval = null;\n  },\n  _each: function(iterator) {\n    this.effects._each(iterator);\n  },\n  add: function(effect) {\n    var timestamp = new Date().getTime();\n\n    var position = Object.isString(effect.options.queue) ?\n      effect.options.queue : effect.options.queue.position;\n\n    switch(position) {\n      case 'front':\n        // move unstarted effects after this effect\n        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {\n            e.startOn  += effect.finishOn;\n            e.finishOn += effect.finishOn;\n          });\n        break;\n      case 'with-last':\n        timestamp = this.effects.pluck('startOn').max() || timestamp;\n        break;\n      case 'end':\n        // start effect after last queued effect has finished\n        timestamp = this.effects.pluck('finishOn').max() || timestamp;\n        break;\n    }\n\n    effect.startOn  += timestamp;\n    effect.finishOn += timestamp;\n\n    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))\n      this.effects.push(effect);\n\n    if (!this.interval)\n      this.interval = setInterval(this.loop.bind(this), 15);\n  },\n  remove: function(effect) {\n    this.effects = this.effects.reject(function(e) { return e==effect });\n    if (this.effects.length == 0) {\n      clearInterval(this.interval);\n      this.interval = null;\n    }\n  },\n  loop: function() {\n    var timePos = new Date().getTime();\n    for(var i=0, len=this.effects.length;i<len;i++)\n      this.effects[i] && this.effects[i].loop(timePos);\n  }\n});\n\nEffect.Queues = {\n  instances: $H(),\n  get: function(queueName) {\n    if (!Object.isString(queueName)) return queueName;\n\n    return this.instances.get(queueName) ||\n      this.instances.set(queueName, new Effect.ScopedQueue());\n  }\n};\nEffect.Queue = Effect.Queues.get('global');\n\nEffect.Base = Class.create({\n  position: null,\n  start: function(options) {\n    function codeForEvent(options,eventName){\n      return (\n        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +\n        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')\n      );\n    }\n    if (options && options.transition === false) options.transition = Effect.Transitions.linear;\n    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });\n    this.currentFrame = 0;\n    this.state        = 'idle';\n    this.startOn      = this.options.delay*1000;\n    this.finishOn     = this.startOn+(this.options.duration*1000);\n    this.fromToDelta  = this.options.to-this.options.from;\n    this.totalTime    = this.finishOn-this.startOn;\n    this.totalFrames  = this.options.fps*this.options.duration;\n\n    this.render = (function() {\n      function dispatch(effect, eventName) {\n        if (effect.options[eventName + 'Internal'])\n          effect.options[eventName + 'Internal'](effect);\n        if (effect.options[eventName])\n          effect.options[eventName](effect);\n      }\n\n      return function(pos) {\n        if (this.state === \"idle\") {\n          this.state = \"running\";\n          dispatch(this, 'beforeSetup');\n          if (this.setup) this.setup();\n          dispatch(this, 'afterSetup');\n        }\n        if (this.state === \"running\") {\n          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;\n          this.position = pos;\n          dispatch(this, 'beforeUpdate');\n          if (this.update) this.update(pos);\n          dispatch(this, 'afterUpdate');\n        }\n      };\n    })();\n\n    this.event('beforeStart');\n    if (!this.options.sync)\n      Effect.Queues.get(Object.isString(this.options.queue) ?\n        'global' : this.options.queue.scope).add(this);\n  },\n  loop: function(timePos) {\n    if (timePos >= this.startOn) {\n      if (timePos >= this.finishOn) {\n        this.render(1.0);\n        this.cancel();\n        this.event('beforeFinish');\n        if (this.finish) this.finish();\n        this.event('afterFinish');\n        return;\n      }\n      var pos   = (timePos - this.startOn) / this.totalTime,\n          frame = (pos * this.totalFrames).round();\n      if (frame > this.currentFrame) {\n        this.render(pos);\n        this.currentFrame = frame;\n      }\n    }\n  },\n  cancel: function() {\n    if (!this.options.sync)\n      Effect.Queues.get(Object.isString(this.options.queue) ?\n        'global' : this.options.queue.scope).remove(this);\n    this.state = 'finished';\n  },\n  event: function(eventName) {\n    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);\n    if (this.options[eventName]) this.options[eventName](this);\n  },\n  inspect: function() {\n    var data = $H();\n    for(property in this)\n      if (!Object.isFunction(this[property])) data.set(property, this[property]);\n    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';\n  }\n});\n\nEffect.Parallel = Class.create(Effect.Base, {\n  initialize: function(effects) {\n    this.effects = effects || [];\n    this.start(arguments[1]);\n  },\n  update: function(position) {\n    this.effects.invoke('render', position);\n  },\n  finish: function(position) {\n    this.effects.each( function(effect) {\n      effect.render(1.0);\n      effect.cancel();\n      effect.event('beforeFinish');\n      if (effect.finish) effect.finish(position);\n      effect.event('afterFinish');\n    });\n  }\n});\n\nEffect.Tween = Class.create(Effect.Base, {\n  initialize: function(object, from, to) {\n    object = Object.isString(object) ? $(object) : object;\n    var args = $A(arguments), method = args.last(),\n      options = args.length == 5 ? args[3] : null;\n    this.method = Object.isFunction(method) ? method.bind(object) :\n      Object.isFunction(object[method]) ? object[method].bind(object) :\n      function(value) { object[method] = value };\n    this.start(Object.extend({ from: from, to: to }, options || { }));\n  },\n  update: function(position) {\n    this.method(position);\n  }\n});\n\nEffect.Event = Class.create(Effect.Base, {\n  initialize: function() {\n    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));\n  },\n  update: Prototype.emptyFunction\n});\n\nEffect.Opacity = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    // make this work on IE on elements without 'layout'\n    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))\n      this.element.setStyle({zoom: 1});\n    var options = Object.extend({\n      from: this.element.getOpacity() || 0.0,\n      to:   1.0\n    }, arguments[1] || { });\n    this.start(options);\n  },\n  update: function(position) {\n    this.element.setOpacity(position);\n  }\n});\n\nEffect.Move = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({\n      x:    0,\n      y:    0,\n      mode: 'relative'\n    }, arguments[1] || { });\n    this.start(options);\n  },\n  setup: function() {\n    this.element.makePositioned();\n    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');\n    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');\n    if (this.options.mode == 'absolute') {\n      this.options.x = this.options.x - this.originalLeft;\n      this.options.y = this.options.y - this.originalTop;\n    }\n  },\n  update: function(position) {\n    this.element.setStyle({\n      left: (this.options.x  * position + this.originalLeft).round() + 'px',\n      top:  (this.options.y  * position + this.originalTop).round()  + 'px'\n    });\n  }\n});\n\n// for backwards compatibility\nEffect.MoveBy = function(element, toTop, toLeft) {\n  return new Effect.Move(element,\n    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));\n};\n\nEffect.Scale = Class.create(Effect.Base, {\n  initialize: function(element, percent) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({\n      scaleX: true,\n      scaleY: true,\n      scaleContent: true,\n      scaleFromCenter: false,\n      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values\n      scaleFrom: 100.0,\n      scaleTo:   percent\n    }, arguments[2] || { });\n    this.start(options);\n  },\n  setup: function() {\n    this.restoreAfterFinish = this.options.restoreAfterFinish || false;\n    this.elementPositioning = this.element.getStyle('position');\n\n    this.originalStyle = { };\n    ['top','left','width','height','fontSize'].each( function(k) {\n      this.originalStyle[k] = this.element.style[k];\n    }.bind(this));\n\n    this.originalTop  = this.element.offsetTop;\n    this.originalLeft = this.element.offsetLeft;\n\n    var fontSize = this.element.getStyle('font-size') || '100%';\n    ['em','px','%','pt'].each( function(fontSizeType) {\n      if (fontSize.indexOf(fontSizeType)>0) {\n        this.fontSize     = parseFloat(fontSize);\n        this.fontSizeType = fontSizeType;\n      }\n    }.bind(this));\n\n    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;\n\n    this.dims = null;\n    if (this.options.scaleMode=='box')\n      this.dims = [this.element.offsetHeight, this.element.offsetWidth];\n    if (/^content/.test(this.options.scaleMode))\n      this.dims = [this.element.scrollHeight, this.element.scrollWidth];\n    if (!this.dims)\n      this.dims = [this.options.scaleMode.originalHeight,\n                   this.options.scaleMode.originalWidth];\n  },\n  update: function(position) {\n    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);\n    if (this.options.scaleContent && this.fontSize)\n      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });\n    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);\n  },\n  finish: function(position) {\n    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);\n  },\n  setDimensions: function(height, width) {\n    var d = { };\n    if (this.options.scaleX) d.width = width.round() + 'px';\n    if (this.options.scaleY) d.height = height.round() + 'px';\n    if (this.options.scaleFromCenter) {\n      var topd  = (height - this.dims[0])/2;\n      var leftd = (width  - this.dims[1])/2;\n      if (this.elementPositioning == 'absolute') {\n        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';\n        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';\n      } else {\n        if (this.options.scaleY) d.top = -topd + 'px';\n        if (this.options.scaleX) d.left = -leftd + 'px';\n      }\n    }\n    this.element.setStyle(d);\n  }\n});\n\nEffect.Highlight = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });\n    this.start(options);\n  },\n  setup: function() {\n    // Prevent executing on elements not in the layout flow\n    if (this.element.getStyle('display')=='none') { this.cancel(); return; }\n    // Disable background image during the effect\n    this.oldStyle = { };\n    if (!this.options.keepBackgroundImage) {\n      this.oldStyle.backgroundImage = this.element.getStyle('background-image');\n      this.element.setStyle({backgroundImage: 'none'});\n    }\n    if (!this.options.endcolor)\n      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');\n    if (!this.options.restorecolor)\n      this.options.restorecolor = this.element.getStyle('background-color');\n    // init color calculations\n    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));\n    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));\n  },\n  update: function(position) {\n    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){\n      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });\n  },\n  finish: function() {\n    this.element.setStyle(Object.extend(this.oldStyle, {\n      backgroundColor: this.options.restorecolor\n    }));\n  }\n});\n\nEffect.ScrollTo = function(element) {\n  var options = arguments[1] || { },\n  scrollOffsets = document.viewport.getScrollOffsets(),\n  elementOffsets = $(element).cumulativeOffset();\n\n  if (options.offset) elementOffsets[1] += options.offset;\n\n  return new Effect.Tween(null,\n    scrollOffsets.top,\n    elementOffsets[1],\n    options,\n    function(p){ scrollTo(scrollOffsets.left, p.round()); }\n  );\n};\n\n/* ------------- combination effects ------------- */\n\nEffect.Fade = function(element) {\n  element = $(element);\n  var oldOpacity = element.getInlineOpacity();\n  var options = Object.extend({\n    from: element.getOpacity() || 1.0,\n    to:   0.0,\n    afterFinishInternal: function(effect) {\n      if (effect.options.to!=0) return;\n      effect.element.hide().setStyle({opacity: oldOpacity});\n    }\n  }, arguments[1] || { });\n  return new Effect.Opacity(element,options);\n};\n\nEffect.Appear = function(element) {\n  element = $(element);\n  var options = Object.extend({\n  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),\n  to:   1.0,\n  // force Safari to render floated elements properly\n  afterFinishInternal: function(effect) {\n    effect.element.forceRerendering();\n  },\n  beforeSetup: function(effect) {\n    effect.element.setOpacity(effect.options.from).show();\n  }}, arguments[1] || { });\n  return new Effect.Opacity(element,options);\n};\n\nEffect.Puff = function(element) {\n  element = $(element);\n  var oldStyle = {\n    opacity: element.getInlineOpacity(),\n    position: element.getStyle('position'),\n    top:  element.style.top,\n    left: element.style.left,\n    width: element.style.width,\n    height: element.style.height\n  };\n  return new Effect.Parallel(\n   [ new Effect.Scale(element, 200,\n      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),\n     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],\n     Object.extend({ duration: 1.0,\n      beforeSetupInternal: function(effect) {\n        Position.absolutize(effect.effects[0].element);\n      },\n      afterFinishInternal: function(effect) {\n         effect.effects[0].element.hide().setStyle(oldStyle); }\n     }, arguments[1] || { })\n   );\n};\n\nEffect.BlindUp = function(element) {\n  element = $(element);\n  element.makeClipping();\n  return new Effect.Scale(element, 0,\n    Object.extend({ scaleContent: false,\n      scaleX: false,\n      restoreAfterFinish: true,\n      afterFinishInternal: function(effect) {\n        effect.element.hide().undoClipping();\n      }\n    }, arguments[1] || { })\n  );\n};\n\nEffect.BlindDown = function(element) {\n  element = $(element);\n  var elementDimensions = element.getDimensions();\n  return new Effect.Scale(element, 100, Object.extend({\n    scaleContent: false,\n    scaleX: false,\n    scaleFrom: 0,\n    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},\n    restoreAfterFinish: true,\n    afterSetup: function(effect) {\n      effect.element.makeClipping().setStyle({height: '0px'}).show();\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.undoClipping();\n    }\n  }, arguments[1] || { }));\n};\n\nEffect.SwitchOff = function(element) {\n  element = $(element);\n  var oldOpacity = element.getInlineOpacity();\n  return new Effect.Appear(element, Object.extend({\n    duration: 0.4,\n    from: 0,\n    transition: Effect.Transitions.flicker,\n    afterFinishInternal: function(effect) {\n      new Effect.Scale(effect.element, 1, {\n        duration: 0.3, scaleFromCenter: true,\n        scaleX: false, scaleContent: false, restoreAfterFinish: true,\n        beforeSetup: function(effect) {\n          effect.element.makePositioned().makeClipping();\n        },\n        afterFinishInternal: function(effect) {\n          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});\n        }\n      });\n    }\n  }, arguments[1] || { }));\n};\n\nEffect.DropOut = function(element) {\n  element = $(element);\n  var oldStyle = {\n    top: element.getStyle('top'),\n    left: element.getStyle('left'),\n    opacity: element.getInlineOpacity() };\n  return new Effect.Parallel(\n    [ new Effect.Move(element, {x: 0, y: 100, sync: true }),\n      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],\n    Object.extend(\n      { duration: 0.5,\n        beforeSetup: function(effect) {\n          effect.effects[0].element.makePositioned();\n        },\n        afterFinishInternal: function(effect) {\n          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);\n        }\n      }, arguments[1] || { }));\n};\n\nEffect.Shake = function(element) {\n  element = $(element);\n  var options = Object.extend({\n    distance: 20,\n    duration: 0.5\n  }, arguments[1] || {});\n  var distance = parseFloat(options.distance);\n  var split = parseFloat(options.duration) / 10.0;\n  var oldStyle = {\n    top: element.getStyle('top'),\n    left: element.getStyle('left') };\n    return new Effect.Move(element,\n      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {\n        effect.element.undoPositioned().setStyle(oldStyle);\n  }}); }}); }}); }}); }}); }});\n};\n\nEffect.SlideDown = function(element) {\n  element = $(element).cleanWhitespace();\n  // SlideDown need to have the content of the element wrapped in a container element with fixed height!\n  var oldInnerBottom = element.down().getStyle('bottom');\n  var elementDimensions = element.getDimensions();\n  return new Effect.Scale(element, 100, Object.extend({\n    scaleContent: false,\n    scaleX: false,\n    scaleFrom: window.opera ? 0 : 1,\n    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},\n    restoreAfterFinish: true,\n    afterSetup: function(effect) {\n      effect.element.makePositioned();\n      effect.element.down().makePositioned();\n      if (window.opera) effect.element.setStyle({top: ''});\n      effect.element.makeClipping().setStyle({height: '0px'}).show();\n    },\n    afterUpdateInternal: function(effect) {\n      effect.element.down().setStyle({bottom:\n        (effect.dims[0] - effect.element.clientHeight) + 'px' });\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.undoClipping().undoPositioned();\n      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }\n    }, arguments[1] || { })\n  );\n};\n\nEffect.SlideUp = function(element) {\n  element = $(element).cleanWhitespace();\n  var oldInnerBottom = element.down().getStyle('bottom');\n  var elementDimensions = element.getDimensions();\n  return new Effect.Scale(element, window.opera ? 0 : 1,\n   Object.extend({ scaleContent: false,\n    scaleX: false,\n    scaleMode: 'box',\n    scaleFrom: 100,\n    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},\n    restoreAfterFinish: true,\n    afterSetup: function(effect) {\n      effect.element.makePositioned();\n      effect.element.down().makePositioned();\n      if (window.opera) effect.element.setStyle({top: ''});\n      effect.element.makeClipping().show();\n    },\n    afterUpdateInternal: function(effect) {\n      effect.element.down().setStyle({bottom:\n        (effect.dims[0] - effect.element.clientHeight) + 'px' });\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.hide().undoClipping().undoPositioned();\n      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});\n    }\n   }, arguments[1] || { })\n  );\n};\n\n// Bug in opera makes the TD containing this element expand for a instance after finish\nEffect.Squish = function(element) {\n  return new Effect.Scale(element, window.opera ? 1 : 0, {\n    restoreAfterFinish: true,\n    beforeSetup: function(effect) {\n      effect.element.makeClipping();\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.hide().undoClipping();\n    }\n  });\n};\n\nEffect.Grow = function(element) {\n  element = $(element);\n  var options = Object.extend({\n    direction: 'center',\n    moveTransition: Effect.Transitions.sinoidal,\n    scaleTransition: Effect.Transitions.sinoidal,\n    opacityTransition: Effect.Transitions.full\n  }, arguments[1] || { });\n  var oldStyle = {\n    top: element.style.top,\n    left: element.style.left,\n    height: element.style.height,\n    width: element.style.width,\n    opacity: element.getInlineOpacity() };\n\n  var dims = element.getDimensions();\n  var initialMoveX, initialMoveY;\n  var moveX, moveY;\n\n  switch (options.direction) {\n    case 'top-left':\n      initialMoveX = initialMoveY = moveX = moveY = 0;\n      break;\n    case 'top-right':\n      initialMoveX = dims.width;\n      initialMoveY = moveY = 0;\n      moveX = -dims.width;\n      break;\n    case 'bottom-left':\n      initialMoveX = moveX = 0;\n      initialMoveY = dims.height;\n      moveY = -dims.height;\n      break;\n    case 'bottom-right':\n      initialMoveX = dims.width;\n      initialMoveY = dims.height;\n      moveX = -dims.width;\n      moveY = -dims.height;\n      break;\n    case 'center':\n      initialMoveX = dims.width / 2;\n      initialMoveY = dims.height / 2;\n      moveX = -dims.width / 2;\n      moveY = -dims.height / 2;\n      break;\n  }\n\n  return new Effect.Move(element, {\n    x: initialMoveX,\n    y: initialMoveY,\n    duration: 0.01,\n    beforeSetup: function(effect) {\n      effect.element.hide().makeClipping().makePositioned();\n    },\n    afterFinishInternal: function(effect) {\n      new Effect.Parallel(\n        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),\n          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),\n          new Effect.Scale(effect.element, 100, {\n            scaleMode: { originalHeight: dims.height, originalWidth: dims.width },\n            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})\n        ], Object.extend({\n             beforeSetup: function(effect) {\n               effect.effects[0].element.setStyle({height: '0px'}).show();\n             },\n             afterFinishInternal: function(effect) {\n               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);\n             }\n           }, options)\n      );\n    }\n  });\n};\n\nEffect.Shrink = function(element) {\n  element = $(element);\n  var options = Object.extend({\n    direction: 'center',\n    moveTransition: Effect.Transitions.sinoidal,\n    scaleTransition: Effect.Transitions.sinoidal,\n    opacityTransition: Effect.Transitions.none\n  }, arguments[1] || { });\n  var oldStyle = {\n    top: element.style.top,\n    left: element.style.left,\n    height: element.style.height,\n    width: element.style.width,\n    opacity: element.getInlineOpacity() };\n\n  var dims = element.getDimensions();\n  var moveX, moveY;\n\n  switch (options.direction) {\n    case 'top-left':\n      moveX = moveY = 0;\n      break;\n    case 'top-right':\n      moveX = dims.width;\n      moveY = 0;\n      break;\n    case 'bottom-left':\n      moveX = 0;\n      moveY = dims.height;\n      break;\n    case 'bottom-right':\n      moveX = dims.width;\n      moveY = dims.height;\n      break;\n    case 'center':\n      moveX = dims.width / 2;\n      moveY = dims.height / 2;\n      break;\n  }\n\n  return new Effect.Parallel(\n    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),\n      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),\n      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })\n    ], Object.extend({\n         beforeStartInternal: function(effect) {\n           effect.effects[0].element.makePositioned().makeClipping();\n         },\n         afterFinishInternal: function(effect) {\n           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }\n       }, options)\n  );\n};\n\nEffect.Pulsate = function(element) {\n  element = $(element);\n  var options    = arguments[1] || { },\n    oldOpacity = element.getInlineOpacity(),\n    transition = options.transition || Effect.Transitions.linear,\n    reverser   = function(pos){\n      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);\n    };\n\n  return new Effect.Opacity(element,\n    Object.extend(Object.extend({  duration: 2.0, from: 0,\n      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }\n    }, options), {transition: reverser}));\n};\n\nEffect.Fold = function(element) {\n  element = $(element);\n  var oldStyle = {\n    top: element.style.top,\n    left: element.style.left,\n    width: element.style.width,\n    height: element.style.height };\n  element.makeClipping();\n  return new Effect.Scale(element, 5, Object.extend({\n    scaleContent: false,\n    scaleX: false,\n    afterFinishInternal: function(effect) {\n    new Effect.Scale(element, 1, {\n      scaleContent: false,\n      scaleY: false,\n      afterFinishInternal: function(effect) {\n        effect.element.hide().undoClipping().setStyle(oldStyle);\n      } });\n  }}, arguments[1] || { }));\n};\n\nEffect.Morph = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({\n      style: { }\n    }, arguments[1] || { });\n\n    if (!Object.isString(options.style)) this.style = $H(options.style);\n    else {\n      if (options.style.include(':'))\n        this.style = options.style.parseStyle();\n      else {\n        this.element.addClassName(options.style);\n        this.style = $H(this.element.getStyles());\n        this.element.removeClassName(options.style);\n        var css = this.element.getStyles();\n        this.style = this.style.reject(function(style) {\n          return style.value == css[style.key];\n        });\n        options.afterFinishInternal = function(effect) {\n          effect.element.addClassName(effect.options.style);\n          effect.transforms.each(function(transform) {\n            effect.element.style[transform.style] = '';\n          });\n        };\n      }\n    }\n    this.start(options);\n  },\n\n  setup: function(){\n    function parseColor(color){\n      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';\n      color = color.parseColor();\n      return $R(0,2).map(function(i){\n        return parseInt( color.slice(i*2+1,i*2+3), 16 );\n      });\n    }\n    this.transforms = this.style.map(function(pair){\n      var property = pair[0], value = pair[1], unit = null;\n\n      if (value.parseColor('#zzzzzz') != '#zzzzzz') {\n        value = value.parseColor();\n        unit  = 'color';\n      } else if (property == 'opacity') {\n        value = parseFloat(value);\n        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))\n          this.element.setStyle({zoom: 1});\n      } else if (Element.CSS_LENGTH.test(value)) {\n          var components = value.match(/^([\\+\\-]?[0-9\\.]+)(.*)$/);\n          value = parseFloat(components[1]);\n          unit = (components.length == 3) ? components[2] : null;\n      }\n\n      var originalValue = this.element.getStyle(property);\n      return {\n        style: property.camelize(),\n        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),\n        targetValue: unit=='color' ? parseColor(value) : value,\n        unit: unit\n      };\n    }.bind(this)).reject(function(transform){\n      return (\n        (transform.originalValue == transform.targetValue) ||\n        (\n          transform.unit != 'color' &&\n          (isNaN(transform.originalValue) || isNaN(transform.targetValue))\n        )\n      );\n    });\n  },\n  update: function(position) {\n    var style = { }, transform, i = this.transforms.length;\n    while(i--)\n      style[(transform = this.transforms[i]).style] =\n        transform.unit=='color' ? '#'+\n          (Math.round(transform.originalValue[0]+\n            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +\n          (Math.round(transform.originalValue[1]+\n            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +\n          (Math.round(transform.originalValue[2]+\n            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :\n        (transform.originalValue +\n          (transform.targetValue - transform.originalValue) * position).toFixed(3) +\n            (transform.unit === null ? '' : transform.unit);\n    this.element.setStyle(style, true);\n  }\n});\n\nEffect.Transform = Class.create({\n  initialize: function(tracks){\n    this.tracks  = [];\n    this.options = arguments[1] || { };\n    this.addTracks(tracks);\n  },\n  addTracks: function(tracks){\n    tracks.each(function(track){\n      track = $H(track);\n      var data = track.values().first();\n      this.tracks.push($H({\n        ids:     track.keys().first(),\n        effect:  Effect.Morph,\n        options: { style: data }\n      }));\n    }.bind(this));\n    return this;\n  },\n  play: function(){\n    return new Effect.Parallel(\n      this.tracks.map(function(track){\n        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');\n        var elements = [$(ids) || $$(ids)].flatten();\n        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });\n      }).flatten(),\n      this.options\n    );\n  }\n});\n\nElement.CSS_PROPERTIES = $w(\n  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +\n  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +\n  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +\n  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +\n  'fontSize fontWeight height left letterSpacing lineHeight ' +\n  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+\n  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +\n  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +\n  'right textIndent top width wordSpacing zIndex');\n\nElement.CSS_LENGTH = /^(([\\+\\-]?[0-9\\.]+)(em|ex|px|in|cm|mm|pt|pc|\\%))|0$/;\n\nString.__parseStyleElement = document.createElement('div');\nString.prototype.parseStyle = function(){\n  var style, styleRules = $H();\n  if (Prototype.Browser.WebKit)\n    style = new Element('div',{style:this}).style;\n  else {\n    String.__parseStyleElement.innerHTML = '<div style=\"' + this + '\"></div>';\n    style = String.__parseStyleElement.childNodes[0].style;\n  }\n\n  Element.CSS_PROPERTIES.each(function(property){\n    if (style[property]) styleRules.set(property, style[property]);\n  });\n\n  if (Prototype.Browser.IE && this.include('opacity'))\n    styleRules.set('opacity', this.match(/opacity:\\s*((?:0|1)?(?:\\.\\d*)?)/)[1]);\n\n  return styleRules;\n};\n\nif (document.defaultView && document.defaultView.getComputedStyle) {\n  Element.getStyles = function(element) {\n    var css = document.defaultView.getComputedStyle($(element), null);\n    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {\n      styles[property] = css[property];\n      return styles;\n    });\n  };\n} else {\n  Element.getStyles = function(element) {\n    element = $(element);\n    var css = element.currentStyle, styles;\n    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {\n      results[property] = css[property];\n      return results;\n    });\n    if (!styles.opacity) styles.opacity = element.getOpacity();\n    return styles;\n  };\n}\n\nEffect.Methods = {\n  morph: function(element, style) {\n    element = $(element);\n    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));\n    return element;\n  },\n  visualEffect: function(element, effect, options) {\n    element = $(element);\n    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);\n    new Effect[klass](element, options);\n    return element;\n  },\n  highlight: function(element, options) {\n    element = $(element);\n    new Effect.Highlight(element, options);\n    return element;\n  }\n};\n\n$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+\n  'pulsate shake puff squish switchOff dropOut').each(\n  function(effect) {\n    Effect.Methods[effect] = function(element, options){\n      element = $(element);\n      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);\n      return element;\n    };\n  }\n);\n\n$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(\n  function(f) { Effect.Methods[f] = Element[f]; }\n);\n\nElement.addMethods(Effect.Methods);"
  },
  {
    "path": "test/apps/rails2/public/javascripts/prototype.js",
    "content": "/*  Prototype JavaScript framework, version 1.6.0.3\n *  (c) 2005-2008 Sam Stephenson\n *\n *  Prototype is freely distributable under the terms of an MIT-style license.\n *  For details, see the Prototype web site: http://www.prototypejs.org/\n *\n *--------------------------------------------------------------------------*/\n\nvar Prototype = {\n  Version: '1.6.0.3',\n\n  Browser: {\n    IE:     !!(window.attachEvent &&\n      navigator.userAgent.indexOf('Opera') === -1),\n    Opera:  navigator.userAgent.indexOf('Opera') > -1,\n    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,\n    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 &&\n      navigator.userAgent.indexOf('KHTML') === -1,\n    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)\n  },\n\n  BrowserFeatures: {\n    XPath: !!document.evaluate,\n    SelectorsAPI: !!document.querySelector,\n    ElementExtensions: !!window.HTMLElement,\n    SpecificElementExtensions:\n      document.createElement('div')['__proto__'] &&\n      document.createElement('div')['__proto__'] !==\n        document.createElement('form')['__proto__']\n  },\n\n  ScriptFragment: '<script[^>]*>([\\\\S\\\\s]*?)<\\/script>',\n  JSONFilter: /^\\/\\*-secure-([\\s\\S]*)\\*\\/\\s*$/,\n\n  emptyFunction: function() { },\n  K: function(x) { return x }\n};\n\nif (Prototype.Browser.MobileSafari)\n  Prototype.BrowserFeatures.SpecificElementExtensions = false;\n\n\n/* Based on Alex Arnell's inheritance implementation. */\nvar Class = {\n  create: function() {\n    var parent = null, properties = $A(arguments);\n    if (Object.isFunction(properties[0]))\n      parent = properties.shift();\n\n    function klass() {\n      this.initialize.apply(this, arguments);\n    }\n\n    Object.extend(klass, Class.Methods);\n    klass.superclass = parent;\n    klass.subclasses = [];\n\n    if (parent) {\n      var subclass = function() { };\n      subclass.prototype = parent.prototype;\n      klass.prototype = new subclass;\n      parent.subclasses.push(klass);\n    }\n\n    for (var i = 0; i < properties.length; i++)\n      klass.addMethods(properties[i]);\n\n    if (!klass.prototype.initialize)\n      klass.prototype.initialize = Prototype.emptyFunction;\n\n    klass.prototype.constructor = klass;\n\n    return klass;\n  }\n};\n\nClass.Methods = {\n  addMethods: function(source) {\n    var ancestor   = this.superclass && this.superclass.prototype;\n    var properties = Object.keys(source);\n\n    if (!Object.keys({ toString: true }).length)\n      properties.push(\"toString\", \"valueOf\");\n\n    for (var i = 0, length = properties.length; i < length; i++) {\n      var property = properties[i], value = source[property];\n      if (ancestor && Object.isFunction(value) &&\n          value.argumentNames().first() == \"$super\") {\n        var method = value;\n        value = (function(m) {\n          return function() { return ancestor[m].apply(this, arguments) };\n        })(property).wrap(method);\n\n        value.valueOf = method.valueOf.bind(method);\n        value.toString = method.toString.bind(method);\n      }\n      this.prototype[property] = value;\n    }\n\n    return this;\n  }\n};\n\nvar Abstract = { };\n\nObject.extend = function(destination, source) {\n  for (var property in source)\n    destination[property] = source[property];\n  return destination;\n};\n\nObject.extend(Object, {\n  inspect: function(object) {\n    try {\n      if (Object.isUndefined(object)) return 'undefined';\n      if (object === null) return 'null';\n      return object.inspect ? object.inspect() : String(object);\n    } catch (e) {\n      if (e instanceof RangeError) return '...';\n      throw e;\n    }\n  },\n\n  toJSON: function(object) {\n    var type = typeof object;\n    switch (type) {\n      case 'undefined':\n      case 'function':\n      case 'unknown': return;\n      case 'boolean': return object.toString();\n    }\n\n    if (object === null) return 'null';\n    if (object.toJSON) return object.toJSON();\n    if (Object.isElement(object)) return;\n\n    var results = [];\n    for (var property in object) {\n      var value = Object.toJSON(object[property]);\n      if (!Object.isUndefined(value))\n        results.push(property.toJSON() + ': ' + value);\n    }\n\n    return '{' + results.join(', ') + '}';\n  },\n\n  toQueryString: function(object) {\n    return $H(object).toQueryString();\n  },\n\n  toHTML: function(object) {\n    return object && object.toHTML ? object.toHTML() : String.interpret(object);\n  },\n\n  keys: function(object) {\n    var keys = [];\n    for (var property in object)\n      keys.push(property);\n    return keys;\n  },\n\n  values: function(object) {\n    var values = [];\n    for (var property in object)\n      values.push(object[property]);\n    return values;\n  },\n\n  clone: function(object) {\n    return Object.extend({ }, object);\n  },\n\n  isElement: function(object) {\n    return !!(object && object.nodeType == 1);\n  },\n\n  isArray: function(object) {\n    return object != null && typeof object == \"object\" &&\n      'splice' in object && 'join' in object;\n  },\n\n  isHash: function(object) {\n    return object instanceof Hash;\n  },\n\n  isFunction: function(object) {\n    return typeof object == \"function\";\n  },\n\n  isString: function(object) {\n    return typeof object == \"string\";\n  },\n\n  isNumber: function(object) {\n    return typeof object == \"number\";\n  },\n\n  isUndefined: function(object) {\n    return typeof object == \"undefined\";\n  }\n});\n\nObject.extend(Function.prototype, {\n  argumentNames: function() {\n    var names = this.toString().match(/^[\\s\\(]*function[^(]*\\(([^\\)]*)\\)/)[1]\n      .replace(/\\s+/g, '').split(',');\n    return names.length == 1 && !names[0] ? [] : names;\n  },\n\n  bind: function() {\n    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;\n    var __method = this, args = $A(arguments), object = args.shift();\n    return function() {\n      return __method.apply(object, args.concat($A(arguments)));\n    }\n  },\n\n  bindAsEventListener: function() {\n    var __method = this, args = $A(arguments), object = args.shift();\n    return function(event) {\n      return __method.apply(object, [event || window.event].concat(args));\n    }\n  },\n\n  curry: function() {\n    if (!arguments.length) return this;\n    var __method = this, args = $A(arguments);\n    return function() {\n      return __method.apply(this, args.concat($A(arguments)));\n    }\n  },\n\n  delay: function() {\n    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;\n    return window.setTimeout(function() {\n      return __method.apply(__method, args);\n    }, timeout);\n  },\n\n  defer: function() {\n    var args = [0.01].concat($A(arguments));\n    return this.delay.apply(this, args);\n  },\n\n  wrap: function(wrapper) {\n    var __method = this;\n    return function() {\n      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));\n    }\n  },\n\n  methodize: function() {\n    if (this._methodized) return this._methodized;\n    var __method = this;\n    return this._methodized = function() {\n      return __method.apply(null, [this].concat($A(arguments)));\n    };\n  }\n});\n\nDate.prototype.toJSON = function() {\n  return '\"' + this.getUTCFullYear() + '-' +\n    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +\n    this.getUTCDate().toPaddedString(2) + 'T' +\n    this.getUTCHours().toPaddedString(2) + ':' +\n    this.getUTCMinutes().toPaddedString(2) + ':' +\n    this.getUTCSeconds().toPaddedString(2) + 'Z\"';\n};\n\nvar Try = {\n  these: function() {\n    var returnValue;\n\n    for (var i = 0, length = arguments.length; i < length; i++) {\n      var lambda = arguments[i];\n      try {\n        returnValue = lambda();\n        break;\n      } catch (e) { }\n    }\n\n    return returnValue;\n  }\n};\n\nRegExp.prototype.match = RegExp.prototype.test;\n\nRegExp.escape = function(str) {\n  return String(str).replace(/([.*+?^=!:${}()|[\\]\\/\\\\])/g, '\\\\$1');\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar PeriodicalExecuter = Class.create({\n  initialize: function(callback, frequency) {\n    this.callback = callback;\n    this.frequency = frequency;\n    this.currentlyExecuting = false;\n\n    this.registerCallback();\n  },\n\n  registerCallback: function() {\n    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);\n  },\n\n  execute: function() {\n    this.callback(this);\n  },\n\n  stop: function() {\n    if (!this.timer) return;\n    clearInterval(this.timer);\n    this.timer = null;\n  },\n\n  onTimerEvent: function() {\n    if (!this.currentlyExecuting) {\n      try {\n        this.currentlyExecuting = true;\n        this.execute();\n      } finally {\n        this.currentlyExecuting = false;\n      }\n    }\n  }\n});\nObject.extend(String, {\n  interpret: function(value) {\n    return value == null ? '' : String(value);\n  },\n  specialChar: {\n    '\\b': '\\\\b',\n    '\\t': '\\\\t',\n    '\\n': '\\\\n',\n    '\\f': '\\\\f',\n    '\\r': '\\\\r',\n    '\\\\': '\\\\\\\\'\n  }\n});\n\nObject.extend(String.prototype, {\n  gsub: function(pattern, replacement) {\n    var result = '', source = this, match;\n    replacement = arguments.callee.prepareReplacement(replacement);\n\n    while (source.length > 0) {\n      if (match = source.match(pattern)) {\n        result += source.slice(0, match.index);\n        result += String.interpret(replacement(match));\n        source  = source.slice(match.index + match[0].length);\n      } else {\n        result += source, source = '';\n      }\n    }\n    return result;\n  },\n\n  sub: function(pattern, replacement, count) {\n    replacement = this.gsub.prepareReplacement(replacement);\n    count = Object.isUndefined(count) ? 1 : count;\n\n    return this.gsub(pattern, function(match) {\n      if (--count < 0) return match[0];\n      return replacement(match);\n    });\n  },\n\n  scan: function(pattern, iterator) {\n    this.gsub(pattern, iterator);\n    return String(this);\n  },\n\n  truncate: function(length, truncation) {\n    length = length || 30;\n    truncation = Object.isUndefined(truncation) ? '...' : truncation;\n    return this.length > length ?\n      this.slice(0, length - truncation.length) + truncation : String(this);\n  },\n\n  strip: function() {\n    return this.replace(/^\\s+/, '').replace(/\\s+$/, '');\n  },\n\n  stripTags: function() {\n    return this.replace(/<\\/?[^>]+>/gi, '');\n  },\n\n  stripScripts: function() {\n    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');\n  },\n\n  extractScripts: function() {\n    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');\n    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');\n    return (this.match(matchAll) || []).map(function(scriptTag) {\n      return (scriptTag.match(matchOne) || ['', ''])[1];\n    });\n  },\n\n  evalScripts: function() {\n    return this.extractScripts().map(function(script) { return eval(script) });\n  },\n\n  escapeHTML: function() {\n    var self = arguments.callee;\n    self.text.data = this;\n    return self.div.innerHTML;\n  },\n\n  unescapeHTML: function() {\n    var div = new Element('div');\n    div.innerHTML = this.stripTags();\n    return div.childNodes[0] ? (div.childNodes.length > 1 ?\n      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :\n      div.childNodes[0].nodeValue) : '';\n  },\n\n  toQueryParams: function(separator) {\n    var match = this.strip().match(/([^?#]*)(#.*)?$/);\n    if (!match) return { };\n\n    return match[1].split(separator || '&').inject({ }, function(hash, pair) {\n      if ((pair = pair.split('='))[0]) {\n        var key = decodeURIComponent(pair.shift());\n        var value = pair.length > 1 ? pair.join('=') : pair[0];\n        if (value != undefined) value = decodeURIComponent(value);\n\n        if (key in hash) {\n          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];\n          hash[key].push(value);\n        }\n        else hash[key] = value;\n      }\n      return hash;\n    });\n  },\n\n  toArray: function() {\n    return this.split('');\n  },\n\n  succ: function() {\n    return this.slice(0, this.length - 1) +\n      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);\n  },\n\n  times: function(count) {\n    return count < 1 ? '' : new Array(count + 1).join(this);\n  },\n\n  camelize: function() {\n    var parts = this.split('-'), len = parts.length;\n    if (len == 1) return parts[0];\n\n    var camelized = this.charAt(0) == '-'\n      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)\n      : parts[0];\n\n    for (var i = 1; i < len; i++)\n      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);\n\n    return camelized;\n  },\n\n  capitalize: function() {\n    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();\n  },\n\n  underscore: function() {\n    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();\n  },\n\n  dasherize: function() {\n    return this.gsub(/_/,'-');\n  },\n\n  inspect: function(useDoubleQuotes) {\n    var escapedString = this.gsub(/[\\x00-\\x1f\\\\]/, function(match) {\n      var character = String.specialChar[match[0]];\n      return character ? character : '\\\\u00' + match[0].charCodeAt().toPaddedString(2, 16);\n    });\n    if (useDoubleQuotes) return '\"' + escapedString.replace(/\"/g, '\\\\\"') + '\"';\n    return \"'\" + escapedString.replace(/'/g, '\\\\\\'') + \"'\";\n  },\n\n  toJSON: function() {\n    return this.inspect(true);\n  },\n\n  unfilterJSON: function(filter) {\n    return this.sub(filter || Prototype.JSONFilter, '#{1}');\n  },\n\n  isJSON: function() {\n    var str = this;\n    if (str.blank()) return false;\n    str = this.replace(/\\\\./g, '@').replace(/\"[^\"\\\\\\n\\r]*\"/g, '');\n    return (/^[,:{}\\[\\]0-9.\\-+Eaeflnr-u \\n\\r\\t]*$/).test(str);\n  },\n\n  evalJSON: function(sanitize) {\n    var json = this.unfilterJSON();\n    try {\n      if (!sanitize || json.isJSON()) return eval('(' + json + ')');\n    } catch (e) { }\n    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());\n  },\n\n  include: function(pattern) {\n    return this.indexOf(pattern) > -1;\n  },\n\n  startsWith: function(pattern) {\n    return this.indexOf(pattern) === 0;\n  },\n\n  endsWith: function(pattern) {\n    var d = this.length - pattern.length;\n    return d >= 0 && this.lastIndexOf(pattern) === d;\n  },\n\n  empty: function() {\n    return this == '';\n  },\n\n  blank: function() {\n    return /^\\s*$/.test(this);\n  },\n\n  interpolate: function(object, pattern) {\n    return new Template(this, pattern).evaluate(object);\n  }\n});\n\nif (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {\n  escapeHTML: function() {\n    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');\n  },\n  unescapeHTML: function() {\n    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');\n  }\n});\n\nString.prototype.gsub.prepareReplacement = function(replacement) {\n  if (Object.isFunction(replacement)) return replacement;\n  var template = new Template(replacement);\n  return function(match) { return template.evaluate(match) };\n};\n\nString.prototype.parseQuery = String.prototype.toQueryParams;\n\nObject.extend(String.prototype.escapeHTML, {\n  div:  document.createElement('div'),\n  text: document.createTextNode('')\n});\n\nString.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);\n\nvar Template = Class.create({\n  initialize: function(template, pattern) {\n    this.template = template.toString();\n    this.pattern = pattern || Template.Pattern;\n  },\n\n  evaluate: function(object) {\n    if (Object.isFunction(object.toTemplateReplacements))\n      object = object.toTemplateReplacements();\n\n    return this.template.gsub(this.pattern, function(match) {\n      if (object == null) return '';\n\n      var before = match[1] || '';\n      if (before == '\\\\') return match[2];\n\n      var ctx = object, expr = match[3];\n      var pattern = /^([^.[]+|\\[((?:.*?[^\\\\])?)\\])(\\.|\\[|$)/;\n      match = pattern.exec(expr);\n      if (match == null) return before;\n\n      while (match != null) {\n        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\\\\\]', ']') : match[1];\n        ctx = ctx[comp];\n        if (null == ctx || '' == match[3]) break;\n        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);\n        match = pattern.exec(expr);\n      }\n\n      return before + String.interpret(ctx);\n    });\n  }\n});\nTemplate.Pattern = /(^|.|\\r|\\n)(#\\{(.*?)\\})/;\n\nvar $break = { };\n\nvar Enumerable = {\n  each: function(iterator, context) {\n    var index = 0;\n    try {\n      this._each(function(value) {\n        iterator.call(context, value, index++);\n      });\n    } catch (e) {\n      if (e != $break) throw e;\n    }\n    return this;\n  },\n\n  eachSlice: function(number, iterator, context) {\n    var index = -number, slices = [], array = this.toArray();\n    if (number < 1) return array;\n    while ((index += number) < array.length)\n      slices.push(array.slice(index, index+number));\n    return slices.collect(iterator, context);\n  },\n\n  all: function(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result = true;\n    this.each(function(value, index) {\n      result = result && !!iterator.call(context, value, index);\n      if (!result) throw $break;\n    });\n    return result;\n  },\n\n  any: function(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result = false;\n    this.each(function(value, index) {\n      if (result = !!iterator.call(context, value, index))\n        throw $break;\n    });\n    return result;\n  },\n\n  collect: function(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var results = [];\n    this.each(function(value, index) {\n      results.push(iterator.call(context, value, index));\n    });\n    return results;\n  },\n\n  detect: function(iterator, context) {\n    var result;\n    this.each(function(value, index) {\n      if (iterator.call(context, value, index)) {\n        result = value;\n        throw $break;\n      }\n    });\n    return result;\n  },\n\n  findAll: function(iterator, context) {\n    var results = [];\n    this.each(function(value, index) {\n      if (iterator.call(context, value, index))\n        results.push(value);\n    });\n    return results;\n  },\n\n  grep: function(filter, iterator, context) {\n    iterator = iterator || Prototype.K;\n    var results = [];\n\n    if (Object.isString(filter))\n      filter = new RegExp(filter);\n\n    this.each(function(value, index) {\n      if (filter.match(value))\n        results.push(iterator.call(context, value, index));\n    });\n    return results;\n  },\n\n  include: function(object) {\n    if (Object.isFunction(this.indexOf))\n      if (this.indexOf(object) != -1) return true;\n\n    var found = false;\n    this.each(function(value) {\n      if (value == object) {\n        found = true;\n        throw $break;\n      }\n    });\n    return found;\n  },\n\n  inGroupsOf: function(number, fillWith) {\n    fillWith = Object.isUndefined(fillWith) ? null : fillWith;\n    return this.eachSlice(number, function(slice) {\n      while(slice.length < number) slice.push(fillWith);\n      return slice;\n    });\n  },\n\n  inject: function(memo, iterator, context) {\n    this.each(function(value, index) {\n      memo = iterator.call(context, memo, value, index);\n    });\n    return memo;\n  },\n\n  invoke: function(method) {\n    var args = $A(arguments).slice(1);\n    return this.map(function(value) {\n      return value[method].apply(value, args);\n    });\n  },\n\n  max: function(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result;\n    this.each(function(value, index) {\n      value = iterator.call(context, value, index);\n      if (result == null || value >= result)\n        result = value;\n    });\n    return result;\n  },\n\n  min: function(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result;\n    this.each(function(value, index) {\n      value = iterator.call(context, value, index);\n      if (result == null || value < result)\n        result = value;\n    });\n    return result;\n  },\n\n  partition: function(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var trues = [], falses = [];\n    this.each(function(value, index) {\n      (iterator.call(context, value, index) ?\n        trues : falses).push(value);\n    });\n    return [trues, falses];\n  },\n\n  pluck: function(property) {\n    var results = [];\n    this.each(function(value) {\n      results.push(value[property]);\n    });\n    return results;\n  },\n\n  reject: function(iterator, context) {\n    var results = [];\n    this.each(function(value, index) {\n      if (!iterator.call(context, value, index))\n        results.push(value);\n    });\n    return results;\n  },\n\n  sortBy: function(iterator, context) {\n    return this.map(function(value, index) {\n      return {\n        value: value,\n        criteria: iterator.call(context, value, index)\n      };\n    }).sort(function(left, right) {\n      var a = left.criteria, b = right.criteria;\n      return a < b ? -1 : a > b ? 1 : 0;\n    }).pluck('value');\n  },\n\n  toArray: function() {\n    return this.map();\n  },\n\n  zip: function() {\n    var iterator = Prototype.K, args = $A(arguments);\n    if (Object.isFunction(args.last()))\n      iterator = args.pop();\n\n    var collections = [this].concat(args).map($A);\n    return this.map(function(value, index) {\n      return iterator(collections.pluck(index));\n    });\n  },\n\n  size: function() {\n    return this.toArray().length;\n  },\n\n  inspect: function() {\n    return '#<Enumerable:' + this.toArray().inspect() + '>';\n  }\n};\n\nObject.extend(Enumerable, {\n  map:     Enumerable.collect,\n  find:    Enumerable.detect,\n  select:  Enumerable.findAll,\n  filter:  Enumerable.findAll,\n  member:  Enumerable.include,\n  entries: Enumerable.toArray,\n  every:   Enumerable.all,\n  some:    Enumerable.any\n});\nfunction $A(iterable) {\n  if (!iterable) return [];\n  if (iterable.toArray) return iterable.toArray();\n  var length = iterable.length || 0, results = new Array(length);\n  while (length--) results[length] = iterable[length];\n  return results;\n}\n\nif (Prototype.Browser.WebKit) {\n  $A = function(iterable) {\n    if (!iterable) return [];\n    // In Safari, only use the `toArray` method if it's not a NodeList.\n    // A NodeList is a function, has an function `item` property, and a numeric\n    // `length` property. Adapted from Google Doctype.\n    if (!(typeof iterable === 'function' && typeof iterable.length ===\n        'number' && typeof iterable.item === 'function') && iterable.toArray)\n      return iterable.toArray();\n    var length = iterable.length || 0, results = new Array(length);\n    while (length--) results[length] = iterable[length];\n    return results;\n  };\n}\n\nArray.from = $A;\n\nObject.extend(Array.prototype, Enumerable);\n\nif (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;\n\nObject.extend(Array.prototype, {\n  _each: function(iterator) {\n    for (var i = 0, length = this.length; i < length; i++)\n      iterator(this[i]);\n  },\n\n  clear: function() {\n    this.length = 0;\n    return this;\n  },\n\n  first: function() {\n    return this[0];\n  },\n\n  last: function() {\n    return this[this.length - 1];\n  },\n\n  compact: function() {\n    return this.select(function(value) {\n      return value != null;\n    });\n  },\n\n  flatten: function() {\n    return this.inject([], function(array, value) {\n      return array.concat(Object.isArray(value) ?\n        value.flatten() : [value]);\n    });\n  },\n\n  without: function() {\n    var values = $A(arguments);\n    return this.select(function(value) {\n      return !values.include(value);\n    });\n  },\n\n  reverse: function(inline) {\n    return (inline !== false ? this : this.toArray())._reverse();\n  },\n\n  reduce: function() {\n    return this.length > 1 ? this : this[0];\n  },\n\n  uniq: function(sorted) {\n    return this.inject([], function(array, value, index) {\n      if (0 == index || (sorted ? array.last() != value : !array.include(value)))\n        array.push(value);\n      return array;\n    });\n  },\n\n  intersect: function(array) {\n    return this.uniq().findAll(function(item) {\n      return array.detect(function(value) { return item === value });\n    });\n  },\n\n  clone: function() {\n    return [].concat(this);\n  },\n\n  size: function() {\n    return this.length;\n  },\n\n  inspect: function() {\n    return '[' + this.map(Object.inspect).join(', ') + ']';\n  },\n\n  toJSON: function() {\n    var results = [];\n    this.each(function(object) {\n      var value = Object.toJSON(object);\n      if (!Object.isUndefined(value)) results.push(value);\n    });\n    return '[' + results.join(', ') + ']';\n  }\n});\n\n// use native browser JS 1.6 implementation if available\nif (Object.isFunction(Array.prototype.forEach))\n  Array.prototype._each = Array.prototype.forEach;\n\nif (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {\n  i || (i = 0);\n  var length = this.length;\n  if (i < 0) i = length + i;\n  for (; i < length; i++)\n    if (this[i] === item) return i;\n  return -1;\n};\n\nif (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {\n  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;\n  var n = this.slice(0, i).reverse().indexOf(item);\n  return (n < 0) ? n : i - n - 1;\n};\n\nArray.prototype.toArray = Array.prototype.clone;\n\nfunction $w(string) {\n  if (!Object.isString(string)) return [];\n  string = string.strip();\n  return string ? string.split(/\\s+/) : [];\n}\n\nif (Prototype.Browser.Opera){\n  Array.prototype.concat = function() {\n    var array = [];\n    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);\n    for (var i = 0, length = arguments.length; i < length; i++) {\n      if (Object.isArray(arguments[i])) {\n        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)\n          array.push(arguments[i][j]);\n      } else {\n        array.push(arguments[i]);\n      }\n    }\n    return array;\n  };\n}\nObject.extend(Number.prototype, {\n  toColorPart: function() {\n    return this.toPaddedString(2, 16);\n  },\n\n  succ: function() {\n    return this + 1;\n  },\n\n  times: function(iterator, context) {\n    $R(0, this, true).each(iterator, context);\n    return this;\n  },\n\n  toPaddedString: function(length, radix) {\n    var string = this.toString(radix || 10);\n    return '0'.times(length - string.length) + string;\n  },\n\n  toJSON: function() {\n    return isFinite(this) ? this.toString() : 'null';\n  }\n});\n\n$w('abs round ceil floor').each(function(method){\n  Number.prototype[method] = Math[method].methodize();\n});\nfunction $H(object) {\n  return new Hash(object);\n};\n\nvar Hash = Class.create(Enumerable, (function() {\n\n  function toQueryPair(key, value) {\n    if (Object.isUndefined(value)) return key;\n    return key + '=' + encodeURIComponent(String.interpret(value));\n  }\n\n  return {\n    initialize: function(object) {\n      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);\n    },\n\n    _each: function(iterator) {\n      for (var key in this._object) {\n        var value = this._object[key], pair = [key, value];\n        pair.key = key;\n        pair.value = value;\n        iterator(pair);\n      }\n    },\n\n    set: function(key, value) {\n      return this._object[key] = value;\n    },\n\n    get: function(key) {\n      // simulating poorly supported hasOwnProperty\n      if (this._object[key] !== Object.prototype[key])\n        return this._object[key];\n    },\n\n    unset: function(key) {\n      var value = this._object[key];\n      delete this._object[key];\n      return value;\n    },\n\n    toObject: function() {\n      return Object.clone(this._object);\n    },\n\n    keys: function() {\n      return this.pluck('key');\n    },\n\n    values: function() {\n      return this.pluck('value');\n    },\n\n    index: function(value) {\n      var match = this.detect(function(pair) {\n        return pair.value === value;\n      });\n      return match && match.key;\n    },\n\n    merge: function(object) {\n      return this.clone().update(object);\n    },\n\n    update: function(object) {\n      return new Hash(object).inject(this, function(result, pair) {\n        result.set(pair.key, pair.value);\n        return result;\n      });\n    },\n\n    toQueryString: function() {\n      return this.inject([], function(results, pair) {\n        var key = encodeURIComponent(pair.key), values = pair.value;\n\n        if (values && typeof values == 'object') {\n          if (Object.isArray(values))\n            return results.concat(values.map(toQueryPair.curry(key)));\n        } else results.push(toQueryPair(key, values));\n        return results;\n      }).join('&');\n    },\n\n    inspect: function() {\n      return '#<Hash:{' + this.map(function(pair) {\n        return pair.map(Object.inspect).join(': ');\n      }).join(', ') + '}>';\n    },\n\n    toJSON: function() {\n      return Object.toJSON(this.toObject());\n    },\n\n    clone: function() {\n      return new Hash(this);\n    }\n  }\n})());\n\nHash.prototype.toTemplateReplacements = Hash.prototype.toObject;\nHash.from = $H;\nvar ObjectRange = Class.create(Enumerable, {\n  initialize: function(start, end, exclusive) {\n    this.start = start;\n    this.end = end;\n    this.exclusive = exclusive;\n  },\n\n  _each: function(iterator) {\n    var value = this.start;\n    while (this.include(value)) {\n      iterator(value);\n      value = value.succ();\n    }\n  },\n\n  include: function(value) {\n    if (value < this.start)\n      return false;\n    if (this.exclusive)\n      return value < this.end;\n    return value <= this.end;\n  }\n});\n\nvar $R = function(start, end, exclusive) {\n  return new ObjectRange(start, end, exclusive);\n};\n\nvar Ajax = {\n  getTransport: function() {\n    return Try.these(\n      function() {return new XMLHttpRequest()},\n      function() {return new ActiveXObject('Msxml2.XMLHTTP')},\n      function() {return new ActiveXObject('Microsoft.XMLHTTP')}\n    ) || false;\n  },\n\n  activeRequestCount: 0\n};\n\nAjax.Responders = {\n  responders: [],\n\n  _each: function(iterator) {\n    this.responders._each(iterator);\n  },\n\n  register: function(responder) {\n    if (!this.include(responder))\n      this.responders.push(responder);\n  },\n\n  unregister: function(responder) {\n    this.responders = this.responders.without(responder);\n  },\n\n  dispatch: function(callback, request, transport, json) {\n    this.each(function(responder) {\n      if (Object.isFunction(responder[callback])) {\n        try {\n          responder[callback].apply(responder, [request, transport, json]);\n        } catch (e) { }\n      }\n    });\n  }\n};\n\nObject.extend(Ajax.Responders, Enumerable);\n\nAjax.Responders.register({\n  onCreate:   function() { Ajax.activeRequestCount++ },\n  onComplete: function() { Ajax.activeRequestCount-- }\n});\n\nAjax.Base = Class.create({\n  initialize: function(options) {\n    this.options = {\n      method:       'post',\n      asynchronous: true,\n      contentType:  'application/x-www-form-urlencoded',\n      encoding:     'UTF-8',\n      parameters:   '',\n      evalJSON:     true,\n      evalJS:       true\n    };\n    Object.extend(this.options, options || { });\n\n    this.options.method = this.options.method.toLowerCase();\n\n    if (Object.isString(this.options.parameters))\n      this.options.parameters = this.options.parameters.toQueryParams();\n    else if (Object.isHash(this.options.parameters))\n      this.options.parameters = this.options.parameters.toObject();\n  }\n});\n\nAjax.Request = Class.create(Ajax.Base, {\n  _complete: false,\n\n  initialize: function($super, url, options) {\n    $super(options);\n    this.transport = Ajax.getTransport();\n    this.request(url);\n  },\n\n  request: function(url) {\n    this.url = url;\n    this.method = this.options.method;\n    var params = Object.clone(this.options.parameters);\n\n    if (!['get', 'post'].include(this.method)) {\n      // simulate other verbs over post\n      params['_method'] = this.method;\n      this.method = 'post';\n    }\n\n    this.parameters = params;\n\n    if (params = Object.toQueryString(params)) {\n      // when GET, append parameters to URL\n      if (this.method == 'get')\n        this.url += (this.url.include('?') ? '&' : '?') + params;\n      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))\n        params += '&_=';\n    }\n\n    try {\n      var response = new Ajax.Response(this);\n      if (this.options.onCreate) this.options.onCreate(response);\n      Ajax.Responders.dispatch('onCreate', this, response);\n\n      this.transport.open(this.method.toUpperCase(), this.url,\n        this.options.asynchronous);\n\n      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);\n\n      this.transport.onreadystatechange = this.onStateChange.bind(this);\n      this.setRequestHeaders();\n\n      this.body = this.method == 'post' ? (this.options.postBody || params) : null;\n      this.transport.send(this.body);\n\n      /* Force Firefox to handle ready state 4 for synchronous requests */\n      if (!this.options.asynchronous && this.transport.overrideMimeType)\n        this.onStateChange();\n\n    }\n    catch (e) {\n      this.dispatchException(e);\n    }\n  },\n\n  onStateChange: function() {\n    var readyState = this.transport.readyState;\n    if (readyState > 1 && !((readyState == 4) && this._complete))\n      this.respondToReadyState(this.transport.readyState);\n  },\n\n  setRequestHeaders: function() {\n    var headers = {\n      'X-Requested-With': 'XMLHttpRequest',\n      'X-Prototype-Version': Prototype.Version,\n      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'\n    };\n\n    if (this.method == 'post') {\n      headers['Content-type'] = this.options.contentType +\n        (this.options.encoding ? '; charset=' + this.options.encoding : '');\n\n      /* Force \"Connection: close\" for older Mozilla browsers to work\n       * around a bug where XMLHttpRequest sends an incorrect\n       * Content-length header. See Mozilla Bugzilla #246651.\n       */\n      if (this.transport.overrideMimeType &&\n          (navigator.userAgent.match(/Gecko\\/(\\d{4})/) || [0,2005])[1] < 2005)\n            headers['Connection'] = 'close';\n    }\n\n    // user-defined headers\n    if (typeof this.options.requestHeaders == 'object') {\n      var extras = this.options.requestHeaders;\n\n      if (Object.isFunction(extras.push))\n        for (var i = 0, length = extras.length; i < length; i += 2)\n          headers[extras[i]] = extras[i+1];\n      else\n        $H(extras).each(function(pair) { headers[pair.key] = pair.value });\n    }\n\n    for (var name in headers)\n      this.transport.setRequestHeader(name, headers[name]);\n  },\n\n  success: function() {\n    var status = this.getStatus();\n    return !status || (status >= 200 && status < 300);\n  },\n\n  getStatus: function() {\n    try {\n      return this.transport.status || 0;\n    } catch (e) { return 0 }\n  },\n\n  respondToReadyState: function(readyState) {\n    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);\n\n    if (state == 'Complete') {\n      try {\n        this._complete = true;\n        (this.options['on' + response.status]\n         || this.options['on' + (this.success() ? 'Success' : 'Failure')]\n         || Prototype.emptyFunction)(response, response.headerJSON);\n      } catch (e) {\n        this.dispatchException(e);\n      }\n\n      var contentType = response.getHeader('Content-type');\n      if (this.options.evalJS == 'force'\n          || (this.options.evalJS && this.isSameOrigin() && contentType\n          && contentType.match(/^\\s*(text|application)\\/(x-)?(java|ecma)script(;.*)?\\s*$/i)))\n        this.evalResponse();\n    }\n\n    try {\n      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);\n      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);\n    } catch (e) {\n      this.dispatchException(e);\n    }\n\n    if (state == 'Complete') {\n      // avoid memory leak in MSIE: clean up\n      this.transport.onreadystatechange = Prototype.emptyFunction;\n    }\n  },\n\n  isSameOrigin: function() {\n    var m = this.url.match(/^\\s*https?:\\/\\/[^\\/]*/);\n    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({\n      protocol: location.protocol,\n      domain: document.domain,\n      port: location.port ? ':' + location.port : ''\n    }));\n  },\n\n  getHeader: function(name) {\n    try {\n      return this.transport.getResponseHeader(name) || null;\n    } catch (e) { return null }\n  },\n\n  evalResponse: function() {\n    try {\n      return eval((this.transport.responseText || '').unfilterJSON());\n    } catch (e) {\n      this.dispatchException(e);\n    }\n  },\n\n  dispatchException: function(exception) {\n    (this.options.onException || Prototype.emptyFunction)(this, exception);\n    Ajax.Responders.dispatch('onException', this, exception);\n  }\n});\n\nAjax.Request.Events =\n  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];\n\nAjax.Response = Class.create({\n  initialize: function(request){\n    this.request = request;\n    var transport  = this.transport  = request.transport,\n        readyState = this.readyState = transport.readyState;\n\n    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {\n      this.status       = this.getStatus();\n      this.statusText   = this.getStatusText();\n      this.responseText = String.interpret(transport.responseText);\n      this.headerJSON   = this._getHeaderJSON();\n    }\n\n    if(readyState == 4) {\n      var xml = transport.responseXML;\n      this.responseXML  = Object.isUndefined(xml) ? null : xml;\n      this.responseJSON = this._getResponseJSON();\n    }\n  },\n\n  status:      0,\n  statusText: '',\n\n  getStatus: Ajax.Request.prototype.getStatus,\n\n  getStatusText: function() {\n    try {\n      return this.transport.statusText || '';\n    } catch (e) { return '' }\n  },\n\n  getHeader: Ajax.Request.prototype.getHeader,\n\n  getAllHeaders: function() {\n    try {\n      return this.getAllResponseHeaders();\n    } catch (e) { return null }\n  },\n\n  getResponseHeader: function(name) {\n    return this.transport.getResponseHeader(name);\n  },\n\n  getAllResponseHeaders: function() {\n    return this.transport.getAllResponseHeaders();\n  },\n\n  _getHeaderJSON: function() {\n    var json = this.getHeader('X-JSON');\n    if (!json) return null;\n    json = decodeURIComponent(escape(json));\n    try {\n      return json.evalJSON(this.request.options.sanitizeJSON ||\n        !this.request.isSameOrigin());\n    } catch (e) {\n      this.request.dispatchException(e);\n    }\n  },\n\n  _getResponseJSON: function() {\n    var options = this.request.options;\n    if (!options.evalJSON || (options.evalJSON != 'force' &&\n      !(this.getHeader('Content-type') || '').include('application/json')) ||\n        this.responseText.blank())\n          return null;\n    try {\n      return this.responseText.evalJSON(options.sanitizeJSON ||\n        !this.request.isSameOrigin());\n    } catch (e) {\n      this.request.dispatchException(e);\n    }\n  }\n});\n\nAjax.Updater = Class.create(Ajax.Request, {\n  initialize: function($super, container, url, options) {\n    this.container = {\n      success: (container.success || container),\n      failure: (container.failure || (container.success ? null : container))\n    };\n\n    options = Object.clone(options);\n    var onComplete = options.onComplete;\n    options.onComplete = (function(response, json) {\n      this.updateContent(response.responseText);\n      if (Object.isFunction(onComplete)) onComplete(response, json);\n    }).bind(this);\n\n    $super(url, options);\n  },\n\n  updateContent: function(responseText) {\n    var receiver = this.container[this.success() ? 'success' : 'failure'],\n        options = this.options;\n\n    if (!options.evalScripts) responseText = responseText.stripScripts();\n\n    if (receiver = $(receiver)) {\n      if (options.insertion) {\n        if (Object.isString(options.insertion)) {\n          var insertion = { }; insertion[options.insertion] = responseText;\n          receiver.insert(insertion);\n        }\n        else options.insertion(receiver, responseText);\n      }\n      else receiver.update(responseText);\n    }\n  }\n});\n\nAjax.PeriodicalUpdater = Class.create(Ajax.Base, {\n  initialize: function($super, container, url, options) {\n    $super(options);\n    this.onComplete = this.options.onComplete;\n\n    this.frequency = (this.options.frequency || 2);\n    this.decay = (this.options.decay || 1);\n\n    this.updater = { };\n    this.container = container;\n    this.url = url;\n\n    this.start();\n  },\n\n  start: function() {\n    this.options.onComplete = this.updateComplete.bind(this);\n    this.onTimerEvent();\n  },\n\n  stop: function() {\n    this.updater.options.onComplete = undefined;\n    clearTimeout(this.timer);\n    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);\n  },\n\n  updateComplete: function(response) {\n    if (this.options.decay) {\n      this.decay = (response.responseText == this.lastText ?\n        this.decay * this.options.decay : 1);\n\n      this.lastText = response.responseText;\n    }\n    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);\n  },\n\n  onTimerEvent: function() {\n    this.updater = new Ajax.Updater(this.container, this.url, this.options);\n  }\n});\nfunction $(element) {\n  if (arguments.length > 1) {\n    for (var i = 0, elements = [], length = arguments.length; i < length; i++)\n      elements.push($(arguments[i]));\n    return elements;\n  }\n  if (Object.isString(element))\n    element = document.getElementById(element);\n  return Element.extend(element);\n}\n\nif (Prototype.BrowserFeatures.XPath) {\n  document._getElementsByXPath = function(expression, parentElement) {\n    var results = [];\n    var query = document.evaluate(expression, $(parentElement) || document,\n      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);\n    for (var i = 0, length = query.snapshotLength; i < length; i++)\n      results.push(Element.extend(query.snapshotItem(i)));\n    return results;\n  };\n}\n\n/*--------------------------------------------------------------------------*/\n\nif (!window.Node) var Node = { };\n\nif (!Node.ELEMENT_NODE) {\n  // DOM level 2 ECMAScript Language Binding\n  Object.extend(Node, {\n    ELEMENT_NODE: 1,\n    ATTRIBUTE_NODE: 2,\n    TEXT_NODE: 3,\n    CDATA_SECTION_NODE: 4,\n    ENTITY_REFERENCE_NODE: 5,\n    ENTITY_NODE: 6,\n    PROCESSING_INSTRUCTION_NODE: 7,\n    COMMENT_NODE: 8,\n    DOCUMENT_NODE: 9,\n    DOCUMENT_TYPE_NODE: 10,\n    DOCUMENT_FRAGMENT_NODE: 11,\n    NOTATION_NODE: 12\n  });\n}\n\n(function() {\n  var element = this.Element;\n  this.Element = function(tagName, attributes) {\n    attributes = attributes || { };\n    tagName = tagName.toLowerCase();\n    var cache = Element.cache;\n    if (Prototype.Browser.IE && attributes.name) {\n      tagName = '<' + tagName + ' name=\"' + attributes.name + '\">';\n      delete attributes.name;\n      return Element.writeAttribute(document.createElement(tagName), attributes);\n    }\n    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));\n    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);\n  };\n  Object.extend(this.Element, element || { });\n  if (element) this.Element.prototype = element.prototype;\n}).call(window);\n\nElement.cache = { };\n\nElement.Methods = {\n  visible: function(element) {\n    return $(element).style.display != 'none';\n  },\n\n  toggle: function(element) {\n    element = $(element);\n    Element[Element.visible(element) ? 'hide' : 'show'](element);\n    return element;\n  },\n\n  hide: function(element) {\n    element = $(element);\n    element.style.display = 'none';\n    return element;\n  },\n\n  show: function(element) {\n    element = $(element);\n    element.style.display = '';\n    return element;\n  },\n\n  remove: function(element) {\n    element = $(element);\n    element.parentNode.removeChild(element);\n    return element;\n  },\n\n  update: function(element, content) {\n    element = $(element);\n    if (content && content.toElement) content = content.toElement();\n    if (Object.isElement(content)) return element.update().insert(content);\n    content = Object.toHTML(content);\n    element.innerHTML = content.stripScripts();\n    content.evalScripts.bind(content).defer();\n    return element;\n  },\n\n  replace: function(element, content) {\n    element = $(element);\n    if (content && content.toElement) content = content.toElement();\n    else if (!Object.isElement(content)) {\n      content = Object.toHTML(content);\n      var range = element.ownerDocument.createRange();\n      range.selectNode(element);\n      content.evalScripts.bind(content).defer();\n      content = range.createContextualFragment(content.stripScripts());\n    }\n    element.parentNode.replaceChild(content, element);\n    return element;\n  },\n\n  insert: function(element, insertions) {\n    element = $(element);\n\n    if (Object.isString(insertions) || Object.isNumber(insertions) ||\n        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))\n          insertions = {bottom:insertions};\n\n    var content, insert, tagName, childNodes;\n\n    for (var position in insertions) {\n      content  = insertions[position];\n      position = position.toLowerCase();\n      insert = Element._insertionTranslations[position];\n\n      if (content && content.toElement) content = content.toElement();\n      if (Object.isElement(content)) {\n        insert(element, content);\n        continue;\n      }\n\n      content = Object.toHTML(content);\n\n      tagName = ((position == 'before' || position == 'after')\n        ? element.parentNode : element).tagName.toUpperCase();\n\n      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());\n\n      if (position == 'top' || position == 'after') childNodes.reverse();\n      childNodes.each(insert.curry(element));\n\n      content.evalScripts.bind(content).defer();\n    }\n\n    return element;\n  },\n\n  wrap: function(element, wrapper, attributes) {\n    element = $(element);\n    if (Object.isElement(wrapper))\n      $(wrapper).writeAttribute(attributes || { });\n    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);\n    else wrapper = new Element('div', wrapper);\n    if (element.parentNode)\n      element.parentNode.replaceChild(wrapper, element);\n    wrapper.appendChild(element);\n    return wrapper;\n  },\n\n  inspect: function(element) {\n    element = $(element);\n    var result = '<' + element.tagName.toLowerCase();\n    $H({'id': 'id', 'className': 'class'}).each(function(pair) {\n      var property = pair.first(), attribute = pair.last();\n      var value = (element[property] || '').toString();\n      if (value) result += ' ' + attribute + '=' + value.inspect(true);\n    });\n    return result + '>';\n  },\n\n  recursivelyCollect: function(element, property) {\n    element = $(element);\n    var elements = [];\n    while (element = element[property])\n      if (element.nodeType == 1)\n        elements.push(Element.extend(element));\n    return elements;\n  },\n\n  ancestors: function(element) {\n    return $(element).recursivelyCollect('parentNode');\n  },\n\n  descendants: function(element) {\n    return $(element).select(\"*\");\n  },\n\n  firstDescendant: function(element) {\n    element = $(element).firstChild;\n    while (element && element.nodeType != 1) element = element.nextSibling;\n    return $(element);\n  },\n\n  immediateDescendants: function(element) {\n    if (!(element = $(element).firstChild)) return [];\n    while (element && element.nodeType != 1) element = element.nextSibling;\n    if (element) return [element].concat($(element).nextSiblings());\n    return [];\n  },\n\n  previousSiblings: function(element) {\n    return $(element).recursivelyCollect('previousSibling');\n  },\n\n  nextSiblings: function(element) {\n    return $(element).recursivelyCollect('nextSibling');\n  },\n\n  siblings: function(element) {\n    element = $(element);\n    return element.previousSiblings().reverse().concat(element.nextSiblings());\n  },\n\n  match: function(element, selector) {\n    if (Object.isString(selector))\n      selector = new Selector(selector);\n    return selector.match($(element));\n  },\n\n  up: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return $(element.parentNode);\n    var ancestors = element.ancestors();\n    return Object.isNumber(expression) ? ancestors[expression] :\n      Selector.findElement(ancestors, expression, index);\n  },\n\n  down: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return element.firstDescendant();\n    return Object.isNumber(expression) ? element.descendants()[expression] :\n      Element.select(element, expression)[index || 0];\n  },\n\n  previous: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));\n    var previousSiblings = element.previousSiblings();\n    return Object.isNumber(expression) ? previousSiblings[expression] :\n      Selector.findElement(previousSiblings, expression, index);\n  },\n\n  next: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));\n    var nextSiblings = element.nextSiblings();\n    return Object.isNumber(expression) ? nextSiblings[expression] :\n      Selector.findElement(nextSiblings, expression, index);\n  },\n\n  select: function() {\n    var args = $A(arguments), element = $(args.shift());\n    return Selector.findChildElements(element, args);\n  },\n\n  adjacent: function() {\n    var args = $A(arguments), element = $(args.shift());\n    return Selector.findChildElements(element.parentNode, args).without(element);\n  },\n\n  identify: function(element) {\n    element = $(element);\n    var id = element.readAttribute('id'), self = arguments.callee;\n    if (id) return id;\n    do { id = 'anonymous_element_' + self.counter++ } while ($(id));\n    element.writeAttribute('id', id);\n    return id;\n  },\n\n  readAttribute: function(element, name) {\n    element = $(element);\n    if (Prototype.Browser.IE) {\n      var t = Element._attributeTranslations.read;\n      if (t.values[name]) return t.values[name](element, name);\n      if (t.names[name]) name = t.names[name];\n      if (name.include(':')) {\n        return (!element.attributes || !element.attributes[name]) ? null :\n         element.attributes[name].value;\n      }\n    }\n    return element.getAttribute(name);\n  },\n\n  writeAttribute: function(element, name, value) {\n    element = $(element);\n    var attributes = { }, t = Element._attributeTranslations.write;\n\n    if (typeof name == 'object') attributes = name;\n    else attributes[name] = Object.isUndefined(value) ? true : value;\n\n    for (var attr in attributes) {\n      name = t.names[attr] || attr;\n      value = attributes[attr];\n      if (t.values[attr]) name = t.values[attr](element, value);\n      if (value === false || value === null)\n        element.removeAttribute(name);\n      else if (value === true)\n        element.setAttribute(name, name);\n      else element.setAttribute(name, value);\n    }\n    return element;\n  },\n\n  getHeight: function(element) {\n    return $(element).getDimensions().height;\n  },\n\n  getWidth: function(element) {\n    return $(element).getDimensions().width;\n  },\n\n  classNames: function(element) {\n    return new Element.ClassNames(element);\n  },\n\n  hasClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    var elementClassName = element.className;\n    return (elementClassName.length > 0 && (elementClassName == className ||\n      new RegExp(\"(^|\\\\s)\" + className + \"(\\\\s|$)\").test(elementClassName)));\n  },\n\n  addClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    if (!element.hasClassName(className))\n      element.className += (element.className ? ' ' : '') + className;\n    return element;\n  },\n\n  removeClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    element.className = element.className.replace(\n      new RegExp(\"(^|\\\\s+)\" + className + \"(\\\\s+|$)\"), ' ').strip();\n    return element;\n  },\n\n  toggleClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    return element[element.hasClassName(className) ?\n      'removeClassName' : 'addClassName'](className);\n  },\n\n  // removes whitespace-only text node children\n  cleanWhitespace: function(element) {\n    element = $(element);\n    var node = element.firstChild;\n    while (node) {\n      var nextNode = node.nextSibling;\n      if (node.nodeType == 3 && !/\\S/.test(node.nodeValue))\n        element.removeChild(node);\n      node = nextNode;\n    }\n    return element;\n  },\n\n  empty: function(element) {\n    return $(element).innerHTML.blank();\n  },\n\n  descendantOf: function(element, ancestor) {\n    element = $(element), ancestor = $(ancestor);\n\n    if (element.compareDocumentPosition)\n      return (element.compareDocumentPosition(ancestor) & 8) === 8;\n\n    if (ancestor.contains)\n      return ancestor.contains(element) && ancestor !== element;\n\n    while (element = element.parentNode)\n      if (element == ancestor) return true;\n\n    return false;\n  },\n\n  scrollTo: function(element) {\n    element = $(element);\n    var pos = element.cumulativeOffset();\n    window.scrollTo(pos[0], pos[1]);\n    return element;\n  },\n\n  getStyle: function(element, style) {\n    element = $(element);\n    style = style == 'float' ? 'cssFloat' : style.camelize();\n    var value = element.style[style];\n    if (!value || value == 'auto') {\n      var css = document.defaultView.getComputedStyle(element, null);\n      value = css ? css[style] : null;\n    }\n    if (style == 'opacity') return value ? parseFloat(value) : 1.0;\n    return value == 'auto' ? null : value;\n  },\n\n  getOpacity: function(element) {\n    return $(element).getStyle('opacity');\n  },\n\n  setStyle: function(element, styles) {\n    element = $(element);\n    var elementStyle = element.style, match;\n    if (Object.isString(styles)) {\n      element.style.cssText += ';' + styles;\n      return styles.include('opacity') ?\n        element.setOpacity(styles.match(/opacity:\\s*(\\d?\\.?\\d*)/)[1]) : element;\n    }\n    for (var property in styles)\n      if (property == 'opacity') element.setOpacity(styles[property]);\n      else\n        elementStyle[(property == 'float' || property == 'cssFloat') ?\n          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :\n            property] = styles[property];\n\n    return element;\n  },\n\n  setOpacity: function(element, value) {\n    element = $(element);\n    element.style.opacity = (value == 1 || value === '') ? '' :\n      (value < 0.00001) ? 0 : value;\n    return element;\n  },\n\n  getDimensions: function(element) {\n    element = $(element);\n    var display = element.getStyle('display');\n    if (display != 'none' && display != null) // Safari bug\n      return {width: element.offsetWidth, height: element.offsetHeight};\n\n    // All *Width and *Height properties give 0 on elements with display none,\n    // so enable the element temporarily\n    var els = element.style;\n    var originalVisibility = els.visibility;\n    var originalPosition = els.position;\n    var originalDisplay = els.display;\n    els.visibility = 'hidden';\n    els.position = 'absolute';\n    els.display = 'block';\n    var originalWidth = element.clientWidth;\n    var originalHeight = element.clientHeight;\n    els.display = originalDisplay;\n    els.position = originalPosition;\n    els.visibility = originalVisibility;\n    return {width: originalWidth, height: originalHeight};\n  },\n\n  makePositioned: function(element) {\n    element = $(element);\n    var pos = Element.getStyle(element, 'position');\n    if (pos == 'static' || !pos) {\n      element._madePositioned = true;\n      element.style.position = 'relative';\n      // Opera returns the offset relative to the positioning context, when an\n      // element is position relative but top and left have not been defined\n      if (Prototype.Browser.Opera) {\n        element.style.top = 0;\n        element.style.left = 0;\n      }\n    }\n    return element;\n  },\n\n  undoPositioned: function(element) {\n    element = $(element);\n    if (element._madePositioned) {\n      element._madePositioned = undefined;\n      element.style.position =\n        element.style.top =\n        element.style.left =\n        element.style.bottom =\n        element.style.right = '';\n    }\n    return element;\n  },\n\n  makeClipping: function(element) {\n    element = $(element);\n    if (element._overflow) return element;\n    element._overflow = Element.getStyle(element, 'overflow') || 'auto';\n    if (element._overflow !== 'hidden')\n      element.style.overflow = 'hidden';\n    return element;\n  },\n\n  undoClipping: function(element) {\n    element = $(element);\n    if (!element._overflow) return element;\n    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;\n    element._overflow = null;\n    return element;\n  },\n\n  cumulativeOffset: function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      element = element.offsetParent;\n    } while (element);\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  positionedOffset: function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      element = element.offsetParent;\n      if (element) {\n        if (element.tagName.toUpperCase() == 'BODY') break;\n        var p = Element.getStyle(element, 'position');\n        if (p !== 'static') break;\n      }\n    } while (element);\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  absolutize: function(element) {\n    element = $(element);\n    if (element.getStyle('position') == 'absolute') return element;\n    // Position.prepare(); // To be done manually by Scripty when it needs it.\n\n    var offsets = element.positionedOffset();\n    var top     = offsets[1];\n    var left    = offsets[0];\n    var width   = element.clientWidth;\n    var height  = element.clientHeight;\n\n    element._originalLeft   = left - parseFloat(element.style.left  || 0);\n    element._originalTop    = top  - parseFloat(element.style.top || 0);\n    element._originalWidth  = element.style.width;\n    element._originalHeight = element.style.height;\n\n    element.style.position = 'absolute';\n    element.style.top    = top + 'px';\n    element.style.left   = left + 'px';\n    element.style.width  = width + 'px';\n    element.style.height = height + 'px';\n    return element;\n  },\n\n  relativize: function(element) {\n    element = $(element);\n    if (element.getStyle('position') == 'relative') return element;\n    // Position.prepare(); // To be done manually by Scripty when it needs it.\n\n    element.style.position = 'relative';\n    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);\n    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);\n\n    element.style.top    = top + 'px';\n    element.style.left   = left + 'px';\n    element.style.height = element._originalHeight;\n    element.style.width  = element._originalWidth;\n    return element;\n  },\n\n  cumulativeScrollOffset: function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.scrollTop  || 0;\n      valueL += element.scrollLeft || 0;\n      element = element.parentNode;\n    } while (element);\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  getOffsetParent: function(element) {\n    if (element.offsetParent) return $(element.offsetParent);\n    if (element == document.body) return $(element);\n\n    while ((element = element.parentNode) && element != document.body)\n      if (Element.getStyle(element, 'position') != 'static')\n        return $(element);\n\n    return $(document.body);\n  },\n\n  viewportOffset: function(forElement) {\n    var valueT = 0, valueL = 0;\n\n    var element = forElement;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n\n      // Safari fix\n      if (element.offsetParent == document.body &&\n        Element.getStyle(element, 'position') == 'absolute') break;\n\n    } while (element = element.offsetParent);\n\n    element = forElement;\n    do {\n      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {\n        valueT -= element.scrollTop  || 0;\n        valueL -= element.scrollLeft || 0;\n      }\n    } while (element = element.parentNode);\n\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  clonePosition: function(element, source) {\n    var options = Object.extend({\n      setLeft:    true,\n      setTop:     true,\n      setWidth:   true,\n      setHeight:  true,\n      offsetTop:  0,\n      offsetLeft: 0\n    }, arguments[2] || { });\n\n    // find page position of source\n    source = $(source);\n    var p = source.viewportOffset();\n\n    // find coordinate system to use\n    element = $(element);\n    var delta = [0, 0];\n    var parent = null;\n    // delta [0,0] will do fine with position: fixed elements,\n    // position:absolute needs offsetParent deltas\n    if (Element.getStyle(element, 'position') == 'absolute') {\n      parent = element.getOffsetParent();\n      delta = parent.viewportOffset();\n    }\n\n    // correct by body offsets (fixes Safari)\n    if (parent == document.body) {\n      delta[0] -= document.body.offsetLeft;\n      delta[1] -= document.body.offsetTop;\n    }\n\n    // set position\n    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';\n    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';\n    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';\n    if (options.setHeight) element.style.height = source.offsetHeight + 'px';\n    return element;\n  }\n};\n\nElement.Methods.identify.counter = 1;\n\nObject.extend(Element.Methods, {\n  getElementsBySelector: Element.Methods.select,\n  childElements: Element.Methods.immediateDescendants\n});\n\nElement._attributeTranslations = {\n  write: {\n    names: {\n      className: 'class',\n      htmlFor:   'for'\n    },\n    values: { }\n  }\n};\n\nif (Prototype.Browser.Opera) {\n  Element.Methods.getStyle = Element.Methods.getStyle.wrap(\n    function(proceed, element, style) {\n      switch (style) {\n        case 'left': case 'top': case 'right': case 'bottom':\n          if (proceed(element, 'position') === 'static') return null;\n        case 'height': case 'width':\n          // returns '0px' for hidden elements; we want it to return null\n          if (!Element.visible(element)) return null;\n\n          // returns the border-box dimensions rather than the content-box\n          // dimensions, so we subtract padding and borders from the value\n          var dim = parseInt(proceed(element, style), 10);\n\n          if (dim !== element['offset' + style.capitalize()])\n            return dim + 'px';\n\n          var properties;\n          if (style === 'height') {\n            properties = ['border-top-width', 'padding-top',\n             'padding-bottom', 'border-bottom-width'];\n          }\n          else {\n            properties = ['border-left-width', 'padding-left',\n             'padding-right', 'border-right-width'];\n          }\n          return properties.inject(dim, function(memo, property) {\n            var val = proceed(element, property);\n            return val === null ? memo : memo - parseInt(val, 10);\n          }) + 'px';\n        default: return proceed(element, style);\n      }\n    }\n  );\n\n  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(\n    function(proceed, element, attribute) {\n      if (attribute === 'title') return element.title;\n      return proceed(element, attribute);\n    }\n  );\n}\n\nelse if (Prototype.Browser.IE) {\n  // IE doesn't report offsets correctly for static elements, so we change them\n  // to \"relative\" to get the values, then change them back.\n  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(\n    function(proceed, element) {\n      element = $(element);\n      // IE throws an error if element is not in document\n      try { element.offsetParent }\n      catch(e) { return $(document.body) }\n      var position = element.getStyle('position');\n      if (position !== 'static') return proceed(element);\n      element.setStyle({ position: 'relative' });\n      var value = proceed(element);\n      element.setStyle({ position: position });\n      return value;\n    }\n  );\n\n  $w('positionedOffset viewportOffset').each(function(method) {\n    Element.Methods[method] = Element.Methods[method].wrap(\n      function(proceed, element) {\n        element = $(element);\n        try { element.offsetParent }\n        catch(e) { return Element._returnOffset(0,0) }\n        var position = element.getStyle('position');\n        if (position !== 'static') return proceed(element);\n        // Trigger hasLayout on the offset parent so that IE6 reports\n        // accurate offsetTop and offsetLeft values for position: fixed.\n        var offsetParent = element.getOffsetParent();\n        if (offsetParent && offsetParent.getStyle('position') === 'fixed')\n          offsetParent.setStyle({ zoom: 1 });\n        element.setStyle({ position: 'relative' });\n        var value = proceed(element);\n        element.setStyle({ position: position });\n        return value;\n      }\n    );\n  });\n\n  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(\n    function(proceed, element) {\n      try { element.offsetParent }\n      catch(e) { return Element._returnOffset(0,0) }\n      return proceed(element);\n    }\n  );\n\n  Element.Methods.getStyle = function(element, style) {\n    element = $(element);\n    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();\n    var value = element.style[style];\n    if (!value && element.currentStyle) value = element.currentStyle[style];\n\n    if (style == 'opacity') {\n      if (value = (element.getStyle('filter') || '').match(/alpha\\(opacity=(.*)\\)/))\n        if (value[1]) return parseFloat(value[1]) / 100;\n      return 1.0;\n    }\n\n    if (value == 'auto') {\n      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))\n        return element['offset' + style.capitalize()] + 'px';\n      return null;\n    }\n    return value;\n  };\n\n  Element.Methods.setOpacity = function(element, value) {\n    function stripAlpha(filter){\n      return filter.replace(/alpha\\([^\\)]*\\)/gi,'');\n    }\n    element = $(element);\n    var currentStyle = element.currentStyle;\n    if ((currentStyle && !currentStyle.hasLayout) ||\n      (!currentStyle && element.style.zoom == 'normal'))\n        element.style.zoom = 1;\n\n    var filter = element.getStyle('filter'), style = element.style;\n    if (value == 1 || value === '') {\n      (filter = stripAlpha(filter)) ?\n        style.filter = filter : style.removeAttribute('filter');\n      return element;\n    } else if (value < 0.00001) value = 0;\n    style.filter = stripAlpha(filter) +\n      'alpha(opacity=' + (value * 100) + ')';\n    return element;\n  };\n\n  Element._attributeTranslations = {\n    read: {\n      names: {\n        'class': 'className',\n        'for':   'htmlFor'\n      },\n      values: {\n        _getAttr: function(element, attribute) {\n          return element.getAttribute(attribute, 2);\n        },\n        _getAttrNode: function(element, attribute) {\n          var node = element.getAttributeNode(attribute);\n          return node ? node.value : \"\";\n        },\n        _getEv: function(element, attribute) {\n          attribute = element.getAttribute(attribute);\n          return attribute ? attribute.toString().slice(23, -2) : null;\n        },\n        _flag: function(element, attribute) {\n          return $(element).hasAttribute(attribute) ? attribute : null;\n        },\n        style: function(element) {\n          return element.style.cssText.toLowerCase();\n        },\n        title: function(element) {\n          return element.title;\n        }\n      }\n    }\n  };\n\n  Element._attributeTranslations.write = {\n    names: Object.extend({\n      cellpadding: 'cellPadding',\n      cellspacing: 'cellSpacing'\n    }, Element._attributeTranslations.read.names),\n    values: {\n      checked: function(element, value) {\n        element.checked = !!value;\n      },\n\n      style: function(element, value) {\n        element.style.cssText = value ? value : '';\n      }\n    }\n  };\n\n  Element._attributeTranslations.has = {};\n\n  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +\n      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {\n    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;\n    Element._attributeTranslations.has[attr.toLowerCase()] = attr;\n  });\n\n  (function(v) {\n    Object.extend(v, {\n      href:        v._getAttr,\n      src:         v._getAttr,\n      type:        v._getAttr,\n      action:      v._getAttrNode,\n      disabled:    v._flag,\n      checked:     v._flag,\n      readonly:    v._flag,\n      multiple:    v._flag,\n      onload:      v._getEv,\n      onunload:    v._getEv,\n      onclick:     v._getEv,\n      ondblclick:  v._getEv,\n      onmousedown: v._getEv,\n      onmouseup:   v._getEv,\n      onmouseover: v._getEv,\n      onmousemove: v._getEv,\n      onmouseout:  v._getEv,\n      onfocus:     v._getEv,\n      onblur:      v._getEv,\n      onkeypress:  v._getEv,\n      onkeydown:   v._getEv,\n      onkeyup:     v._getEv,\n      onsubmit:    v._getEv,\n      onreset:     v._getEv,\n      onselect:    v._getEv,\n      onchange:    v._getEv\n    });\n  })(Element._attributeTranslations.read.values);\n}\n\nelse if (Prototype.Browser.Gecko && /rv:1\\.8\\.0/.test(navigator.userAgent)) {\n  Element.Methods.setOpacity = function(element, value) {\n    element = $(element);\n    element.style.opacity = (value == 1) ? 0.999999 :\n      (value === '') ? '' : (value < 0.00001) ? 0 : value;\n    return element;\n  };\n}\n\nelse if (Prototype.Browser.WebKit) {\n  Element.Methods.setOpacity = function(element, value) {\n    element = $(element);\n    element.style.opacity = (value == 1 || value === '') ? '' :\n      (value < 0.00001) ? 0 : value;\n\n    if (value == 1)\n      if(element.tagName.toUpperCase() == 'IMG' && element.width) {\n        element.width++; element.width--;\n      } else try {\n        var n = document.createTextNode(' ');\n        element.appendChild(n);\n        element.removeChild(n);\n      } catch (e) { }\n\n    return element;\n  };\n\n  // Safari returns margins on body which is incorrect if the child is absolutely\n  // positioned.  For performance reasons, redefine Element#cumulativeOffset for\n  // KHTML/WebKit only.\n  Element.Methods.cumulativeOffset = function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      if (element.offsetParent == document.body)\n        if (Element.getStyle(element, 'position') == 'absolute') break;\n\n      element = element.offsetParent;\n    } while (element);\n\n    return Element._returnOffset(valueL, valueT);\n  };\n}\n\nif (Prototype.Browser.IE || Prototype.Browser.Opera) {\n  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements\n  Element.Methods.update = function(element, content) {\n    element = $(element);\n\n    if (content && content.toElement) content = content.toElement();\n    if (Object.isElement(content)) return element.update().insert(content);\n\n    content = Object.toHTML(content);\n    var tagName = element.tagName.toUpperCase();\n\n    if (tagName in Element._insertionTranslations.tags) {\n      $A(element.childNodes).each(function(node) { element.removeChild(node) });\n      Element._getContentFromAnonymousElement(tagName, content.stripScripts())\n        .each(function(node) { element.appendChild(node) });\n    }\n    else element.innerHTML = content.stripScripts();\n\n    content.evalScripts.bind(content).defer();\n    return element;\n  };\n}\n\nif ('outerHTML' in document.createElement('div')) {\n  Element.Methods.replace = function(element, content) {\n    element = $(element);\n\n    if (content && content.toElement) content = content.toElement();\n    if (Object.isElement(content)) {\n      element.parentNode.replaceChild(content, element);\n      return element;\n    }\n\n    content = Object.toHTML(content);\n    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();\n\n    if (Element._insertionTranslations.tags[tagName]) {\n      var nextSibling = element.next();\n      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());\n      parent.removeChild(element);\n      if (nextSibling)\n        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });\n      else\n        fragments.each(function(node) { parent.appendChild(node) });\n    }\n    else element.outerHTML = content.stripScripts();\n\n    content.evalScripts.bind(content).defer();\n    return element;\n  };\n}\n\nElement._returnOffset = function(l, t) {\n  var result = [l, t];\n  result.left = l;\n  result.top = t;\n  return result;\n};\n\nElement._getContentFromAnonymousElement = function(tagName, html) {\n  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];\n  if (t) {\n    div.innerHTML = t[0] + html + t[1];\n    t[2].times(function() { div = div.firstChild });\n  } else div.innerHTML = html;\n  return $A(div.childNodes);\n};\n\nElement._insertionTranslations = {\n  before: function(element, node) {\n    element.parentNode.insertBefore(node, element);\n  },\n  top: function(element, node) {\n    element.insertBefore(node, element.firstChild);\n  },\n  bottom: function(element, node) {\n    element.appendChild(node);\n  },\n  after: function(element, node) {\n    element.parentNode.insertBefore(node, element.nextSibling);\n  },\n  tags: {\n    TABLE:  ['<table>',                '</table>',                   1],\n    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],\n    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],\n    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],\n    SELECT: ['<select>',               '</select>',                  1]\n  }\n};\n\n(function() {\n  Object.extend(this.tags, {\n    THEAD: this.tags.TBODY,\n    TFOOT: this.tags.TBODY,\n    TH:    this.tags.TD\n  });\n}).call(Element._insertionTranslations);\n\nElement.Methods.Simulated = {\n  hasAttribute: function(element, attribute) {\n    attribute = Element._attributeTranslations.has[attribute] || attribute;\n    var node = $(element).getAttributeNode(attribute);\n    return !!(node && node.specified);\n  }\n};\n\nElement.Methods.ByTag = { };\n\nObject.extend(Element, Element.Methods);\n\nif (!Prototype.BrowserFeatures.ElementExtensions &&\n    document.createElement('div')['__proto__']) {\n  window.HTMLElement = { };\n  window.HTMLElement.prototype = document.createElement('div')['__proto__'];\n  Prototype.BrowserFeatures.ElementExtensions = true;\n}\n\nElement.extend = (function() {\n  if (Prototype.BrowserFeatures.SpecificElementExtensions)\n    return Prototype.K;\n\n  var Methods = { }, ByTag = Element.Methods.ByTag;\n\n  var extend = Object.extend(function(element) {\n    if (!element || element._extendedByPrototype ||\n        element.nodeType != 1 || element == window) return element;\n\n    var methods = Object.clone(Methods),\n      tagName = element.tagName.toUpperCase(), property, value;\n\n    // extend methods for specific tags\n    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);\n\n    for (property in methods) {\n      value = methods[property];\n      if (Object.isFunction(value) && !(property in element))\n        element[property] = value.methodize();\n    }\n\n    element._extendedByPrototype = Prototype.emptyFunction;\n    return element;\n\n  }, {\n    refresh: function() {\n      // extend methods for all tags (Safari doesn't need this)\n      if (!Prototype.BrowserFeatures.ElementExtensions) {\n        Object.extend(Methods, Element.Methods);\n        Object.extend(Methods, Element.Methods.Simulated);\n      }\n    }\n  });\n\n  extend.refresh();\n  return extend;\n})();\n\nElement.hasAttribute = function(element, attribute) {\n  if (element.hasAttribute) return element.hasAttribute(attribute);\n  return Element.Methods.Simulated.hasAttribute(element, attribute);\n};\n\nElement.addMethods = function(methods) {\n  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;\n\n  if (!methods) {\n    Object.extend(Form, Form.Methods);\n    Object.extend(Form.Element, Form.Element.Methods);\n    Object.extend(Element.Methods.ByTag, {\n      \"FORM\":     Object.clone(Form.Methods),\n      \"INPUT\":    Object.clone(Form.Element.Methods),\n      \"SELECT\":   Object.clone(Form.Element.Methods),\n      \"TEXTAREA\": Object.clone(Form.Element.Methods)\n    });\n  }\n\n  if (arguments.length == 2) {\n    var tagName = methods;\n    methods = arguments[1];\n  }\n\n  if (!tagName) Object.extend(Element.Methods, methods || { });\n  else {\n    if (Object.isArray(tagName)) tagName.each(extend);\n    else extend(tagName);\n  }\n\n  function extend(tagName) {\n    tagName = tagName.toUpperCase();\n    if (!Element.Methods.ByTag[tagName])\n      Element.Methods.ByTag[tagName] = { };\n    Object.extend(Element.Methods.ByTag[tagName], methods);\n  }\n\n  function copy(methods, destination, onlyIfAbsent) {\n    onlyIfAbsent = onlyIfAbsent || false;\n    for (var property in methods) {\n      var value = methods[property];\n      if (!Object.isFunction(value)) continue;\n      if (!onlyIfAbsent || !(property in destination))\n        destination[property] = value.methodize();\n    }\n  }\n\n  function findDOMClass(tagName) {\n    var klass;\n    var trans = {\n      \"OPTGROUP\": \"OptGroup\", \"TEXTAREA\": \"TextArea\", \"P\": \"Paragraph\",\n      \"FIELDSET\": \"FieldSet\", \"UL\": \"UList\", \"OL\": \"OList\", \"DL\": \"DList\",\n      \"DIR\": \"Directory\", \"H1\": \"Heading\", \"H2\": \"Heading\", \"H3\": \"Heading\",\n      \"H4\": \"Heading\", \"H5\": \"Heading\", \"H6\": \"Heading\", \"Q\": \"Quote\",\n      \"INS\": \"Mod\", \"DEL\": \"Mod\", \"A\": \"Anchor\", \"IMG\": \"Image\", \"CAPTION\":\n      \"TableCaption\", \"COL\": \"TableCol\", \"COLGROUP\": \"TableCol\", \"THEAD\":\n      \"TableSection\", \"TFOOT\": \"TableSection\", \"TBODY\": \"TableSection\", \"TR\":\n      \"TableRow\", \"TH\": \"TableCell\", \"TD\": \"TableCell\", \"FRAMESET\":\n      \"FrameSet\", \"IFRAME\": \"IFrame\"\n    };\n    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';\n    if (window[klass]) return window[klass];\n    klass = 'HTML' + tagName + 'Element';\n    if (window[klass]) return window[klass];\n    klass = 'HTML' + tagName.capitalize() + 'Element';\n    if (window[klass]) return window[klass];\n\n    window[klass] = { };\n    window[klass].prototype = document.createElement(tagName)['__proto__'];\n    return window[klass];\n  }\n\n  if (F.ElementExtensions) {\n    copy(Element.Methods, HTMLElement.prototype);\n    copy(Element.Methods.Simulated, HTMLElement.prototype, true);\n  }\n\n  if (F.SpecificElementExtensions) {\n    for (var tag in Element.Methods.ByTag) {\n      var klass = findDOMClass(tag);\n      if (Object.isUndefined(klass)) continue;\n      copy(T[tag], klass.prototype);\n    }\n  }\n\n  Object.extend(Element, Element.Methods);\n  delete Element.ByTag;\n\n  if (Element.extend.refresh) Element.extend.refresh();\n  Element.cache = { };\n};\n\ndocument.viewport = {\n  getDimensions: function() {\n    var dimensions = { }, B = Prototype.Browser;\n    $w('width height').each(function(d) {\n      var D = d.capitalize();\n      if (B.WebKit && !document.evaluate) {\n        // Safari <3.0 needs self.innerWidth/Height\n        dimensions[d] = self['inner' + D];\n      } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {\n        // Opera <9.5 needs document.body.clientWidth/Height\n        dimensions[d] = document.body['client' + D]\n      } else {\n        dimensions[d] = document.documentElement['client' + D];\n      }\n    });\n    return dimensions;\n  },\n\n  getWidth: function() {\n    return this.getDimensions().width;\n  },\n\n  getHeight: function() {\n    return this.getDimensions().height;\n  },\n\n  getScrollOffsets: function() {\n    return Element._returnOffset(\n      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,\n      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);\n  }\n};\n/* Portions of the Selector class are derived from Jack Slocum's DomQuery,\n * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style\n * license.  Please see http://www.yui-ext.com/ for more information. */\n\nvar Selector = Class.create({\n  initialize: function(expression) {\n    this.expression = expression.strip();\n\n    if (this.shouldUseSelectorsAPI()) {\n      this.mode = 'selectorsAPI';\n    } else if (this.shouldUseXPath()) {\n      this.mode = 'xpath';\n      this.compileXPathMatcher();\n    } else {\n      this.mode = \"normal\";\n      this.compileMatcher();\n    }\n\n  },\n\n  shouldUseXPath: function() {\n    if (!Prototype.BrowserFeatures.XPath) return false;\n\n    var e = this.expression;\n\n    // Safari 3 chokes on :*-of-type and :empty\n    if (Prototype.Browser.WebKit &&\n     (e.include(\"-of-type\") || e.include(\":empty\")))\n      return false;\n\n    // XPath can't do namespaced attributes, nor can it read\n    // the \"checked\" property from DOM nodes\n    if ((/(\\[[\\w-]*?:|:checked)/).test(e))\n      return false;\n\n    return true;\n  },\n\n  shouldUseSelectorsAPI: function() {\n    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;\n\n    if (!Selector._div) Selector._div = new Element('div');\n\n    // Make sure the browser treats the selector as valid. Test on an\n    // isolated element to minimize cost of this check.\n    try {\n      Selector._div.querySelector(this.expression);\n    } catch(e) {\n      return false;\n    }\n\n    return true;\n  },\n\n  compileMatcher: function() {\n    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,\n        c = Selector.criteria, le, p, m;\n\n    if (Selector._cache[e]) {\n      this.matcher = Selector._cache[e];\n      return;\n    }\n\n    this.matcher = [\"this.matcher = function(root) {\",\n                    \"var r = root, h = Selector.handlers, c = false, n;\"];\n\n    while (e && le != e && (/\\S/).test(e)) {\n      le = e;\n      for (var i in ps) {\n        p = ps[i];\n        if (m = e.match(p)) {\n          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :\n            new Template(c[i]).evaluate(m));\n          e = e.replace(m[0], '');\n          break;\n        }\n      }\n    }\n\n    this.matcher.push(\"return h.unique(n);\\n}\");\n    eval(this.matcher.join('\\n'));\n    Selector._cache[this.expression] = this.matcher;\n  },\n\n  compileXPathMatcher: function() {\n    var e = this.expression, ps = Selector.patterns,\n        x = Selector.xpath, le, m;\n\n    if (Selector._cache[e]) {\n      this.xpath = Selector._cache[e]; return;\n    }\n\n    this.matcher = ['.//*'];\n    while (e && le != e && (/\\S/).test(e)) {\n      le = e;\n      for (var i in ps) {\n        if (m = e.match(ps[i])) {\n          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :\n            new Template(x[i]).evaluate(m));\n          e = e.replace(m[0], '');\n          break;\n        }\n      }\n    }\n\n    this.xpath = this.matcher.join('');\n    Selector._cache[this.expression] = this.xpath;\n  },\n\n  findElements: function(root) {\n    root = root || document;\n    var e = this.expression, results;\n\n    switch (this.mode) {\n      case 'selectorsAPI':\n        // querySelectorAll queries document-wide, then filters to descendants\n        // of the context element. That's not what we want.\n        // Add an explicit context to the selector if necessary.\n        if (root !== document) {\n          var oldId = root.id, id = $(root).identify();\n          e = \"#\" + id + \" \" + e;\n        }\n\n        results = $A(root.querySelectorAll(e)).map(Element.extend);\n        root.id = oldId;\n\n        return results;\n      case 'xpath':\n        return document._getElementsByXPath(this.xpath, root);\n      default:\n       return this.matcher(root);\n    }\n  },\n\n  match: function(element) {\n    this.tokens = [];\n\n    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;\n    var le, p, m;\n\n    while (e && le !== e && (/\\S/).test(e)) {\n      le = e;\n      for (var i in ps) {\n        p = ps[i];\n        if (m = e.match(p)) {\n          // use the Selector.assertions methods unless the selector\n          // is too complex.\n          if (as[i]) {\n            this.tokens.push([i, Object.clone(m)]);\n            e = e.replace(m[0], '');\n          } else {\n            // reluctantly do a document-wide search\n            // and look for a match in the array\n            return this.findElements(document).include(element);\n          }\n        }\n      }\n    }\n\n    var match = true, name, matches;\n    for (var i = 0, token; token = this.tokens[i]; i++) {\n      name = token[0], matches = token[1];\n      if (!Selector.assertions[name](element, matches)) {\n        match = false; break;\n      }\n    }\n\n    return match;\n  },\n\n  toString: function() {\n    return this.expression;\n  },\n\n  inspect: function() {\n    return \"#<Selector:\" + this.expression.inspect() + \">\";\n  }\n});\n\nObject.extend(Selector, {\n  _cache: { },\n\n  xpath: {\n    descendant:   \"//*\",\n    child:        \"/*\",\n    adjacent:     \"/following-sibling::*[1]\",\n    laterSibling: '/following-sibling::*',\n    tagName:      function(m) {\n      if (m[1] == '*') return '';\n      return \"[local-name()='\" + m[1].toLowerCase() +\n             \"' or local-name()='\" + m[1].toUpperCase() + \"']\";\n    },\n    className:    \"[contains(concat(' ', @class, ' '), ' #{1} ')]\",\n    id:           \"[@id='#{1}']\",\n    attrPresence: function(m) {\n      m[1] = m[1].toLowerCase();\n      return new Template(\"[@#{1}]\").evaluate(m);\n    },\n    attr: function(m) {\n      m[1] = m[1].toLowerCase();\n      m[3] = m[5] || m[6];\n      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);\n    },\n    pseudo: function(m) {\n      var h = Selector.xpath.pseudos[m[1]];\n      if (!h) return '';\n      if (Object.isFunction(h)) return h(m);\n      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);\n    },\n    operators: {\n      '=':  \"[@#{1}='#{3}']\",\n      '!=': \"[@#{1}!='#{3}']\",\n      '^=': \"[starts-with(@#{1}, '#{3}')]\",\n      '$=': \"[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']\",\n      '*=': \"[contains(@#{1}, '#{3}')]\",\n      '~=': \"[contains(concat(' ', @#{1}, ' '), ' #{3} ')]\",\n      '|=': \"[contains(concat('-', @#{1}, '-'), '-#{3}-')]\"\n    },\n    pseudos: {\n      'first-child': '[not(preceding-sibling::*)]',\n      'last-child':  '[not(following-sibling::*)]',\n      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',\n      'empty':       \"[count(*) = 0 and (count(text()) = 0)]\",\n      'checked':     \"[@checked]\",\n      'disabled':    \"[(@disabled) and (@type!='hidden')]\",\n      'enabled':     \"[not(@disabled) and (@type!='hidden')]\",\n      'not': function(m) {\n        var e = m[6], p = Selector.patterns,\n            x = Selector.xpath, le, v;\n\n        var exclusion = [];\n        while (e && le != e && (/\\S/).test(e)) {\n          le = e;\n          for (var i in p) {\n            if (m = e.match(p[i])) {\n              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);\n              exclusion.push(\"(\" + v.substring(1, v.length - 1) + \")\");\n              e = e.replace(m[0], '');\n              break;\n            }\n          }\n        }\n        return \"[not(\" + exclusion.join(\" and \") + \")]\";\n      },\n      'nth-child':      function(m) {\n        return Selector.xpath.pseudos.nth(\"(count(./preceding-sibling::*) + 1) \", m);\n      },\n      'nth-last-child': function(m) {\n        return Selector.xpath.pseudos.nth(\"(count(./following-sibling::*) + 1) \", m);\n      },\n      'nth-of-type':    function(m) {\n        return Selector.xpath.pseudos.nth(\"position() \", m);\n      },\n      'nth-last-of-type': function(m) {\n        return Selector.xpath.pseudos.nth(\"(last() + 1 - position()) \", m);\n      },\n      'first-of-type':  function(m) {\n        m[6] = \"1\"; return Selector.xpath.pseudos['nth-of-type'](m);\n      },\n      'last-of-type':   function(m) {\n        m[6] = \"1\"; return Selector.xpath.pseudos['nth-last-of-type'](m);\n      },\n      'only-of-type':   function(m) {\n        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);\n      },\n      nth: function(fragment, m) {\n        var mm, formula = m[6], predicate;\n        if (formula == 'even') formula = '2n+0';\n        if (formula == 'odd')  formula = '2n+1';\n        if (mm = formula.match(/^(\\d+)$/)) // digit only\n          return '[' + fragment + \"= \" + mm[1] + ']';\n        if (mm = formula.match(/^(-?\\d*)?n(([+-])(\\d+))?/)) { // an+b\n          if (mm[1] == \"-\") mm[1] = -1;\n          var a = mm[1] ? Number(mm[1]) : 1;\n          var b = mm[2] ? Number(mm[2]) : 0;\n          predicate = \"[((#{fragment} - #{b}) mod #{a} = 0) and \" +\n          \"((#{fragment} - #{b}) div #{a} >= 0)]\";\n          return new Template(predicate).evaluate({\n            fragment: fragment, a: a, b: b });\n        }\n      }\n    }\n  },\n\n  criteria: {\n    tagName:      'n = h.tagName(n, r, \"#{1}\", c);      c = false;',\n    className:    'n = h.className(n, r, \"#{1}\", c);    c = false;',\n    id:           'n = h.id(n, r, \"#{1}\", c);           c = false;',\n    attrPresence: 'n = h.attrPresence(n, r, \"#{1}\", c); c = false;',\n    attr: function(m) {\n      m[3] = (m[5] || m[6]);\n      return new Template('n = h.attr(n, r, \"#{1}\", \"#{3}\", \"#{2}\", c); c = false;').evaluate(m);\n    },\n    pseudo: function(m) {\n      if (m[6]) m[6] = m[6].replace(/\"/g, '\\\\\"');\n      return new Template('n = h.pseudo(n, \"#{1}\", \"#{6}\", r, c); c = false;').evaluate(m);\n    },\n    descendant:   'c = \"descendant\";',\n    child:        'c = \"child\";',\n    adjacent:     'c = \"adjacent\";',\n    laterSibling: 'c = \"laterSibling\";'\n  },\n\n  patterns: {\n    // combinators must be listed first\n    // (and descendant needs to be last combinator)\n    laterSibling: /^\\s*~\\s*/,\n    child:        /^\\s*>\\s*/,\n    adjacent:     /^\\s*\\+\\s*/,\n    descendant:   /^\\s/,\n\n    // selectors follow\n    tagName:      /^\\s*(\\*|[\\w\\-]+)(\\b|$)?/,\n    id:           /^#([\\w\\-\\*]+)(\\b|$)/,\n    className:    /^\\.([\\w\\-\\*]+)(\\b|$)/,\n    pseudo:\n/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\\((.*?)\\))?(\\b|$|(?=\\s|[:+~>]))/,\n    attrPresence: /^\\[((?:[\\w]+:)?[\\w]+)\\]/,\n    attr:         /\\[((?:[\\w-]*:)?[\\w-]+)\\s*(?:([!^$*~|]?=)\\s*((['\"])([^\\4]*?)\\4|([^'\"][^\\]]*?)))?\\]/\n  },\n\n  // for Selector.match and Element#match\n  assertions: {\n    tagName: function(element, matches) {\n      return matches[1].toUpperCase() == element.tagName.toUpperCase();\n    },\n\n    className: function(element, matches) {\n      return Element.hasClassName(element, matches[1]);\n    },\n\n    id: function(element, matches) {\n      return element.id === matches[1];\n    },\n\n    attrPresence: function(element, matches) {\n      return Element.hasAttribute(element, matches[1]);\n    },\n\n    attr: function(element, matches) {\n      var nodeValue = Element.readAttribute(element, matches[1]);\n      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);\n    }\n  },\n\n  handlers: {\n    // UTILITY FUNCTIONS\n    // joins two collections\n    concat: function(a, b) {\n      for (var i = 0, node; node = b[i]; i++)\n        a.push(node);\n      return a;\n    },\n\n    // marks an array of nodes for counting\n    mark: function(nodes) {\n      var _true = Prototype.emptyFunction;\n      for (var i = 0, node; node = nodes[i]; i++)\n        node._countedByPrototype = _true;\n      return nodes;\n    },\n\n    unmark: function(nodes) {\n      for (var i = 0, node; node = nodes[i]; i++)\n        node._countedByPrototype = undefined;\n      return nodes;\n    },\n\n    // mark each child node with its position (for nth calls)\n    // \"ofType\" flag indicates whether we're indexing for nth-of-type\n    // rather than nth-child\n    index: function(parentNode, reverse, ofType) {\n      parentNode._countedByPrototype = Prototype.emptyFunction;\n      if (reverse) {\n        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {\n          var node = nodes[i];\n          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;\n        }\n      } else {\n        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)\n          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;\n      }\n    },\n\n    // filters out duplicates and extends all nodes\n    unique: function(nodes) {\n      if (nodes.length == 0) return nodes;\n      var results = [], n;\n      for (var i = 0, l = nodes.length; i < l; i++)\n        if (!(n = nodes[i])._countedByPrototype) {\n          n._countedByPrototype = Prototype.emptyFunction;\n          results.push(Element.extend(n));\n        }\n      return Selector.handlers.unmark(results);\n    },\n\n    // COMBINATOR FUNCTIONS\n    descendant: function(nodes) {\n      var h = Selector.handlers;\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        h.concat(results, node.getElementsByTagName('*'));\n      return results;\n    },\n\n    child: function(nodes) {\n      var h = Selector.handlers;\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        for (var j = 0, child; child = node.childNodes[j]; j++)\n          if (child.nodeType == 1 && child.tagName != '!') results.push(child);\n      }\n      return results;\n    },\n\n    adjacent: function(nodes) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        var next = this.nextElementSibling(node);\n        if (next) results.push(next);\n      }\n      return results;\n    },\n\n    laterSibling: function(nodes) {\n      var h = Selector.handlers;\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        h.concat(results, Element.nextSiblings(node));\n      return results;\n    },\n\n    nextElementSibling: function(node) {\n      while (node = node.nextSibling)\n        if (node.nodeType == 1) return node;\n      return null;\n    },\n\n    previousElementSibling: function(node) {\n      while (node = node.previousSibling)\n        if (node.nodeType == 1) return node;\n      return null;\n    },\n\n    // TOKEN FUNCTIONS\n    tagName: function(nodes, root, tagName, combinator) {\n      var uTagName = tagName.toUpperCase();\n      var results = [], h = Selector.handlers;\n      if (nodes) {\n        if (combinator) {\n          // fastlane for ordinary descendant combinators\n          if (combinator == \"descendant\") {\n            for (var i = 0, node; node = nodes[i]; i++)\n              h.concat(results, node.getElementsByTagName(tagName));\n            return results;\n          } else nodes = this[combinator](nodes);\n          if (tagName == \"*\") return nodes;\n        }\n        for (var i = 0, node; node = nodes[i]; i++)\n          if (node.tagName.toUpperCase() === uTagName) results.push(node);\n        return results;\n      } else return root.getElementsByTagName(tagName);\n    },\n\n    id: function(nodes, root, id, combinator) {\n      var targetNode = $(id), h = Selector.handlers;\n      if (!targetNode) return [];\n      if (!nodes && root == document) return [targetNode];\n      if (nodes) {\n        if (combinator) {\n          if (combinator == 'child') {\n            for (var i = 0, node; node = nodes[i]; i++)\n              if (targetNode.parentNode == node) return [targetNode];\n          } else if (combinator == 'descendant') {\n            for (var i = 0, node; node = nodes[i]; i++)\n              if (Element.descendantOf(targetNode, node)) return [targetNode];\n          } else if (combinator == 'adjacent') {\n            for (var i = 0, node; node = nodes[i]; i++)\n              if (Selector.handlers.previousElementSibling(targetNode) == node)\n                return [targetNode];\n          } else nodes = h[combinator](nodes);\n        }\n        for (var i = 0, node; node = nodes[i]; i++)\n          if (node == targetNode) return [targetNode];\n        return [];\n      }\n      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];\n    },\n\n    className: function(nodes, root, className, combinator) {\n      if (nodes && combinator) nodes = this[combinator](nodes);\n      return Selector.handlers.byClassName(nodes, root, className);\n    },\n\n    byClassName: function(nodes, root, className) {\n      if (!nodes) nodes = Selector.handlers.descendant([root]);\n      var needle = ' ' + className + ' ';\n      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {\n        nodeClassName = node.className;\n        if (nodeClassName.length == 0) continue;\n        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))\n          results.push(node);\n      }\n      return results;\n    },\n\n    attrPresence: function(nodes, root, attr, combinator) {\n      if (!nodes) nodes = root.getElementsByTagName(\"*\");\n      if (nodes && combinator) nodes = this[combinator](nodes);\n      var results = [];\n      for (var i = 0, node; node = nodes[i]; i++)\n        if (Element.hasAttribute(node, attr)) results.push(node);\n      return results;\n    },\n\n    attr: function(nodes, root, attr, value, operator, combinator) {\n      if (!nodes) nodes = root.getElementsByTagName(\"*\");\n      if (nodes && combinator) nodes = this[combinator](nodes);\n      var handler = Selector.operators[operator], results = [];\n      for (var i = 0, node; node = nodes[i]; i++) {\n        var nodeValue = Element.readAttribute(node, attr);\n        if (nodeValue === null) continue;\n        if (handler(nodeValue, value)) results.push(node);\n      }\n      return results;\n    },\n\n    pseudo: function(nodes, name, value, root, combinator) {\n      if (nodes && combinator) nodes = this[combinator](nodes);\n      if (!nodes) nodes = root.getElementsByTagName(\"*\");\n      return Selector.pseudos[name](nodes, value, root);\n    }\n  },\n\n  pseudos: {\n    'first-child': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        if (Selector.handlers.previousElementSibling(node)) continue;\n          results.push(node);\n      }\n      return results;\n    },\n    'last-child': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        if (Selector.handlers.nextElementSibling(node)) continue;\n          results.push(node);\n      }\n      return results;\n    },\n    'only-child': function(nodes, value, root) {\n      var h = Selector.handlers;\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))\n          results.push(node);\n      return results;\n    },\n    'nth-child':        function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, formula, root);\n    },\n    'nth-last-child':   function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, formula, root, true);\n    },\n    'nth-of-type':      function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, formula, root, false, true);\n    },\n    'nth-last-of-type': function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, formula, root, true, true);\n    },\n    'first-of-type':    function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, \"1\", root, false, true);\n    },\n    'last-of-type':     function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, \"1\", root, true, true);\n    },\n    'only-of-type':     function(nodes, formula, root) {\n      var p = Selector.pseudos;\n      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);\n    },\n\n    // handles the an+b logic\n    getIndices: function(a, b, total) {\n      if (a == 0) return b > 0 ? [b] : [];\n      return $R(1, total).inject([], function(memo, i) {\n        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);\n        return memo;\n      });\n    },\n\n    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type\n    nth: function(nodes, formula, root, reverse, ofType) {\n      if (nodes.length == 0) return [];\n      if (formula == 'even') formula = '2n+0';\n      if (formula == 'odd')  formula = '2n+1';\n      var h = Selector.handlers, results = [], indexed = [], m;\n      h.mark(nodes);\n      for (var i = 0, node; node = nodes[i]; i++) {\n        if (!node.parentNode._countedByPrototype) {\n          h.index(node.parentNode, reverse, ofType);\n          indexed.push(node.parentNode);\n        }\n      }\n      if (formula.match(/^\\d+$/)) { // just a number\n        formula = Number(formula);\n        for (var i = 0, node; node = nodes[i]; i++)\n          if (node.nodeIndex == formula) results.push(node);\n      } else if (m = formula.match(/^(-?\\d*)?n(([+-])(\\d+))?/)) { // an+b\n        if (m[1] == \"-\") m[1] = -1;\n        var a = m[1] ? Number(m[1]) : 1;\n        var b = m[2] ? Number(m[2]) : 0;\n        var indices = Selector.pseudos.getIndices(a, b, nodes.length);\n        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {\n          for (var j = 0; j < l; j++)\n            if (node.nodeIndex == indices[j]) results.push(node);\n        }\n      }\n      h.unmark(nodes);\n      h.unmark(indexed);\n      return results;\n    },\n\n    'empty': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        // IE treats comments as element nodes\n        if (node.tagName == '!' || node.firstChild) continue;\n        results.push(node);\n      }\n      return results;\n    },\n\n    'not': function(nodes, selector, root) {\n      var h = Selector.handlers, selectorType, m;\n      var exclusions = new Selector(selector).findElements(root);\n      h.mark(exclusions);\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (!node._countedByPrototype) results.push(node);\n      h.unmark(exclusions);\n      return results;\n    },\n\n    'enabled': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (!node.disabled && (!node.type || node.type !== 'hidden'))\n          results.push(node);\n      return results;\n    },\n\n    'disabled': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (node.disabled) results.push(node);\n      return results;\n    },\n\n    'checked': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (node.checked) results.push(node);\n      return results;\n    }\n  },\n\n  operators: {\n    '=':  function(nv, v) { return nv == v; },\n    '!=': function(nv, v) { return nv != v; },\n    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },\n    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },\n    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },\n    '$=': function(nv, v) { return nv.endsWith(v); },\n    '*=': function(nv, v) { return nv.include(v); },\n    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },\n    '|=': function(nv, v) { return ('-' + (nv || \"\").toUpperCase() +\n     '-').include('-' + (v || \"\").toUpperCase() + '-'); }\n  },\n\n  split: function(expression) {\n    var expressions = [];\n    expression.scan(/(([\\w#:.~>+()\\s-]+|\\*|\\[.*?\\])+)\\s*(,|$)/, function(m) {\n      expressions.push(m[1].strip());\n    });\n    return expressions;\n  },\n\n  matchElements: function(elements, expression) {\n    var matches = $$(expression), h = Selector.handlers;\n    h.mark(matches);\n    for (var i = 0, results = [], element; element = elements[i]; i++)\n      if (element._countedByPrototype) results.push(element);\n    h.unmark(matches);\n    return results;\n  },\n\n  findElement: function(elements, expression, index) {\n    if (Object.isNumber(expression)) {\n      index = expression; expression = false;\n    }\n    return Selector.matchElements(elements, expression || '*')[index || 0];\n  },\n\n  findChildElements: function(element, expressions) {\n    expressions = Selector.split(expressions.join(','));\n    var results = [], h = Selector.handlers;\n    for (var i = 0, l = expressions.length, selector; i < l; i++) {\n      selector = new Selector(expressions[i].strip());\n      h.concat(results, selector.findElements(element));\n    }\n    return (l > 1) ? h.unique(results) : results;\n  }\n});\n\nif (Prototype.Browser.IE) {\n  Object.extend(Selector.handlers, {\n    // IE returns comment nodes on getElementsByTagName(\"*\").\n    // Filter them out.\n    concat: function(a, b) {\n      for (var i = 0, node; node = b[i]; i++)\n        if (node.tagName !== \"!\") a.push(node);\n      return a;\n    },\n\n    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.\n    unmark: function(nodes) {\n      for (var i = 0, node; node = nodes[i]; i++)\n        node.removeAttribute('_countedByPrototype');\n      return nodes;\n    }\n  });\n}\n\nfunction $$() {\n  return Selector.findChildElements(document, $A(arguments));\n}\nvar Form = {\n  reset: function(form) {\n    $(form).reset();\n    return form;\n  },\n\n  serializeElements: function(elements, options) {\n    if (typeof options != 'object') options = { hash: !!options };\n    else if (Object.isUndefined(options.hash)) options.hash = true;\n    var key, value, submitted = false, submit = options.submit;\n\n    var data = elements.inject({ }, function(result, element) {\n      if (!element.disabled && element.name) {\n        key = element.name; value = $(element).getValue();\n        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&\n            submit !== false && (!submit || key == submit) && (submitted = true)))) {\n          if (key in result) {\n            // a key is already present; construct an array of values\n            if (!Object.isArray(result[key])) result[key] = [result[key]];\n            result[key].push(value);\n          }\n          else result[key] = value;\n        }\n      }\n      return result;\n    });\n\n    return options.hash ? data : Object.toQueryString(data);\n  }\n};\n\nForm.Methods = {\n  serialize: function(form, options) {\n    return Form.serializeElements(Form.getElements(form), options);\n  },\n\n  getElements: function(form) {\n    return $A($(form).getElementsByTagName('*')).inject([],\n      function(elements, child) {\n        if (Form.Element.Serializers[child.tagName.toLowerCase()])\n          elements.push(Element.extend(child));\n        return elements;\n      }\n    );\n  },\n\n  getInputs: function(form, typeName, name) {\n    form = $(form);\n    var inputs = form.getElementsByTagName('input');\n\n    if (!typeName && !name) return $A(inputs).map(Element.extend);\n\n    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {\n      var input = inputs[i];\n      if ((typeName && input.type != typeName) || (name && input.name != name))\n        continue;\n      matchingInputs.push(Element.extend(input));\n    }\n\n    return matchingInputs;\n  },\n\n  disable: function(form) {\n    form = $(form);\n    Form.getElements(form).invoke('disable');\n    return form;\n  },\n\n  enable: function(form) {\n    form = $(form);\n    Form.getElements(form).invoke('enable');\n    return form;\n  },\n\n  findFirstElement: function(form) {\n    var elements = $(form).getElements().findAll(function(element) {\n      return 'hidden' != element.type && !element.disabled;\n    });\n    var firstByIndex = elements.findAll(function(element) {\n      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;\n    }).sortBy(function(element) { return element.tabIndex }).first();\n\n    return firstByIndex ? firstByIndex : elements.find(function(element) {\n      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());\n    });\n  },\n\n  focusFirstElement: function(form) {\n    form = $(form);\n    form.findFirstElement().activate();\n    return form;\n  },\n\n  request: function(form, options) {\n    form = $(form), options = Object.clone(options || { });\n\n    var params = options.parameters, action = form.readAttribute('action') || '';\n    if (action.blank()) action = window.location.href;\n    options.parameters = form.serialize(true);\n\n    if (params) {\n      if (Object.isString(params)) params = params.toQueryParams();\n      Object.extend(options.parameters, params);\n    }\n\n    if (form.hasAttribute('method') && !options.method)\n      options.method = form.method;\n\n    return new Ajax.Request(action, options);\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nForm.Element = {\n  focus: function(element) {\n    $(element).focus();\n    return element;\n  },\n\n  select: function(element) {\n    $(element).select();\n    return element;\n  }\n};\n\nForm.Element.Methods = {\n  serialize: function(element) {\n    element = $(element);\n    if (!element.disabled && element.name) {\n      var value = element.getValue();\n      if (value != undefined) {\n        var pair = { };\n        pair[element.name] = value;\n        return Object.toQueryString(pair);\n      }\n    }\n    return '';\n  },\n\n  getValue: function(element) {\n    element = $(element);\n    var method = element.tagName.toLowerCase();\n    return Form.Element.Serializers[method](element);\n  },\n\n  setValue: function(element, value) {\n    element = $(element);\n    var method = element.tagName.toLowerCase();\n    Form.Element.Serializers[method](element, value);\n    return element;\n  },\n\n  clear: function(element) {\n    $(element).value = '';\n    return element;\n  },\n\n  present: function(element) {\n    return $(element).value != '';\n  },\n\n  activate: function(element) {\n    element = $(element);\n    try {\n      element.focus();\n      if (element.select && (element.tagName.toLowerCase() != 'input' ||\n          !['button', 'reset', 'submit'].include(element.type)))\n        element.select();\n    } catch (e) { }\n    return element;\n  },\n\n  disable: function(element) {\n    element = $(element);\n    element.disabled = true;\n    return element;\n  },\n\n  enable: function(element) {\n    element = $(element);\n    element.disabled = false;\n    return element;\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Field = Form.Element;\nvar $F = Form.Element.Methods.getValue;\n\n/*--------------------------------------------------------------------------*/\n\nForm.Element.Serializers = {\n  input: function(element, value) {\n    switch (element.type.toLowerCase()) {\n      case 'checkbox':\n      case 'radio':\n        return Form.Element.Serializers.inputSelector(element, value);\n      default:\n        return Form.Element.Serializers.textarea(element, value);\n    }\n  },\n\n  inputSelector: function(element, value) {\n    if (Object.isUndefined(value)) return element.checked ? element.value : null;\n    else element.checked = !!value;\n  },\n\n  textarea: function(element, value) {\n    if (Object.isUndefined(value)) return element.value;\n    else element.value = value;\n  },\n\n  select: function(element, value) {\n    if (Object.isUndefined(value))\n      return this[element.type == 'select-one' ?\n        'selectOne' : 'selectMany'](element);\n    else {\n      var opt, currentValue, single = !Object.isArray(value);\n      for (var i = 0, length = element.length; i < length; i++) {\n        opt = element.options[i];\n        currentValue = this.optionValue(opt);\n        if (single) {\n          if (currentValue == value) {\n            opt.selected = true;\n            return;\n          }\n        }\n        else opt.selected = value.include(currentValue);\n      }\n    }\n  },\n\n  selectOne: function(element) {\n    var index = element.selectedIndex;\n    return index >= 0 ? this.optionValue(element.options[index]) : null;\n  },\n\n  selectMany: function(element) {\n    var values, length = element.length;\n    if (!length) return null;\n\n    for (var i = 0, values = []; i < length; i++) {\n      var opt = element.options[i];\n      if (opt.selected) values.push(this.optionValue(opt));\n    }\n    return values;\n  },\n\n  optionValue: function(opt) {\n    // extend element because hasAttribute may not be native\n    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nAbstract.TimedObserver = Class.create(PeriodicalExecuter, {\n  initialize: function($super, element, frequency, callback) {\n    $super(callback, frequency);\n    this.element   = $(element);\n    this.lastValue = this.getValue();\n  },\n\n  execute: function() {\n    var value = this.getValue();\n    if (Object.isString(this.lastValue) && Object.isString(value) ?\n        this.lastValue != value : String(this.lastValue) != String(value)) {\n      this.callback(this.element, value);\n      this.lastValue = value;\n    }\n  }\n});\n\nForm.Element.Observer = Class.create(Abstract.TimedObserver, {\n  getValue: function() {\n    return Form.Element.getValue(this.element);\n  }\n});\n\nForm.Observer = Class.create(Abstract.TimedObserver, {\n  getValue: function() {\n    return Form.serialize(this.element);\n  }\n});\n\n/*--------------------------------------------------------------------------*/\n\nAbstract.EventObserver = Class.create({\n  initialize: function(element, callback) {\n    this.element  = $(element);\n    this.callback = callback;\n\n    this.lastValue = this.getValue();\n    if (this.element.tagName.toLowerCase() == 'form')\n      this.registerFormCallbacks();\n    else\n      this.registerCallback(this.element);\n  },\n\n  onElementEvent: function() {\n    var value = this.getValue();\n    if (this.lastValue != value) {\n      this.callback(this.element, value);\n      this.lastValue = value;\n    }\n  },\n\n  registerFormCallbacks: function() {\n    Form.getElements(this.element).each(this.registerCallback, this);\n  },\n\n  registerCallback: function(element) {\n    if (element.type) {\n      switch (element.type.toLowerCase()) {\n        case 'checkbox':\n        case 'radio':\n          Event.observe(element, 'click', this.onElementEvent.bind(this));\n          break;\n        default:\n          Event.observe(element, 'change', this.onElementEvent.bind(this));\n          break;\n      }\n    }\n  }\n});\n\nForm.Element.EventObserver = Class.create(Abstract.EventObserver, {\n  getValue: function() {\n    return Form.Element.getValue(this.element);\n  }\n});\n\nForm.EventObserver = Class.create(Abstract.EventObserver, {\n  getValue: function() {\n    return Form.serialize(this.element);\n  }\n});\nif (!window.Event) var Event = { };\n\nObject.extend(Event, {\n  KEY_BACKSPACE: 8,\n  KEY_TAB:       9,\n  KEY_RETURN:   13,\n  KEY_ESC:      27,\n  KEY_LEFT:     37,\n  KEY_UP:       38,\n  KEY_RIGHT:    39,\n  KEY_DOWN:     40,\n  KEY_DELETE:   46,\n  KEY_HOME:     36,\n  KEY_END:      35,\n  KEY_PAGEUP:   33,\n  KEY_PAGEDOWN: 34,\n  KEY_INSERT:   45,\n\n  cache: { },\n\n  relatedTarget: function(event) {\n    var element;\n    switch(event.type) {\n      case 'mouseover': element = event.fromElement; break;\n      case 'mouseout':  element = event.toElement;   break;\n      default: return null;\n    }\n    return Element.extend(element);\n  }\n});\n\nEvent.Methods = (function() {\n  var isButton;\n\n  if (Prototype.Browser.IE) {\n    var buttonMap = { 0: 1, 1: 4, 2: 2 };\n    isButton = function(event, code) {\n      return event.button == buttonMap[code];\n    };\n\n  } else if (Prototype.Browser.WebKit) {\n    isButton = function(event, code) {\n      switch (code) {\n        case 0: return event.which == 1 && !event.metaKey;\n        case 1: return event.which == 1 && event.metaKey;\n        default: return false;\n      }\n    };\n\n  } else {\n    isButton = function(event, code) {\n      return event.which ? (event.which === code + 1) : (event.button === code);\n    };\n  }\n\n  return {\n    isLeftClick:   function(event) { return isButton(event, 0) },\n    isMiddleClick: function(event) { return isButton(event, 1) },\n    isRightClick:  function(event) { return isButton(event, 2) },\n\n    element: function(event) {\n      event = Event.extend(event);\n\n      var node          = event.target,\n          type          = event.type,\n          currentTarget = event.currentTarget;\n\n      if (currentTarget && currentTarget.tagName) {\n        // Firefox screws up the \"click\" event when moving between radio buttons\n        // via arrow keys. It also screws up the \"load\" and \"error\" events on images,\n        // reporting the document as the target instead of the original image.\n        if (type === 'load' || type === 'error' ||\n          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'\n            && currentTarget.type === 'radio'))\n              node = currentTarget;\n      }\n      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;\n      return Element.extend(node);\n    },\n\n    findElement: function(event, expression) {\n      var element = Event.element(event);\n      if (!expression) return element;\n      var elements = [element].concat(element.ancestors());\n      return Selector.findElement(elements, expression, 0);\n    },\n\n    pointer: function(event) {\n      var docElement = document.documentElement,\n      body = document.body || { scrollLeft: 0, scrollTop: 0 };\n      return {\n        x: event.pageX || (event.clientX +\n          (docElement.scrollLeft || body.scrollLeft) -\n          (docElement.clientLeft || 0)),\n        y: event.pageY || (event.clientY +\n          (docElement.scrollTop || body.scrollTop) -\n          (docElement.clientTop || 0))\n      };\n    },\n\n    pointerX: function(event) { return Event.pointer(event).x },\n    pointerY: function(event) { return Event.pointer(event).y },\n\n    stop: function(event) {\n      Event.extend(event);\n      event.preventDefault();\n      event.stopPropagation();\n      event.stopped = true;\n    }\n  };\n})();\n\nEvent.extend = (function() {\n  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {\n    m[name] = Event.Methods[name].methodize();\n    return m;\n  });\n\n  if (Prototype.Browser.IE) {\n    Object.extend(methods, {\n      stopPropagation: function() { this.cancelBubble = true },\n      preventDefault:  function() { this.returnValue = false },\n      inspect: function() { return \"[object Event]\" }\n    });\n\n    return function(event) {\n      if (!event) return false;\n      if (event._extendedByPrototype) return event;\n\n      event._extendedByPrototype = Prototype.emptyFunction;\n      var pointer = Event.pointer(event);\n      Object.extend(event, {\n        target: event.srcElement,\n        relatedTarget: Event.relatedTarget(event),\n        pageX:  pointer.x,\n        pageY:  pointer.y\n      });\n      return Object.extend(event, methods);\n    };\n\n  } else {\n    Event.prototype = Event.prototype || document.createEvent(\"HTMLEvents\")['__proto__'];\n    Object.extend(Event.prototype, methods);\n    return Prototype.K;\n  }\n})();\n\nObject.extend(Event, (function() {\n  var cache = Event.cache;\n\n  function getEventID(element) {\n    if (element._prototypeEventID) return element._prototypeEventID[0];\n    arguments.callee.id = arguments.callee.id || 1;\n    return element._prototypeEventID = [++arguments.callee.id];\n  }\n\n  function getDOMEventName(eventName) {\n    if (eventName && eventName.include(':')) return \"dataavailable\";\n    return eventName;\n  }\n\n  function getCacheForID(id) {\n    return cache[id] = cache[id] || { };\n  }\n\n  function getWrappersForEventName(id, eventName) {\n    var c = getCacheForID(id);\n    return c[eventName] = c[eventName] || [];\n  }\n\n  function createWrapper(element, eventName, handler) {\n    var id = getEventID(element);\n    var c = getWrappersForEventName(id, eventName);\n    if (c.pluck(\"handler\").include(handler)) return false;\n\n    var wrapper = function(event) {\n      if (!Event || !Event.extend ||\n        (event.eventName && event.eventName != eventName))\n          return false;\n\n      Event.extend(event);\n      handler.call(element, event);\n    };\n\n    wrapper.handler = handler;\n    c.push(wrapper);\n    return wrapper;\n  }\n\n  function findWrapper(id, eventName, handler) {\n    var c = getWrappersForEventName(id, eventName);\n    return c.find(function(wrapper) { return wrapper.handler == handler });\n  }\n\n  function destroyWrapper(id, eventName, handler) {\n    var c = getCacheForID(id);\n    if (!c[eventName]) return false;\n    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));\n  }\n\n  function destroyCache() {\n    for (var id in cache)\n      for (var eventName in cache[id])\n        cache[id][eventName] = null;\n  }\n\n\n  // Internet Explorer needs to remove event handlers on page unload\n  // in order to avoid memory leaks.\n  if (window.attachEvent) {\n    window.attachEvent(\"onunload\", destroyCache);\n  }\n\n  // Safari has a dummy event handler on page unload so that it won't\n  // use its bfcache. Safari <= 3.1 has an issue with restoring the \"document\"\n  // object when page is returned to via the back button using its bfcache.\n  if (Prototype.Browser.WebKit) {\n    window.addEventListener('unload', Prototype.emptyFunction, false);\n  }\n\n  return {\n    observe: function(element, eventName, handler) {\n      element = $(element);\n      var name = getDOMEventName(eventName);\n\n      var wrapper = createWrapper(element, eventName, handler);\n      if (!wrapper) return element;\n\n      if (element.addEventListener) {\n        element.addEventListener(name, wrapper, false);\n      } else {\n        element.attachEvent(\"on\" + name, wrapper);\n      }\n\n      return element;\n    },\n\n    stopObserving: function(element, eventName, handler) {\n      element = $(element);\n      var id = getEventID(element), name = getDOMEventName(eventName);\n\n      if (!handler && eventName) {\n        getWrappersForEventName(id, eventName).each(function(wrapper) {\n          element.stopObserving(eventName, wrapper.handler);\n        });\n        return element;\n\n      } else if (!eventName) {\n        Object.keys(getCacheForID(id)).each(function(eventName) {\n          element.stopObserving(eventName);\n        });\n        return element;\n      }\n\n      var wrapper = findWrapper(id, eventName, handler);\n      if (!wrapper) return element;\n\n      if (element.removeEventListener) {\n        element.removeEventListener(name, wrapper, false);\n      } else {\n        element.detachEvent(\"on\" + name, wrapper);\n      }\n\n      destroyWrapper(id, eventName, handler);\n\n      return element;\n    },\n\n    fire: function(element, eventName, memo) {\n      element = $(element);\n      if (element == document && document.createEvent && !element.dispatchEvent)\n        element = document.documentElement;\n\n      var event;\n      if (document.createEvent) {\n        event = document.createEvent(\"HTMLEvents\");\n        event.initEvent(\"dataavailable\", true, true);\n      } else {\n        event = document.createEventObject();\n        event.eventType = \"ondataavailable\";\n      }\n\n      event.eventName = eventName;\n      event.memo = memo || { };\n\n      if (document.createEvent) {\n        element.dispatchEvent(event);\n      } else {\n        element.fireEvent(event.eventType, event);\n      }\n\n      return Event.extend(event);\n    }\n  };\n})());\n\nObject.extend(Event, Event.Methods);\n\nElement.addMethods({\n  fire:          Event.fire,\n  observe:       Event.observe,\n  stopObserving: Event.stopObserving\n});\n\nObject.extend(document, {\n  fire:          Element.Methods.fire.methodize(),\n  observe:       Element.Methods.observe.methodize(),\n  stopObserving: Element.Methods.stopObserving.methodize(),\n  loaded:        false\n});\n\n(function() {\n  /* Support for the DOMContentLoaded event is based on work by Dan Webb,\n     Matthias Miller, Dean Edwards and John Resig. */\n\n  var timer;\n\n  function fireContentLoadedEvent() {\n    if (document.loaded) return;\n    if (timer) window.clearInterval(timer);\n    document.fire(\"dom:loaded\");\n    document.loaded = true;\n  }\n\n  if (document.addEventListener) {\n    if (Prototype.Browser.WebKit) {\n      timer = window.setInterval(function() {\n        if (/loaded|complete/.test(document.readyState))\n          fireContentLoadedEvent();\n      }, 0);\n\n      Event.observe(window, \"load\", fireContentLoadedEvent);\n\n    } else {\n      document.addEventListener(\"DOMContentLoaded\",\n        fireContentLoadedEvent, false);\n    }\n\n  } else {\n    document.write(\"<script id=__onDOMContentLoaded defer src=//:><\\/script>\");\n    $(\"__onDOMContentLoaded\").onreadystatechange = function() {\n      if (this.readyState == \"complete\") {\n        this.onreadystatechange = null;\n        fireContentLoadedEvent();\n      }\n    };\n  }\n})();\n/*------------------------------- DEPRECATED -------------------------------*/\n\nHash.toQueryString = Object.toQueryString;\n\nvar Toggle = { display: Element.toggle };\n\nElement.Methods.childOf = Element.Methods.descendantOf;\n\nvar Insertion = {\n  Before: function(element, content) {\n    return Element.insert(element, {before:content});\n  },\n\n  Top: function(element, content) {\n    return Element.insert(element, {top:content});\n  },\n\n  Bottom: function(element, content) {\n    return Element.insert(element, {bottom:content});\n  },\n\n  After: function(element, content) {\n    return Element.insert(element, {after:content});\n  }\n};\n\nvar $continue = new Error('\"throw $continue\" is deprecated, use \"return\" instead');\n\n// This should be moved to script.aculo.us; notice the deprecated methods\n// further below, that map to the newer Element methods.\nvar Position = {\n  // set to true if needed, warning: firefox performance problems\n  // NOT needed for page scrolling, only if draggable contained in\n  // scrollable elements\n  includeScrollOffsets: false,\n\n  // must be called before calling withinIncludingScrolloffset, every time the\n  // page is scrolled\n  prepare: function() {\n    this.deltaX =  window.pageXOffset\n                || document.documentElement.scrollLeft\n                || document.body.scrollLeft\n                || 0;\n    this.deltaY =  window.pageYOffset\n                || document.documentElement.scrollTop\n                || document.body.scrollTop\n                || 0;\n  },\n\n  // caches x/y coordinate pair to use with overlap\n  within: function(element, x, y) {\n    if (this.includeScrollOffsets)\n      return this.withinIncludingScrolloffsets(element, x, y);\n    this.xcomp = x;\n    this.ycomp = y;\n    this.offset = Element.cumulativeOffset(element);\n\n    return (y >= this.offset[1] &&\n            y <  this.offset[1] + element.offsetHeight &&\n            x >= this.offset[0] &&\n            x <  this.offset[0] + element.offsetWidth);\n  },\n\n  withinIncludingScrolloffsets: function(element, x, y) {\n    var offsetcache = Element.cumulativeScrollOffset(element);\n\n    this.xcomp = x + offsetcache[0] - this.deltaX;\n    this.ycomp = y + offsetcache[1] - this.deltaY;\n    this.offset = Element.cumulativeOffset(element);\n\n    return (this.ycomp >= this.offset[1] &&\n            this.ycomp <  this.offset[1] + element.offsetHeight &&\n            this.xcomp >= this.offset[0] &&\n            this.xcomp <  this.offset[0] + element.offsetWidth);\n  },\n\n  // within must be called directly before\n  overlap: function(mode, element) {\n    if (!mode) return 0;\n    if (mode == 'vertical')\n      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /\n        element.offsetHeight;\n    if (mode == 'horizontal')\n      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /\n        element.offsetWidth;\n  },\n\n  // Deprecation layer -- use newer Element methods now (1.5.2).\n\n  cumulativeOffset: Element.Methods.cumulativeOffset,\n\n  positionedOffset: Element.Methods.positionedOffset,\n\n  absolutize: function(element) {\n    Position.prepare();\n    return Element.absolutize(element);\n  },\n\n  relativize: function(element) {\n    Position.prepare();\n    return Element.relativize(element);\n  },\n\n  realOffset: Element.Methods.cumulativeScrollOffset,\n\n  offsetParent: Element.Methods.getOffsetParent,\n\n  page: Element.Methods.viewportOffset,\n\n  clone: function(source, target, options) {\n    options = options || { };\n    return Element.clonePosition(target, source, options);\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nif (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){\n  function iter(name) {\n    return name.blank() ? null : \"[contains(concat(' ', @class, ' '), ' \" + name + \" ')]\";\n  }\n\n  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?\n  function(element, className) {\n    className = className.toString().strip();\n    var cond = /\\s/.test(className) ? $w(className).map(iter).join('') : iter(className);\n    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];\n  } : function(element, className) {\n    className = className.toString().strip();\n    var elements = [], classNames = (/\\s/.test(className) ? $w(className) : null);\n    if (!classNames && !className) return elements;\n\n    var nodes = $(element).getElementsByTagName('*');\n    className = ' ' + className + ' ';\n\n    for (var i = 0, child, cn; child = nodes[i]; i++) {\n      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||\n          (classNames && classNames.all(function(name) {\n            return !name.toString().blank() && cn.include(' ' + name + ' ');\n          }))))\n        elements.push(Element.extend(child));\n    }\n    return elements;\n  };\n\n  return function(className, parentElement) {\n    return $(parentElement || document.body).getElementsByClassName(className);\n  };\n}(Element.Methods);\n\n/*--------------------------------------------------------------------------*/\n\nElement.ClassNames = Class.create();\nElement.ClassNames.prototype = {\n  initialize: function(element) {\n    this.element = $(element);\n  },\n\n  _each: function(iterator) {\n    this.element.className.split(/\\s+/).select(function(name) {\n      return name.length > 0;\n    })._each(iterator);\n  },\n\n  set: function(className) {\n    this.element.className = className;\n  },\n\n  add: function(classNameToAdd) {\n    if (this.include(classNameToAdd)) return;\n    this.set($A(this).concat(classNameToAdd).join(' '));\n  },\n\n  remove: function(classNameToRemove) {\n    if (!this.include(classNameToRemove)) return;\n    this.set($A(this).without(classNameToRemove).join(' '));\n  },\n\n  toString: function() {\n    return $A(this).join(' ');\n  }\n};\n\nObject.extend(Element.ClassNames.prototype, Enumerable);\n\n/*--------------------------------------------------------------------------*/\n\nElement.addMethods();"
  },
  {
    "path": "test/apps/rails2/public/robots.txt",
    "content": "# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file\n#\n# To ban all spiders from the entire site uncomment the next two lines:\n# User-Agent: *\n# Disallow: /\n"
  },
  {
    "path": "test/apps/rails2/script/about",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\n$LOAD_PATH.unshift \"#{RAILTIES_PATH}/builtin/rails_info\"\nrequire 'commands/about'\n"
  },
  {
    "path": "test/apps/rails2/script/console",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/console'\n"
  },
  {
    "path": "test/apps/rails2/script/dbconsole",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/dbconsole'\n"
  },
  {
    "path": "test/apps/rails2/script/destroy",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/destroy'\n"
  },
  {
    "path": "test/apps/rails2/script/generate",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/generate'\n"
  },
  {
    "path": "test/apps/rails2/script/performance/benchmarker",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../../config/boot',  __FILE__)\nrequire 'commands/performance/benchmarker'\n"
  },
  {
    "path": "test/apps/rails2/script/performance/profiler",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../../config/boot',  __FILE__)\nrequire 'commands/performance/profiler'\n"
  },
  {
    "path": "test/apps/rails2/script/plugin",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/plugin'\n"
  },
  {
    "path": "test/apps/rails2/script/runner",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/runner'\n"
  },
  {
    "path": "test/apps/rails2/script/server",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/server'\n"
  },
  {
    "path": "test/apps/rails2/test/fixtures/accounts.yml",
    "content": "# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html\n\n# This model initially had no columns defined.  If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n#  column: value\n"
  },
  {
    "path": "test/apps/rails2/test/fixtures/users.yml",
    "content": "# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html\n\n# This model initially had no columns defined.  If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n#  column: value\n"
  },
  {
    "path": "test/apps/rails2/test/functional/home_controller_test.rb",
    "content": "require 'test_helper'\n\nclass HomeControllerTest < ActionController::TestCase\n  # Replace this with your real tests.\n  test \"the truth\" do\n    assert true\n  end\nend\n"
  },
  {
    "path": "test/apps/rails2/test/functional/other_controller_test.rb",
    "content": "require 'test_helper'\n\nclass OtherControllerTest < ActionController::TestCase\n  # Replace this with your real tests.\n  test \"the truth\" do\n    assert true\n  end\nend\n"
  },
  {
    "path": "test/apps/rails2/test/performance/browsing_test.rb",
    "content": "require 'test_helper'\nrequire 'performance_test_help'\n\n# Profiling results for each test method are written to tmp/performance.\nclass BrowsingTest < ActionController::PerformanceTest\n  def test_homepage\n    get '/'\n  end\nend\n"
  },
  {
    "path": "test/apps/rails2/test/test_helper.rb",
    "content": "ENV[\"RAILS_ENV\"] = \"test\"\nrequire File.expand_path(File.dirname(__FILE__) + \"/../config/environment\")\nrequire 'test_help'\n\nclass ActiveSupport::TestCase\n  # Transactional fixtures accelerate your tests by wrapping each test method\n  # in a transaction that's rolled back on completion.  This ensures that the\n  # test database remains unchanged so your fixtures don't have to be reloaded\n  # between every test method.  Fewer database queries means faster tests.\n  #\n  # Read Mike Clark's excellent walkthrough at\n  #   http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting\n  #\n  # Every Active Record database supports transactions except MyISAM tables\n  # in MySQL.  Turn off transactional fixtures in this case; however, if you\n  # don't care one way or the other, switching from MyISAM to InnoDB tables\n  # is recommended.\n  #\n  # The only drawback to using transactional fixtures is when you actually \n  # need to test transactions.  Since your test is bracketed by a transaction,\n  # any transactions started in your code will be automatically rolled back.\n  self.use_transactional_fixtures = true\n\n  # Instantiated fixtures are slow, but give you @david where otherwise you\n  # would need people(:david).  If you don't want to migrate your existing\n  # test cases which use the @david style and don't mind the speed hit (each\n  # instantiated fixtures translates to a database query per test method),\n  # then set this back to true.\n  self.use_instantiated_fixtures  = false\n\n  # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.\n  #\n  # Note: You'll currently still have to declare fixtures explicitly in integration tests\n  # -- they do not yet inherit this setting\n  fixtures :all\n\n  # Add more helper methods to be used by all tests here...\nend\n"
  },
  {
    "path": "test/apps/rails2/test/unit/account_test.rb",
    "content": "require 'test_helper'\n\nclass AccountTest < ActiveSupport::TestCase\n  # Replace this with your real tests.\n  test \"the truth\" do\n    assert true\n  end\nend\n"
  },
  {
    "path": "test/apps/rails2/test/unit/helpers/home_helper_test.rb",
    "content": "require 'test_helper'\n\nclass HomeHelperTest < ActionView::TestCase\nend\n"
  },
  {
    "path": "test/apps/rails2/test/unit/helpers/other_helper_test.rb",
    "content": "require 'test_helper'\n\nclass OtherHelperTest < ActionView::TestCase\nend\n"
  },
  {
    "path": "test/apps/rails2/test/unit/user_test.rb",
    "content": "require 'test_helper'\n\nclass UserTest < ActiveSupport::TestCase\n  # Replace this with your real tests.\n  test \"the truth\" do\n    assert true\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/.gitignore",
    "content": ".bundle\ndb/*.sqlite3\nlog/*.log\ntmp/\nsecret_token.rb\n"
  },
  {
    "path": "test/apps/rails3/Gemfile",
    "content": "source 'http://rubygems.org'\n\ngem 'rails', '3.0.3'\n\n# Bundle edge Rails instead:\n# gem 'rails', :git => 'git://github.com/rails/rails.git'\n\ngem 'sqlite3'\n\n# Use unicorn as the web server\n# gem 'unicorn'\n\n# Deploy with Capistrano\n# gem 'capistrano'\n\n# To use debugger (ruby-debug for Ruby 1.8.7+, ruby-debug19 for Ruby 1.9.2+)\n# gem 'ruby-debug'\n# gem 'ruby-debug19', :require => 'ruby-debug'\n\n# Bundle the extra gems:\n# gem 'bj'\n# gem 'nokogiri'\n# gem 'sqlite3-ruby', :require => 'sqlite3'\n# gem 'aws-s3', :require => 'aws/s3'\n\n# Bundle gems for the local environment. Make sure to\n# put test-only gems in this group so their generators\n# and rake tasks are available in development mode:\n# group :development, :test do\n#   gem 'webrat'\n# end\n"
  },
  {
    "path": "test/apps/rails3/README",
    "content": "== Welcome to Rails\n\nRails is a web-application framework that includes everything needed to create\ndatabase-backed web applications according to the Model-View-Control pattern.\n\nThis pattern splits the view (also called the presentation) into \"dumb\"\ntemplates that are primarily responsible for inserting pre-built data in between\nHTML tags. The model contains the \"smart\" domain objects (such as Account,\nProduct, Person, Post) that holds all the business logic and knows how to\npersist themselves to a database. The controller handles the incoming requests\n(such as Save New Account, Update Product, Show Post) by manipulating the model\nand directing data to the view.\n\nIn Rails, the model is handled by what's called an object-relational mapping\nlayer entitled Active Record. This layer allows you to present the data from\ndatabase rows as objects and embellish these data objects with business logic\nmethods. You can read more about Active Record in\nlink:files/vendor/rails/activerecord/README.html.\n\nThe controller and view are handled by the Action Pack, which handles both\nlayers by its two parts: Action View and Action Controller. These two layers\nare bundled in a single package due to their heavy interdependence. This is\nunlike the relationship between the Active Record and Action Pack that is much\nmore separate. Each of these packages can be used independently outside of\nRails. You can read more about Action Pack in\nlink:files/vendor/rails/actionpack/README.html.\n\n\n== Getting Started\n\n1. At the command prompt, create a new Rails application:\n       <tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)\n\n2. Change directory to <tt>myapp</tt> and start the web server:\n       <tt>cd myapp; rails server</tt> (run with --help for options)\n\n3. Go to http://localhost:3000/ and you'll see:\n       \"Welcome aboard: You're riding Ruby on Rails!\"\n\n4. Follow the guidelines to start developing your application. You can find\nthe following resources handy:\n\n* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html\n* Ruby on Rails Tutorial Book: http://www.railstutorial.org/\n\n\n== Debugging Rails\n\nSometimes your application goes wrong. Fortunately there are a lot of tools that\nwill help you debug it and get it back on the rails.\n\nFirst area to check is the application log files. Have \"tail -f\" commands\nrunning on the server.log and development.log. Rails will automatically display\ndebugging and runtime information to these files. Debugging info will also be\nshown in the browser on requests from 127.0.0.1.\n\nYou can also log your own messages directly into the log file from your code\nusing the Ruby logger class from inside your controllers. Example:\n\n  class WeblogController < ActionController::Base\n    def destroy\n      @weblog = Weblog.find(params[:id])\n      @weblog.destroy\n      logger.info(\"#{Time.now} Destroyed Weblog ID ##{@weblog.id}!\")\n    end\n  end\n\nThe result will be a message in your log file along the lines of:\n\n  Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!\n\nMore information on how to use the logger is at http://www.ruby-doc.org/core/\n\nAlso, Ruby documentation can be found at http://www.ruby-lang.org/. There are\nseveral books available online as well:\n\n* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)\n* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)\n\nThese two books will bring you up to speed on the Ruby language and also on\nprogramming in general.\n\n\n== Debugger\n\nDebugger support is available through the debugger command when you start your\nMongrel or WEBrick server with --debugger. This means that you can break out of\nexecution at any point in the code, investigate and change the model, and then,\nresume execution! You need to install ruby-debug to run the server in debugging\nmode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:\n\n  class WeblogController < ActionController::Base\n    def index\n      @posts = Post.find(:all)\n      debugger\n    end\n  end\n\nSo the controller will accept the action, run the first line, then present you\nwith a IRB prompt in the server window. Here you can do things like:\n\n  >> @posts.inspect\n  => \"[#<Post:0x14a6be8\n          @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,\n       #<Post:0x14a6620\n          @attributes={\"title\"=>\"Rails\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]\"\n  >> @posts.first.title = \"hello from a debugger\"\n  => \"hello from a debugger\"\n\n...and even better, you can examine how your runtime objects actually work:\n\n  >> f = @posts.first\n  => #<Post:0x13630c4 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>\n  >> f.\n  Display all 152 possibilities? (y or n)\n\nFinally, when you're ready to resume execution, you can enter \"cont\".\n\n\n== Console\n\nThe console is a Ruby shell, which allows you to interact with your\napplication's domain model. Here you'll have all parts of the application\nconfigured, just like it is when the application is running. You can inspect\ndomain models, change values, and save to the database. Starting the script\nwithout arguments will launch it in the development environment.\n\nTo start the console, run <tt>rails console</tt> from the application\ndirectory.\n\nOptions:\n\n* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications\n  made to the database.\n* Passing an environment name as an argument will load the corresponding\n  environment. Example: <tt>rails console production</tt>.\n\nTo reload your controllers and models after launching the console run\n<tt>reload!</tt>\n\nMore information about irb can be found at:\nlink:http://www.rubycentral.com/pickaxe/irb.html\n\n\n== dbconsole\n\nYou can go to the command line of your database directly through <tt>rails\ndbconsole</tt>. You would be connected to the database with the credentials\ndefined in database.yml. Starting the script without arguments will connect you\nto the development database. Passing an argument will connect you to a different\ndatabase, like <tt>rails dbconsole production</tt>. Currently works for MySQL,\nPostgreSQL and SQLite 3.\n\n== Description of Contents\n\nThe default directory structure of a generated Ruby on Rails application:\n\n  |-- app\n  |   |-- controllers\n  |   |-- helpers\n  |   |-- mailers\n  |   |-- models\n  |   `-- views\n  |       `-- layouts\n  |-- config\n  |   |-- environments\n  |   |-- initializers\n  |   `-- locales\n  |-- db\n  |-- doc\n  |-- lib\n  |   `-- tasks\n  |-- log\n  |-- public\n  |   |-- images\n  |   |-- javascripts\n  |   `-- stylesheets\n  |-- script\n  |-- test\n  |   |-- fixtures\n  |   |-- functional\n  |   |-- integration\n  |   |-- performance\n  |   `-- unit\n  |-- tmp\n  |   |-- cache\n  |   |-- pids\n  |   |-- sessions\n  |   `-- sockets\n  `-- vendor\n      `-- plugins\n\napp\n  Holds all the code that's specific to this particular application.\n\napp/controllers\n  Holds controllers that should be named like weblogs_controller.rb for\n  automated URL mapping. All controllers should descend from\n  ApplicationController which itself descends from ActionController::Base.\n\napp/models\n  Holds models that should be named like post.rb. Models descend from\n  ActiveRecord::Base by default.\n\napp/views\n  Holds the template files for the view that should be named like\n  weblogs/index.html.erb for the WeblogsController#index action. All views use\n  eRuby syntax by default.\n\napp/views/layouts\n  Holds the template files for layouts to be used with views. This models the\n  common header/footer method of wrapping views. In your views, define a layout\n  using the <tt>layout :default</tt> and create a file named default.html.erb.\n  Inside default.html.erb, call <% yield %> to render the view using this\n  layout.\n\napp/helpers\n  Holds view helpers that should be named like weblogs_helper.rb. These are\n  generated for you automatically when using generators for controllers.\n  Helpers can be used to wrap functionality for your views into methods.\n\nconfig\n  Configuration files for the Rails environment, the routing map, the database,\n  and other dependencies.\n\ndb\n  Contains the database schema in schema.rb. db/migrate contains all the\n  sequence of Migrations for your schema.\n\ndoc\n  This directory is where your application documentation will be stored when\n  generated using <tt>rake doc:app</tt>\n\nlib\n  Application specific libraries. Basically, any kind of custom code that\n  doesn't belong under controllers, models, or helpers. This directory is in\n  the load path.\n\npublic\n  The directory available for the web server. Contains subdirectories for\n  images, stylesheets, and javascripts. Also contains the dispatchers and the\n  default HTML files. This should be set as the DOCUMENT_ROOT of your web\n  server.\n\nscript\n  Helper scripts for automation and generation.\n\ntest\n  Unit and functional tests along with fixtures. When using the rails generate\n  command, template test files will be generated for you and placed in this\n  directory.\n\nvendor\n  External libraries that the application depends on. Also includes the plugins\n  subdirectory. If the app has frozen rails, those gems also go here, under\n  vendor/rails/. This directory is in the load path.\n"
  },
  {
    "path": "test/apps/rails3/Rakefile",
    "content": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire File.expand_path('../config/application', __FILE__)\nrequire 'rake'\n\nRails3::Application.load_tasks\n"
  },
  {
    "path": "test/apps/rails3/app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::Base\n#  protect_from_forgery\n  before_filter :action_in_parent, :only => :action_in_child\nend\n"
  },
  {
    "path": "test/apps/rails3/app/controllers/base_thing.rb",
    "content": "class BaseThing < ApplicationController\n  def action_in_parent\n    @from_parent = params[:horrible_thing]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/app/controllers/before_controller.rb",
    "content": "class BeforeController < ApplicationController\n  before_filter :filter1\n  before_filter :filter2\n  before_filter :filter3, :only => [:use_filter3, :use_filter12345]\n  prepend_before_filter :filter4, :only => [:use_filter12345]\n  append_before_filter :filter5, :only => [:use_filter12345]\n\n  def use_filters12\n  end\n\n  def use_filter123\n  end\n\n  def use_filter12345\n  end\n\n  private\n\n  def filter1\n    @user = User.find(params[:user_id])\n  end\n\n  def filter2\n    @bill = @user.bill\n  end\n\n  def filter3\n    @account = @user.account\n  end\n\n  def filter4\n    @user = params[:user][:name] #overwritten in other filters\n    @query = params[:search]\n    @bill = something_else\n  end\n  \n  def filter5\n    @purchase = @account.purchases.last\n  end\n\n  include ControllerFilter\nend\n"
  },
  {
    "path": "test/apps/rails3/app/controllers/child_controller.rb",
    "content": "class ChildController < BaseThing\n  def action_in_child\n    #Should get @from_parent here\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/app/controllers/home_controller.rb",
    "content": "class HomeController < ApplicationController\n  before_filter :filter_it, :only => :test_filter\n\n  def index\n  end\n\n  def test_params\n    @name = params[:name]\n    @indirect = indirect_method(params[:input])\n  end\n\n  def test_model\n    @name = User.first.name\n  end\n\n  def test_cookie\n    @name = cookies[:name]\n  end\n\n  def test_filter\n  end\n\n  def test_file_access\n    File.open RAILS_ROOT + \"/\" + params[:file]\n  end\n\n  def test_sql some_var = \"hello\"\n    User.find_by_sql \"select * from users where something = '#{some_var}'\"\n    User.all(:conditions => \"status = '#{happy}'\")\n    @user = User.first(:conditions => \"name = '#{params[:name]}'\")\n  end\n\n  def test_command\n    `ls #{params[:file_name]}`\n\n    system params[:user_input]\n  end\n\n  def test_eval\n    eval params[:dangerous_input]\n  end\n\n  def test_redirect\n    params[:action] = :index\n    redirect_to params\n  end\n\n  def test_render\n    @some_variable = params[:unsafe_input]\n    render :index\n  end\n\n  def test_mass_assignment\n    User.new(params[:user])\n  end\n\n  def test_mass_assignment_with_hash\n    User.new(:name => params[:user][:name])\n  end\n\n  def test_dynamic_render\n    page = params[:page]\n    render :file => \"/some/path/#{page}\"\n  end\n\n  def test_load_params\n    load params[:file]\n    RandomClass.load params[:file]\n  end\n\n  def test_model_build\n    current_user = User.new\n    current_user.something.something.build(params[:awesome_user])\n  end\n\n  def test_only_path_wrong\n    redirect_to params[:user], :only_path => true #This should still warn\n  end\n\n  def test_url_for_only_path\n    url = params\n    url[:only_path] = false\n    redirect_to url_for(url)\n  end\n\n  def test_render_a_method_call\n    @user = User.find(params['user']).name\n    render :test_render\n  end\n\n  def test_number_alias\n    y + 1 + 2\n  end\n\n  def test_only_path_correct\n    params.merge! :only_path => true\n    redirect_to params\n  end\n\n  def test_content_tag\n    @user = User.find(current_user)\n  end\n\n  def test_yaml_file_access\n    #Should not warn about access, but about remote code execution\n    YAML.load \"some/path/#{params[:user][:file]}\"\n\n    #Should warn\n    YAML.parse_file(\"whatever/\" + params[:file_name])\n  end\n\n  def test_more_mass_assignment_methods\n    #Additional mass assignment methods\n    User.first_or_create(params[:user])\n    User.first_or_create!(:name => params[:user][:name])\n    User.first_or_initialize!(params[:user])\n    User.update(params[:id], :alive => false) #No warning\n    User.update(1, params[:update])\n    User.find(1).assign_attributes(params[:update])\n  end\n\n  def test_yaml_load\n    YAML.load params[:input]\n    YAML.load some_method #No warning\n    YAML.load x(cookies[:store])\n    YAML.load User.first.bad_stuff\n  end\n\n  def test_more_yaml_methods\n    YAML.load_documents params[:input]\n    YAML.load_stream cookies[:thing]\n    YAML.parse_documents \"a: #{params[:a]}\"\n    YAML.parse_stream User.find(1).upload\n  end\n\n  def parse_json\n    JSON.parse params[:input]\n  end\n\n  def mass_assign_slice_only\n    Account.new(params.slice(:name, :email))\n    Account.new(params.only(:name, email))\n  end\n\n  def test_more_ways_to_execute\n    Open3.capture2 \"ls #{params[:dir]}\"\n    Open3.capture2e \"ls #{params[:dir]}\"\n    Open3.capture3 \"ls #{params[:dir]}\"\n    Open3.pipeline \"sort\", \"uniq\", :in => params[:file]\n    Open3.pipeline_r \"sort #{params[:file]}\", \"uniq\"\n    Open3.pipeline_rw params[:cmd], \"sort -g\"\n    Open3.pipeline_start *params[:cmds]\n    spawn \"some_cool_command #{params[:opts]}\"\n    POSIX::Spawn::spawn params[:cmd]\n  end\n\n  def test_only_path_also_correct\n    redirect_to(params.merge(:only_path => true, :display => nil))\n  end\n\n  def test_more_uses_of_pipelines\n    Open3.pipeline ['sort', params[:file]] # safe-ish\n    Open3.pipeline_r ['ls', '*'], \"sort #{params[:order]}\"\n    Open3.pipeline_rw ['ls'], [params[:cmd]]\n    Open3.pipeline_start ['bash', '-c', params[:cmd]]\n  end\n\n  private\n\n  def filter_it\n    @filtered = params[:evil_input]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/app/controllers/nested_controller.rb",
    "content": "class Whatever\n  module Wherever\n    class NestedController < ApplicationController\n      def so_nested\n        @bad_thing = params[:x]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/app/controllers/other_controller.rb",
    "content": "class OtherController < ApplicationController\n  def test_locals\n    render :locals => { :input => params[:user_input] }\n  end\n\n  def test_object\n    render :partial => \"account\", :object => Account.first\n  end\n\n  def test_collection\n    users = User.all\n    partial = \"user\"\n    render :partial => partial, :collection => users\n  end\n\n  def test_iteration\n    @users = User.all\n  end\n\n  def test_send_file\n    send_file params[:file]\n  end\n\n  def test_update_attribute\n    @user = User.first\n    @user.update_attribute(:attr, params[:attr])\n  end\n\n  def test_sql_with_non_active_record_model\n    Noticia.where(params[:bad_stuff])\n  end\n\n  def test_http_digest\n    authenticate_or_request_with_http_digest do\n      something\n    end\n  end\n\n  def test_render_with_nonsymbol_key\n    render x => :y\n  end\n\n  def test_mail_to\n    @user = User.find(current_user)\n  end\n\n  def test_command_injection_locals\n    `#{some_command}`\n    system(\"ls #{some_files}\")\n  end\n\n  def test_mass_assign_with_strong_params\n    Bill.create(params[:charge])\n  end\n\n  def test_sql_deletes\n    User.delete_all(\"name = #{params[:name]}\")\n    User.destroy_all(\"human = #{User.current.humanity}\")\n  end\n\n  def test_sql_to_s status\n    column = \"#{product_action_type_key.to_s}_count\"\n    # Should warn about \"product_action_type_key\", not \"product_action_type_key.to_s\"\n    Product.where(id: product_id).update_all [\"#{column} = #{column} + ?\", delta]\n    # Should not warn\n    Product.where(\"id = #{id.to_s}\")\n    # Should warn about \"status\" not \"status.to_s\"\n    Product.find(:all, :conditions => \"product_status_id = \" + status.to_s)\n    # Show not warn\n    Product.find(:all, :conditions => \"id = \" + Product.first.id.to_s)\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/app/controllers/products_controller.rb",
    "content": "class ProductsController < ApplicationController\n  # GET /products\n  # GET /products.xml\n  def index\n    @products = Product.all\n\n    respond_to do |format|\n      format.html # index.html.erb\n      format.xml  { render :xml => @products }\n    end\n  end\n\n  # GET /products/1\n  # GET /products/1.xml\n  def show\n    @product = Product.find(params[:id])\n\n    respond_to do |format|\n      format.html # show.html.erb\n      format.xml  { render :xml => @product }\n    end\n  end\n\n  # GET /products/new\n  # GET /products/new.xml\n  def new\n    @product = Product.new\n\n    respond_to do |format|\n      format.html # new.html.erb\n      format.xml  { render :xml => @product }\n    end\n  end\n\n  # GET /products/1/edit\n  def edit\n    @product = Product.find(params[:id])\n  end\n\n  # POST /products\n  # POST /products.xml\n  def create\n    @product = Product.new(params[:product])\n\n    respond_to do |format|\n      if @product.save\n        format.html { redirect_to(@product, :notice => 'Product was successfully created.') }\n        format.xml  { render :xml => @product, :status => :created, :location => @product }\n      else\n        format.html { render :action => \"new\" }\n        format.xml  { render :xml => @product.errors, :status => :unprocessable_entity }\n      end\n    end\n  end\n\n  # PUT /products/1\n  # PUT /products/1.xml\n  def update\n    @product = Product.find(params[:id])\n\n    respond_to do |format|\n      if @product.update_attributes(params[:product])\n        format.html { redirect_to(@product, :notice => 'Product was successfully updated.') }\n        format.xml  { head :ok }\n      else\n        format.html { render :action => \"edit\" }\n        format.xml  { render :xml => @product.errors, :status => :unprocessable_entity }\n      end\n    end\n  end\n\n  # DELETE /products/1\n  # DELETE /products/1.xml\n  def destroy\n    @product = Product.find(params[:id])\n    @product.destroy\n\n    respond_to do |format|\n      format.html { redirect_to(products_url) }\n      format.xml  { head :ok }\n    end\n  end\n\n  def render_some_text\n    render :text => \"jello\"\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails3/app/helpers/home_helper.rb",
    "content": "module HomeHelper\nend\n"
  },
  {
    "path": "test/apps/rails3/app/helpers/other_helper.rb",
    "content": "module OtherHelper\nend\n"
  },
  {
    "path": "test/apps/rails3/app/helpers/products_helper.rb",
    "content": "module ProductsHelper\nend\n"
  },
  {
    "path": "test/apps/rails3/app/models/account.rb",
    "content": "class Account < ActiveRecord::Base\n  validates_format_of :name, :with => /^[a-zA-Z]+$/\n  validates_format_of :blah, :with => /\\A[a-zA-Z]+$/\n  validates_format_of :something, :with => /[a-zA-Z]\\z/\n  validates_format_of :good_valid, :with => /\\A[a-zA-Z]\\z/ #No warning\n  validates_format_of :not_bad, :with => /\\A[a-zA-Z]\\Z/ #No warning\n\n  def mass_assign_it\n    Account.new(params[:account_info]).some_other_method\n  end\n\n  def test_class_eval\n    #Should not raise a warning\n    User.class_eval do\n      attr_reader :some_private_thing\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/app/models/bill.rb",
    "content": "class Bill < ActiveRecord::Base\n  include ActiveModel::ForbiddenAttributesProtection\nend\n"
  },
  {
    "path": "test/apps/rails3/app/models/noticia.rb",
    "content": "class Noticia\nend\n"
  },
  {
    "path": "test/apps/rails3/app/models/notifier.rb",
    "content": "class Notifier < ActionMailer::Base\n  def nsfree_deactivation_heroku(account, allowed, used)\n    # ...   \n    subject \"#{Zerigo.service_provider[:company_name]} add-on at Heroku: #{Zerigo.sites[:ns][:app_name]} service deactivated\"\n    from Zerigo.service_provider[:company_support_email]\n    recipients rcpts\n    bcc Zerigo.service_provider[:company_bcc_email]\n    sent_on Time.now\n\n    body :allowed => allowed, :used => used, :account => account\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/app/models/product.rb",
    "content": "class Product < ActiveRecord::Base\n  serialize :price\n  attr_protected :price\nend\n"
  },
  {
    "path": "test/apps/rails3/app/models/purchase.rb",
    "content": "class Purchase < ActiveRecord::Base\n  attr_accessible\n  serialize :something\nend\n"
  },
  {
    "path": "test/apps/rails3/app/models/underline_model.rb",
    "content": "class Underline_Model\n  def inject!(b)\n    User.where(\"a < #{b}\")\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/app/models/user.rb",
    "content": "class User < ActiveRecord::Base\n  #Should not raise a warning\n  def unused_sql\n    if true\n      @x = z\n    elsif somethingelse\n      @x = \"name like '%#{params[:name]}%'\"\n    else\n      @x =\"foo\"\n    end\n\n    User.where(@x)\n\n    if true\n      x = z\n    elsif false\n      x = \"name like '%#{params[:name]}%'\"\n    else\n      x =\"foo\"\n    end\n\n    User.where(x)\n  end\n\n  def sql_in_if_branches\n    if condition\n      x = z\n    elsif other_condition\n      x = \"name like '%#{params[:name]}%'\"\n    end\n\n    User.where(x)\n  end\n\n  def safe_sql\n    User.where \"something = ?\", \"#{params[:awesome]}\"\n  end\n\n  def sanitized_profile\n    sanitize self.profile.to_s\n  end\n\n  serialize :something\nend\n"
  },
  {
    "path": "test/apps/rails3/app/views/before/use_filter12345.html.erb",
    "content": "<h2>Search: <%= raw @query %></h2>\n\n<h2>Last purchase: <%= raw @purchase.total %>\n\n<h1>Bill for <%= raw @user.name %></h2>\n\n<ul>\n  <li>Total: <%= raw @bill.total %></li>\n</ul>\n"
  },
  {
    "path": "test/apps/rails3/app/views/before/use_filters12.html.erb",
    "content": "<h1>Bill for <%= raw @user.name %></h2>\n\n<ul>\n  <li>Total: <%= raw @bill.total %></li>\n</ul>\n"
  },
  {
    "path": "test/apps/rails3/app/views/child/action_in_child.html.erb",
    "content": "<%= raw @from_parent %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/index.html.erb",
    "content": "<h1>Home#index</h1>\n<p>Find me in app/views/home/index.html.erb</p>\n<%= raw params[:user_input] %>\n\n<%= raw @some_variable %>\n\n<%= raw escape_once(params[:user_input]) %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_command.html.erb",
    "content": "<h1>Home#test_command</h1>\n<p>Find me in app/views/home/test_command.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_content_tag.html.erb",
    "content": "Should not warn\n<%= content_tag :p, h(params[:something]) %>\n\nShould not warn\n<%= content_tag :span, @user.name %>\n\nShould warn\n<%= content_tag :span, raw(params[:blah]) %>\n\nShould not warn\n<%= content_tag :div, \"Blah!\", { :class => params[:class] }, true %>\n\nShould warn\n<%= content_tag :div, \"Blah!\", { cookies[:weird] => \"bad idea\" } %>\n\nShould not warn\n<%= content_tag :h1, params[:x] == 1 ? \"totally\" : \"safe\" %>\n\nShould still warn\n<%= content_tag :div, \"Blah!\", { @user.something => \"bad idea\"}, true %>\n\nShould not warn\n<%= content_tag :div, \"Blah!\", { :class => params[:class] } %>\n\nShould warn\n<%= content_tag :div, \"Blah!\", { :id => @user.name }, false %>\n\nShould not warn\n<%= content_tag :div, x(params[:maybe_bad]) %>\n\nShould warn\n<%= content_tag params[:whyyy], \"Don't do this\" %>\n\nShould warn\n<%= content_tag @user.preferred_markup, \"Seriously\" %>\n\nShould not warn\n<%= content_tag :span, \"test\", { u(params[:class]) => \"display:none\" } %>"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_cookie.html.erb",
    "content": "<h1>Home#test_cookie</h1>\n<p>Find me in app/views/home/test_cookie.html.erb</p>\nHello, cookie named <%= raw @name %>!\n\n<%= raw indirect(cookies[:chipsahoy]) %>\n\nAnd: <%= raw cookies[:x][:y] %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_dynamic_render.html.erb",
    "content": "<h1>Home#test_dynamic_render</h1>\n<p>Find me in app/views/home/test_dynamic_render.html.erb</p>\n\nThis is not a problem, because this page is not rendered: <%= raw @page %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_eval.html.erb",
    "content": "<h1>Home#test_eval</h1>\n<p>Find me in app/views/home/test_eval.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_file_access.html.erb",
    "content": "<h1>Home#test_file_access</h1>\n<p>Find me in app/views/home/test_file_access.html.erb</p>\n<%= File.open params[:name] %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_filter.html.erb",
    "content": "<h1>Home#test_filter</h1>\n<p>Find me in app/views/home/test_filter.html.erb</p>\nValue from filter: <%= raw @filtered %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_mass_assignment.html.erb",
    "content": "<h1>Home#test_mass_assignment</h1>\n<p>Find me in app/views/home/test_mass_assignment.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_model.html.erb",
    "content": "<h1>Home#test_model</h1>\n<p>Find me in app/views/home/test_model.html.erb</p>\nHello, <%= raw @name %>!\n\n\nVery likely bad: <%= raw auto_link User.profile %>\n\nNot a problem in Rails 3: <%= link_to User.first.name, \"some url\" %>\n\nIt's just a model <%= link_to \"Hipster ipsum\", User.first %>\n\nIt's just a couple of models <%= link_to \"Hipster ipsum\", [Account.first, User.last] %>\n\nSafe link_to herf fun: <%= link_to \"test\", u(params[:user_id]) %>"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_newlines.html.erb",
    "content": "<% if @name %>\n\n\n  <% if @indirect %>\n    Dangerous hrefs in nested logic after multiple newlines: <%= link_to \"newlines between ruby code\", params[:dangerous] %>\n  <% end %>\n<% end %>\n\n<h1>Home#test_newlines</h1>\n\n<% if @indirect99 %>\n  Dangerous hrefs in nested logic after multiple newlines: <%= link_to \"newlines between HTML and ruby code\", params[:dangerous] %>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_params.html.erb",
    "content": "<h1>Home#test_params</h1>\n<p>Find me in app/views/home/test_params.html.erb</p>\n\nJello, <%= raw @name %>\n\nMore: <%= raw @indirect %>\n\nIndirectly: <%= some_method params[:bad_stuff] %>\n\nAnd: <%= raw params[:x][:y] %>\n\nNot-so-dangerous href: <%= link_to \"some text\", ensure_valid_proto!(params[:not_so_bad], :js) %>\n\nDangerous href: <%= link_to \"more text\", params[:dangerous] %>\n\nNot going to warn: <%= link_to \"donkey\", not_safe(params[:bad_robot]) %>\n\nNot completely safe: <%= link_to \"Helvetica hoodie bushwick\", h(params[:js_xss]) %>\n\nRequest parameters: <%= raw request.parameters %>\n\nShould not warn <%= raw u(params[:w00t]) %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_redirect.html.erb",
    "content": "<h1>Home#test_redirect</h1>\n<p>Find me in app/views/home/test_redirect.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_render.html.erb",
    "content": "<h1>Home#test_render</h1>\n<p>Find me in app/views/home/test_render.html.erb</p>\n<%= render @user %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/home/test_sql.html.erb",
    "content": "<h1>Home#test_sql</h1>\n<p>Find me in app/views/home/test_sql.html.erb</p>\n\n<%= raw @user %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>Rails3</title>\n  <%= stylesheet_link_tag :all %>\n  <%= javascript_include_tag :defaults %>\n  <%= csrf_meta_tag %>\n</head>\n<body>\n\n<%= yield %>\n\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails3/app/views/other/_account.html.haml",
    "content": "%p Name:\n!= account.name\n!= account.type\n"
  },
  {
    "path": "test/apps/rails3/app/views/other/_user.html.erb",
    "content": "Name: <%= raw user.first_name %> <%= raw user.last_name %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/other/test_collection.html.erb",
    "content": "<h1>Other#test_collection</h1>\n<p>Find me in app/views/other/test_collection.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails3/app/views/other/test_iteration.html.erb",
    "content": "<h2>This it test_iteration</h2>\n<% @users.each do |user| %>\n  <%= raw user.name %>\n  <%= raw user.email %>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/other/test_locals.html.erb",
    "content": "<h1>Other#test_locals</h1>\n<p>Find me in app/views/other/test_locals.html.erb</p>\n\nThis is user input: <%= raw input %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/other/test_mail_to.html.erb",
    "content": "<%= mail_to @user.email, @user.name, :encode => :javascript %>\n\nShould not warn:\n<%= mail_to @user.email, @user.name, :encode => :hex %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/other/test_object.html.erb",
    "content": "<h1>Other#test_object</h1>\n<p>Find me in app/views/other/test_object.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails3/app/views/other/test_select_tag.html.erb",
    "content": "<%= select_tag \"name\", options, :prompt => something_benign %>\n\n<%= select_tag \"name\", options, :prompt => \"Select #{params[:name]}\" %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/other/test_send_file.html.erb",
    "content": "<h1>Other#test_send_file</h1>\n<p>Find me in app/views/other/test_send_file.html.erb</p>\n"
  },
  {
    "path": "test/apps/rails3/app/views/other/test_strip_tags.html.erb",
    "content": "<%= strip_tags params[:body] %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/products/_form.html.erb",
    "content": "<%= form_for(@product) do |f| %>\n  <% if @product.errors.any? %>\n    <div id=\"error_explanation\">\n      <h2><%= pluralize(@product.errors.count, \"error\") %> prohibited this product from being saved:</h2>\n\n      <ul>\n      <% @product.errors.full_messages.each do |msg| %>\n        <li><%= msg %></li>\n      <% end %>\n      </ul>\n    </div>\n  <% end %>\n\n  <div class=\"field\">\n    <%= raw @product.something %>\n    <%= f.label :price %><br />\n    <%= f.text_field :price %>\n  </div>\n  <div class=\"actions\">\n    <%= f.submit %>\n  </div>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/products/edit.html.erb",
    "content": "<h1>Editing product</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Show', @product %> |\n<%= link_to 'Back', products_path %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/products/index.html.erb",
    "content": "<h1>Listing products</h1>\n\n<table>\n  <tr>\n    <th>Price</th>\n    <th></th>\n    <th></th>\n    <th></th>\n  </tr>\n\n<% @products.each do |product| %>\n  <tr>\n    <td><%= product.price %></td>\n    <td><%= link_to 'Show', product %></td>\n    <td><%= link_to 'Edit', edit_product_path(product) %></td>\n    <td><%= link_to 'Destroy', product, :confirm => 'Are you sure?', :method => :delete %></td>\n  </tr>\n<% end %>\n</table>\n\n<br />\n\n<%= link_to 'New Product', new_product_path %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/products/new.html.erb",
    "content": "<h1>New product</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', products_path %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/products/show.html.erb",
    "content": "<p id=\"notice\"><%= notice %></p>\n\n<p>\n  <b>Price:</b>\n  <%= @product.price %>\n</p>\n\n\n<%= link_to 'Edit', edit_product_path(@product) %> |\n<%= link_to 'Back', products_path %>\n"
  },
  {
    "path": "test/apps/rails3/app/views/whatever/wherever/nested/so_nested.html.erb",
    "content": "<%= raw @bad_thing %>\n"
  },
  {
    "path": "test/apps/rails3/config/application.rb",
    "content": "require File.expand_path('../boot', __FILE__)\n\nrequire 'rails/all'\n\n# If you have a Gemfile, require the gems listed there, including any gems\n# you've limited to :test, :development, or :production.\nBundler.require(:default, Rails.env) if defined?(Bundler)\n\nmodule Rails3\n  class Application < Rails::Application\n    # Settings in config/environments/* take precedence over those specified here.\n    # Application configuration should go into files in config/initializers\n    # -- all .rb files in that directory are automatically loaded.\n\n    # Custom directories with classes and modules you want to be autoloadable.\n    # config.autoload_paths += %W(#{config.root}/extras)\n\n    # Only load the plugins named here, in the order given (default is alphabetical).\n    # :all can be used as a placeholder for all plugins not explicitly named.\n    # config.plugins = [ :exception_notification, :ssl_requirement, :all ]\n\n    # Activate observers that should always be running.\n    # config.active_record.observers = :cacher, :garbage_collector, :forum_observer\n\n    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.\n    # Run \"rake -D time\" for a list of tasks for finding time zone names. Default is UTC.\n    # config.time_zone = 'Central Time (US & Canada)'\n\n    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.\n    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]\n    # config.i18n.default_locale = :de\n\n    # JavaScript files you want as :defaults (application.js is always included).\n    # config.action_view.javascript_expansions[:defaults] = %w(jquery rails)\n\n    # Configure the default encoding used in templates for Ruby 1.9.\n    config.encoding = \"utf-8\"\n\n    # Configure sensitive parameters which will be filtered from the log file.\n    config.filter_parameters += [:password]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/config/boot.rb",
    "content": "require 'rubygems'\n\n# Set up gems listed in the Gemfile.\nENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)\n\nrequire 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])\n"
  },
  {
    "path": "test/apps/rails3/config/brakeman.yml",
    "content": ""
  },
  {
    "path": "test/apps/rails3/config/database.yml",
    "content": "# SQLite version 3.x\n#   gem install sqlite3\ndevelopment:\n  adapter: sqlite3\n  database: db/development.sqlite3\n  pool: 5\n  timeout: 5000\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  adapter: sqlite3\n  database: db/test.sqlite3\n  pool: 5\n  timeout: 5000\n\nproduction:\n  adapter: sqlite3\n  database: db/production.sqlite3\n  pool: 5\n  timeout: 5000\n"
  },
  {
    "path": "test/apps/rails3/config/environment.rb",
    "content": "# Load the rails application\nrequire File.expand_path('../application', __FILE__)\n\n# Initialize the rails application\nRails3::Application.initialize!\n"
  },
  {
    "path": "test/apps/rails3/config/environments/development.rb",
    "content": "Rails3::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # In the development environment your application's code is reloaded on\n  # every request.  This slows down response time but is perfect for development\n  # since you don't have to restart the webserver when you make code changes.\n  config.cache_classes = false\n\n  # Log error messages when you accidentally call methods on nil.\n  config.whiny_nils = true\n\n  # Show full error reports and disable caching\n  config.consider_all_requests_local       = true\n  config.action_view.debug_rjs             = true\n  config.action_controller.perform_caching = false\n\n  # Don't care if the mailer can't send\n  config.action_mailer.raise_delivery_errors = false\n\n  # Print deprecation notices to the Rails logger\n  config.active_support.deprecation = :log\n\n  # Only use best-standards-support built into browsers\n  config.action_dispatch.best_standards_support = :builtin\nend\n\n"
  },
  {
    "path": "test/apps/rails3/config/environments/production.rb",
    "content": "Rails3::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # The production environment is meant for finished, \"live\" apps.\n  # Code is not reloaded between requests\n  config.cache_classes = true\n\n  # Full error reports are disabled and caching is turned on\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Specifies the header that your server uses for sending files\n  config.action_dispatch.x_sendfile_header = \"X-Sendfile\"\n\n  # For nginx:\n  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'\n\n  # If you have no front-end server that supports something like X-Sendfile,\n  # just comment this out and Rails will serve the files\n\n  # See everything in the log (default is :info)\n  # config.log_level = :debug\n\n  # Use a different logger for distributed setups\n  # config.logger = SyslogLogger.new\n\n  # Use a different cache store in production\n  # config.cache_store = :mem_cache_store\n\n  # Disable Rails's static asset server\n  # In production, Apache or nginx will already do this\n  config.serve_static_assets = false\n\n  # Enable serving of images, stylesheets, and javascripts from an asset server\n  # config.action_controller.asset_host = \"http://assets.example.com\"\n\n  # Disable delivery errors, bad email addresses will be ignored\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable threaded mode\n  # config.threadsafe!\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation can not be found)\n  config.i18n.fallbacks = true\n\n  # Send deprecation notices to registered listeners\n  config.active_support.deprecation = :notify\nend\n"
  },
  {
    "path": "test/apps/rails3/config/environments/test.rb",
    "content": "Rails3::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # The test environment is used exclusively to run your application's\n  # test suite.  You never need to work with it otherwise.  Remember that\n  # your test database is \"scratch space\" for the test suite and is wiped\n  # and recreated between test runs.  Don't rely on the data there!\n  config.cache_classes = true\n\n  # Log error messages when you accidentally call methods on nil.\n  config.whiny_nils = true\n\n  # Show full error reports and disable caching\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Raise exceptions instead of rendering exception templates\n  config.action_dispatch.show_exceptions = false\n\n  # Disable request forgery protection in test environment\n  config.action_controller.allow_forgery_protection    = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Use SQL instead of Active Record's schema dumper when creating the test database.\n  # This is necessary if your schema can't be completely dumped by the schema dumper,\n  # like if you have constraints or database-specific column types\n  # config.active_record.schema_format = :sql\n\n  # Print deprecation notices to the stderr\n  config.active_support.deprecation = :stderr\nend\n"
  },
  {
    "path": "test/apps/rails3/config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "test/apps/rails3/config/initializers/disable_xml_parsing.rb",
    "content": "ActionDispatch::ParamsParser::DEFAULT_PARSERS.delete(Mime::XML)\n"
  },
  {
    "path": "test/apps/rails3/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format\n# (all these examples are active by default):\n# ActiveSupport::Inflector.inflections do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n"
  },
  {
    "path": "test/apps/rails3/config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n# Mime::Type.register_alias \"text/html\", :iphone\n"
  },
  {
    "path": "test/apps/rails3/config/initializers/session_store.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\nRails3::Application.config.session_store :cookie_store, :key => '_rails3_session', :httponly => false, :secure => false\n\n# Use the database for sessions instead of the cookie-based default,\n# which shouldn't be used to store highly confidential information\n# (create the session table with \"rails generate session_migration\")\n# Rails3::Application.config.session_store :active_record_store\n"
  },
  {
    "path": "test/apps/rails3/config/locales/en.yml",
    "content": "# Sample localization file for English. Add more files in this directory for other locales.\n# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/apps/rails3/config/routes.rb",
    "content": "Rails3::Application.routes.draw do\n  resources :products\n\n  get \"other/test_locals\"\n\n  get \"other/test_object\"\n\n  get \"other/test_collection\"\n\n  get \"other/test_iteration\"\n\n  get \"other/test_send_file\"\n\n  get \"other/test_mail_to\"\n\n  get \"home/index\"\n\n  get \"home/test_params\"\n\n  get \"home/test_model\"\n\n  get \"home/test_cookie\"\n\n  get \"home/test_filter\"\n\n  get \"home/test_file_access\"\n\n  get \"home/test_sql\"\n\n  get \"home/test_command\"\n\n  get \"home/test_eval\"\n\n  get \"home/test_redirect\"\n\n  get \"home/test_render\"\n\n  get \"home/test_mass_assignment\"\n\n  get \"home/test_dynamic_render\"\n\n  controller \"a#{controller_name}\" do\n    get \"some_route\"\n  end\n\n  # The priority is based upon order of creation:\n  # first created -> highest priority.\n\n  # Sample of regular route:\n  #   match 'products/:id' => 'catalog#view'\n  # Keep in mind you can assign values other than :controller and :action\n\n  # Sample of named route:\n  #   match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase\n  # This route can be invoked with purchase_url(:id => product.id)\n\n  # Sample resource route (maps HTTP verbs to controller actions automatically):\n  #   resources :products\n\n  # Sample resource route with options:\n  #   resources :products do\n  #     member do\n  #       get 'short'\n  #       post 'toggle'\n  #     end\n  #\n  #     collection do\n  #       get 'sold'\n  #     end\n  #   end\n\n  # Sample resource route with sub-resources:\n  #   resources :products do\n  #     resources :comments, :sales\n  #     resource :seller\n  #   end\n\n  # Sample resource route with more complex sub-resources\n  #   resources :products do\n  #     resources :comments\n  #     resources :sales do\n  #       get 'recent', :on => :collection\n  #     end\n  #   end\n\n  # Sample resource route within a namespace:\n  #   namespace :admin do\n  #     # Directs /admin/products/* to Admin::ProductsController\n  #     # (app/controllers/admin/products_controller.rb)\n  #     resources :products\n  #   end\n\n  # You can have the root of your site routed with \"root\"\n  # just remember to delete public/index.html.\n  # root :to => \"welcome#index\"\n\n  # See how all your routes lay out with \"rake routes\"\n\n  # This is a legacy wild controller route that's not recommended for RESTful applications.\n  # Note: This route will make all actions in every controller accessible via GET requests.\n  match ':controller(/:action(/:id(.:format)))'\nend\n"
  },
  {
    "path": "test/apps/rails3/config.ru",
    "content": "# This file is used by Rack-based servers to start the application.\n\nrequire ::File.expand_path('../config/environment',  __FILE__)\nrun Rails3::Application\n"
  },
  {
    "path": "test/apps/rails3/db/seeds.rb",
    "content": "# This file should contain all the record creation needed to seed the database with its default values.\n# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).\n#\n# Examples:\n#\n#   cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])\n#   Mayor.create(:name => 'Daley', :city => cities.first)\n"
  },
  {
    "path": "test/apps/rails3/doc/README_FOR_APP",
    "content": "Use this README file to introduce your application and point to useful places in the API for learning more.\nRun \"rake doc:app\" to generate API documentation for your models, controllers, helpers, and libraries.\n"
  },
  {
    "path": "test/apps/rails3/lib/controller_filter.rb",
    "content": "module ControllerFilter\n  # Basically copied from the wilds\n  def self.included somewhere\n    somewhere.class_eval do\n      before_filter do\n        do_something\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/lib/tasks/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3/public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The page you were looking for doesn't exist (404)</title>\n  <style type=\"text/css\">\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <h1>The page you were looking for doesn't exist.</h1>\n    <p>You may have mistyped the address or the page may have moved.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails3/public/422.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The change you wanted was rejected (422)</title>\n  <style type=\"text/css\">\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <h1>The change you wanted was rejected.</h1>\n    <p>Maybe you tried to change something you didn't have access to.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails3/public/500.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>We're sorry, but something went wrong (500)</title>\n  <style type=\"text/css\">\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <h1>We're sorry, but something went wrong.</h1>\n    <p>We've been notified about this issue and we'll take a look at it shortly.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails3/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Ruby on Rails: Welcome aboard</title>\n    <style type=\"text/css\" media=\"screen\">\n      body {\n        margin: 0;\n        margin-bottom: 25px;\n        padding: 0;\n        background-color: #f0f0f0;\n        font-family: \"Lucida Grande\", \"Bitstream Vera Sans\", \"Verdana\";\n        font-size: 13px;\n        color: #333;\n      }\n\n      h1 {\n        font-size: 28px;\n        color: #000;\n      }\n\n      a  {color: #03c}\n      a:hover {\n        background-color: #03c;\n        color: white;\n        text-decoration: none;\n      }\n\n\n      #page {\n        background-color: #f0f0f0;\n        width: 750px;\n        margin: 0;\n        margin-left: auto;\n        margin-right: auto;\n      }\n\n      #content {\n        float: left;\n        background-color: white;\n        border: 3px solid #aaa;\n        border-top: none;\n        padding: 25px;\n        width: 500px;\n      }\n\n      #sidebar {\n        float: right;\n        width: 175px;\n      }\n\n      #footer {\n        clear: both;\n      }\n\n\n      #header, #about, #getting-started {\n        padding-left: 75px;\n        padding-right: 30px;\n      }\n\n\n      #header {\n        background-image: url(\"images/rails.png\");\n        background-repeat: no-repeat;\n        background-position: top left;\n        height: 64px;\n      }\n      #header h1, #header h2 {margin: 0}\n      #header h2 {\n        color: #888;\n        font-weight: normal;\n        font-size: 16px;\n      }\n\n\n      #about h3 {\n        margin: 0;\n        margin-bottom: 10px;\n        font-size: 14px;\n      }\n\n      #about-content {\n        background-color: #ffd;\n        border: 1px solid #fc0;\n        margin-left: -55px;\n        margin-right: -10px;\n      }\n      #about-content table {\n        margin-top: 10px;\n        margin-bottom: 10px;\n        font-size: 11px;\n        border-collapse: collapse;\n      }\n      #about-content td {\n        padding: 10px;\n        padding-top: 3px;\n        padding-bottom: 3px;\n      }\n      #about-content td.name  {color: #555}\n      #about-content td.value {color: #000}\n\n      #about-content ul {\n        padding: 0;\n        list-style-type: none;\n      }\n\n      #about-content.failure {\n        background-color: #fcc;\n        border: 1px solid #f00;\n      }\n      #about-content.failure p {\n        margin: 0;\n        padding: 10px;\n      }\n\n\n      #getting-started {\n        border-top: 1px solid #ccc;\n        margin-top: 25px;\n        padding-top: 15px;\n      }\n      #getting-started h1 {\n        margin: 0;\n        font-size: 20px;\n      }\n      #getting-started h2 {\n        margin: 0;\n        font-size: 14px;\n        font-weight: normal;\n        color: #333;\n        margin-bottom: 25px;\n      }\n      #getting-started ol {\n        margin-left: 0;\n        padding-left: 0;\n      }\n      #getting-started li {\n        font-size: 18px;\n        color: #888;\n        margin-bottom: 25px;\n      }\n      #getting-started li h2 {\n        margin: 0;\n        font-weight: normal;\n        font-size: 18px;\n        color: #333;\n      }\n      #getting-started li p {\n        color: #555;\n        font-size: 13px;\n      }\n\n\n      #sidebar ul {\n        margin-left: 0;\n        padding-left: 0;\n      }\n      #sidebar ul h3 {\n        margin-top: 25px;\n        font-size: 16px;\n        padding-bottom: 10px;\n        border-bottom: 1px solid #ccc;\n      }\n      #sidebar li {\n        list-style-type: none;\n      }\n      #sidebar ul.links li {\n        margin-bottom: 5px;\n      }\n\n    </style>\n    <script type=\"text/javascript\">\n      function about() {\n        info = document.getElementById('about-content');\n        if (window.XMLHttpRequest)\n          { xhr = new XMLHttpRequest(); }\n        else\n          { xhr = new ActiveXObject(\"Microsoft.XMLHTTP\"); }\n        xhr.open(\"GET\",\"rails/info/properties\",false);\n        xhr.send(\"\");\n        info.innerHTML = xhr.responseText;\n        info.style.display = 'block'\n      }\n    </script>\n  </head>\n  <body>\n    <div id=\"page\">\n      <div id=\"sidebar\">\n        <ul id=\"sidebar-items\">\n          <li>\n            <h3>Browse the documentation</h3>\n            <ul class=\"links\">\n              <li><a href=\"http://api.rubyonrails.org/\">Rails API</a></li>\n              <li><a href=\"http://stdlib.rubyonrails.org/\">Ruby standard library</a></li>\n              <li><a href=\"http://corelib.rubyonrails.org/\">Ruby core</a></li>\n              <li><a href=\"http://guides.rubyonrails.org/\">Rails Guides</a></li>\n            </ul>\n          </li>\n        </ul>\n      </div>\n\n      <div id=\"content\">\n        <div id=\"header\">\n          <h1>Welcome aboard</h1>\n          <h2>You&rsquo;re riding Ruby on Rails!</h2>\n        </div>\n\n        <div id=\"about\">\n          <h3><a href=\"rails/info/properties\" onclick=\"about(); return false\">About your application&rsquo;s environment</a></h3>\n          <div id=\"about-content\" style=\"display: none\"></div>\n        </div>\n\n        <div id=\"getting-started\">\n          <h1>Getting started</h1>\n          <h2>Here&rsquo;s how to get rolling:</h2>\n\n          <ol>\n            <li>\n              <h2>Use <code>rails generate</code> to create your models and controllers</h2>\n              <p>To see all available options, run it without parameters.</p>\n            </li>\n\n            <li>\n              <h2>Set up a default route and remove or rename this file</h2>\n              <p>Routes are set up in config/routes.rb.</p>\n            </li>\n\n            <li>\n              <h2>Create your database</h2>\n              <p>Run <code>rake db:migrate</code> to create your database. If you're not using SQLite (the default), edit <code>config/database.yml</code> with your username and password.</p>\n            </li>\n          </ol>\n        </div>\n      </div>\n\n      <div id=\"footer\">&nbsp;</div>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/apps/rails3/public/javascripts/application.js",
    "content": "// Place your application-specific JavaScript functions and classes here\n// This file is automatically included by javascript_include_tag :defaults\n"
  },
  {
    "path": "test/apps/rails3/public/javascripts/controls.js",
    "content": "// script.aculo.us controls.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009\n\n// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n//           (c) 2005-2009 Ivan Krstic (http://blogs.law.harvard.edu/ivan)\n//           (c) 2005-2009 Jon Tirsen (http://www.tirsen.com)\n// Contributors:\n//  Richard Livsey\n//  Rahul Bhargava\n//  Rob Wills\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\n// Autocompleter.Base handles all the autocompletion functionality\n// that's independent of the data source for autocompletion. This\n// includes drawing the autocompletion menu, observing keyboard\n// and mouse events, and similar.\n//\n// Specific autocompleters need to provide, at the very least,\n// a getUpdatedChoices function that will be invoked every time\n// the text inside the monitored textbox changes. This method\n// should get the text for which to provide autocompletion by\n// invoking this.getToken(), NOT by directly accessing\n// this.element.value. This is to allow incremental tokenized\n// autocompletion. Specific auto-completion logic (AJAX, etc)\n// belongs in getUpdatedChoices.\n//\n// Tokenized incremental autocompletion is enabled automatically\n// when an autocompleter is instantiated with the 'tokens' option\n// in the options parameter, e.g.:\n// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });\n// will incrementally autocomplete with a comma as the token.\n// Additionally, ',' in the above example can be replaced with\n// a token array, e.g. { tokens: [',', '\\n'] } which\n// enables autocompletion on multiple tokens. This is most\n// useful when one of the tokens is \\n (a newline), as it\n// allows smart autocompletion after linebreaks.\n\nif(typeof Effect == 'undefined')\n  throw(\"controls.js requires including script.aculo.us' effects.js library\");\n\nvar Autocompleter = { };\nAutocompleter.Base = Class.create({\n  baseInitialize: function(element, update, options) {\n    element          = $(element);\n    this.element     = element;\n    this.update      = $(update);\n    this.hasFocus    = false;\n    this.changed     = false;\n    this.active      = false;\n    this.index       = 0;\n    this.entryCount  = 0;\n    this.oldElementValue = this.element.value;\n\n    if(this.setOptions)\n      this.setOptions(options);\n    else\n      this.options = options || { };\n\n    this.options.paramName    = this.options.paramName || this.element.name;\n    this.options.tokens       = this.options.tokens || [];\n    this.options.frequency    = this.options.frequency || 0.4;\n    this.options.minChars     = this.options.minChars || 1;\n    this.options.onShow       = this.options.onShow ||\n      function(element, update){\n        if(!update.style.position || update.style.position=='absolute') {\n          update.style.position = 'absolute';\n          Position.clone(element, update, {\n            setHeight: false,\n            offsetTop: element.offsetHeight\n          });\n        }\n        Effect.Appear(update,{duration:0.15});\n      };\n    this.options.onHide = this.options.onHide ||\n      function(element, update){ new Effect.Fade(update,{duration:0.15}) };\n\n    if(typeof(this.options.tokens) == 'string')\n      this.options.tokens = new Array(this.options.tokens);\n    // Force carriage returns as token delimiters anyway\n    if (!this.options.tokens.include('\\n'))\n      this.options.tokens.push('\\n');\n\n    this.observer = null;\n\n    this.element.setAttribute('autocomplete','off');\n\n    Element.hide(this.update);\n\n    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));\n    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));\n  },\n\n  show: function() {\n    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);\n    if(!this.iefix &&\n      (Prototype.Browser.IE) &&\n      (Element.getStyle(this.update, 'position')=='absolute')) {\n      new Insertion.After(this.update,\n       '<iframe id=\"' + this.update.id + '_iefix\" '+\n       'style=\"display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);\" ' +\n       'src=\"javascript:false;\" frameborder=\"0\" scrolling=\"no\"></iframe>');\n      this.iefix = $(this.update.id+'_iefix');\n    }\n    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);\n  },\n\n  fixIEOverlapping: function() {\n    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});\n    this.iefix.style.zIndex = 1;\n    this.update.style.zIndex = 2;\n    Element.show(this.iefix);\n  },\n\n  hide: function() {\n    this.stopIndicator();\n    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);\n    if(this.iefix) Element.hide(this.iefix);\n  },\n\n  startIndicator: function() {\n    if(this.options.indicator) Element.show(this.options.indicator);\n  },\n\n  stopIndicator: function() {\n    if(this.options.indicator) Element.hide(this.options.indicator);\n  },\n\n  onKeyPress: function(event) {\n    if(this.active)\n      switch(event.keyCode) {\n       case Event.KEY_TAB:\n       case Event.KEY_RETURN:\n         this.selectEntry();\n         Event.stop(event);\n       case Event.KEY_ESC:\n         this.hide();\n         this.active = false;\n         Event.stop(event);\n         return;\n       case Event.KEY_LEFT:\n       case Event.KEY_RIGHT:\n         return;\n       case Event.KEY_UP:\n         this.markPrevious();\n         this.render();\n         Event.stop(event);\n         return;\n       case Event.KEY_DOWN:\n         this.markNext();\n         this.render();\n         Event.stop(event);\n         return;\n      }\n     else\n       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||\n         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;\n\n    this.changed = true;\n    this.hasFocus = true;\n\n    if(this.observer) clearTimeout(this.observer);\n      this.observer =\n        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);\n  },\n\n  activate: function() {\n    this.changed = false;\n    this.hasFocus = true;\n    this.getUpdatedChoices();\n  },\n\n  onHover: function(event) {\n    var element = Event.findElement(event, 'LI');\n    if(this.index != element.autocompleteIndex)\n    {\n        this.index = element.autocompleteIndex;\n        this.render();\n    }\n    Event.stop(event);\n  },\n\n  onClick: function(event) {\n    var element = Event.findElement(event, 'LI');\n    this.index = element.autocompleteIndex;\n    this.selectEntry();\n    this.hide();\n  },\n\n  onBlur: function(event) {\n    // needed to make click events working\n    setTimeout(this.hide.bind(this), 250);\n    this.hasFocus = false;\n    this.active = false;\n  },\n\n  render: function() {\n    if(this.entryCount > 0) {\n      for (var i = 0; i < this.entryCount; i++)\n        this.index==i ?\n          Element.addClassName(this.getEntry(i),\"selected\") :\n          Element.removeClassName(this.getEntry(i),\"selected\");\n      if(this.hasFocus) {\n        this.show();\n        this.active = true;\n      }\n    } else {\n      this.active = false;\n      this.hide();\n    }\n  },\n\n  markPrevious: function() {\n    if(this.index > 0) this.index--;\n      else this.index = this.entryCount-1;\n    this.getEntry(this.index).scrollIntoView(true);\n  },\n\n  markNext: function() {\n    if(this.index < this.entryCount-1) this.index++;\n      else this.index = 0;\n    this.getEntry(this.index).scrollIntoView(false);\n  },\n\n  getEntry: function(index) {\n    return this.update.firstChild.childNodes[index];\n  },\n\n  getCurrentEntry: function() {\n    return this.getEntry(this.index);\n  },\n\n  selectEntry: function() {\n    this.active = false;\n    this.updateElement(this.getCurrentEntry());\n  },\n\n  updateElement: function(selectedElement) {\n    if (this.options.updateElement) {\n      this.options.updateElement(selectedElement);\n      return;\n    }\n    var value = '';\n    if (this.options.select) {\n      var nodes = $(selectedElement).select('.' + this.options.select) || [];\n      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);\n    } else\n      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');\n\n    var bounds = this.getTokenBounds();\n    if (bounds[0] != -1) {\n      var newValue = this.element.value.substr(0, bounds[0]);\n      var whitespace = this.element.value.substr(bounds[0]).match(/^\\s+/);\n      if (whitespace)\n        newValue += whitespace[0];\n      this.element.value = newValue + value + this.element.value.substr(bounds[1]);\n    } else {\n      this.element.value = value;\n    }\n    this.oldElementValue = this.element.value;\n    this.element.focus();\n\n    if (this.options.afterUpdateElement)\n      this.options.afterUpdateElement(this.element, selectedElement);\n  },\n\n  updateChoices: function(choices) {\n    if(!this.changed && this.hasFocus) {\n      this.update.innerHTML = choices;\n      Element.cleanWhitespace(this.update);\n      Element.cleanWhitespace(this.update.down());\n\n      if(this.update.firstChild && this.update.down().childNodes) {\n        this.entryCount =\n          this.update.down().childNodes.length;\n        for (var i = 0; i < this.entryCount; i++) {\n          var entry = this.getEntry(i);\n          entry.autocompleteIndex = i;\n          this.addObservers(entry);\n        }\n      } else {\n        this.entryCount = 0;\n      }\n\n      this.stopIndicator();\n      this.index = 0;\n\n      if(this.entryCount==1 && this.options.autoSelect) {\n        this.selectEntry();\n        this.hide();\n      } else {\n        this.render();\n      }\n    }\n  },\n\n  addObservers: function(element) {\n    Event.observe(element, \"mouseover\", this.onHover.bindAsEventListener(this));\n    Event.observe(element, \"click\", this.onClick.bindAsEventListener(this));\n  },\n\n  onObserverEvent: function() {\n    this.changed = false;\n    this.tokenBounds = null;\n    if(this.getToken().length>=this.options.minChars) {\n      this.getUpdatedChoices();\n    } else {\n      this.active = false;\n      this.hide();\n    }\n    this.oldElementValue = this.element.value;\n  },\n\n  getToken: function() {\n    var bounds = this.getTokenBounds();\n    return this.element.value.substring(bounds[0], bounds[1]).strip();\n  },\n\n  getTokenBounds: function() {\n    if (null != this.tokenBounds) return this.tokenBounds;\n    var value = this.element.value;\n    if (value.strip().empty()) return [-1, 0];\n    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);\n    var offset = (diff == this.oldElementValue.length ? 1 : 0);\n    var prevTokenPos = -1, nextTokenPos = value.length;\n    var tp;\n    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {\n      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);\n      if (tp > prevTokenPos) prevTokenPos = tp;\n      tp = value.indexOf(this.options.tokens[index], diff + offset);\n      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;\n    }\n    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);\n  }\n});\n\nAutocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {\n  var boundary = Math.min(newS.length, oldS.length);\n  for (var index = 0; index < boundary; ++index)\n    if (newS[index] != oldS[index])\n      return index;\n  return boundary;\n};\n\nAjax.Autocompleter = Class.create(Autocompleter.Base, {\n  initialize: function(element, update, url, options) {\n    this.baseInitialize(element, update, options);\n    this.options.asynchronous  = true;\n    this.options.onComplete    = this.onComplete.bind(this);\n    this.options.defaultParams = this.options.parameters || null;\n    this.url                   = url;\n  },\n\n  getUpdatedChoices: function() {\n    this.startIndicator();\n\n    var entry = encodeURIComponent(this.options.paramName) + '=' +\n      encodeURIComponent(this.getToken());\n\n    this.options.parameters = this.options.callback ?\n      this.options.callback(this.element, entry) : entry;\n\n    if(this.options.defaultParams)\n      this.options.parameters += '&' + this.options.defaultParams;\n\n    new Ajax.Request(this.url, this.options);\n  },\n\n  onComplete: function(request) {\n    this.updateChoices(request.responseText);\n  }\n});\n\n// The local array autocompleter. Used when you'd prefer to\n// inject an array of autocompletion options into the page, rather\n// than sending out Ajax queries, which can be quite slow sometimes.\n//\n// The constructor takes four parameters. The first two are, as usual,\n// the id of the monitored textbox, and id of the autocompletion menu.\n// The third is the array you want to autocomplete from, and the fourth\n// is the options block.\n//\n// Extra local autocompletion options:\n// - choices - How many autocompletion choices to offer\n//\n// - partialSearch - If false, the autocompleter will match entered\n//                    text only at the beginning of strings in the\n//                    autocomplete array. Defaults to true, which will\n//                    match text at the beginning of any *word* in the\n//                    strings in the autocomplete array. If you want to\n//                    search anywhere in the string, additionally set\n//                    the option fullSearch to true (default: off).\n//\n// - fullSsearch - Search anywhere in autocomplete array strings.\n//\n// - partialChars - How many characters to enter before triggering\n//                   a partial match (unlike minChars, which defines\n//                   how many characters are required to do any match\n//                   at all). Defaults to 2.\n//\n// - ignoreCase - Whether to ignore case when autocompleting.\n//                 Defaults to true.\n//\n// It's possible to pass in a custom function as the 'selector'\n// option, if you prefer to write your own autocompletion logic.\n// In that case, the other options above will not apply unless\n// you support them.\n\nAutocompleter.Local = Class.create(Autocompleter.Base, {\n  initialize: function(element, update, array, options) {\n    this.baseInitialize(element, update, options);\n    this.options.array = array;\n  },\n\n  getUpdatedChoices: function() {\n    this.updateChoices(this.options.selector(this));\n  },\n\n  setOptions: function(options) {\n    this.options = Object.extend({\n      choices: 10,\n      partialSearch: true,\n      partialChars: 2,\n      ignoreCase: true,\n      fullSearch: false,\n      selector: function(instance) {\n        var ret       = []; // Beginning matches\n        var partial   = []; // Inside matches\n        var entry     = instance.getToken();\n        var count     = 0;\n\n        for (var i = 0; i < instance.options.array.length &&\n          ret.length < instance.options.choices ; i++) {\n\n          var elem = instance.options.array[i];\n          var foundPos = instance.options.ignoreCase ?\n            elem.toLowerCase().indexOf(entry.toLowerCase()) :\n            elem.indexOf(entry);\n\n          while (foundPos != -1) {\n            if (foundPos == 0 && elem.length != entry.length) {\n              ret.push(\"<li><strong>\" + elem.substr(0, entry.length) + \"</strong>\" +\n                elem.substr(entry.length) + \"</li>\");\n              break;\n            } else if (entry.length >= instance.options.partialChars &&\n              instance.options.partialSearch && foundPos != -1) {\n              if (instance.options.fullSearch || /\\s/.test(elem.substr(foundPos-1,1))) {\n                partial.push(\"<li>\" + elem.substr(0, foundPos) + \"<strong>\" +\n                  elem.substr(foundPos, entry.length) + \"</strong>\" + elem.substr(\n                  foundPos + entry.length) + \"</li>\");\n                break;\n              }\n            }\n\n            foundPos = instance.options.ignoreCase ?\n              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :\n              elem.indexOf(entry, foundPos + 1);\n\n          }\n        }\n        if (partial.length)\n          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));\n        return \"<ul>\" + ret.join('') + \"</ul>\";\n      }\n    }, options || { });\n  }\n});\n\n// AJAX in-place editor and collection editor\n// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).\n\n// Use this if you notice weird scrolling problems on some browsers,\n// the DOM might be a bit confused when this gets called so do this\n// waits 1 ms (with setTimeout) until it does the activation\nField.scrollFreeActivate = function(field) {\n  setTimeout(function() {\n    Field.activate(field);\n  }, 1);\n};\n\nAjax.InPlaceEditor = Class.create({\n  initialize: function(element, url, options) {\n    this.url = url;\n    this.element = element = $(element);\n    this.prepareOptions();\n    this._controls = { };\n    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!\n    Object.extend(this.options, options || { });\n    if (!this.options.formId && this.element.id) {\n      this.options.formId = this.element.id + '-inplaceeditor';\n      if ($(this.options.formId))\n        this.options.formId = '';\n    }\n    if (this.options.externalControl)\n      this.options.externalControl = $(this.options.externalControl);\n    if (!this.options.externalControl)\n      this.options.externalControlOnly = false;\n    this._originalBackground = this.element.getStyle('background-color') || 'transparent';\n    this.element.title = this.options.clickToEditText;\n    this._boundCancelHandler = this.handleFormCancellation.bind(this);\n    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);\n    this._boundFailureHandler = this.handleAJAXFailure.bind(this);\n    this._boundSubmitHandler = this.handleFormSubmission.bind(this);\n    this._boundWrapperHandler = this.wrapUp.bind(this);\n    this.registerListeners();\n  },\n  checkForEscapeOrReturn: function(e) {\n    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;\n    if (Event.KEY_ESC == e.keyCode)\n      this.handleFormCancellation(e);\n    else if (Event.KEY_RETURN == e.keyCode)\n      this.handleFormSubmission(e);\n  },\n  createControl: function(mode, handler, extraClasses) {\n    var control = this.options[mode + 'Control'];\n    var text = this.options[mode + 'Text'];\n    if ('button' == control) {\n      var btn = document.createElement('input');\n      btn.type = 'submit';\n      btn.value = text;\n      btn.className = 'editor_' + mode + '_button';\n      if ('cancel' == mode)\n        btn.onclick = this._boundCancelHandler;\n      this._form.appendChild(btn);\n      this._controls[mode] = btn;\n    } else if ('link' == control) {\n      var link = document.createElement('a');\n      link.href = '#';\n      link.appendChild(document.createTextNode(text));\n      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;\n      link.className = 'editor_' + mode + '_link';\n      if (extraClasses)\n        link.className += ' ' + extraClasses;\n      this._form.appendChild(link);\n      this._controls[mode] = link;\n    }\n  },\n  createEditField: function() {\n    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());\n    var fld;\n    if (1 >= this.options.rows && !/\\r|\\n/.test(this.getText())) {\n      fld = document.createElement('input');\n      fld.type = 'text';\n      var size = this.options.size || this.options.cols || 0;\n      if (0 < size) fld.size = size;\n    } else {\n      fld = document.createElement('textarea');\n      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);\n      fld.cols = this.options.cols || 40;\n    }\n    fld.name = this.options.paramName;\n    fld.value = text; // No HTML breaks conversion anymore\n    fld.className = 'editor_field';\n    if (this.options.submitOnBlur)\n      fld.onblur = this._boundSubmitHandler;\n    this._controls.editor = fld;\n    if (this.options.loadTextURL)\n      this.loadExternalText();\n    this._form.appendChild(this._controls.editor);\n  },\n  createForm: function() {\n    var ipe = this;\n    function addText(mode, condition) {\n      var text = ipe.options['text' + mode + 'Controls'];\n      if (!text || condition === false) return;\n      ipe._form.appendChild(document.createTextNode(text));\n    };\n    this._form = $(document.createElement('form'));\n    this._form.id = this.options.formId;\n    this._form.addClassName(this.options.formClassName);\n    this._form.onsubmit = this._boundSubmitHandler;\n    this.createEditField();\n    if ('textarea' == this._controls.editor.tagName.toLowerCase())\n      this._form.appendChild(document.createElement('br'));\n    if (this.options.onFormCustomization)\n      this.options.onFormCustomization(this, this._form);\n    addText('Before', this.options.okControl || this.options.cancelControl);\n    this.createControl('ok', this._boundSubmitHandler);\n    addText('Between', this.options.okControl && this.options.cancelControl);\n    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');\n    addText('After', this.options.okControl || this.options.cancelControl);\n  },\n  destroy: function() {\n    if (this._oldInnerHTML)\n      this.element.innerHTML = this._oldInnerHTML;\n    this.leaveEditMode();\n    this.unregisterListeners();\n  },\n  enterEditMode: function(e) {\n    if (this._saving || this._editing) return;\n    this._editing = true;\n    this.triggerCallback('onEnterEditMode');\n    if (this.options.externalControl)\n      this.options.externalControl.hide();\n    this.element.hide();\n    this.createForm();\n    this.element.parentNode.insertBefore(this._form, this.element);\n    if (!this.options.loadTextURL)\n      this.postProcessEditField();\n    if (e) Event.stop(e);\n  },\n  enterHover: function(e) {\n    if (this.options.hoverClassName)\n      this.element.addClassName(this.options.hoverClassName);\n    if (this._saving) return;\n    this.triggerCallback('onEnterHover');\n  },\n  getText: function() {\n    return this.element.innerHTML.unescapeHTML();\n  },\n  handleAJAXFailure: function(transport) {\n    this.triggerCallback('onFailure', transport);\n    if (this._oldInnerHTML) {\n      this.element.innerHTML = this._oldInnerHTML;\n      this._oldInnerHTML = null;\n    }\n  },\n  handleFormCancellation: function(e) {\n    this.wrapUp();\n    if (e) Event.stop(e);\n  },\n  handleFormSubmission: function(e) {\n    var form = this._form;\n    var value = $F(this._controls.editor);\n    this.prepareSubmission();\n    var params = this.options.callback(form, value) || '';\n    if (Object.isString(params))\n      params = params.toQueryParams();\n    params.editorId = this.element.id;\n    if (this.options.htmlResponse) {\n      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);\n      Object.extend(options, {\n        parameters: params,\n        onComplete: this._boundWrapperHandler,\n        onFailure: this._boundFailureHandler\n      });\n      new Ajax.Updater({ success: this.element }, this.url, options);\n    } else {\n      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n      Object.extend(options, {\n        parameters: params,\n        onComplete: this._boundWrapperHandler,\n        onFailure: this._boundFailureHandler\n      });\n      new Ajax.Request(this.url, options);\n    }\n    if (e) Event.stop(e);\n  },\n  leaveEditMode: function() {\n    this.element.removeClassName(this.options.savingClassName);\n    this.removeForm();\n    this.leaveHover();\n    this.element.style.backgroundColor = this._originalBackground;\n    this.element.show();\n    if (this.options.externalControl)\n      this.options.externalControl.show();\n    this._saving = false;\n    this._editing = false;\n    this._oldInnerHTML = null;\n    this.triggerCallback('onLeaveEditMode');\n  },\n  leaveHover: function(e) {\n    if (this.options.hoverClassName)\n      this.element.removeClassName(this.options.hoverClassName);\n    if (this._saving) return;\n    this.triggerCallback('onLeaveHover');\n  },\n  loadExternalText: function() {\n    this._form.addClassName(this.options.loadingClassName);\n    this._controls.editor.disabled = true;\n    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n    Object.extend(options, {\n      parameters: 'editorId=' + encodeURIComponent(this.element.id),\n      onComplete: Prototype.emptyFunction,\n      onSuccess: function(transport) {\n        this._form.removeClassName(this.options.loadingClassName);\n        var text = transport.responseText;\n        if (this.options.stripLoadedTextTags)\n          text = text.stripTags();\n        this._controls.editor.value = text;\n        this._controls.editor.disabled = false;\n        this.postProcessEditField();\n      }.bind(this),\n      onFailure: this._boundFailureHandler\n    });\n    new Ajax.Request(this.options.loadTextURL, options);\n  },\n  postProcessEditField: function() {\n    var fpc = this.options.fieldPostCreation;\n    if (fpc)\n      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();\n  },\n  prepareOptions: function() {\n    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);\n    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);\n    [this._extraDefaultOptions].flatten().compact().each(function(defs) {\n      Object.extend(this.options, defs);\n    }.bind(this));\n  },\n  prepareSubmission: function() {\n    this._saving = true;\n    this.removeForm();\n    this.leaveHover();\n    this.showSaving();\n  },\n  registerListeners: function() {\n    this._listeners = { };\n    var listener;\n    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {\n      listener = this[pair.value].bind(this);\n      this._listeners[pair.key] = listener;\n      if (!this.options.externalControlOnly)\n        this.element.observe(pair.key, listener);\n      if (this.options.externalControl)\n        this.options.externalControl.observe(pair.key, listener);\n    }.bind(this));\n  },\n  removeForm: function() {\n    if (!this._form) return;\n    this._form.remove();\n    this._form = null;\n    this._controls = { };\n  },\n  showSaving: function() {\n    this._oldInnerHTML = this.element.innerHTML;\n    this.element.innerHTML = this.options.savingText;\n    this.element.addClassName(this.options.savingClassName);\n    this.element.style.backgroundColor = this._originalBackground;\n    this.element.show();\n  },\n  triggerCallback: function(cbName, arg) {\n    if ('function' == typeof this.options[cbName]) {\n      this.options[cbName](this, arg);\n    }\n  },\n  unregisterListeners: function() {\n    $H(this._listeners).each(function(pair) {\n      if (!this.options.externalControlOnly)\n        this.element.stopObserving(pair.key, pair.value);\n      if (this.options.externalControl)\n        this.options.externalControl.stopObserving(pair.key, pair.value);\n    }.bind(this));\n  },\n  wrapUp: function(transport) {\n    this.leaveEditMode();\n    // Can't use triggerCallback due to backward compatibility: requires\n    // binding + direct element\n    this._boundComplete(transport, this.element);\n  }\n});\n\nObject.extend(Ajax.InPlaceEditor.prototype, {\n  dispose: Ajax.InPlaceEditor.prototype.destroy\n});\n\nAjax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {\n  initialize: function($super, element, url, options) {\n    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;\n    $super(element, url, options);\n  },\n\n  createEditField: function() {\n    var list = document.createElement('select');\n    list.name = this.options.paramName;\n    list.size = 1;\n    this._controls.editor = list;\n    this._collection = this.options.collection || [];\n    if (this.options.loadCollectionURL)\n      this.loadCollection();\n    else\n      this.checkForExternalText();\n    this._form.appendChild(this._controls.editor);\n  },\n\n  loadCollection: function() {\n    this._form.addClassName(this.options.loadingClassName);\n    this.showLoadingText(this.options.loadingCollectionText);\n    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n    Object.extend(options, {\n      parameters: 'editorId=' + encodeURIComponent(this.element.id),\n      onComplete: Prototype.emptyFunction,\n      onSuccess: function(transport) {\n        var js = transport.responseText.strip();\n        if (!/^\\[.*\\]$/.test(js)) // TODO: improve sanity check\n          throw('Server returned an invalid collection representation.');\n        this._collection = eval(js);\n        this.checkForExternalText();\n      }.bind(this),\n      onFailure: this.onFailure\n    });\n    new Ajax.Request(this.options.loadCollectionURL, options);\n  },\n\n  showLoadingText: function(text) {\n    this._controls.editor.disabled = true;\n    var tempOption = this._controls.editor.firstChild;\n    if (!tempOption) {\n      tempOption = document.createElement('option');\n      tempOption.value = '';\n      this._controls.editor.appendChild(tempOption);\n      tempOption.selected = true;\n    }\n    tempOption.update((text || '').stripScripts().stripTags());\n  },\n\n  checkForExternalText: function() {\n    this._text = this.getText();\n    if (this.options.loadTextURL)\n      this.loadExternalText();\n    else\n      this.buildOptionList();\n  },\n\n  loadExternalText: function() {\n    this.showLoadingText(this.options.loadingText);\n    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n    Object.extend(options, {\n      parameters: 'editorId=' + encodeURIComponent(this.element.id),\n      onComplete: Prototype.emptyFunction,\n      onSuccess: function(transport) {\n        this._text = transport.responseText.strip();\n        this.buildOptionList();\n      }.bind(this),\n      onFailure: this.onFailure\n    });\n    new Ajax.Request(this.options.loadTextURL, options);\n  },\n\n  buildOptionList: function() {\n    this._form.removeClassName(this.options.loadingClassName);\n    this._collection = this._collection.map(function(entry) {\n      return 2 === entry.length ? entry : [entry, entry].flatten();\n    });\n    var marker = ('value' in this.options) ? this.options.value : this._text;\n    var textFound = this._collection.any(function(entry) {\n      return entry[0] == marker;\n    }.bind(this));\n    this._controls.editor.update('');\n    var option;\n    this._collection.each(function(entry, index) {\n      option = document.createElement('option');\n      option.value = entry[0];\n      option.selected = textFound ? entry[0] == marker : 0 == index;\n      option.appendChild(document.createTextNode(entry[1]));\n      this._controls.editor.appendChild(option);\n    }.bind(this));\n    this._controls.editor.disabled = false;\n    Field.scrollFreeActivate(this._controls.editor);\n  }\n});\n\n//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****\n//**** This only  exists for a while,  in order to  let ****\n//**** users adapt to  the new API.  Read up on the new ****\n//**** API and convert your code to it ASAP!            ****\n\nAjax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {\n  if (!options) return;\n  function fallback(name, expr) {\n    if (name in options || expr === undefined) return;\n    options[name] = expr;\n  };\n  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :\n    options.cancelLink == options.cancelButton == false ? false : undefined)));\n  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :\n    options.okLink == options.okButton == false ? false : undefined)));\n  fallback('highlightColor', options.highlightcolor);\n  fallback('highlightEndColor', options.highlightendcolor);\n};\n\nObject.extend(Ajax.InPlaceEditor, {\n  DefaultOptions: {\n    ajaxOptions: { },\n    autoRows: 3,                                // Use when multi-line w/ rows == 1\n    cancelControl: 'link',                      // 'link'|'button'|false\n    cancelText: 'cancel',\n    clickToEditText: 'Click to edit',\n    externalControl: null,                      // id|elt\n    externalControlOnly: false,\n    fieldPostCreation: 'activate',              // 'activate'|'focus'|false\n    formClassName: 'inplaceeditor-form',\n    formId: null,                               // id|elt\n    highlightColor: '#ffff99',\n    highlightEndColor: '#ffffff',\n    hoverClassName: '',\n    htmlResponse: true,\n    loadingClassName: 'inplaceeditor-loading',\n    loadingText: 'Loading...',\n    okControl: 'button',                        // 'link'|'button'|false\n    okText: 'ok',\n    paramName: 'value',\n    rows: 1,                                    // If 1 and multi-line, uses autoRows\n    savingClassName: 'inplaceeditor-saving',\n    savingText: 'Saving...',\n    size: 0,\n    stripLoadedTextTags: false,\n    submitOnBlur: false,\n    textAfterControls: '',\n    textBeforeControls: '',\n    textBetweenControls: ''\n  },\n  DefaultCallbacks: {\n    callback: function(form) {\n      return Form.serialize(form);\n    },\n    onComplete: function(transport, element) {\n      // For backward compatibility, this one is bound to the IPE, and passes\n      // the element directly.  It was too often customized, so we don't break it.\n      new Effect.Highlight(element, {\n        startcolor: this.options.highlightColor, keepBackgroundImage: true });\n    },\n    onEnterEditMode: null,\n    onEnterHover: function(ipe) {\n      ipe.element.style.backgroundColor = ipe.options.highlightColor;\n      if (ipe._effect)\n        ipe._effect.cancel();\n    },\n    onFailure: function(transport, ipe) {\n      alert('Error communication with the server: ' + transport.responseText.stripTags());\n    },\n    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.\n    onLeaveEditMode: null,\n    onLeaveHover: function(ipe) {\n      ipe._effect = new Effect.Highlight(ipe.element, {\n        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,\n        restorecolor: ipe._originalBackground, keepBackgroundImage: true\n      });\n    }\n  },\n  Listeners: {\n    click: 'enterEditMode',\n    keydown: 'checkForEscapeOrReturn',\n    mouseover: 'enterHover',\n    mouseout: 'leaveHover'\n  }\n});\n\nAjax.InPlaceCollectionEditor.DefaultOptions = {\n  loadingCollectionText: 'Loading options...'\n};\n\n// Delayed observer, like Form.Element.Observer,\n// but waits for delay after last key input\n// Ideal for live-search fields\n\nForm.Element.DelayedObserver = Class.create({\n  initialize: function(element, delay, callback) {\n    this.delay     = delay || 0.5;\n    this.element   = $(element);\n    this.callback  = callback;\n    this.timer     = null;\n    this.lastValue = $F(this.element);\n    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));\n  },\n  delayedListener: function(event) {\n    if(this.lastValue == $F(this.element)) return;\n    if(this.timer) clearTimeout(this.timer);\n    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);\n    this.lastValue = $F(this.element);\n  },\n  onTimerEvent: function() {\n    this.timer = null;\n    this.callback(this.element, $F(this.element));\n  }\n});"
  },
  {
    "path": "test/apps/rails3/public/javascripts/dragdrop.js",
    "content": "// script.aculo.us dragdrop.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009\n\n// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\nif(Object.isUndefined(Effect))\n  throw(\"dragdrop.js requires including script.aculo.us' effects.js library\");\n\nvar Droppables = {\n  drops: [],\n\n  remove: function(element) {\n    this.drops = this.drops.reject(function(d) { return d.element==$(element) });\n  },\n\n  add: function(element) {\n    element = $(element);\n    var options = Object.extend({\n      greedy:     true,\n      hoverclass: null,\n      tree:       false\n    }, arguments[1] || { });\n\n    // cache containers\n    if(options.containment) {\n      options._containers = [];\n      var containment = options.containment;\n      if(Object.isArray(containment)) {\n        containment.each( function(c) { options._containers.push($(c)) });\n      } else {\n        options._containers.push($(containment));\n      }\n    }\n\n    if(options.accept) options.accept = [options.accept].flatten();\n\n    Element.makePositioned(element); // fix IE\n    options.element = element;\n\n    this.drops.push(options);\n  },\n\n  findDeepestChild: function(drops) {\n    deepest = drops[0];\n\n    for (i = 1; i < drops.length; ++i)\n      if (Element.isParent(drops[i].element, deepest.element))\n        deepest = drops[i];\n\n    return deepest;\n  },\n\n  isContained: function(element, drop) {\n    var containmentNode;\n    if(drop.tree) {\n      containmentNode = element.treeNode;\n    } else {\n      containmentNode = element.parentNode;\n    }\n    return drop._containers.detect(function(c) { return containmentNode == c });\n  },\n\n  isAffected: function(point, element, drop) {\n    return (\n      (drop.element!=element) &&\n      ((!drop._containers) ||\n        this.isContained(element, drop)) &&\n      ((!drop.accept) ||\n        (Element.classNames(element).detect(\n          function(v) { return drop.accept.include(v) } ) )) &&\n      Position.within(drop.element, point[0], point[1]) );\n  },\n\n  deactivate: function(drop) {\n    if(drop.hoverclass)\n      Element.removeClassName(drop.element, drop.hoverclass);\n    this.last_active = null;\n  },\n\n  activate: function(drop) {\n    if(drop.hoverclass)\n      Element.addClassName(drop.element, drop.hoverclass);\n    this.last_active = drop;\n  },\n\n  show: function(point, element) {\n    if(!this.drops.length) return;\n    var drop, affected = [];\n\n    this.drops.each( function(drop) {\n      if(Droppables.isAffected(point, element, drop))\n        affected.push(drop);\n    });\n\n    if(affected.length>0)\n      drop = Droppables.findDeepestChild(affected);\n\n    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);\n    if (drop) {\n      Position.within(drop.element, point[0], point[1]);\n      if(drop.onHover)\n        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));\n\n      if (drop != this.last_active) Droppables.activate(drop);\n    }\n  },\n\n  fire: function(event, element) {\n    if(!this.last_active) return;\n    Position.prepare();\n\n    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))\n      if (this.last_active.onDrop) {\n        this.last_active.onDrop(element, this.last_active.element, event);\n        return true;\n      }\n  },\n\n  reset: function() {\n    if(this.last_active)\n      this.deactivate(this.last_active);\n  }\n};\n\nvar Draggables = {\n  drags: [],\n  observers: [],\n\n  register: function(draggable) {\n    if(this.drags.length == 0) {\n      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);\n      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);\n      this.eventKeypress  = this.keyPress.bindAsEventListener(this);\n\n      Event.observe(document, \"mouseup\", this.eventMouseUp);\n      Event.observe(document, \"mousemove\", this.eventMouseMove);\n      Event.observe(document, \"keypress\", this.eventKeypress);\n    }\n    this.drags.push(draggable);\n  },\n\n  unregister: function(draggable) {\n    this.drags = this.drags.reject(function(d) { return d==draggable });\n    if(this.drags.length == 0) {\n      Event.stopObserving(document, \"mouseup\", this.eventMouseUp);\n      Event.stopObserving(document, \"mousemove\", this.eventMouseMove);\n      Event.stopObserving(document, \"keypress\", this.eventKeypress);\n    }\n  },\n\n  activate: function(draggable) {\n    if(draggable.options.delay) {\n      this._timeout = setTimeout(function() {\n        Draggables._timeout = null;\n        window.focus();\n        Draggables.activeDraggable = draggable;\n      }.bind(this), draggable.options.delay);\n    } else {\n      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari\n      this.activeDraggable = draggable;\n    }\n  },\n\n  deactivate: function() {\n    this.activeDraggable = null;\n  },\n\n  updateDrag: function(event) {\n    if(!this.activeDraggable) return;\n    var pointer = [Event.pointerX(event), Event.pointerY(event)];\n    // Mozilla-based browsers fire successive mousemove events with\n    // the same coordinates, prevent needless redrawing (moz bug?)\n    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;\n    this._lastPointer = pointer;\n\n    this.activeDraggable.updateDrag(event, pointer);\n  },\n\n  endDrag: function(event) {\n    if(this._timeout) {\n      clearTimeout(this._timeout);\n      this._timeout = null;\n    }\n    if(!this.activeDraggable) return;\n    this._lastPointer = null;\n    this.activeDraggable.endDrag(event);\n    this.activeDraggable = null;\n  },\n\n  keyPress: function(event) {\n    if(this.activeDraggable)\n      this.activeDraggable.keyPress(event);\n  },\n\n  addObserver: function(observer) {\n    this.observers.push(observer);\n    this._cacheObserverCallbacks();\n  },\n\n  removeObserver: function(element) {  // element instead of observer fixes mem leaks\n    this.observers = this.observers.reject( function(o) { return o.element==element });\n    this._cacheObserverCallbacks();\n  },\n\n  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'\n    if(this[eventName+'Count'] > 0)\n      this.observers.each( function(o) {\n        if(o[eventName]) o[eventName](eventName, draggable, event);\n      });\n    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);\n  },\n\n  _cacheObserverCallbacks: function() {\n    ['onStart','onEnd','onDrag'].each( function(eventName) {\n      Draggables[eventName+'Count'] = Draggables.observers.select(\n        function(o) { return o[eventName]; }\n      ).length;\n    });\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Draggable = Class.create({\n  initialize: function(element) {\n    var defaults = {\n      handle: false,\n      reverteffect: function(element, top_offset, left_offset) {\n        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;\n        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,\n          queue: {scope:'_draggable', position:'end'}\n        });\n      },\n      endeffect: function(element) {\n        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;\n        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,\n          queue: {scope:'_draggable', position:'end'},\n          afterFinish: function(){\n            Draggable._dragging[element] = false\n          }\n        });\n      },\n      zindex: 1000,\n      revert: false,\n      quiet: false,\n      scroll: false,\n      scrollSensitivity: 20,\n      scrollSpeed: 15,\n      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }\n      delay: 0\n    };\n\n    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))\n      Object.extend(defaults, {\n        starteffect: function(element) {\n          element._opacity = Element.getOpacity(element);\n          Draggable._dragging[element] = true;\n          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});\n        }\n      });\n\n    var options = Object.extend(defaults, arguments[1] || { });\n\n    this.element = $(element);\n\n    if(options.handle && Object.isString(options.handle))\n      this.handle = this.element.down('.'+options.handle, 0);\n\n    if(!this.handle) this.handle = $(options.handle);\n    if(!this.handle) this.handle = this.element;\n\n    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {\n      options.scroll = $(options.scroll);\n      this._isScrollChild = Element.childOf(this.element, options.scroll);\n    }\n\n    Element.makePositioned(this.element); // fix IE\n\n    this.options  = options;\n    this.dragging = false;\n\n    this.eventMouseDown = this.initDrag.bindAsEventListener(this);\n    Event.observe(this.handle, \"mousedown\", this.eventMouseDown);\n\n    Draggables.register(this);\n  },\n\n  destroy: function() {\n    Event.stopObserving(this.handle, \"mousedown\", this.eventMouseDown);\n    Draggables.unregister(this);\n  },\n\n  currentDelta: function() {\n    return([\n      parseInt(Element.getStyle(this.element,'left') || '0'),\n      parseInt(Element.getStyle(this.element,'top') || '0')]);\n  },\n\n  initDrag: function(event) {\n    if(!Object.isUndefined(Draggable._dragging[this.element]) &&\n      Draggable._dragging[this.element]) return;\n    if(Event.isLeftClick(event)) {\n      // abort on form elements, fixes a Firefox issue\n      var src = Event.element(event);\n      if((tag_name = src.tagName.toUpperCase()) && (\n        tag_name=='INPUT' ||\n        tag_name=='SELECT' ||\n        tag_name=='OPTION' ||\n        tag_name=='BUTTON' ||\n        tag_name=='TEXTAREA')) return;\n\n      var pointer = [Event.pointerX(event), Event.pointerY(event)];\n      var pos     = this.element.cumulativeOffset();\n      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });\n\n      Draggables.activate(this);\n      Event.stop(event);\n    }\n  },\n\n  startDrag: function(event) {\n    this.dragging = true;\n    if(!this.delta)\n      this.delta = this.currentDelta();\n\n    if(this.options.zindex) {\n      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);\n      this.element.style.zIndex = this.options.zindex;\n    }\n\n    if(this.options.ghosting) {\n      this._clone = this.element.cloneNode(true);\n      this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');\n      if (!this._originallyAbsolute)\n        Position.absolutize(this.element);\n      this.element.parentNode.insertBefore(this._clone, this.element);\n    }\n\n    if(this.options.scroll) {\n      if (this.options.scroll == window) {\n        var where = this._getWindowScroll(this.options.scroll);\n        this.originalScrollLeft = where.left;\n        this.originalScrollTop = where.top;\n      } else {\n        this.originalScrollLeft = this.options.scroll.scrollLeft;\n        this.originalScrollTop = this.options.scroll.scrollTop;\n      }\n    }\n\n    Draggables.notify('onStart', this, event);\n\n    if(this.options.starteffect) this.options.starteffect(this.element);\n  },\n\n  updateDrag: function(event, pointer) {\n    if(!this.dragging) this.startDrag(event);\n\n    if(!this.options.quiet){\n      Position.prepare();\n      Droppables.show(pointer, this.element);\n    }\n\n    Draggables.notify('onDrag', this, event);\n\n    this.draw(pointer);\n    if(this.options.change) this.options.change(this);\n\n    if(this.options.scroll) {\n      this.stopScrolling();\n\n      var p;\n      if (this.options.scroll == window) {\n        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }\n      } else {\n        p = Position.page(this.options.scroll);\n        p[0] += this.options.scroll.scrollLeft + Position.deltaX;\n        p[1] += this.options.scroll.scrollTop + Position.deltaY;\n        p.push(p[0]+this.options.scroll.offsetWidth);\n        p.push(p[1]+this.options.scroll.offsetHeight);\n      }\n      var speed = [0,0];\n      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);\n      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);\n      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);\n      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);\n      this.startScrolling(speed);\n    }\n\n    // fix AppleWebKit rendering\n    if(Prototype.Browser.WebKit) window.scrollBy(0,0);\n\n    Event.stop(event);\n  },\n\n  finishDrag: function(event, success) {\n    this.dragging = false;\n\n    if(this.options.quiet){\n      Position.prepare();\n      var pointer = [Event.pointerX(event), Event.pointerY(event)];\n      Droppables.show(pointer, this.element);\n    }\n\n    if(this.options.ghosting) {\n      if (!this._originallyAbsolute)\n        Position.relativize(this.element);\n      delete this._originallyAbsolute;\n      Element.remove(this._clone);\n      this._clone = null;\n    }\n\n    var dropped = false;\n    if(success) {\n      dropped = Droppables.fire(event, this.element);\n      if (!dropped) dropped = false;\n    }\n    if(dropped && this.options.onDropped) this.options.onDropped(this.element);\n    Draggables.notify('onEnd', this, event);\n\n    var revert = this.options.revert;\n    if(revert && Object.isFunction(revert)) revert = revert(this.element);\n\n    var d = this.currentDelta();\n    if(revert && this.options.reverteffect) {\n      if (dropped == 0 || revert != 'failure')\n        this.options.reverteffect(this.element,\n          d[1]-this.delta[1], d[0]-this.delta[0]);\n    } else {\n      this.delta = d;\n    }\n\n    if(this.options.zindex)\n      this.element.style.zIndex = this.originalZ;\n\n    if(this.options.endeffect)\n      this.options.endeffect(this.element);\n\n    Draggables.deactivate(this);\n    Droppables.reset();\n  },\n\n  keyPress: function(event) {\n    if(event.keyCode!=Event.KEY_ESC) return;\n    this.finishDrag(event, false);\n    Event.stop(event);\n  },\n\n  endDrag: function(event) {\n    if(!this.dragging) return;\n    this.stopScrolling();\n    this.finishDrag(event, true);\n    Event.stop(event);\n  },\n\n  draw: function(point) {\n    var pos = this.element.cumulativeOffset();\n    if(this.options.ghosting) {\n      var r   = Position.realOffset(this.element);\n      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;\n    }\n\n    var d = this.currentDelta();\n    pos[0] -= d[0]; pos[1] -= d[1];\n\n    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {\n      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;\n      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;\n    }\n\n    var p = [0,1].map(function(i){\n      return (point[i]-pos[i]-this.offset[i])\n    }.bind(this));\n\n    if(this.options.snap) {\n      if(Object.isFunction(this.options.snap)) {\n        p = this.options.snap(p[0],p[1],this);\n      } else {\n      if(Object.isArray(this.options.snap)) {\n        p = p.map( function(v, i) {\n          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));\n      } else {\n        p = p.map( function(v) {\n          return (v/this.options.snap).round()*this.options.snap }.bind(this));\n      }\n    }}\n\n    var style = this.element.style;\n    if((!this.options.constraint) || (this.options.constraint=='horizontal'))\n      style.left = p[0] + \"px\";\n    if((!this.options.constraint) || (this.options.constraint=='vertical'))\n      style.top  = p[1] + \"px\";\n\n    if(style.visibility==\"hidden\") style.visibility = \"\"; // fix gecko rendering\n  },\n\n  stopScrolling: function() {\n    if(this.scrollInterval) {\n      clearInterval(this.scrollInterval);\n      this.scrollInterval = null;\n      Draggables._lastScrollPointer = null;\n    }\n  },\n\n  startScrolling: function(speed) {\n    if(!(speed[0] || speed[1])) return;\n    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];\n    this.lastScrolled = new Date();\n    this.scrollInterval = setInterval(this.scroll.bind(this), 10);\n  },\n\n  scroll: function() {\n    var current = new Date();\n    var delta = current - this.lastScrolled;\n    this.lastScrolled = current;\n    if(this.options.scroll == window) {\n      with (this._getWindowScroll(this.options.scroll)) {\n        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {\n          var d = delta / 1000;\n          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );\n        }\n      }\n    } else {\n      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;\n      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;\n    }\n\n    Position.prepare();\n    Droppables.show(Draggables._lastPointer, this.element);\n    Draggables.notify('onDrag', this);\n    if (this._isScrollChild) {\n      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);\n      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;\n      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;\n      if (Draggables._lastScrollPointer[0] < 0)\n        Draggables._lastScrollPointer[0] = 0;\n      if (Draggables._lastScrollPointer[1] < 0)\n        Draggables._lastScrollPointer[1] = 0;\n      this.draw(Draggables._lastScrollPointer);\n    }\n\n    if(this.options.change) this.options.change(this);\n  },\n\n  _getWindowScroll: function(w) {\n    var T, L, W, H;\n    with (w.document) {\n      if (w.document.documentElement && documentElement.scrollTop) {\n        T = documentElement.scrollTop;\n        L = documentElement.scrollLeft;\n      } else if (w.document.body) {\n        T = body.scrollTop;\n        L = body.scrollLeft;\n      }\n      if (w.innerWidth) {\n        W = w.innerWidth;\n        H = w.innerHeight;\n      } else if (w.document.documentElement && documentElement.clientWidth) {\n        W = documentElement.clientWidth;\n        H = documentElement.clientHeight;\n      } else {\n        W = body.offsetWidth;\n        H = body.offsetHeight;\n      }\n    }\n    return { top: T, left: L, width: W, height: H };\n  }\n});\n\nDraggable._dragging = { };\n\n/*--------------------------------------------------------------------------*/\n\nvar SortableObserver = Class.create({\n  initialize: function(element, observer) {\n    this.element   = $(element);\n    this.observer  = observer;\n    this.lastValue = Sortable.serialize(this.element);\n  },\n\n  onStart: function() {\n    this.lastValue = Sortable.serialize(this.element);\n  },\n\n  onEnd: function() {\n    Sortable.unmark();\n    if(this.lastValue != Sortable.serialize(this.element))\n      this.observer(this.element)\n  }\n});\n\nvar Sortable = {\n  SERIALIZE_RULE: /^[^_\\-](?:[A-Za-z0-9\\-\\_]*)[_](.*)$/,\n\n  sortables: { },\n\n  _findRootElement: function(element) {\n    while (element.tagName.toUpperCase() != \"BODY\") {\n      if(element.id && Sortable.sortables[element.id]) return element;\n      element = element.parentNode;\n    }\n  },\n\n  options: function(element) {\n    element = Sortable._findRootElement($(element));\n    if(!element) return;\n    return Sortable.sortables[element.id];\n  },\n\n  destroy: function(element){\n    element = $(element);\n    var s = Sortable.sortables[element.id];\n\n    if(s) {\n      Draggables.removeObserver(s.element);\n      s.droppables.each(function(d){ Droppables.remove(d) });\n      s.draggables.invoke('destroy');\n\n      delete Sortable.sortables[s.element.id];\n    }\n  },\n\n  create: function(element) {\n    element = $(element);\n    var options = Object.extend({\n      element:     element,\n      tag:         'li',       // assumes li children, override with tag: 'tagname'\n      dropOnEmpty: false,\n      tree:        false,\n      treeTag:     'ul',\n      overlap:     'vertical', // one of 'vertical', 'horizontal'\n      constraint:  'vertical', // one of 'vertical', 'horizontal', false\n      containment: element,    // also takes array of elements (or id's); or false\n      handle:      false,      // or a CSS class\n      only:        false,\n      delay:       0,\n      hoverclass:  null,\n      ghosting:    false,\n      quiet:       false,\n      scroll:      false,\n      scrollSensitivity: 20,\n      scrollSpeed: 15,\n      format:      this.SERIALIZE_RULE,\n\n      // these take arrays of elements or ids and can be\n      // used for better initialization performance\n      elements:    false,\n      handles:     false,\n\n      onChange:    Prototype.emptyFunction,\n      onUpdate:    Prototype.emptyFunction\n    }, arguments[1] || { });\n\n    // clear any old sortable with same element\n    this.destroy(element);\n\n    // build options for the draggables\n    var options_for_draggable = {\n      revert:      true,\n      quiet:       options.quiet,\n      scroll:      options.scroll,\n      scrollSpeed: options.scrollSpeed,\n      scrollSensitivity: options.scrollSensitivity,\n      delay:       options.delay,\n      ghosting:    options.ghosting,\n      constraint:  options.constraint,\n      handle:      options.handle };\n\n    if(options.starteffect)\n      options_for_draggable.starteffect = options.starteffect;\n\n    if(options.reverteffect)\n      options_for_draggable.reverteffect = options.reverteffect;\n    else\n      if(options.ghosting) options_for_draggable.reverteffect = function(element) {\n        element.style.top  = 0;\n        element.style.left = 0;\n      };\n\n    if(options.endeffect)\n      options_for_draggable.endeffect = options.endeffect;\n\n    if(options.zindex)\n      options_for_draggable.zindex = options.zindex;\n\n    // build options for the droppables\n    var options_for_droppable = {\n      overlap:     options.overlap,\n      containment: options.containment,\n      tree:        options.tree,\n      hoverclass:  options.hoverclass,\n      onHover:     Sortable.onHover\n    };\n\n    var options_for_tree = {\n      onHover:      Sortable.onEmptyHover,\n      overlap:      options.overlap,\n      containment:  options.containment,\n      hoverclass:   options.hoverclass\n    };\n\n    // fix for gecko engine\n    Element.cleanWhitespace(element);\n\n    options.draggables = [];\n    options.droppables = [];\n\n    // drop on empty handling\n    if(options.dropOnEmpty || options.tree) {\n      Droppables.add(element, options_for_tree);\n      options.droppables.push(element);\n    }\n\n    (options.elements || this.findElements(element, options) || []).each( function(e,i) {\n      var handle = options.handles ? $(options.handles[i]) :\n        (options.handle ? $(e).select('.' + options.handle)[0] : e);\n      options.draggables.push(\n        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));\n      Droppables.add(e, options_for_droppable);\n      if(options.tree) e.treeNode = element;\n      options.droppables.push(e);\n    });\n\n    if(options.tree) {\n      (Sortable.findTreeElements(element, options) || []).each( function(e) {\n        Droppables.add(e, options_for_tree);\n        e.treeNode = element;\n        options.droppables.push(e);\n      });\n    }\n\n    // keep reference\n    this.sortables[element.identify()] = options;\n\n    // for onupdate\n    Draggables.addObserver(new SortableObserver(element, options.onUpdate));\n\n  },\n\n  // return all suitable-for-sortable elements in a guaranteed order\n  findElements: function(element, options) {\n    return Element.findChildren(\n      element, options.only, options.tree ? true : false, options.tag);\n  },\n\n  findTreeElements: function(element, options) {\n    return Element.findChildren(\n      element, options.only, options.tree ? true : false, options.treeTag);\n  },\n\n  onHover: function(element, dropon, overlap) {\n    if(Element.isParent(dropon, element)) return;\n\n    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {\n      return;\n    } else if(overlap>0.5) {\n      Sortable.mark(dropon, 'before');\n      if(dropon.previousSibling != element) {\n        var oldParentNode = element.parentNode;\n        element.style.visibility = \"hidden\"; // fix gecko rendering\n        dropon.parentNode.insertBefore(element, dropon);\n        if(dropon.parentNode!=oldParentNode)\n          Sortable.options(oldParentNode).onChange(element);\n        Sortable.options(dropon.parentNode).onChange(element);\n      }\n    } else {\n      Sortable.mark(dropon, 'after');\n      var nextElement = dropon.nextSibling || null;\n      if(nextElement != element) {\n        var oldParentNode = element.parentNode;\n        element.style.visibility = \"hidden\"; // fix gecko rendering\n        dropon.parentNode.insertBefore(element, nextElement);\n        if(dropon.parentNode!=oldParentNode)\n          Sortable.options(oldParentNode).onChange(element);\n        Sortable.options(dropon.parentNode).onChange(element);\n      }\n    }\n  },\n\n  onEmptyHover: function(element, dropon, overlap) {\n    var oldParentNode = element.parentNode;\n    var droponOptions = Sortable.options(dropon);\n\n    if(!Element.isParent(dropon, element)) {\n      var index;\n\n      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});\n      var child = null;\n\n      if(children) {\n        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);\n\n        for (index = 0; index < children.length; index += 1) {\n          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {\n            offset -= Element.offsetSize (children[index], droponOptions.overlap);\n          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {\n            child = index + 1 < children.length ? children[index + 1] : null;\n            break;\n          } else {\n            child = children[index];\n            break;\n          }\n        }\n      }\n\n      dropon.insertBefore(element, child);\n\n      Sortable.options(oldParentNode).onChange(element);\n      droponOptions.onChange(element);\n    }\n  },\n\n  unmark: function() {\n    if(Sortable._marker) Sortable._marker.hide();\n  },\n\n  mark: function(dropon, position) {\n    // mark on ghosting only\n    var sortable = Sortable.options(dropon.parentNode);\n    if(sortable && !sortable.ghosting) return;\n\n    if(!Sortable._marker) {\n      Sortable._marker =\n        ($('dropmarker') || Element.extend(document.createElement('DIV'))).\n          hide().addClassName('dropmarker').setStyle({position:'absolute'});\n      document.getElementsByTagName(\"body\").item(0).appendChild(Sortable._marker);\n    }\n    var offsets = dropon.cumulativeOffset();\n    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});\n\n    if(position=='after')\n      if(sortable.overlap == 'horizontal')\n        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});\n      else\n        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});\n\n    Sortable._marker.show();\n  },\n\n  _tree: function(element, options, parent) {\n    var children = Sortable.findElements(element, options) || [];\n\n    for (var i = 0; i < children.length; ++i) {\n      var match = children[i].id.match(options.format);\n\n      if (!match) continue;\n\n      var child = {\n        id: encodeURIComponent(match ? match[1] : null),\n        element: element,\n        parent: parent,\n        children: [],\n        position: parent.children.length,\n        container: $(children[i]).down(options.treeTag)\n      };\n\n      /* Get the element containing the children and recurse over it */\n      if (child.container)\n        this._tree(child.container, options, child);\n\n      parent.children.push (child);\n    }\n\n    return parent;\n  },\n\n  tree: function(element) {\n    element = $(element);\n    var sortableOptions = this.options(element);\n    var options = Object.extend({\n      tag: sortableOptions.tag,\n      treeTag: sortableOptions.treeTag,\n      only: sortableOptions.only,\n      name: element.id,\n      format: sortableOptions.format\n    }, arguments[1] || { });\n\n    var root = {\n      id: null,\n      parent: null,\n      children: [],\n      container: element,\n      position: 0\n    };\n\n    return Sortable._tree(element, options, root);\n  },\n\n  /* Construct a [i] index for a particular node */\n  _constructIndex: function(node) {\n    var index = '';\n    do {\n      if (node.id) index = '[' + node.position + ']' + index;\n    } while ((node = node.parent) != null);\n    return index;\n  },\n\n  sequence: function(element) {\n    element = $(element);\n    var options = Object.extend(this.options(element), arguments[1] || { });\n\n    return $(this.findElements(element, options) || []).map( function(item) {\n      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';\n    });\n  },\n\n  setSequence: function(element, new_sequence) {\n    element = $(element);\n    var options = Object.extend(this.options(element), arguments[2] || { });\n\n    var nodeMap = { };\n    this.findElements(element, options).each( function(n) {\n        if (n.id.match(options.format))\n            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];\n        n.parentNode.removeChild(n);\n    });\n\n    new_sequence.each(function(ident) {\n      var n = nodeMap[ident];\n      if (n) {\n        n[1].appendChild(n[0]);\n        delete nodeMap[ident];\n      }\n    });\n  },\n\n  serialize: function(element) {\n    element = $(element);\n    var options = Object.extend(Sortable.options(element), arguments[1] || { });\n    var name = encodeURIComponent(\n      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);\n\n    if (options.tree) {\n      return Sortable.tree(element, arguments[1]).children.map( function (item) {\n        return [name + Sortable._constructIndex(item) + \"[id]=\" +\n                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));\n      }).flatten().join('&');\n    } else {\n      return Sortable.sequence(element, arguments[1]).map( function(item) {\n        return name + \"[]=\" + encodeURIComponent(item);\n      }).join('&');\n    }\n  }\n};\n\n// Returns true if child is contained within element\nElement.isParent = function(child, element) {\n  if (!child.parentNode || child == element) return false;\n  if (child.parentNode == element) return true;\n  return Element.isParent(child.parentNode, element);\n};\n\nElement.findChildren = function(element, only, recursive, tagName) {\n  if(!element.hasChildNodes()) return null;\n  tagName = tagName.toUpperCase();\n  if(only) only = [only].flatten();\n  var elements = [];\n  $A(element.childNodes).each( function(e) {\n    if(e.tagName && e.tagName.toUpperCase()==tagName &&\n      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))\n        elements.push(e);\n    if(recursive) {\n      var grandchildren = Element.findChildren(e, only, recursive, tagName);\n      if(grandchildren) elements.push(grandchildren);\n    }\n  });\n\n  return (elements.length>0 ? elements.flatten() : []);\n};\n\nElement.offsetSize = function (element, type) {\n  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];\n};"
  },
  {
    "path": "test/apps/rails3/public/javascripts/effects.js",
    "content": "// script.aculo.us effects.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009\n\n// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n// Contributors:\n//  Justin Palmer (http://encytemedia.com/)\n//  Mark Pilgrim (http://diveintomark.org/)\n//  Martin Bialasinki\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\n// converts rgb() and #xxx to #xxxxxx format,\n// returns self (or first argument) if not convertable\nString.prototype.parseColor = function() {\n  var color = '#';\n  if (this.slice(0,4) == 'rgb(') {\n    var cols = this.slice(4,this.length-1).split(',');\n    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);\n  } else {\n    if (this.slice(0,1) == '#') {\n      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();\n      if (this.length==7) color = this.toLowerCase();\n    }\n  }\n  return (color.length==7 ? color : (arguments[0] || this));\n};\n\n/*--------------------------------------------------------------------------*/\n\nElement.collectTextNodes = function(element) {\n  return $A($(element).childNodes).collect( function(node) {\n    return (node.nodeType==3 ? node.nodeValue :\n      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));\n  }).flatten().join('');\n};\n\nElement.collectTextNodesIgnoreClass = function(element, className) {\n  return $A($(element).childNodes).collect( function(node) {\n    return (node.nodeType==3 ? node.nodeValue :\n      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?\n        Element.collectTextNodesIgnoreClass(node, className) : ''));\n  }).flatten().join('');\n};\n\nElement.setContentZoom = function(element, percent) {\n  element = $(element);\n  element.setStyle({fontSize: (percent/100) + 'em'});\n  if (Prototype.Browser.WebKit) window.scrollBy(0,0);\n  return element;\n};\n\nElement.getInlineOpacity = function(element){\n  return $(element).style.opacity || '';\n};\n\nElement.forceRerendering = function(element) {\n  try {\n    element = $(element);\n    var n = document.createTextNode(' ');\n    element.appendChild(n);\n    element.removeChild(n);\n  } catch(e) { }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Effect = {\n  _elementDoesNotExistError: {\n    name: 'ElementDoesNotExistError',\n    message: 'The specified DOM element does not exist, but is required for this effect to operate'\n  },\n  Transitions: {\n    linear: Prototype.K,\n    sinoidal: function(pos) {\n      return (-Math.cos(pos*Math.PI)/2) + .5;\n    },\n    reverse: function(pos) {\n      return 1-pos;\n    },\n    flicker: function(pos) {\n      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;\n      return pos > 1 ? 1 : pos;\n    },\n    wobble: function(pos) {\n      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;\n    },\n    pulse: function(pos, pulses) {\n      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;\n    },\n    spring: function(pos) {\n      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));\n    },\n    none: function(pos) {\n      return 0;\n    },\n    full: function(pos) {\n      return 1;\n    }\n  },\n  DefaultOptions: {\n    duration:   1.0,   // seconds\n    fps:        100,   // 100= assume 66fps max.\n    sync:       false, // true for combining\n    from:       0.0,\n    to:         1.0,\n    delay:      0.0,\n    queue:      'parallel'\n  },\n  tagifyText: function(element) {\n    var tagifyStyle = 'position:relative';\n    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';\n\n    element = $(element);\n    $A(element.childNodes).each( function(child) {\n      if (child.nodeType==3) {\n        child.nodeValue.toArray().each( function(character) {\n          element.insertBefore(\n            new Element('span', {style: tagifyStyle}).update(\n              character == ' ' ? String.fromCharCode(160) : character),\n              child);\n        });\n        Element.remove(child);\n      }\n    });\n  },\n  multiple: function(element, effect) {\n    var elements;\n    if (((typeof element == 'object') ||\n        Object.isFunction(element)) &&\n       (element.length))\n      elements = element;\n    else\n      elements = $(element).childNodes;\n\n    var options = Object.extend({\n      speed: 0.1,\n      delay: 0.0\n    }, arguments[2] || { });\n    var masterDelay = options.delay;\n\n    $A(elements).each( function(element, index) {\n      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));\n    });\n  },\n  PAIRS: {\n    'slide':  ['SlideDown','SlideUp'],\n    'blind':  ['BlindDown','BlindUp'],\n    'appear': ['Appear','Fade']\n  },\n  toggle: function(element, effect, options) {\n    element = $(element);\n    effect  = (effect || 'appear').toLowerCase();\n\n    return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({\n      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }\n    }, options || {}));\n  }\n};\n\nEffect.DefaultOptions.transition = Effect.Transitions.sinoidal;\n\n/* ------------- core effects ------------- */\n\nEffect.ScopedQueue = Class.create(Enumerable, {\n  initialize: function() {\n    this.effects  = [];\n    this.interval = null;\n  },\n  _each: function(iterator) {\n    this.effects._each(iterator);\n  },\n  add: function(effect) {\n    var timestamp = new Date().getTime();\n\n    var position = Object.isString(effect.options.queue) ?\n      effect.options.queue : effect.options.queue.position;\n\n    switch(position) {\n      case 'front':\n        // move unstarted effects after this effect\n        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {\n            e.startOn  += effect.finishOn;\n            e.finishOn += effect.finishOn;\n          });\n        break;\n      case 'with-last':\n        timestamp = this.effects.pluck('startOn').max() || timestamp;\n        break;\n      case 'end':\n        // start effect after last queued effect has finished\n        timestamp = this.effects.pluck('finishOn').max() || timestamp;\n        break;\n    }\n\n    effect.startOn  += timestamp;\n    effect.finishOn += timestamp;\n\n    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))\n      this.effects.push(effect);\n\n    if (!this.interval)\n      this.interval = setInterval(this.loop.bind(this), 15);\n  },\n  remove: function(effect) {\n    this.effects = this.effects.reject(function(e) { return e==effect });\n    if (this.effects.length == 0) {\n      clearInterval(this.interval);\n      this.interval = null;\n    }\n  },\n  loop: function() {\n    var timePos = new Date().getTime();\n    for(var i=0, len=this.effects.length;i<len;i++)\n      this.effects[i] && this.effects[i].loop(timePos);\n  }\n});\n\nEffect.Queues = {\n  instances: $H(),\n  get: function(queueName) {\n    if (!Object.isString(queueName)) return queueName;\n\n    return this.instances.get(queueName) ||\n      this.instances.set(queueName, new Effect.ScopedQueue());\n  }\n};\nEffect.Queue = Effect.Queues.get('global');\n\nEffect.Base = Class.create({\n  position: null,\n  start: function(options) {\n    if (options && options.transition === false) options.transition = Effect.Transitions.linear;\n    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });\n    this.currentFrame = 0;\n    this.state        = 'idle';\n    this.startOn      = this.options.delay*1000;\n    this.finishOn     = this.startOn+(this.options.duration*1000);\n    this.fromToDelta  = this.options.to-this.options.from;\n    this.totalTime    = this.finishOn-this.startOn;\n    this.totalFrames  = this.options.fps*this.options.duration;\n\n    this.render = (function() {\n      function dispatch(effect, eventName) {\n        if (effect.options[eventName + 'Internal'])\n          effect.options[eventName + 'Internal'](effect);\n        if (effect.options[eventName])\n          effect.options[eventName](effect);\n      }\n\n      return function(pos) {\n        if (this.state === \"idle\") {\n          this.state = \"running\";\n          dispatch(this, 'beforeSetup');\n          if (this.setup) this.setup();\n          dispatch(this, 'afterSetup');\n        }\n        if (this.state === \"running\") {\n          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;\n          this.position = pos;\n          dispatch(this, 'beforeUpdate');\n          if (this.update) this.update(pos);\n          dispatch(this, 'afterUpdate');\n        }\n      };\n    })();\n\n    this.event('beforeStart');\n    if (!this.options.sync)\n      Effect.Queues.get(Object.isString(this.options.queue) ?\n        'global' : this.options.queue.scope).add(this);\n  },\n  loop: function(timePos) {\n    if (timePos >= this.startOn) {\n      if (timePos >= this.finishOn) {\n        this.render(1.0);\n        this.cancel();\n        this.event('beforeFinish');\n        if (this.finish) this.finish();\n        this.event('afterFinish');\n        return;\n      }\n      var pos   = (timePos - this.startOn) / this.totalTime,\n          frame = (pos * this.totalFrames).round();\n      if (frame > this.currentFrame) {\n        this.render(pos);\n        this.currentFrame = frame;\n      }\n    }\n  },\n  cancel: function() {\n    if (!this.options.sync)\n      Effect.Queues.get(Object.isString(this.options.queue) ?\n        'global' : this.options.queue.scope).remove(this);\n    this.state = 'finished';\n  },\n  event: function(eventName) {\n    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);\n    if (this.options[eventName]) this.options[eventName](this);\n  },\n  inspect: function() {\n    var data = $H();\n    for(property in this)\n      if (!Object.isFunction(this[property])) data.set(property, this[property]);\n    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';\n  }\n});\n\nEffect.Parallel = Class.create(Effect.Base, {\n  initialize: function(effects) {\n    this.effects = effects || [];\n    this.start(arguments[1]);\n  },\n  update: function(position) {\n    this.effects.invoke('render', position);\n  },\n  finish: function(position) {\n    this.effects.each( function(effect) {\n      effect.render(1.0);\n      effect.cancel();\n      effect.event('beforeFinish');\n      if (effect.finish) effect.finish(position);\n      effect.event('afterFinish');\n    });\n  }\n});\n\nEffect.Tween = Class.create(Effect.Base, {\n  initialize: function(object, from, to) {\n    object = Object.isString(object) ? $(object) : object;\n    var args = $A(arguments), method = args.last(),\n      options = args.length == 5 ? args[3] : null;\n    this.method = Object.isFunction(method) ? method.bind(object) :\n      Object.isFunction(object[method]) ? object[method].bind(object) :\n      function(value) { object[method] = value };\n    this.start(Object.extend({ from: from, to: to }, options || { }));\n  },\n  update: function(position) {\n    this.method(position);\n  }\n});\n\nEffect.Event = Class.create(Effect.Base, {\n  initialize: function() {\n    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));\n  },\n  update: Prototype.emptyFunction\n});\n\nEffect.Opacity = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    // make this work on IE on elements without 'layout'\n    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))\n      this.element.setStyle({zoom: 1});\n    var options = Object.extend({\n      from: this.element.getOpacity() || 0.0,\n      to:   1.0\n    }, arguments[1] || { });\n    this.start(options);\n  },\n  update: function(position) {\n    this.element.setOpacity(position);\n  }\n});\n\nEffect.Move = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({\n      x:    0,\n      y:    0,\n      mode: 'relative'\n    }, arguments[1] || { });\n    this.start(options);\n  },\n  setup: function() {\n    this.element.makePositioned();\n    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');\n    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');\n    if (this.options.mode == 'absolute') {\n      this.options.x = this.options.x - this.originalLeft;\n      this.options.y = this.options.y - this.originalTop;\n    }\n  },\n  update: function(position) {\n    this.element.setStyle({\n      left: (this.options.x  * position + this.originalLeft).round() + 'px',\n      top:  (this.options.y  * position + this.originalTop).round()  + 'px'\n    });\n  }\n});\n\n// for backwards compatibility\nEffect.MoveBy = function(element, toTop, toLeft) {\n  return new Effect.Move(element,\n    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));\n};\n\nEffect.Scale = Class.create(Effect.Base, {\n  initialize: function(element, percent) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({\n      scaleX: true,\n      scaleY: true,\n      scaleContent: true,\n      scaleFromCenter: false,\n      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values\n      scaleFrom: 100.0,\n      scaleTo:   percent\n    }, arguments[2] || { });\n    this.start(options);\n  },\n  setup: function() {\n    this.restoreAfterFinish = this.options.restoreAfterFinish || false;\n    this.elementPositioning = this.element.getStyle('position');\n\n    this.originalStyle = { };\n    ['top','left','width','height','fontSize'].each( function(k) {\n      this.originalStyle[k] = this.element.style[k];\n    }.bind(this));\n\n    this.originalTop  = this.element.offsetTop;\n    this.originalLeft = this.element.offsetLeft;\n\n    var fontSize = this.element.getStyle('font-size') || '100%';\n    ['em','px','%','pt'].each( function(fontSizeType) {\n      if (fontSize.indexOf(fontSizeType)>0) {\n        this.fontSize     = parseFloat(fontSize);\n        this.fontSizeType = fontSizeType;\n      }\n    }.bind(this));\n\n    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;\n\n    this.dims = null;\n    if (this.options.scaleMode=='box')\n      this.dims = [this.element.offsetHeight, this.element.offsetWidth];\n    if (/^content/.test(this.options.scaleMode))\n      this.dims = [this.element.scrollHeight, this.element.scrollWidth];\n    if (!this.dims)\n      this.dims = [this.options.scaleMode.originalHeight,\n                   this.options.scaleMode.originalWidth];\n  },\n  update: function(position) {\n    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);\n    if (this.options.scaleContent && this.fontSize)\n      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });\n    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);\n  },\n  finish: function(position) {\n    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);\n  },\n  setDimensions: function(height, width) {\n    var d = { };\n    if (this.options.scaleX) d.width = width.round() + 'px';\n    if (this.options.scaleY) d.height = height.round() + 'px';\n    if (this.options.scaleFromCenter) {\n      var topd  = (height - this.dims[0])/2;\n      var leftd = (width  - this.dims[1])/2;\n      if (this.elementPositioning == 'absolute') {\n        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';\n        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';\n      } else {\n        if (this.options.scaleY) d.top = -topd + 'px';\n        if (this.options.scaleX) d.left = -leftd + 'px';\n      }\n    }\n    this.element.setStyle(d);\n  }\n});\n\nEffect.Highlight = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });\n    this.start(options);\n  },\n  setup: function() {\n    // Prevent executing on elements not in the layout flow\n    if (this.element.getStyle('display')=='none') { this.cancel(); return; }\n    // Disable background image during the effect\n    this.oldStyle = { };\n    if (!this.options.keepBackgroundImage) {\n      this.oldStyle.backgroundImage = this.element.getStyle('background-image');\n      this.element.setStyle({backgroundImage: 'none'});\n    }\n    if (!this.options.endcolor)\n      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');\n    if (!this.options.restorecolor)\n      this.options.restorecolor = this.element.getStyle('background-color');\n    // init color calculations\n    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));\n    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));\n  },\n  update: function(position) {\n    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){\n      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });\n  },\n  finish: function() {\n    this.element.setStyle(Object.extend(this.oldStyle, {\n      backgroundColor: this.options.restorecolor\n    }));\n  }\n});\n\nEffect.ScrollTo = function(element) {\n  var options = arguments[1] || { },\n  scrollOffsets = document.viewport.getScrollOffsets(),\n  elementOffsets = $(element).cumulativeOffset();\n\n  if (options.offset) elementOffsets[1] += options.offset;\n\n  return new Effect.Tween(null,\n    scrollOffsets.top,\n    elementOffsets[1],\n    options,\n    function(p){ scrollTo(scrollOffsets.left, p.round()); }\n  );\n};\n\n/* ------------- combination effects ------------- */\n\nEffect.Fade = function(element) {\n  element = $(element);\n  var oldOpacity = element.getInlineOpacity();\n  var options = Object.extend({\n    from: element.getOpacity() || 1.0,\n    to:   0.0,\n    afterFinishInternal: function(effect) {\n      if (effect.options.to!=0) return;\n      effect.element.hide().setStyle({opacity: oldOpacity});\n    }\n  }, arguments[1] || { });\n  return new Effect.Opacity(element,options);\n};\n\nEffect.Appear = function(element) {\n  element = $(element);\n  var options = Object.extend({\n  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),\n  to:   1.0,\n  // force Safari to render floated elements properly\n  afterFinishInternal: function(effect) {\n    effect.element.forceRerendering();\n  },\n  beforeSetup: function(effect) {\n    effect.element.setOpacity(effect.options.from).show();\n  }}, arguments[1] || { });\n  return new Effect.Opacity(element,options);\n};\n\nEffect.Puff = function(element) {\n  element = $(element);\n  var oldStyle = {\n    opacity: element.getInlineOpacity(),\n    position: element.getStyle('position'),\n    top:  element.style.top,\n    left: element.style.left,\n    width: element.style.width,\n    height: element.style.height\n  };\n  return new Effect.Parallel(\n   [ new Effect.Scale(element, 200,\n      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),\n     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],\n     Object.extend({ duration: 1.0,\n      beforeSetupInternal: function(effect) {\n        Position.absolutize(effect.effects[0].element);\n      },\n      afterFinishInternal: function(effect) {\n         effect.effects[0].element.hide().setStyle(oldStyle); }\n     }, arguments[1] || { })\n   );\n};\n\nEffect.BlindUp = function(element) {\n  element = $(element);\n  element.makeClipping();\n  return new Effect.Scale(element, 0,\n    Object.extend({ scaleContent: false,\n      scaleX: false,\n      restoreAfterFinish: true,\n      afterFinishInternal: function(effect) {\n        effect.element.hide().undoClipping();\n      }\n    }, arguments[1] || { })\n  );\n};\n\nEffect.BlindDown = function(element) {\n  element = $(element);\n  var elementDimensions = element.getDimensions();\n  return new Effect.Scale(element, 100, Object.extend({\n    scaleContent: false,\n    scaleX: false,\n    scaleFrom: 0,\n    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},\n    restoreAfterFinish: true,\n    afterSetup: function(effect) {\n      effect.element.makeClipping().setStyle({height: '0px'}).show();\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.undoClipping();\n    }\n  }, arguments[1] || { }));\n};\n\nEffect.SwitchOff = function(element) {\n  element = $(element);\n  var oldOpacity = element.getInlineOpacity();\n  return new Effect.Appear(element, Object.extend({\n    duration: 0.4,\n    from: 0,\n    transition: Effect.Transitions.flicker,\n    afterFinishInternal: function(effect) {\n      new Effect.Scale(effect.element, 1, {\n        duration: 0.3, scaleFromCenter: true,\n        scaleX: false, scaleContent: false, restoreAfterFinish: true,\n        beforeSetup: function(effect) {\n          effect.element.makePositioned().makeClipping();\n        },\n        afterFinishInternal: function(effect) {\n          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});\n        }\n      });\n    }\n  }, arguments[1] || { }));\n};\n\nEffect.DropOut = function(element) {\n  element = $(element);\n  var oldStyle = {\n    top: element.getStyle('top'),\n    left: element.getStyle('left'),\n    opacity: element.getInlineOpacity() };\n  return new Effect.Parallel(\n    [ new Effect.Move(element, {x: 0, y: 100, sync: true }),\n      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],\n    Object.extend(\n      { duration: 0.5,\n        beforeSetup: function(effect) {\n          effect.effects[0].element.makePositioned();\n        },\n        afterFinishInternal: function(effect) {\n          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);\n        }\n      }, arguments[1] || { }));\n};\n\nEffect.Shake = function(element) {\n  element = $(element);\n  var options = Object.extend({\n    distance: 20,\n    duration: 0.5\n  }, arguments[1] || {});\n  var distance = parseFloat(options.distance);\n  var split = parseFloat(options.duration) / 10.0;\n  var oldStyle = {\n    top: element.getStyle('top'),\n    left: element.getStyle('left') };\n    return new Effect.Move(element,\n      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {\n        effect.element.undoPositioned().setStyle(oldStyle);\n  }}); }}); }}); }}); }}); }});\n};\n\nEffect.SlideDown = function(element) {\n  element = $(element).cleanWhitespace();\n  // SlideDown need to have the content of the element wrapped in a container element with fixed height!\n  var oldInnerBottom = element.down().getStyle('bottom');\n  var elementDimensions = element.getDimensions();\n  return new Effect.Scale(element, 100, Object.extend({\n    scaleContent: false,\n    scaleX: false,\n    scaleFrom: window.opera ? 0 : 1,\n    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},\n    restoreAfterFinish: true,\n    afterSetup: function(effect) {\n      effect.element.makePositioned();\n      effect.element.down().makePositioned();\n      if (window.opera) effect.element.setStyle({top: ''});\n      effect.element.makeClipping().setStyle({height: '0px'}).show();\n    },\n    afterUpdateInternal: function(effect) {\n      effect.element.down().setStyle({bottom:\n        (effect.dims[0] - effect.element.clientHeight) + 'px' });\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.undoClipping().undoPositioned();\n      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }\n    }, arguments[1] || { })\n  );\n};\n\nEffect.SlideUp = function(element) {\n  element = $(element).cleanWhitespace();\n  var oldInnerBottom = element.down().getStyle('bottom');\n  var elementDimensions = element.getDimensions();\n  return new Effect.Scale(element, window.opera ? 0 : 1,\n   Object.extend({ scaleContent: false,\n    scaleX: false,\n    scaleMode: 'box',\n    scaleFrom: 100,\n    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},\n    restoreAfterFinish: true,\n    afterSetup: function(effect) {\n      effect.element.makePositioned();\n      effect.element.down().makePositioned();\n      if (window.opera) effect.element.setStyle({top: ''});\n      effect.element.makeClipping().show();\n    },\n    afterUpdateInternal: function(effect) {\n      effect.element.down().setStyle({bottom:\n        (effect.dims[0] - effect.element.clientHeight) + 'px' });\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.hide().undoClipping().undoPositioned();\n      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});\n    }\n   }, arguments[1] || { })\n  );\n};\n\n// Bug in opera makes the TD containing this element expand for a instance after finish\nEffect.Squish = function(element) {\n  return new Effect.Scale(element, window.opera ? 1 : 0, {\n    restoreAfterFinish: true,\n    beforeSetup: function(effect) {\n      effect.element.makeClipping();\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.hide().undoClipping();\n    }\n  });\n};\n\nEffect.Grow = function(element) {\n  element = $(element);\n  var options = Object.extend({\n    direction: 'center',\n    moveTransition: Effect.Transitions.sinoidal,\n    scaleTransition: Effect.Transitions.sinoidal,\n    opacityTransition: Effect.Transitions.full\n  }, arguments[1] || { });\n  var oldStyle = {\n    top: element.style.top,\n    left: element.style.left,\n    height: element.style.height,\n    width: element.style.width,\n    opacity: element.getInlineOpacity() };\n\n  var dims = element.getDimensions();\n  var initialMoveX, initialMoveY;\n  var moveX, moveY;\n\n  switch (options.direction) {\n    case 'top-left':\n      initialMoveX = initialMoveY = moveX = moveY = 0;\n      break;\n    case 'top-right':\n      initialMoveX = dims.width;\n      initialMoveY = moveY = 0;\n      moveX = -dims.width;\n      break;\n    case 'bottom-left':\n      initialMoveX = moveX = 0;\n      initialMoveY = dims.height;\n      moveY = -dims.height;\n      break;\n    case 'bottom-right':\n      initialMoveX = dims.width;\n      initialMoveY = dims.height;\n      moveX = -dims.width;\n      moveY = -dims.height;\n      break;\n    case 'center':\n      initialMoveX = dims.width / 2;\n      initialMoveY = dims.height / 2;\n      moveX = -dims.width / 2;\n      moveY = -dims.height / 2;\n      break;\n  }\n\n  return new Effect.Move(element, {\n    x: initialMoveX,\n    y: initialMoveY,\n    duration: 0.01,\n    beforeSetup: function(effect) {\n      effect.element.hide().makeClipping().makePositioned();\n    },\n    afterFinishInternal: function(effect) {\n      new Effect.Parallel(\n        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),\n          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),\n          new Effect.Scale(effect.element, 100, {\n            scaleMode: { originalHeight: dims.height, originalWidth: dims.width },\n            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})\n        ], Object.extend({\n             beforeSetup: function(effect) {\n               effect.effects[0].element.setStyle({height: '0px'}).show();\n             },\n             afterFinishInternal: function(effect) {\n               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);\n             }\n           }, options)\n      );\n    }\n  });\n};\n\nEffect.Shrink = function(element) {\n  element = $(element);\n  var options = Object.extend({\n    direction: 'center',\n    moveTransition: Effect.Transitions.sinoidal,\n    scaleTransition: Effect.Transitions.sinoidal,\n    opacityTransition: Effect.Transitions.none\n  }, arguments[1] || { });\n  var oldStyle = {\n    top: element.style.top,\n    left: element.style.left,\n    height: element.style.height,\n    width: element.style.width,\n    opacity: element.getInlineOpacity() };\n\n  var dims = element.getDimensions();\n  var moveX, moveY;\n\n  switch (options.direction) {\n    case 'top-left':\n      moveX = moveY = 0;\n      break;\n    case 'top-right':\n      moveX = dims.width;\n      moveY = 0;\n      break;\n    case 'bottom-left':\n      moveX = 0;\n      moveY = dims.height;\n      break;\n    case 'bottom-right':\n      moveX = dims.width;\n      moveY = dims.height;\n      break;\n    case 'center':\n      moveX = dims.width / 2;\n      moveY = dims.height / 2;\n      break;\n  }\n\n  return new Effect.Parallel(\n    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),\n      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),\n      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })\n    ], Object.extend({\n         beforeStartInternal: function(effect) {\n           effect.effects[0].element.makePositioned().makeClipping();\n         },\n         afterFinishInternal: function(effect) {\n           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }\n       }, options)\n  );\n};\n\nEffect.Pulsate = function(element) {\n  element = $(element);\n  var options    = arguments[1] || { },\n    oldOpacity = element.getInlineOpacity(),\n    transition = options.transition || Effect.Transitions.linear,\n    reverser   = function(pos){\n      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);\n    };\n\n  return new Effect.Opacity(element,\n    Object.extend(Object.extend({  duration: 2.0, from: 0,\n      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }\n    }, options), {transition: reverser}));\n};\n\nEffect.Fold = function(element) {\n  element = $(element);\n  var oldStyle = {\n    top: element.style.top,\n    left: element.style.left,\n    width: element.style.width,\n    height: element.style.height };\n  element.makeClipping();\n  return new Effect.Scale(element, 5, Object.extend({\n    scaleContent: false,\n    scaleX: false,\n    afterFinishInternal: function(effect) {\n    new Effect.Scale(element, 1, {\n      scaleContent: false,\n      scaleY: false,\n      afterFinishInternal: function(effect) {\n        effect.element.hide().undoClipping().setStyle(oldStyle);\n      } });\n  }}, arguments[1] || { }));\n};\n\nEffect.Morph = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({\n      style: { }\n    }, arguments[1] || { });\n\n    if (!Object.isString(options.style)) this.style = $H(options.style);\n    else {\n      if (options.style.include(':'))\n        this.style = options.style.parseStyle();\n      else {\n        this.element.addClassName(options.style);\n        this.style = $H(this.element.getStyles());\n        this.element.removeClassName(options.style);\n        var css = this.element.getStyles();\n        this.style = this.style.reject(function(style) {\n          return style.value == css[style.key];\n        });\n        options.afterFinishInternal = function(effect) {\n          effect.element.addClassName(effect.options.style);\n          effect.transforms.each(function(transform) {\n            effect.element.style[transform.style] = '';\n          });\n        };\n      }\n    }\n    this.start(options);\n  },\n\n  setup: function(){\n    function parseColor(color){\n      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';\n      color = color.parseColor();\n      return $R(0,2).map(function(i){\n        return parseInt( color.slice(i*2+1,i*2+3), 16 );\n      });\n    }\n    this.transforms = this.style.map(function(pair){\n      var property = pair[0], value = pair[1], unit = null;\n\n      if (value.parseColor('#zzzzzz') != '#zzzzzz') {\n        value = value.parseColor();\n        unit  = 'color';\n      } else if (property == 'opacity') {\n        value = parseFloat(value);\n        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))\n          this.element.setStyle({zoom: 1});\n      } else if (Element.CSS_LENGTH.test(value)) {\n          var components = value.match(/^([\\+\\-]?[0-9\\.]+)(.*)$/);\n          value = parseFloat(components[1]);\n          unit = (components.length == 3) ? components[2] : null;\n      }\n\n      var originalValue = this.element.getStyle(property);\n      return {\n        style: property.camelize(),\n        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),\n        targetValue: unit=='color' ? parseColor(value) : value,\n        unit: unit\n      };\n    }.bind(this)).reject(function(transform){\n      return (\n        (transform.originalValue == transform.targetValue) ||\n        (\n          transform.unit != 'color' &&\n          (isNaN(transform.originalValue) || isNaN(transform.targetValue))\n        )\n      );\n    });\n  },\n  update: function(position) {\n    var style = { }, transform, i = this.transforms.length;\n    while(i--)\n      style[(transform = this.transforms[i]).style] =\n        transform.unit=='color' ? '#'+\n          (Math.round(transform.originalValue[0]+\n            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +\n          (Math.round(transform.originalValue[1]+\n            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +\n          (Math.round(transform.originalValue[2]+\n            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :\n        (transform.originalValue +\n          (transform.targetValue - transform.originalValue) * position).toFixed(3) +\n            (transform.unit === null ? '' : transform.unit);\n    this.element.setStyle(style, true);\n  }\n});\n\nEffect.Transform = Class.create({\n  initialize: function(tracks){\n    this.tracks  = [];\n    this.options = arguments[1] || { };\n    this.addTracks(tracks);\n  },\n  addTracks: function(tracks){\n    tracks.each(function(track){\n      track = $H(track);\n      var data = track.values().first();\n      this.tracks.push($H({\n        ids:     track.keys().first(),\n        effect:  Effect.Morph,\n        options: { style: data }\n      }));\n    }.bind(this));\n    return this;\n  },\n  play: function(){\n    return new Effect.Parallel(\n      this.tracks.map(function(track){\n        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');\n        var elements = [$(ids) || $$(ids)].flatten();\n        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });\n      }).flatten(),\n      this.options\n    );\n  }\n});\n\nElement.CSS_PROPERTIES = $w(\n  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +\n  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +\n  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +\n  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +\n  'fontSize fontWeight height left letterSpacing lineHeight ' +\n  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+\n  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +\n  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +\n  'right textIndent top width wordSpacing zIndex');\n\nElement.CSS_LENGTH = /^(([\\+\\-]?[0-9\\.]+)(em|ex|px|in|cm|mm|pt|pc|\\%))|0$/;\n\nString.__parseStyleElement = document.createElement('div');\nString.prototype.parseStyle = function(){\n  var style, styleRules = $H();\n  if (Prototype.Browser.WebKit)\n    style = new Element('div',{style:this}).style;\n  else {\n    String.__parseStyleElement.innerHTML = '<div style=\"' + this + '\"></div>';\n    style = String.__parseStyleElement.childNodes[0].style;\n  }\n\n  Element.CSS_PROPERTIES.each(function(property){\n    if (style[property]) styleRules.set(property, style[property]);\n  });\n\n  if (Prototype.Browser.IE && this.include('opacity'))\n    styleRules.set('opacity', this.match(/opacity:\\s*((?:0|1)?(?:\\.\\d*)?)/)[1]);\n\n  return styleRules;\n};\n\nif (document.defaultView && document.defaultView.getComputedStyle) {\n  Element.getStyles = function(element) {\n    var css = document.defaultView.getComputedStyle($(element), null);\n    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {\n      styles[property] = css[property];\n      return styles;\n    });\n  };\n} else {\n  Element.getStyles = function(element) {\n    element = $(element);\n    var css = element.currentStyle, styles;\n    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {\n      results[property] = css[property];\n      return results;\n    });\n    if (!styles.opacity) styles.opacity = element.getOpacity();\n    return styles;\n  };\n}\n\nEffect.Methods = {\n  morph: function(element, style) {\n    element = $(element);\n    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));\n    return element;\n  },\n  visualEffect: function(element, effect, options) {\n    element = $(element);\n    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);\n    new Effect[klass](element, options);\n    return element;\n  },\n  highlight: function(element, options) {\n    element = $(element);\n    new Effect.Highlight(element, options);\n    return element;\n  }\n};\n\n$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+\n  'pulsate shake puff squish switchOff dropOut').each(\n  function(effect) {\n    Effect.Methods[effect] = function(element, options){\n      element = $(element);\n      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);\n      return element;\n    };\n  }\n);\n\n$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(\n  function(f) { Effect.Methods[f] = Element[f]; }\n);\n\nElement.addMethods(Effect.Methods);"
  },
  {
    "path": "test/apps/rails3/public/javascripts/prototype.js",
    "content": "/*  Prototype JavaScript framework, version 1.7_rc2\n *  (c) 2005-2010 Sam Stephenson\n *\n *  Prototype is freely distributable under the terms of an MIT-style license.\n *  For details, see the Prototype web site: http://www.prototypejs.org/\n *\n *--------------------------------------------------------------------------*/\n\nvar Prototype = {\n\n  Version: '1.7_rc2',\n\n  Browser: (function(){\n    var ua = navigator.userAgent;\n    var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';\n    return {\n      IE:             !!window.attachEvent && !isOpera,\n      Opera:          isOpera,\n      WebKit:         ua.indexOf('AppleWebKit/') > -1,\n      Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,\n      MobileSafari:   /Apple.*Mobile/.test(ua)\n    }\n  })(),\n\n  BrowserFeatures: {\n    XPath: !!document.evaluate,\n\n    SelectorsAPI: !!document.querySelector,\n\n    ElementExtensions: (function() {\n      var constructor = window.Element || window.HTMLElement;\n      return !!(constructor && constructor.prototype);\n    })(),\n    SpecificElementExtensions: (function() {\n      if (typeof window.HTMLDivElement !== 'undefined')\n        return true;\n\n      var div = document.createElement('div'),\n          form = document.createElement('form'),\n          isSupported = false;\n\n      if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {\n        isSupported = true;\n      }\n\n      div = form = null;\n\n      return isSupported;\n    })()\n  },\n\n  ScriptFragment: '<script[^>]*>([\\\\S\\\\s]*?)<\\/script>',\n  JSONFilter: /^\\/\\*-secure-([\\s\\S]*)\\*\\/\\s*$/,\n\n  emptyFunction: function() { },\n\n  K: function(x) { return x }\n};\n\nif (Prototype.Browser.MobileSafari)\n  Prototype.BrowserFeatures.SpecificElementExtensions = false;\n\n\nvar Abstract = { };\n\n\nvar Try = {\n  these: function() {\n    var returnValue;\n\n    for (var i = 0, length = arguments.length; i < length; i++) {\n      var lambda = arguments[i];\n      try {\n        returnValue = lambda();\n        break;\n      } catch (e) { }\n    }\n\n    return returnValue;\n  }\n};\n\n/* Based on Alex Arnell's inheritance implementation. */\n\nvar Class = (function() {\n\n  var IS_DONTENUM_BUGGY = (function(){\n    for (var p in { toString: 1 }) {\n      if (p === 'toString') return false;\n    }\n    return true;\n  })();\n\n  function subclass() {};\n  function create() {\n    var parent = null, properties = $A(arguments);\n    if (Object.isFunction(properties[0]))\n      parent = properties.shift();\n\n    function klass() {\n      this.initialize.apply(this, arguments);\n    }\n\n    Object.extend(klass, Class.Methods);\n    klass.superclass = parent;\n    klass.subclasses = [];\n\n    if (parent) {\n      subclass.prototype = parent.prototype;\n      klass.prototype = new subclass;\n      parent.subclasses.push(klass);\n    }\n\n    for (var i = 0, length = properties.length; i < length; i++)\n      klass.addMethods(properties[i]);\n\n    if (!klass.prototype.initialize)\n      klass.prototype.initialize = Prototype.emptyFunction;\n\n    klass.prototype.constructor = klass;\n    return klass;\n  }\n\n  function addMethods(source) {\n    var ancestor   = this.superclass && this.superclass.prototype,\n        properties = Object.keys(source);\n\n    if (IS_DONTENUM_BUGGY) {\n      if (source.toString != Object.prototype.toString)\n        properties.push(\"toString\");\n      if (source.valueOf != Object.prototype.valueOf)\n        properties.push(\"valueOf\");\n    }\n\n    for (var i = 0, length = properties.length; i < length; i++) {\n      var property = properties[i], value = source[property];\n      if (ancestor && Object.isFunction(value) &&\n          value.argumentNames()[0] == \"$super\") {\n        var method = value;\n        value = (function(m) {\n          return function() { return ancestor[m].apply(this, arguments); };\n        })(property).wrap(method);\n\n        value.valueOf = method.valueOf.bind(method);\n        value.toString = method.toString.bind(method);\n      }\n      this.prototype[property] = value;\n    }\n\n    return this;\n  }\n\n  return {\n    create: create,\n    Methods: {\n      addMethods: addMethods\n    }\n  };\n})();\n(function() {\n\n  var _toString = Object.prototype.toString,\n      NULL_TYPE = 'Null',\n      UNDEFINED_TYPE = 'Undefined',\n      BOOLEAN_TYPE = 'Boolean',\n      NUMBER_TYPE = 'Number',\n      STRING_TYPE = 'String',\n      OBJECT_TYPE = 'Object',\n      BOOLEAN_CLASS = '[object Boolean]',\n      NUMBER_CLASS = '[object Number]',\n      STRING_CLASS = '[object String]',\n      ARRAY_CLASS = '[object Array]',\n      NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&\n        typeof JSON.stringify === 'function' &&\n        JSON.stringify(0) === '0' &&\n        typeof JSON.stringify(Prototype.K) === 'undefined';\n\n  function Type(o) {\n    switch(o) {\n      case null: return NULL_TYPE;\n      case (void 0): return UNDEFINED_TYPE;\n    }\n    var type = typeof o;\n    switch(type) {\n      case 'boolean': return BOOLEAN_TYPE;\n      case 'number':  return NUMBER_TYPE;\n      case 'string':  return STRING_TYPE;\n    }\n    return OBJECT_TYPE;\n  }\n\n  function extend(destination, source) {\n    for (var property in source)\n      destination[property] = source[property];\n    return destination;\n  }\n\n  function inspect(object) {\n    try {\n      if (isUndefined(object)) return 'undefined';\n      if (object === null) return 'null';\n      return object.inspect ? object.inspect() : String(object);\n    } catch (e) {\n      if (e instanceof RangeError) return '...';\n      throw e;\n    }\n  }\n\n  function toJSON(value) {\n    return Str('', { '': value }, []);\n  }\n\n  function Str(key, holder, stack) {\n    var value = holder[key],\n        type = typeof value;\n\n    if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {\n      value = value.toJSON(key);\n    }\n\n    var _class = _toString.call(value);\n\n    switch (_class) {\n      case NUMBER_CLASS:\n      case BOOLEAN_CLASS:\n      case STRING_CLASS:\n        value = value.valueOf();\n    }\n\n    switch (value) {\n      case null: return 'null';\n      case true: return 'true';\n      case false: return 'false';\n    }\n\n    type = typeof value;\n    switch (type) {\n      case 'string':\n        return value.inspect(true);\n      case 'number':\n        return isFinite(value) ? String(value) : 'null';\n      case 'object':\n\n        for (var i = 0, length = stack.length; i < length; i++) {\n          if (stack[i] === value) { throw new TypeError(); }\n        }\n        stack.push(value);\n\n        var partial = [];\n        if (_class === ARRAY_CLASS) {\n          for (var i = 0, length = value.length; i < length; i++) {\n            var str = Str(i, value, stack);\n            partial.push(typeof str === 'undefined' ? 'null' : str);\n          }\n          partial = '[' + partial.join(',') + ']';\n        } else {\n          var keys = Object.keys(value);\n          for (var i = 0, length = keys.length; i < length; i++) {\n            var key = keys[i], str = Str(key, value, stack);\n            if (typeof str !== \"undefined\") {\n               partial.push(key.inspect(true)+ ':' + str);\n             }\n          }\n          partial = '{' + partial.join(',') + '}';\n        }\n        stack.pop();\n        return partial;\n    }\n  }\n\n  function stringify(object) {\n    return JSON.stringify(object);\n  }\n\n  function toQueryString(object) {\n    return $H(object).toQueryString();\n  }\n\n  function toHTML(object) {\n    return object && object.toHTML ? object.toHTML() : String.interpret(object);\n  }\n\n  function keys(object) {\n    if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }\n    var results = [];\n    for (var property in object) {\n      if (object.hasOwnProperty(property)) {\n        results.push(property);\n      }\n    }\n    return results;\n  }\n\n  function values(object) {\n    var results = [];\n    for (var property in object)\n      results.push(object[property]);\n    return results;\n  }\n\n  function clone(object) {\n    return extend({ }, object);\n  }\n\n  function isElement(object) {\n    return !!(object && object.nodeType == 1);\n  }\n\n  function isArray(object) {\n    return _toString.call(object) === ARRAY_CLASS;\n  }\n\n  var hasNativeIsArray = (typeof Array.isArray == 'function')\n    && Array.isArray([]) && !Array.isArray({});\n\n  if (hasNativeIsArray) {\n    isArray = Array.isArray;\n  }\n\n  function isHash(object) {\n    return object instanceof Hash;\n  }\n\n  function isFunction(object) {\n    return typeof object === \"function\";\n  }\n\n  function isString(object) {\n    return _toString.call(object) === STRING_CLASS;\n  }\n\n  function isNumber(object) {\n    return _toString.call(object) === NUMBER_CLASS;\n  }\n\n  function isUndefined(object) {\n    return typeof object === \"undefined\";\n  }\n\n  extend(Object, {\n    extend:        extend,\n    inspect:       inspect,\n    toJSON:        NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,\n    toQueryString: toQueryString,\n    toHTML:        toHTML,\n    keys:          Object.keys || keys,\n    values:        values,\n    clone:         clone,\n    isElement:     isElement,\n    isArray:       isArray,\n    isHash:        isHash,\n    isFunction:    isFunction,\n    isString:      isString,\n    isNumber:      isNumber,\n    isUndefined:   isUndefined\n  });\n})();\nObject.extend(Function.prototype, (function() {\n  var slice = Array.prototype.slice;\n\n  function update(array, args) {\n    var arrayLength = array.length, length = args.length;\n    while (length--) array[arrayLength + length] = args[length];\n    return array;\n  }\n\n  function merge(array, args) {\n    array = slice.call(array, 0);\n    return update(array, args);\n  }\n\n  function argumentNames() {\n    var names = this.toString().match(/^[\\s\\(]*function[^(]*\\(([^)]*)\\)/)[1]\n      .replace(/\\/\\/.*?[\\r\\n]|\\/\\*(?:.|[\\r\\n])*?\\*\\//g, '')\n      .replace(/\\s+/g, '').split(',');\n    return names.length == 1 && !names[0] ? [] : names;\n  }\n\n  function bind(context) {\n    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;\n    var __method = this, args = slice.call(arguments, 1);\n    return function() {\n      var a = merge(args, arguments);\n      return __method.apply(context, a);\n    }\n  }\n\n  function bindAsEventListener(context) {\n    var __method = this, args = slice.call(arguments, 1);\n    return function(event) {\n      var a = update([event || window.event], args);\n      return __method.apply(context, a);\n    }\n  }\n\n  function curry() {\n    if (!arguments.length) return this;\n    var __method = this, args = slice.call(arguments, 0);\n    return function() {\n      var a = merge(args, arguments);\n      return __method.apply(this, a);\n    }\n  }\n\n  function delay(timeout) {\n    var __method = this, args = slice.call(arguments, 1);\n    timeout = timeout * 1000;\n    return window.setTimeout(function() {\n      return __method.apply(__method, args);\n    }, timeout);\n  }\n\n  function defer() {\n    var args = update([0.01], arguments);\n    return this.delay.apply(this, args);\n  }\n\n  function wrap(wrapper) {\n    var __method = this;\n    return function() {\n      var a = update([__method.bind(this)], arguments);\n      return wrapper.apply(this, a);\n    }\n  }\n\n  function methodize() {\n    if (this._methodized) return this._methodized;\n    var __method = this;\n    return this._methodized = function() {\n      var a = update([this], arguments);\n      return __method.apply(null, a);\n    };\n  }\n\n  return {\n    argumentNames:       argumentNames,\n    bind:                bind,\n    bindAsEventListener: bindAsEventListener,\n    curry:               curry,\n    delay:               delay,\n    defer:               defer,\n    wrap:                wrap,\n    methodize:           methodize\n  }\n})());\n\n\n\n(function(proto) {\n\n\n  function toISOString() {\n    return this.getUTCFullYear() + '-' +\n      (this.getUTCMonth() + 1).toPaddedString(2) + '-' +\n      this.getUTCDate().toPaddedString(2) + 'T' +\n      this.getUTCHours().toPaddedString(2) + ':' +\n      this.getUTCMinutes().toPaddedString(2) + ':' +\n      this.getUTCSeconds().toPaddedString(2) + 'Z';\n  }\n\n\n  function toJSON() {\n    return this.toISOString();\n  }\n\n  if (!proto.toISOString) proto.toISOString = toISOString;\n  if (!proto.toJSON) proto.toJSON = toJSON;\n\n})(Date.prototype);\n\n\nRegExp.prototype.match = RegExp.prototype.test;\n\nRegExp.escape = function(str) {\n  return String(str).replace(/([.*+?^=!:${}()|[\\]\\/\\\\])/g, '\\\\$1');\n};\nvar PeriodicalExecuter = Class.create({\n  initialize: function(callback, frequency) {\n    this.callback = callback;\n    this.frequency = frequency;\n    this.currentlyExecuting = false;\n\n    this.registerCallback();\n  },\n\n  registerCallback: function() {\n    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);\n  },\n\n  execute: function() {\n    this.callback(this);\n  },\n\n  stop: function() {\n    if (!this.timer) return;\n    clearInterval(this.timer);\n    this.timer = null;\n  },\n\n  onTimerEvent: function() {\n    if (!this.currentlyExecuting) {\n      try {\n        this.currentlyExecuting = true;\n        this.execute();\n        this.currentlyExecuting = false;\n      } catch(e) {\n        this.currentlyExecuting = false;\n        throw e;\n      }\n    }\n  }\n});\nObject.extend(String, {\n  interpret: function(value) {\n    return value == null ? '' : String(value);\n  },\n  specialChar: {\n    '\\b': '\\\\b',\n    '\\t': '\\\\t',\n    '\\n': '\\\\n',\n    '\\f': '\\\\f',\n    '\\r': '\\\\r',\n    '\\\\': '\\\\\\\\'\n  }\n});\n\nObject.extend(String.prototype, (function() {\n  var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&\n    typeof JSON.parse === 'function' &&\n    JSON.parse('{\"test\": true}').test;\n\n  function prepareReplacement(replacement) {\n    if (Object.isFunction(replacement)) return replacement;\n    var template = new Template(replacement);\n    return function(match) { return template.evaluate(match) };\n  }\n\n  function gsub(pattern, replacement) {\n    var result = '', source = this, match;\n    replacement = prepareReplacement(replacement);\n\n    if (Object.isString(pattern))\n      pattern = RegExp.escape(pattern);\n\n    if (!(pattern.length || pattern.source)) {\n      replacement = replacement('');\n      return replacement + source.split('').join(replacement) + replacement;\n    }\n\n    while (source.length > 0) {\n      if (match = source.match(pattern)) {\n        result += source.slice(0, match.index);\n        result += String.interpret(replacement(match));\n        source  = source.slice(match.index + match[0].length);\n      } else {\n        result += source, source = '';\n      }\n    }\n    return result;\n  }\n\n  function sub(pattern, replacement, count) {\n    replacement = prepareReplacement(replacement);\n    count = Object.isUndefined(count) ? 1 : count;\n\n    return this.gsub(pattern, function(match) {\n      if (--count < 0) return match[0];\n      return replacement(match);\n    });\n  }\n\n  function scan(pattern, iterator) {\n    this.gsub(pattern, iterator);\n    return String(this);\n  }\n\n  function truncate(length, truncation) {\n    length = length || 30;\n    truncation = Object.isUndefined(truncation) ? '...' : truncation;\n    return this.length > length ?\n      this.slice(0, length - truncation.length) + truncation : String(this);\n  }\n\n  function strip() {\n    return this.replace(/^\\s+/, '').replace(/\\s+$/, '');\n  }\n\n  function stripTags() {\n    return this.replace(/<\\w+(\\s+(\"[^\"]*\"|'[^']*'|[^>])+)?>|<\\/\\w+>/gi, '');\n  }\n\n  function stripScripts() {\n    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');\n  }\n\n  function extractScripts() {\n    var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),\n        matchOne = new RegExp(Prototype.ScriptFragment, 'im');\n    return (this.match(matchAll) || []).map(function(scriptTag) {\n      return (scriptTag.match(matchOne) || ['', ''])[1];\n    });\n  }\n\n  function evalScripts() {\n    return this.extractScripts().map(function(script) { return eval(script) });\n  }\n\n  function escapeHTML() {\n    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');\n  }\n\n  function unescapeHTML() {\n    return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');\n  }\n\n\n  function toQueryParams(separator) {\n    var match = this.strip().match(/([^?#]*)(#.*)?$/);\n    if (!match) return { };\n\n    return match[1].split(separator || '&').inject({ }, function(hash, pair) {\n      if ((pair = pair.split('='))[0]) {\n        var key = decodeURIComponent(pair.shift()),\n            value = pair.length > 1 ? pair.join('=') : pair[0];\n\n        if (value != undefined) value = decodeURIComponent(value);\n\n        if (key in hash) {\n          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];\n          hash[key].push(value);\n        }\n        else hash[key] = value;\n      }\n      return hash;\n    });\n  }\n\n  function toArray() {\n    return this.split('');\n  }\n\n  function succ() {\n    return this.slice(0, this.length - 1) +\n      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);\n  }\n\n  function times(count) {\n    return count < 1 ? '' : new Array(count + 1).join(this);\n  }\n\n  function camelize() {\n    return this.replace(/-+(.)?/g, function(match, chr) {\n      return chr ? chr.toUpperCase() : '';\n    });\n  }\n\n  function capitalize() {\n    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();\n  }\n\n  function underscore() {\n    return this.replace(/::/g, '/')\n               .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')\n               .replace(/([a-z\\d])([A-Z])/g, '$1_$2')\n               .replace(/-/g, '_')\n               .toLowerCase();\n  }\n\n  function dasherize() {\n    return this.replace(/_/g, '-');\n  }\n\n  function inspect(useDoubleQuotes) {\n    var escapedString = this.replace(/[\\x00-\\x1f\\\\]/g, function(character) {\n      if (character in String.specialChar) {\n        return String.specialChar[character];\n      }\n      return '\\\\u00' + character.charCodeAt().toPaddedString(2, 16);\n    });\n    if (useDoubleQuotes) return '\"' + escapedString.replace(/\"/g, '\\\\\"') + '\"';\n    return \"'\" + escapedString.replace(/'/g, '\\\\\\'') + \"'\";\n  }\n\n  function unfilterJSON(filter) {\n    return this.replace(filter || Prototype.JSONFilter, '$1');\n  }\n\n  function isJSON() {\n    var str = this;\n    if (str.blank()) return false;\n    str = str.replace(/\\\\(?:[\"\\\\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');\n    str = str.replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, ']');\n    str = str.replace(/(?:^|:|,)(?:\\s*\\[)+/g, '');\n    return (/^[\\],:{}\\s]*$/).test(str);\n  }\n\n  function evalJSON(sanitize) {\n    var json = this.unfilterJSON(),\n        cx = /[\\u0000\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g;\n    if (cx.test(json)) {\n      json = json.replace(cx, function (a) {\n        return '\\\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);\n      });\n    }\n    try {\n      if (!sanitize || json.isJSON()) return eval('(' + json + ')');\n    } catch (e) { }\n    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());\n  }\n\n  function parseJSON() {\n    var json = this.unfilterJSON();\n    return JSON.parse(json);\n  }\n\n  function include(pattern) {\n    return this.indexOf(pattern) > -1;\n  }\n\n  function startsWith(pattern) {\n    return this.lastIndexOf(pattern, 0) === 0;\n  }\n\n  function endsWith(pattern) {\n    var d = this.length - pattern.length;\n    return d >= 0 && this.indexOf(pattern, d) === d;\n  }\n\n  function empty() {\n    return this == '';\n  }\n\n  function blank() {\n    return /^\\s*$/.test(this);\n  }\n\n  function interpolate(object, pattern) {\n    return new Template(this, pattern).evaluate(object);\n  }\n\n  return {\n    gsub:           gsub,\n    sub:            sub,\n    scan:           scan,\n    truncate:       truncate,\n    strip:          String.prototype.trim || strip,\n    stripTags:      stripTags,\n    stripScripts:   stripScripts,\n    extractScripts: extractScripts,\n    evalScripts:    evalScripts,\n    escapeHTML:     escapeHTML,\n    unescapeHTML:   unescapeHTML,\n    toQueryParams:  toQueryParams,\n    parseQuery:     toQueryParams,\n    toArray:        toArray,\n    succ:           succ,\n    times:          times,\n    camelize:       camelize,\n    capitalize:     capitalize,\n    underscore:     underscore,\n    dasherize:      dasherize,\n    inspect:        inspect,\n    unfilterJSON:   unfilterJSON,\n    isJSON:         isJSON,\n    evalJSON:       NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,\n    include:        include,\n    startsWith:     startsWith,\n    endsWith:       endsWith,\n    empty:          empty,\n    blank:          blank,\n    interpolate:    interpolate\n  };\n})());\n\nvar Template = Class.create({\n  initialize: function(template, pattern) {\n    this.template = template.toString();\n    this.pattern = pattern || Template.Pattern;\n  },\n\n  evaluate: function(object) {\n    if (object && Object.isFunction(object.toTemplateReplacements))\n      object = object.toTemplateReplacements();\n\n    return this.template.gsub(this.pattern, function(match) {\n      if (object == null) return (match[1] + '');\n\n      var before = match[1] || '';\n      if (before == '\\\\') return match[2];\n\n      var ctx = object, expr = match[3],\n          pattern = /^([^.[]+|\\[((?:.*?[^\\\\])?)\\])(\\.|\\[|$)/;\n\n      match = pattern.exec(expr);\n      if (match == null) return before;\n\n      while (match != null) {\n        var comp = match[1].startsWith('[') ? match[2].replace(/\\\\\\\\]/g, ']') : match[1];\n        ctx = ctx[comp];\n        if (null == ctx || '' == match[3]) break;\n        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);\n        match = pattern.exec(expr);\n      }\n\n      return before + String.interpret(ctx);\n    });\n  }\n});\nTemplate.Pattern = /(^|.|\\r|\\n)(#\\{(.*?)\\})/;\n\nvar $break = { };\n\nvar Enumerable = (function() {\n  function each(iterator, context) {\n    var index = 0;\n    try {\n      this._each(function(value) {\n        iterator.call(context, value, index++);\n      });\n    } catch (e) {\n      if (e != $break) throw e;\n    }\n    return this;\n  }\n\n  function eachSlice(number, iterator, context) {\n    var index = -number, slices = [], array = this.toArray();\n    if (number < 1) return array;\n    while ((index += number) < array.length)\n      slices.push(array.slice(index, index+number));\n    return slices.collect(iterator, context);\n  }\n\n  function all(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result = true;\n    this.each(function(value, index) {\n      result = result && !!iterator.call(context, value, index);\n      if (!result) throw $break;\n    });\n    return result;\n  }\n\n  function any(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result = false;\n    this.each(function(value, index) {\n      if (result = !!iterator.call(context, value, index))\n        throw $break;\n    });\n    return result;\n  }\n\n  function collect(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var results = [];\n    this.each(function(value, index) {\n      results.push(iterator.call(context, value, index));\n    });\n    return results;\n  }\n\n  function detect(iterator, context) {\n    var result;\n    this.each(function(value, index) {\n      if (iterator.call(context, value, index)) {\n        result = value;\n        throw $break;\n      }\n    });\n    return result;\n  }\n\n  function findAll(iterator, context) {\n    var results = [];\n    this.each(function(value, index) {\n      if (iterator.call(context, value, index))\n        results.push(value);\n    });\n    return results;\n  }\n\n  function grep(filter, iterator, context) {\n    iterator = iterator || Prototype.K;\n    var results = [];\n\n    if (Object.isString(filter))\n      filter = new RegExp(RegExp.escape(filter));\n\n    this.each(function(value, index) {\n      if (filter.match(value))\n        results.push(iterator.call(context, value, index));\n    });\n    return results;\n  }\n\n  function include(object) {\n    if (Object.isFunction(this.indexOf))\n      if (this.indexOf(object) != -1) return true;\n\n    var found = false;\n    this.each(function(value) {\n      if (value == object) {\n        found = true;\n        throw $break;\n      }\n    });\n    return found;\n  }\n\n  function inGroupsOf(number, fillWith) {\n    fillWith = Object.isUndefined(fillWith) ? null : fillWith;\n    return this.eachSlice(number, function(slice) {\n      while(slice.length < number) slice.push(fillWith);\n      return slice;\n    });\n  }\n\n  function inject(memo, iterator, context) {\n    this.each(function(value, index) {\n      memo = iterator.call(context, memo, value, index);\n    });\n    return memo;\n  }\n\n  function invoke(method) {\n    var args = $A(arguments).slice(1);\n    return this.map(function(value) {\n      return value[method].apply(value, args);\n    });\n  }\n\n  function max(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result;\n    this.each(function(value, index) {\n      value = iterator.call(context, value, index);\n      if (result == null || value >= result)\n        result = value;\n    });\n    return result;\n  }\n\n  function min(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result;\n    this.each(function(value, index) {\n      value = iterator.call(context, value, index);\n      if (result == null || value < result)\n        result = value;\n    });\n    return result;\n  }\n\n  function partition(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var trues = [], falses = [];\n    this.each(function(value, index) {\n      (iterator.call(context, value, index) ?\n        trues : falses).push(value);\n    });\n    return [trues, falses];\n  }\n\n  function pluck(property) {\n    var results = [];\n    this.each(function(value) {\n      results.push(value[property]);\n    });\n    return results;\n  }\n\n  function reject(iterator, context) {\n    var results = [];\n    this.each(function(value, index) {\n      if (!iterator.call(context, value, index))\n        results.push(value);\n    });\n    return results;\n  }\n\n  function sortBy(iterator, context) {\n    return this.map(function(value, index) {\n      return {\n        value: value,\n        criteria: iterator.call(context, value, index)\n      };\n    }).sort(function(left, right) {\n      var a = left.criteria, b = right.criteria;\n      return a < b ? -1 : a > b ? 1 : 0;\n    }).pluck('value');\n  }\n\n  function toArray() {\n    return this.map();\n  }\n\n  function zip() {\n    var iterator = Prototype.K, args = $A(arguments);\n    if (Object.isFunction(args.last()))\n      iterator = args.pop();\n\n    var collections = [this].concat(args).map($A);\n    return this.map(function(value, index) {\n      return iterator(collections.pluck(index));\n    });\n  }\n\n  function size() {\n    return this.toArray().length;\n  }\n\n  function inspect() {\n    return '#<Enumerable:' + this.toArray().inspect() + '>';\n  }\n\n\n\n\n\n\n\n\n\n  return {\n    each:       each,\n    eachSlice:  eachSlice,\n    all:        all,\n    every:      all,\n    any:        any,\n    some:       any,\n    collect:    collect,\n    map:        collect,\n    detect:     detect,\n    findAll:    findAll,\n    select:     findAll,\n    filter:     findAll,\n    grep:       grep,\n    include:    include,\n    member:     include,\n    inGroupsOf: inGroupsOf,\n    inject:     inject,\n    invoke:     invoke,\n    max:        max,\n    min:        min,\n    partition:  partition,\n    pluck:      pluck,\n    reject:     reject,\n    sortBy:     sortBy,\n    toArray:    toArray,\n    entries:    toArray,\n    zip:        zip,\n    size:       size,\n    inspect:    inspect,\n    find:       detect\n  };\n})();\n\nfunction $A(iterable) {\n  if (!iterable) return [];\n  if ('toArray' in Object(iterable)) return iterable.toArray();\n  var length = iterable.length || 0, results = new Array(length);\n  while (length--) results[length] = iterable[length];\n  return results;\n}\n\n\nfunction $w(string) {\n  if (!Object.isString(string)) return [];\n  string = string.strip();\n  return string ? string.split(/\\s+/) : [];\n}\n\nArray.from = $A;\n\n\n(function() {\n  var arrayProto = Array.prototype,\n      slice = arrayProto.slice,\n      _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available\n\n  function each(iterator) {\n    for (var i = 0, length = this.length; i < length; i++)\n      iterator(this[i]);\n  }\n  if (!_each) _each = each;\n\n  function clear() {\n    this.length = 0;\n    return this;\n  }\n\n  function first() {\n    return this[0];\n  }\n\n  function last() {\n    return this[this.length - 1];\n  }\n\n  function compact() {\n    return this.select(function(value) {\n      return value != null;\n    });\n  }\n\n  function flatten() {\n    return this.inject([], function(array, value) {\n      if (Object.isArray(value))\n        return array.concat(value.flatten());\n      array.push(value);\n      return array;\n    });\n  }\n\n  function without() {\n    var values = slice.call(arguments, 0);\n    return this.select(function(value) {\n      return !values.include(value);\n    });\n  }\n\n  function reverse(inline) {\n    return (inline === false ? this.toArray() : this)._reverse();\n  }\n\n  function uniq(sorted) {\n    return this.inject([], function(array, value, index) {\n      if (0 == index || (sorted ? array.last() != value : !array.include(value)))\n        array.push(value);\n      return array;\n    });\n  }\n\n  function intersect(array) {\n    return this.uniq().findAll(function(item) {\n      return array.detect(function(value) { return item === value });\n    });\n  }\n\n\n  function clone() {\n    return slice.call(this, 0);\n  }\n\n  function size() {\n    return this.length;\n  }\n\n  function inspect() {\n    return '[' + this.map(Object.inspect).join(', ') + ']';\n  }\n\n  function indexOf(item, i) {\n    i || (i = 0);\n    var length = this.length;\n    if (i < 0) i = length + i;\n    for (; i < length; i++)\n      if (this[i] === item) return i;\n    return -1;\n  }\n\n  function lastIndexOf(item, i) {\n    i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;\n    var n = this.slice(0, i).reverse().indexOf(item);\n    return (n < 0) ? n : i - n - 1;\n  }\n\n  function concat() {\n    var array = slice.call(this, 0), item;\n    for (var i = 0, length = arguments.length; i < length; i++) {\n      item = arguments[i];\n      if (Object.isArray(item) && !('callee' in item)) {\n        for (var j = 0, arrayLength = item.length; j < arrayLength; j++)\n          array.push(item[j]);\n      } else {\n        array.push(item);\n      }\n    }\n    return array;\n  }\n\n  Object.extend(arrayProto, Enumerable);\n\n  if (!arrayProto._reverse)\n    arrayProto._reverse = arrayProto.reverse;\n\n  Object.extend(arrayProto, {\n    _each:     _each,\n    clear:     clear,\n    first:     first,\n    last:      last,\n    compact:   compact,\n    flatten:   flatten,\n    without:   without,\n    reverse:   reverse,\n    uniq:      uniq,\n    intersect: intersect,\n    clone:     clone,\n    toArray:   clone,\n    size:      size,\n    inspect:   inspect\n  });\n\n  var CONCAT_ARGUMENTS_BUGGY = (function() {\n    return [].concat(arguments)[0][0] !== 1;\n  })(1,2)\n\n  if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;\n\n  if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;\n  if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;\n})();\nfunction $H(object) {\n  return new Hash(object);\n};\n\nvar Hash = Class.create(Enumerable, (function() {\n  function initialize(object) {\n    this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);\n  }\n\n\n  function _each(iterator) {\n    for (var key in this._object) {\n      var value = this._object[key], pair = [key, value];\n      pair.key = key;\n      pair.value = value;\n      iterator(pair);\n    }\n  }\n\n  function set(key, value) {\n    return this._object[key] = value;\n  }\n\n  function get(key) {\n    if (this._object[key] !== Object.prototype[key])\n      return this._object[key];\n  }\n\n  function unset(key) {\n    var value = this._object[key];\n    delete this._object[key];\n    return value;\n  }\n\n  function toObject() {\n    return Object.clone(this._object);\n  }\n\n\n\n  function keys() {\n    return this.pluck('key');\n  }\n\n  function values() {\n    return this.pluck('value');\n  }\n\n  function index(value) {\n    var match = this.detect(function(pair) {\n      return pair.value === value;\n    });\n    return match && match.key;\n  }\n\n  function merge(object) {\n    return this.clone().update(object);\n  }\n\n  function update(object) {\n    return new Hash(object).inject(this, function(result, pair) {\n      result.set(pair.key, pair.value);\n      return result;\n    });\n  }\n\n  function toQueryPair(key, value) {\n    if (Object.isUndefined(value)) return key;\n    return key + '=' + encodeURIComponent(String.interpret(value));\n  }\n\n  function toQueryString() {\n    return this.inject([], function(results, pair) {\n      var key = encodeURIComponent(pair.key), values = pair.value;\n\n      if (values && typeof values == 'object') {\n        if (Object.isArray(values))\n          return results.concat(values.map(toQueryPair.curry(key)));\n      } else results.push(toQueryPair(key, values));\n      return results;\n    }).join('&');\n  }\n\n  function inspect() {\n    return '#<Hash:{' + this.map(function(pair) {\n      return pair.map(Object.inspect).join(': ');\n    }).join(', ') + '}>';\n  }\n\n  function clone() {\n    return new Hash(this);\n  }\n\n  return {\n    initialize:             initialize,\n    _each:                  _each,\n    set:                    set,\n    get:                    get,\n    unset:                  unset,\n    toObject:               toObject,\n    toTemplateReplacements: toObject,\n    keys:                   keys,\n    values:                 values,\n    index:                  index,\n    merge:                  merge,\n    update:                 update,\n    toQueryString:          toQueryString,\n    inspect:                inspect,\n    toJSON:                 toObject,\n    clone:                  clone\n  };\n})());\n\nHash.from = $H;\nObject.extend(Number.prototype, (function() {\n  function toColorPart() {\n    return this.toPaddedString(2, 16);\n  }\n\n  function succ() {\n    return this + 1;\n  }\n\n  function times(iterator, context) {\n    $R(0, this, true).each(iterator, context);\n    return this;\n  }\n\n  function toPaddedString(length, radix) {\n    var string = this.toString(radix || 10);\n    return '0'.times(length - string.length) + string;\n  }\n\n  function abs() {\n    return Math.abs(this);\n  }\n\n  function round() {\n    return Math.round(this);\n  }\n\n  function ceil() {\n    return Math.ceil(this);\n  }\n\n  function floor() {\n    return Math.floor(this);\n  }\n\n  return {\n    toColorPart:    toColorPart,\n    succ:           succ,\n    times:          times,\n    toPaddedString: toPaddedString,\n    abs:            abs,\n    round:          round,\n    ceil:           ceil,\n    floor:          floor\n  };\n})());\n\nfunction $R(start, end, exclusive) {\n  return new ObjectRange(start, end, exclusive);\n}\n\nvar ObjectRange = Class.create(Enumerable, (function() {\n  function initialize(start, end, exclusive) {\n    this.start = start;\n    this.end = end;\n    this.exclusive = exclusive;\n  }\n\n  function _each(iterator) {\n    var value = this.start;\n    while (this.include(value)) {\n      iterator(value);\n      value = value.succ();\n    }\n  }\n\n  function include(value) {\n    if (value < this.start)\n      return false;\n    if (this.exclusive)\n      return value < this.end;\n    return value <= this.end;\n  }\n\n  return {\n    initialize: initialize,\n    _each:      _each,\n    include:    include\n  };\n})());\n\n\n\nvar Ajax = {\n  getTransport: function() {\n    return Try.these(\n      function() {return new XMLHttpRequest()},\n      function() {return new ActiveXObject('Msxml2.XMLHTTP')},\n      function() {return new ActiveXObject('Microsoft.XMLHTTP')}\n    ) || false;\n  },\n\n  activeRequestCount: 0\n};\n\nAjax.Responders = {\n  responders: [],\n\n  _each: function(iterator) {\n    this.responders._each(iterator);\n  },\n\n  register: function(responder) {\n    if (!this.include(responder))\n      this.responders.push(responder);\n  },\n\n  unregister: function(responder) {\n    this.responders = this.responders.without(responder);\n  },\n\n  dispatch: function(callback, request, transport, json) {\n    this.each(function(responder) {\n      if (Object.isFunction(responder[callback])) {\n        try {\n          responder[callback].apply(responder, [request, transport, json]);\n        } catch (e) { }\n      }\n    });\n  }\n};\n\nObject.extend(Ajax.Responders, Enumerable);\n\nAjax.Responders.register({\n  onCreate:   function() { Ajax.activeRequestCount++ },\n  onComplete: function() { Ajax.activeRequestCount-- }\n});\nAjax.Base = Class.create({\n  initialize: function(options) {\n    this.options = {\n      method:       'post',\n      asynchronous: true,\n      contentType:  'application/x-www-form-urlencoded',\n      encoding:     'UTF-8',\n      parameters:   '',\n      evalJSON:     true,\n      evalJS:       true\n    };\n    Object.extend(this.options, options || { });\n\n    this.options.method = this.options.method.toLowerCase();\n\n    if (Object.isString(this.options.parameters))\n      this.options.parameters = this.options.parameters.toQueryParams();\n    else if (Object.isHash(this.options.parameters))\n      this.options.parameters = this.options.parameters.toObject();\n  }\n});\nAjax.Request = Class.create(Ajax.Base, {\n  _complete: false,\n\n  initialize: function($super, url, options) {\n    $super(options);\n    this.transport = Ajax.getTransport();\n    this.request(url);\n  },\n\n  request: function(url) {\n    this.url = url;\n    this.method = this.options.method;\n    var params = Object.clone(this.options.parameters);\n\n    if (!['get', 'post'].include(this.method)) {\n      params['_method'] = this.method;\n      this.method = 'post';\n    }\n\n    this.parameters = params;\n\n    if (params = Object.toQueryString(params)) {\n      if (this.method == 'get')\n        this.url += (this.url.include('?') ? '&' : '?') + params;\n      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))\n        params += '&_=';\n    }\n\n    try {\n      var response = new Ajax.Response(this);\n      if (this.options.onCreate) this.options.onCreate(response);\n      Ajax.Responders.dispatch('onCreate', this, response);\n\n      this.transport.open(this.method.toUpperCase(), this.url,\n        this.options.asynchronous);\n\n      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);\n\n      this.transport.onreadystatechange = this.onStateChange.bind(this);\n      this.setRequestHeaders();\n\n      this.body = this.method == 'post' ? (this.options.postBody || params) : null;\n      this.transport.send(this.body);\n\n      /* Force Firefox to handle ready state 4 for synchronous requests */\n      if (!this.options.asynchronous && this.transport.overrideMimeType)\n        this.onStateChange();\n\n    }\n    catch (e) {\n      this.dispatchException(e);\n    }\n  },\n\n  onStateChange: function() {\n    var readyState = this.transport.readyState;\n    if (readyState > 1 && !((readyState == 4) && this._complete))\n      this.respondToReadyState(this.transport.readyState);\n  },\n\n  setRequestHeaders: function() {\n    var headers = {\n      'X-Requested-With': 'XMLHttpRequest',\n      'X-Prototype-Version': Prototype.Version,\n      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'\n    };\n\n    if (this.method == 'post') {\n      headers['Content-type'] = this.options.contentType +\n        (this.options.encoding ? '; charset=' + this.options.encoding : '');\n\n      /* Force \"Connection: close\" for older Mozilla browsers to work\n       * around a bug where XMLHttpRequest sends an incorrect\n       * Content-length header. See Mozilla Bugzilla #246651.\n       */\n      if (this.transport.overrideMimeType &&\n          (navigator.userAgent.match(/Gecko\\/(\\d{4})/) || [0,2005])[1] < 2005)\n            headers['Connection'] = 'close';\n    }\n\n    if (typeof this.options.requestHeaders == 'object') {\n      var extras = this.options.requestHeaders;\n\n      if (Object.isFunction(extras.push))\n        for (var i = 0, length = extras.length; i < length; i += 2)\n          headers[extras[i]] = extras[i+1];\n      else\n        $H(extras).each(function(pair) { headers[pair.key] = pair.value });\n    }\n\n    for (var name in headers)\n      this.transport.setRequestHeader(name, headers[name]);\n  },\n\n  success: function() {\n    var status = this.getStatus();\n    return !status || (status >= 200 && status < 300);\n  },\n\n  getStatus: function() {\n    try {\n      return this.transport.status || 0;\n    } catch (e) { return 0 }\n  },\n\n  respondToReadyState: function(readyState) {\n    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);\n\n    if (state == 'Complete') {\n      try {\n        this._complete = true;\n        (this.options['on' + response.status]\n         || this.options['on' + (this.success() ? 'Success' : 'Failure')]\n         || Prototype.emptyFunction)(response, response.headerJSON);\n      } catch (e) {\n        this.dispatchException(e);\n      }\n\n      var contentType = response.getHeader('Content-type');\n      if (this.options.evalJS == 'force'\n          || (this.options.evalJS && this.isSameOrigin() && contentType\n          && contentType.match(/^\\s*(text|application)\\/(x-)?(java|ecma)script(;.*)?\\s*$/i)))\n        this.evalResponse();\n    }\n\n    try {\n      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);\n      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);\n    } catch (e) {\n      this.dispatchException(e);\n    }\n\n    if (state == 'Complete') {\n      this.transport.onreadystatechange = Prototype.emptyFunction;\n    }\n  },\n\n  isSameOrigin: function() {\n    var m = this.url.match(/^\\s*https?:\\/\\/[^\\/]*/);\n    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({\n      protocol: location.protocol,\n      domain: document.domain,\n      port: location.port ? ':' + location.port : ''\n    }));\n  },\n\n  getHeader: function(name) {\n    try {\n      return this.transport.getResponseHeader(name) || null;\n    } catch (e) { return null; }\n  },\n\n  evalResponse: function() {\n    try {\n      return eval((this.transport.responseText || '').unfilterJSON());\n    } catch (e) {\n      this.dispatchException(e);\n    }\n  },\n\n  dispatchException: function(exception) {\n    (this.options.onException || Prototype.emptyFunction)(this, exception);\n    Ajax.Responders.dispatch('onException', this, exception);\n  }\n});\n\nAjax.Request.Events =\n  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];\n\n\n\n\n\n\n\n\nAjax.Response = Class.create({\n  initialize: function(request){\n    this.request = request;\n    var transport  = this.transport  = request.transport,\n        readyState = this.readyState = transport.readyState;\n\n    if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {\n      this.status       = this.getStatus();\n      this.statusText   = this.getStatusText();\n      this.responseText = String.interpret(transport.responseText);\n      this.headerJSON   = this._getHeaderJSON();\n    }\n\n    if (readyState == 4) {\n      var xml = transport.responseXML;\n      this.responseXML  = Object.isUndefined(xml) ? null : xml;\n      this.responseJSON = this._getResponseJSON();\n    }\n  },\n\n  status:      0,\n\n  statusText: '',\n\n  getStatus: Ajax.Request.prototype.getStatus,\n\n  getStatusText: function() {\n    try {\n      return this.transport.statusText || '';\n    } catch (e) { return '' }\n  },\n\n  getHeader: Ajax.Request.prototype.getHeader,\n\n  getAllHeaders: function() {\n    try {\n      return this.getAllResponseHeaders();\n    } catch (e) { return null }\n  },\n\n  getResponseHeader: function(name) {\n    return this.transport.getResponseHeader(name);\n  },\n\n  getAllResponseHeaders: function() {\n    return this.transport.getAllResponseHeaders();\n  },\n\n  _getHeaderJSON: function() {\n    var json = this.getHeader('X-JSON');\n    if (!json) return null;\n    json = decodeURIComponent(escape(json));\n    try {\n      return json.evalJSON(this.request.options.sanitizeJSON ||\n        !this.request.isSameOrigin());\n    } catch (e) {\n      this.request.dispatchException(e);\n    }\n  },\n\n  _getResponseJSON: function() {\n    var options = this.request.options;\n    if (!options.evalJSON || (options.evalJSON != 'force' &&\n      !(this.getHeader('Content-type') || '').include('application/json')) ||\n        this.responseText.blank())\n          return null;\n    try {\n      return this.responseText.evalJSON(options.sanitizeJSON ||\n        !this.request.isSameOrigin());\n    } catch (e) {\n      this.request.dispatchException(e);\n    }\n  }\n});\n\nAjax.Updater = Class.create(Ajax.Request, {\n  initialize: function($super, container, url, options) {\n    this.container = {\n      success: (container.success || container),\n      failure: (container.failure || (container.success ? null : container))\n    };\n\n    options = Object.clone(options);\n    var onComplete = options.onComplete;\n    options.onComplete = (function(response, json) {\n      this.updateContent(response.responseText);\n      if (Object.isFunction(onComplete)) onComplete(response, json);\n    }).bind(this);\n\n    $super(url, options);\n  },\n\n  updateContent: function(responseText) {\n    var receiver = this.container[this.success() ? 'success' : 'failure'],\n        options = this.options;\n\n    if (!options.evalScripts) responseText = responseText.stripScripts();\n\n    if (receiver = $(receiver)) {\n      if (options.insertion) {\n        if (Object.isString(options.insertion)) {\n          var insertion = { }; insertion[options.insertion] = responseText;\n          receiver.insert(insertion);\n        }\n        else options.insertion(receiver, responseText);\n      }\n      else receiver.update(responseText);\n    }\n  }\n});\n\nAjax.PeriodicalUpdater = Class.create(Ajax.Base, {\n  initialize: function($super, container, url, options) {\n    $super(options);\n    this.onComplete = this.options.onComplete;\n\n    this.frequency = (this.options.frequency || 2);\n    this.decay = (this.options.decay || 1);\n\n    this.updater = { };\n    this.container = container;\n    this.url = url;\n\n    this.start();\n  },\n\n  start: function() {\n    this.options.onComplete = this.updateComplete.bind(this);\n    this.onTimerEvent();\n  },\n\n  stop: function() {\n    this.updater.options.onComplete = undefined;\n    clearTimeout(this.timer);\n    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);\n  },\n\n  updateComplete: function(response) {\n    if (this.options.decay) {\n      this.decay = (response.responseText == this.lastText ?\n        this.decay * this.options.decay : 1);\n\n      this.lastText = response.responseText;\n    }\n    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);\n  },\n\n  onTimerEvent: function() {\n    this.updater = new Ajax.Updater(this.container, this.url, this.options);\n  }\n});\n\n\nfunction $(element) {\n  if (arguments.length > 1) {\n    for (var i = 0, elements = [], length = arguments.length; i < length; i++)\n      elements.push($(arguments[i]));\n    return elements;\n  }\n  if (Object.isString(element))\n    element = document.getElementById(element);\n  return Element.extend(element);\n}\n\nif (Prototype.BrowserFeatures.XPath) {\n  document._getElementsByXPath = function(expression, parentElement) {\n    var results = [];\n    var query = document.evaluate(expression, $(parentElement) || document,\n      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);\n    for (var i = 0, length = query.snapshotLength; i < length; i++)\n      results.push(Element.extend(query.snapshotItem(i)));\n    return results;\n  };\n}\n\n/*--------------------------------------------------------------------------*/\n\nif (!Node) var Node = { };\n\nif (!Node.ELEMENT_NODE) {\n  Object.extend(Node, {\n    ELEMENT_NODE: 1,\n    ATTRIBUTE_NODE: 2,\n    TEXT_NODE: 3,\n    CDATA_SECTION_NODE: 4,\n    ENTITY_REFERENCE_NODE: 5,\n    ENTITY_NODE: 6,\n    PROCESSING_INSTRUCTION_NODE: 7,\n    COMMENT_NODE: 8,\n    DOCUMENT_NODE: 9,\n    DOCUMENT_TYPE_NODE: 10,\n    DOCUMENT_FRAGMENT_NODE: 11,\n    NOTATION_NODE: 12\n  });\n}\n\n\n\n(function(global) {\n\n  var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){\n    try {\n      var el = document.createElement('<input name=\"x\">');\n      return el.tagName.toLowerCase() === 'input' && el.name === 'x';\n    }\n    catch(err) {\n      return false;\n    }\n  })();\n\n  var element = global.Element;\n\n  global.Element = function(tagName, attributes) {\n    attributes = attributes || { };\n    tagName = tagName.toLowerCase();\n    var cache = Element.cache;\n    if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {\n      tagName = '<' + tagName + ' name=\"' + attributes.name + '\">';\n      delete attributes.name;\n      return Element.writeAttribute(document.createElement(tagName), attributes);\n    }\n    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));\n    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);\n  };\n\n  Object.extend(global.Element, element || { });\n  if (element) global.Element.prototype = element.prototype;\n\n})(this);\n\nElement.idCounter = 1;\nElement.cache = { };\n\nfunction purgeElement(element) {\n  var uid = element._prototypeUID;\n  if (uid) {\n    Element.stopObserving(element);\n    element._prototypeUID = void 0;\n    delete Element.Storage[uid];\n  }\n}\n\nElement.Methods = {\n  visible: function(element) {\n    return $(element).style.display != 'none';\n  },\n\n  toggle: function(element) {\n    element = $(element);\n    Element[Element.visible(element) ? 'hide' : 'show'](element);\n    return element;\n  },\n\n  hide: function(element) {\n    element = $(element);\n    element.style.display = 'none';\n    return element;\n  },\n\n  show: function(element) {\n    element = $(element);\n    element.style.display = '';\n    return element;\n  },\n\n  remove: function(element) {\n    element = $(element);\n    element.parentNode.removeChild(element);\n    return element;\n  },\n\n  update: (function(){\n\n    var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){\n      var el = document.createElement(\"select\"),\n          isBuggy = true;\n      el.innerHTML = \"<option value=\\\"test\\\">test</option>\";\n      if (el.options && el.options[0]) {\n        isBuggy = el.options[0].nodeName.toUpperCase() !== \"OPTION\";\n      }\n      el = null;\n      return isBuggy;\n    })();\n\n    var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){\n      try {\n        var el = document.createElement(\"table\");\n        if (el && el.tBodies) {\n          el.innerHTML = \"<tbody><tr><td>test</td></tr></tbody>\";\n          var isBuggy = typeof el.tBodies[0] == \"undefined\";\n          el = null;\n          return isBuggy;\n        }\n      } catch (e) {\n        return true;\n      }\n    })();\n\n    var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {\n      var s = document.createElement(\"script\"),\n          isBuggy = false;\n      try {\n        s.appendChild(document.createTextNode(\"\"));\n        isBuggy = !s.firstChild ||\n          s.firstChild && s.firstChild.nodeType !== 3;\n      } catch (e) {\n        isBuggy = true;\n      }\n      s = null;\n      return isBuggy;\n    })();\n\n    function update(element, content) {\n      element = $(element);\n\n      var descendants = element.getElementsByTagName('*'),\n       i = descendants.length;\n      while (i--) purgeElement(descendants[i]);\n\n      if (content && content.toElement)\n        content = content.toElement();\n\n      if (Object.isElement(content))\n        return element.update().insert(content);\n\n      content = Object.toHTML(content);\n\n      var tagName = element.tagName.toUpperCase();\n\n      if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {\n        element.text = content;\n        return element;\n      }\n\n      if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) {\n        if (tagName in Element._insertionTranslations.tags) {\n          while (element.firstChild) {\n            element.removeChild(element.firstChild);\n          }\n          Element._getContentFromAnonymousElement(tagName, content.stripScripts())\n            .each(function(node) {\n              element.appendChild(node)\n            });\n        }\n        else {\n          element.innerHTML = content.stripScripts();\n        }\n      }\n      else {\n        element.innerHTML = content.stripScripts();\n      }\n\n      content.evalScripts.bind(content).defer();\n      return element;\n    }\n\n    return update;\n  })(),\n\n  replace: function(element, content) {\n    element = $(element);\n    if (content && content.toElement) content = content.toElement();\n    else if (!Object.isElement(content)) {\n      content = Object.toHTML(content);\n      var range = element.ownerDocument.createRange();\n      range.selectNode(element);\n      content.evalScripts.bind(content).defer();\n      content = range.createContextualFragment(content.stripScripts());\n    }\n    element.parentNode.replaceChild(content, element);\n    return element;\n  },\n\n  insert: function(element, insertions) {\n    element = $(element);\n\n    if (Object.isString(insertions) || Object.isNumber(insertions) ||\n        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))\n          insertions = {bottom:insertions};\n\n    var content, insert, tagName, childNodes;\n\n    for (var position in insertions) {\n      content  = insertions[position];\n      position = position.toLowerCase();\n      insert = Element._insertionTranslations[position];\n\n      if (content && content.toElement) content = content.toElement();\n      if (Object.isElement(content)) {\n        insert(element, content);\n        continue;\n      }\n\n      content = Object.toHTML(content);\n\n      tagName = ((position == 'before' || position == 'after')\n        ? element.parentNode : element).tagName.toUpperCase();\n\n      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());\n\n      if (position == 'top' || position == 'after') childNodes.reverse();\n      childNodes.each(insert.curry(element));\n\n      content.evalScripts.bind(content).defer();\n    }\n\n    return element;\n  },\n\n  wrap: function(element, wrapper, attributes) {\n    element = $(element);\n    if (Object.isElement(wrapper))\n      $(wrapper).writeAttribute(attributes || { });\n    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);\n    else wrapper = new Element('div', wrapper);\n    if (element.parentNode)\n      element.parentNode.replaceChild(wrapper, element);\n    wrapper.appendChild(element);\n    return wrapper;\n  },\n\n  inspect: function(element) {\n    element = $(element);\n    var result = '<' + element.tagName.toLowerCase();\n    $H({'id': 'id', 'className': 'class'}).each(function(pair) {\n      var property = pair.first(),\n          attribute = pair.last(),\n          value = (element[property] || '').toString();\n      if (value) result += ' ' + attribute + '=' + value.inspect(true);\n    });\n    return result + '>';\n  },\n\n  recursivelyCollect: function(element, property, maximumLength) {\n    element = $(element);\n    maximumLength = maximumLength || -1;\n    var elements = [];\n\n    while (element = element[property]) {\n      if (element.nodeType == 1)\n        elements.push(Element.extend(element));\n      if (elements.length == maximumLength)\n        break;\n    }\n\n    return elements;\n  },\n\n  ancestors: function(element) {\n    return Element.recursivelyCollect(element, 'parentNode');\n  },\n\n  descendants: function(element) {\n    return Element.select(element, \"*\");\n  },\n\n  firstDescendant: function(element) {\n    element = $(element).firstChild;\n    while (element && element.nodeType != 1) element = element.nextSibling;\n    return $(element);\n  },\n\n  immediateDescendants: function(element) {\n    var results = [], child = $(element).firstChild;\n    while (child) {\n      if (child.nodeType === 1) {\n        results.push(Element.extend(child));\n      }\n      child = child.nextSibling;\n    }\n    return results;\n  },\n\n  previousSiblings: function(element, maximumLength) {\n    return Element.recursivelyCollect(element, 'previousSibling');\n  },\n\n  nextSiblings: function(element) {\n    return Element.recursivelyCollect(element, 'nextSibling');\n  },\n\n  siblings: function(element) {\n    element = $(element);\n    return Element.previousSiblings(element).reverse()\n      .concat(Element.nextSiblings(element));\n  },\n\n  match: function(element, selector) {\n    element = $(element);\n    if (Object.isString(selector))\n      return Prototype.Selector.match(element, selector);\n    return selector.match(element);\n  },\n\n  up: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return $(element.parentNode);\n    var ancestors = Element.ancestors(element);\n    return Object.isNumber(expression) ? ancestors[expression] :\n      Prototype.Selector.find(ancestors, expression, index);\n  },\n\n  down: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return Element.firstDescendant(element);\n    return Object.isNumber(expression) ? Element.descendants(element)[expression] :\n      Element.select(element, expression)[index || 0];\n  },\n\n  previous: function(element, expression, index) {\n    element = $(element);\n    if (Object.isNumber(expression)) index = expression, expression = false;\n    if (!Object.isNumber(index)) index = 0;\n\n    if (expression) {\n      return Prototype.Selector.find(element.previousSiblings(), expression, index);\n    } else {\n      return element.recursivelyCollect(\"previousSibling\", index + 1)[index];\n    }\n  },\n\n  next: function(element, expression, index) {\n    element = $(element);\n    if (Object.isNumber(expression)) index = expression, expression = false;\n    if (!Object.isNumber(index)) index = 0;\n\n    if (expression) {\n      return Prototype.Selector.find(element.nextSiblings(), expression, index);\n    } else {\n      var maximumLength = Object.isNumber(index) ? index + 1 : 1;\n      return element.recursivelyCollect(\"nextSibling\", index + 1)[index];\n    }\n  },\n\n\n  select: function(element) {\n    element = $(element);\n    var expressions = Array.prototype.slice.call(arguments, 1).join(', ');\n    return Prototype.Selector.select(expressions, element);\n  },\n\n  adjacent: function(element) {\n    element = $(element);\n    var expressions = Array.prototype.slice.call(arguments, 1).join(', ');\n    return Prototype.Selector.select(expressions, element.parentNode).without(element);\n  },\n\n  identify: function(element) {\n    element = $(element);\n    var id = Element.readAttribute(element, 'id');\n    if (id) return id;\n    do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));\n    Element.writeAttribute(element, 'id', id);\n    return id;\n  },\n\n  readAttribute: function(element, name) {\n    element = $(element);\n    if (Prototype.Browser.IE) {\n      var t = Element._attributeTranslations.read;\n      if (t.values[name]) return t.values[name](element, name);\n      if (t.names[name]) name = t.names[name];\n      if (name.include(':')) {\n        return (!element.attributes || !element.attributes[name]) ? null :\n         element.attributes[name].value;\n      }\n    }\n    return element.getAttribute(name);\n  },\n\n  writeAttribute: function(element, name, value) {\n    element = $(element);\n    var attributes = { }, t = Element._attributeTranslations.write;\n\n    if (typeof name == 'object') attributes = name;\n    else attributes[name] = Object.isUndefined(value) ? true : value;\n\n    for (var attr in attributes) {\n      name = t.names[attr] || attr;\n      value = attributes[attr];\n      if (t.values[attr]) name = t.values[attr](element, value);\n      if (value === false || value === null)\n        element.removeAttribute(name);\n      else if (value === true)\n        element.setAttribute(name, name);\n      else element.setAttribute(name, value);\n    }\n    return element;\n  },\n\n  getHeight: function(element) {\n    return Element.getDimensions(element).height;\n  },\n\n  getWidth: function(element) {\n    return Element.getDimensions(element).width;\n  },\n\n  classNames: function(element) {\n    return new Element.ClassNames(element);\n  },\n\n  hasClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    var elementClassName = element.className;\n    return (elementClassName.length > 0 && (elementClassName == className ||\n      new RegExp(\"(^|\\\\s)\" + className + \"(\\\\s|$)\").test(elementClassName)));\n  },\n\n  addClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    if (!Element.hasClassName(element, className))\n      element.className += (element.className ? ' ' : '') + className;\n    return element;\n  },\n\n  removeClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    element.className = element.className.replace(\n      new RegExp(\"(^|\\\\s+)\" + className + \"(\\\\s+|$)\"), ' ').strip();\n    return element;\n  },\n\n  toggleClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    return Element[Element.hasClassName(element, className) ?\n      'removeClassName' : 'addClassName'](element, className);\n  },\n\n  cleanWhitespace: function(element) {\n    element = $(element);\n    var node = element.firstChild;\n    while (node) {\n      var nextNode = node.nextSibling;\n      if (node.nodeType == 3 && !/\\S/.test(node.nodeValue))\n        element.removeChild(node);\n      node = nextNode;\n    }\n    return element;\n  },\n\n  empty: function(element) {\n    return $(element).innerHTML.blank();\n  },\n\n  descendantOf: function(element, ancestor) {\n    element = $(element), ancestor = $(ancestor);\n\n    if (element.compareDocumentPosition)\n      return (element.compareDocumentPosition(ancestor) & 8) === 8;\n\n    if (ancestor.contains)\n      return ancestor.contains(element) && ancestor !== element;\n\n    while (element = element.parentNode)\n      if (element == ancestor) return true;\n\n    return false;\n  },\n\n  scrollTo: function(element) {\n    element = $(element);\n    var pos = Element.cumulativeOffset(element);\n    window.scrollTo(pos[0], pos[1]);\n    return element;\n  },\n\n  getStyle: function(element, style) {\n    element = $(element);\n    style = style == 'float' ? 'cssFloat' : style.camelize();\n    var value = element.style[style];\n    if (!value || value == 'auto') {\n      var css = document.defaultView.getComputedStyle(element, null);\n      value = css ? css[style] : null;\n    }\n    if (style == 'opacity') return value ? parseFloat(value) : 1.0;\n    return value == 'auto' ? null : value;\n  },\n\n  getOpacity: function(element) {\n    return $(element).getStyle('opacity');\n  },\n\n  setStyle: function(element, styles) {\n    element = $(element);\n    var elementStyle = element.style, match;\n    if (Object.isString(styles)) {\n      element.style.cssText += ';' + styles;\n      return styles.include('opacity') ?\n        element.setOpacity(styles.match(/opacity:\\s*(\\d?\\.?\\d*)/)[1]) : element;\n    }\n    for (var property in styles)\n      if (property == 'opacity') element.setOpacity(styles[property]);\n      else\n        elementStyle[(property == 'float' || property == 'cssFloat') ?\n          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :\n            property] = styles[property];\n\n    return element;\n  },\n\n  setOpacity: function(element, value) {\n    element = $(element);\n    element.style.opacity = (value == 1 || value === '') ? '' :\n      (value < 0.00001) ? 0 : value;\n    return element;\n  },\n\n  makePositioned: function(element) {\n    element = $(element);\n    var pos = Element.getStyle(element, 'position');\n    if (pos == 'static' || !pos) {\n      element._madePositioned = true;\n      element.style.position = 'relative';\n      if (Prototype.Browser.Opera) {\n        element.style.top = 0;\n        element.style.left = 0;\n      }\n    }\n    return element;\n  },\n\n  undoPositioned: function(element) {\n    element = $(element);\n    if (element._madePositioned) {\n      element._madePositioned = undefined;\n      element.style.position =\n        element.style.top =\n        element.style.left =\n        element.style.bottom =\n        element.style.right = '';\n    }\n    return element;\n  },\n\n  makeClipping: function(element) {\n    element = $(element);\n    if (element._overflow) return element;\n    element._overflow = Element.getStyle(element, 'overflow') || 'auto';\n    if (element._overflow !== 'hidden')\n      element.style.overflow = 'hidden';\n    return element;\n  },\n\n  undoClipping: function(element) {\n    element = $(element);\n    if (!element._overflow) return element;\n    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;\n    element._overflow = null;\n    return element;\n  },\n\n  cumulativeOffset: function(element) {\n    var valueT = 0, valueL = 0;\n    if (element.parentNode) {\n      do {\n        valueT += element.offsetTop  || 0;\n        valueL += element.offsetLeft || 0;\n        element = element.offsetParent;\n      } while (element);\n    }\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  positionedOffset: function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      element = element.offsetParent;\n      if (element) {\n        if (element.tagName.toUpperCase() == 'BODY') break;\n        var p = Element.getStyle(element, 'position');\n        if (p !== 'static') break;\n      }\n    } while (element);\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  absolutize: function(element) {\n    element = $(element);\n    if (Element.getStyle(element, 'position') == 'absolute') return element;\n\n    var offsets = Element.positionedOffset(element),\n        top     = offsets[1],\n        left    = offsets[0],\n        width   = element.clientWidth,\n        height  = element.clientHeight;\n\n    element._originalLeft   = left - parseFloat(element.style.left  || 0);\n    element._originalTop    = top  - parseFloat(element.style.top || 0);\n    element._originalWidth  = element.style.width;\n    element._originalHeight = element.style.height;\n\n    element.style.position = 'absolute';\n    element.style.top    = top + 'px';\n    element.style.left   = left + 'px';\n    element.style.width  = width + 'px';\n    element.style.height = height + 'px';\n    return element;\n  },\n\n  relativize: function(element) {\n    element = $(element);\n    if (Element.getStyle(element, 'position') == 'relative') return element;\n\n    element.style.position = 'relative';\n    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0),\n        left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);\n\n    element.style.top    = top + 'px';\n    element.style.left   = left + 'px';\n    element.style.height = element._originalHeight;\n    element.style.width  = element._originalWidth;\n    return element;\n  },\n\n  cumulativeScrollOffset: function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.scrollTop  || 0;\n      valueL += element.scrollLeft || 0;\n      element = element.parentNode;\n    } while (element);\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  getOffsetParent: function(element) {\n    if (element.offsetParent) return $(element.offsetParent);\n    if (element == document.body) return $(element);\n\n    while ((element = element.parentNode) && element != document.body)\n      if (Element.getStyle(element, 'position') != 'static')\n        return $(element);\n\n    return $(document.body);\n  },\n\n  viewportOffset: function(forElement) {\n    var valueT = 0,\n        valueL = 0,\n        element = forElement;\n\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n\n      if (element.offsetParent == document.body &&\n        Element.getStyle(element, 'position') == 'absolute') break;\n\n    } while (element = element.offsetParent);\n\n    element = forElement;\n    do {\n      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {\n        valueT -= element.scrollTop  || 0;\n        valueL -= element.scrollLeft || 0;\n      }\n    } while (element = element.parentNode);\n\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  clonePosition: function(element, source) {\n    var options = Object.extend({\n      setLeft:    true,\n      setTop:     true,\n      setWidth:   true,\n      setHeight:  true,\n      offsetTop:  0,\n      offsetLeft: 0\n    }, arguments[2] || { });\n\n    source = $(source);\n    var p = Element.viewportOffset(source), delta = [0, 0], parent = null;\n\n    element = $(element);\n\n    if (Element.getStyle(element, 'position') == 'absolute') {\n      parent = Element.getOffsetParent(element);\n      delta = Element.viewportOffset(parent);\n    }\n\n    if (parent == document.body) {\n      delta[0] -= document.body.offsetLeft;\n      delta[1] -= document.body.offsetTop;\n    }\n\n    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';\n    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';\n    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';\n    if (options.setHeight) element.style.height = source.offsetHeight + 'px';\n    return element;\n  }\n};\n\nObject.extend(Element.Methods, {\n  getElementsBySelector: Element.Methods.select,\n\n  childElements: Element.Methods.immediateDescendants\n});\n\nElement._attributeTranslations = {\n  write: {\n    names: {\n      className: 'class',\n      htmlFor:   'for'\n    },\n    values: { }\n  }\n};\n\nif (Prototype.Browser.Opera) {\n  Element.Methods.getStyle = Element.Methods.getStyle.wrap(\n    function(proceed, element, style) {\n      switch (style) {\n        case 'left': case 'top': case 'right': case 'bottom':\n          if (proceed(element, 'position') === 'static') return null;\n        case 'height': case 'width':\n          if (!Element.visible(element)) return null;\n\n          var dim = parseInt(proceed(element, style), 10);\n\n          if (dim !== element['offset' + style.capitalize()])\n            return dim + 'px';\n\n          var properties;\n          if (style === 'height') {\n            properties = ['border-top-width', 'padding-top',\n             'padding-bottom', 'border-bottom-width'];\n          }\n          else {\n            properties = ['border-left-width', 'padding-left',\n             'padding-right', 'border-right-width'];\n          }\n          return properties.inject(dim, function(memo, property) {\n            var val = proceed(element, property);\n            return val === null ? memo : memo - parseInt(val, 10);\n          }) + 'px';\n        default: return proceed(element, style);\n      }\n    }\n  );\n\n  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(\n    function(proceed, element, attribute) {\n      if (attribute === 'title') return element.title;\n      return proceed(element, attribute);\n    }\n  );\n}\n\nelse if (Prototype.Browser.IE) {\n  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(\n    function(proceed, element) {\n      element = $(element);\n      if (!element.parentNode) return $(document.body);\n      var position = element.getStyle('position');\n      if (position !== 'static') return proceed(element);\n      element.setStyle({ position: 'relative' });\n      var value = proceed(element);\n      element.setStyle({ position: position });\n      return value;\n    }\n  );\n\n  $w('positionedOffset viewportOffset').each(function(method) {\n    Element.Methods[method] = Element.Methods[method].wrap(\n      function(proceed, element) {\n        element = $(element);\n        if (!element.parentNode) return Element._returnOffset(0, 0);\n        var position = element.getStyle('position');\n        if (position !== 'static') return proceed(element);\n        var offsetParent = element.getOffsetParent();\n        if (offsetParent && offsetParent.getStyle('position') === 'fixed')\n          offsetParent.setStyle({ zoom: 1 });\n        element.setStyle({ position: 'relative' });\n        var value = proceed(element);\n        element.setStyle({ position: position });\n        return value;\n      }\n    );\n  });\n\n  Element.Methods.getStyle = function(element, style) {\n    element = $(element);\n    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();\n    var value = element.style[style];\n    if (!value && element.currentStyle) value = element.currentStyle[style];\n\n    if (style == 'opacity') {\n      if (value = (element.getStyle('filter') || '').match(/alpha\\(opacity=(.*)\\)/))\n        if (value[1]) return parseFloat(value[1]) / 100;\n      return 1.0;\n    }\n\n    if (value == 'auto') {\n      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))\n        return element['offset' + style.capitalize()] + 'px';\n      return null;\n    }\n    return value;\n  };\n\n  Element.Methods.setOpacity = function(element, value) {\n    function stripAlpha(filter){\n      return filter.replace(/alpha\\([^\\)]*\\)/gi,'');\n    }\n    element = $(element);\n    var currentStyle = element.currentStyle;\n    if ((currentStyle && !currentStyle.hasLayout) ||\n      (!currentStyle && element.style.zoom == 'normal'))\n        element.style.zoom = 1;\n\n    var filter = element.getStyle('filter'), style = element.style;\n    if (value == 1 || value === '') {\n      (filter = stripAlpha(filter)) ?\n        style.filter = filter : style.removeAttribute('filter');\n      return element;\n    } else if (value < 0.00001) value = 0;\n    style.filter = stripAlpha(filter) +\n      'alpha(opacity=' + (value * 100) + ')';\n    return element;\n  };\n\n  Element._attributeTranslations = (function(){\n\n    var classProp = 'className',\n        forProp = 'for',\n        el = document.createElement('div');\n\n    el.setAttribute(classProp, 'x');\n\n    if (el.className !== 'x') {\n      el.setAttribute('class', 'x');\n      if (el.className === 'x') {\n        classProp = 'class';\n      }\n    }\n    el = null;\n\n    el = document.createElement('label');\n    el.setAttribute(forProp, 'x');\n    if (el.htmlFor !== 'x') {\n      el.setAttribute('htmlFor', 'x');\n      if (el.htmlFor === 'x') {\n        forProp = 'htmlFor';\n      }\n    }\n    el = null;\n\n    return {\n      read: {\n        names: {\n          'class':      classProp,\n          'className':  classProp,\n          'for':        forProp,\n          'htmlFor':    forProp\n        },\n        values: {\n          _getAttr: function(element, attribute) {\n            return element.getAttribute(attribute);\n          },\n          _getAttr2: function(element, attribute) {\n            return element.getAttribute(attribute, 2);\n          },\n          _getAttrNode: function(element, attribute) {\n            var node = element.getAttributeNode(attribute);\n            return node ? node.value : \"\";\n          },\n          _getEv: (function(){\n\n            var el = document.createElement('div'), f;\n            el.onclick = Prototype.emptyFunction;\n            var value = el.getAttribute('onclick');\n\n            if (String(value).indexOf('{') > -1) {\n              f = function(element, attribute) {\n                attribute = element.getAttribute(attribute);\n                if (!attribute) return null;\n                attribute = attribute.toString();\n                attribute = attribute.split('{')[1];\n                attribute = attribute.split('}')[0];\n                return attribute.strip();\n              };\n            }\n            else if (value === '') {\n              f = function(element, attribute) {\n                attribute = element.getAttribute(attribute);\n                if (!attribute) return null;\n                return attribute.strip();\n              };\n            }\n            el = null;\n            return f;\n          })(),\n          _flag: function(element, attribute) {\n            return $(element).hasAttribute(attribute) ? attribute : null;\n          },\n          style: function(element) {\n            return element.style.cssText.toLowerCase();\n          },\n          title: function(element) {\n            return element.title;\n          }\n        }\n      }\n    }\n  })();\n\n  Element._attributeTranslations.write = {\n    names: Object.extend({\n      cellpadding: 'cellPadding',\n      cellspacing: 'cellSpacing'\n    }, Element._attributeTranslations.read.names),\n    values: {\n      checked: function(element, value) {\n        element.checked = !!value;\n      },\n\n      style: function(element, value) {\n        element.style.cssText = value ? value : '';\n      }\n    }\n  };\n\n  Element._attributeTranslations.has = {};\n\n  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +\n      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {\n    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;\n    Element._attributeTranslations.has[attr.toLowerCase()] = attr;\n  });\n\n  (function(v) {\n    Object.extend(v, {\n      href:        v._getAttr2,\n      src:         v._getAttr2,\n      type:        v._getAttr,\n      action:      v._getAttrNode,\n      disabled:    v._flag,\n      checked:     v._flag,\n      readonly:    v._flag,\n      multiple:    v._flag,\n      onload:      v._getEv,\n      onunload:    v._getEv,\n      onclick:     v._getEv,\n      ondblclick:  v._getEv,\n      onmousedown: v._getEv,\n      onmouseup:   v._getEv,\n      onmouseover: v._getEv,\n      onmousemove: v._getEv,\n      onmouseout:  v._getEv,\n      onfocus:     v._getEv,\n      onblur:      v._getEv,\n      onkeypress:  v._getEv,\n      onkeydown:   v._getEv,\n      onkeyup:     v._getEv,\n      onsubmit:    v._getEv,\n      onreset:     v._getEv,\n      onselect:    v._getEv,\n      onchange:    v._getEv\n    });\n  })(Element._attributeTranslations.read.values);\n\n  if (Prototype.BrowserFeatures.ElementExtensions) {\n    (function() {\n      function _descendants(element) {\n        var nodes = element.getElementsByTagName('*'), results = [];\n        for (var i = 0, node; node = nodes[i]; i++)\n          if (node.tagName !== \"!\") // Filter out comment nodes.\n            results.push(node);\n        return results;\n      }\n\n      Element.Methods.down = function(element, expression, index) {\n        element = $(element);\n        if (arguments.length == 1) return element.firstDescendant();\n        return Object.isNumber(expression) ? _descendants(element)[expression] :\n          Element.select(element, expression)[index || 0];\n      }\n    })();\n  }\n\n}\n\nelse if (Prototype.Browser.Gecko && /rv:1\\.8\\.0/.test(navigator.userAgent)) {\n  Element.Methods.setOpacity = function(element, value) {\n    element = $(element);\n    element.style.opacity = (value == 1) ? 0.999999 :\n      (value === '') ? '' : (value < 0.00001) ? 0 : value;\n    return element;\n  };\n}\n\nelse if (Prototype.Browser.WebKit) {\n  Element.Methods.setOpacity = function(element, value) {\n    element = $(element);\n    element.style.opacity = (value == 1 || value === '') ? '' :\n      (value < 0.00001) ? 0 : value;\n\n    if (value == 1)\n      if (element.tagName.toUpperCase() == 'IMG' && element.width) {\n        element.width++; element.width--;\n      } else try {\n        var n = document.createTextNode(' ');\n        element.appendChild(n);\n        element.removeChild(n);\n      } catch (e) { }\n\n    return element;\n  };\n\n  Element.Methods.cumulativeOffset = function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      if (element.offsetParent == document.body)\n        if (Element.getStyle(element, 'position') == 'absolute') break;\n\n      element = element.offsetParent;\n    } while (element);\n\n    return Element._returnOffset(valueL, valueT);\n  };\n}\n\nif ('outerHTML' in document.documentElement) {\n  Element.Methods.replace = function(element, content) {\n    element = $(element);\n\n    if (content && content.toElement) content = content.toElement();\n    if (Object.isElement(content)) {\n      element.parentNode.replaceChild(content, element);\n      return element;\n    }\n\n    content = Object.toHTML(content);\n    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();\n\n    if (Element._insertionTranslations.tags[tagName]) {\n      var nextSibling = element.next(),\n          fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());\n      parent.removeChild(element);\n      if (nextSibling)\n        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });\n      else\n        fragments.each(function(node) { parent.appendChild(node) });\n    }\n    else element.outerHTML = content.stripScripts();\n\n    content.evalScripts.bind(content).defer();\n    return element;\n  };\n}\n\nElement._returnOffset = function(l, t) {\n  var result = [l, t];\n  result.left = l;\n  result.top = t;\n  return result;\n};\n\nElement._getContentFromAnonymousElement = function(tagName, html) {\n  var div = new Element('div'),\n      t = Element._insertionTranslations.tags[tagName];\n  if (t) {\n    div.innerHTML = t[0] + html + t[1];\n    for (var i = t[2]; i--; ) {\n      div = div.firstChild;\n    }\n  }\n  else {\n    div.innerHTML = html;\n  }\n  return $A(div.childNodes);\n};\n\nElement._insertionTranslations = {\n  before: function(element, node) {\n    element.parentNode.insertBefore(node, element);\n  },\n  top: function(element, node) {\n    element.insertBefore(node, element.firstChild);\n  },\n  bottom: function(element, node) {\n    element.appendChild(node);\n  },\n  after: function(element, node) {\n    element.parentNode.insertBefore(node, element.nextSibling);\n  },\n  tags: {\n    TABLE:  ['<table>',                '</table>',                   1],\n    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],\n    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],\n    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],\n    SELECT: ['<select>',               '</select>',                  1]\n  }\n};\n\n(function() {\n  var tags = Element._insertionTranslations.tags;\n  Object.extend(tags, {\n    THEAD: tags.TBODY,\n    TFOOT: tags.TBODY,\n    TH:    tags.TD\n  });\n})();\n\nElement.Methods.Simulated = {\n  hasAttribute: function(element, attribute) {\n    attribute = Element._attributeTranslations.has[attribute] || attribute;\n    var node = $(element).getAttributeNode(attribute);\n    return !!(node && node.specified);\n  }\n};\n\nElement.Methods.ByTag = { };\n\nObject.extend(Element, Element.Methods);\n\n(function(div) {\n\n  if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {\n    window.HTMLElement = { };\n    window.HTMLElement.prototype = div['__proto__'];\n    Prototype.BrowserFeatures.ElementExtensions = true;\n  }\n\n  div = null;\n\n})(document.createElement('div'));\n\nElement.extend = (function() {\n\n  function checkDeficiency(tagName) {\n    if (typeof window.Element != 'undefined') {\n      var proto = window.Element.prototype;\n      if (proto) {\n        var id = '_' + (Math.random()+'').slice(2),\n            el = document.createElement(tagName);\n        proto[id] = 'x';\n        var isBuggy = (el[id] !== 'x');\n        delete proto[id];\n        el = null;\n        return isBuggy;\n      }\n    }\n    return false;\n  }\n\n  function extendElementWith(element, methods) {\n    for (var property in methods) {\n      var value = methods[property];\n      if (Object.isFunction(value) && !(property in element))\n        element[property] = value.methodize();\n    }\n  }\n\n  var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');\n\n  if (Prototype.BrowserFeatures.SpecificElementExtensions) {\n    if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {\n      return function(element) {\n        if (element && typeof element._extendedByPrototype == 'undefined') {\n          var t = element.tagName;\n          if (t && (/^(?:object|applet|embed)$/i.test(t))) {\n            extendElementWith(element, Element.Methods);\n            extendElementWith(element, Element.Methods.Simulated);\n            extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);\n          }\n        }\n        return element;\n      }\n    }\n    return Prototype.K;\n  }\n\n  var Methods = { }, ByTag = Element.Methods.ByTag;\n\n  var extend = Object.extend(function(element) {\n    if (!element || typeof element._extendedByPrototype != 'undefined' ||\n        element.nodeType != 1 || element == window) return element;\n\n    var methods = Object.clone(Methods),\n        tagName = element.tagName.toUpperCase();\n\n    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);\n\n    extendElementWith(element, methods);\n\n    element._extendedByPrototype = Prototype.emptyFunction;\n    return element;\n\n  }, {\n    refresh: function() {\n      if (!Prototype.BrowserFeatures.ElementExtensions) {\n        Object.extend(Methods, Element.Methods);\n        Object.extend(Methods, Element.Methods.Simulated);\n      }\n    }\n  });\n\n  extend.refresh();\n  return extend;\n})();\n\nif (document.documentElement.hasAttribute) {\n  Element.hasAttribute = function(element, attribute) {\n    return element.hasAttribute(attribute);\n  };\n}\nelse {\n  Element.hasAttribute = Element.Methods.Simulated.hasAttribute;\n}\n\nElement.addMethods = function(methods) {\n  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;\n\n  if (!methods) {\n    Object.extend(Form, Form.Methods);\n    Object.extend(Form.Element, Form.Element.Methods);\n    Object.extend(Element.Methods.ByTag, {\n      \"FORM\":     Object.clone(Form.Methods),\n      \"INPUT\":    Object.clone(Form.Element.Methods),\n      \"SELECT\":   Object.clone(Form.Element.Methods),\n      \"TEXTAREA\": Object.clone(Form.Element.Methods)\n    });\n  }\n\n  if (arguments.length == 2) {\n    var tagName = methods;\n    methods = arguments[1];\n  }\n\n  if (!tagName) Object.extend(Element.Methods, methods || { });\n  else {\n    if (Object.isArray(tagName)) tagName.each(extend);\n    else extend(tagName);\n  }\n\n  function extend(tagName) {\n    tagName = tagName.toUpperCase();\n    if (!Element.Methods.ByTag[tagName])\n      Element.Methods.ByTag[tagName] = { };\n    Object.extend(Element.Methods.ByTag[tagName], methods);\n  }\n\n  function copy(methods, destination, onlyIfAbsent) {\n    onlyIfAbsent = onlyIfAbsent || false;\n    for (var property in methods) {\n      var value = methods[property];\n      if (!Object.isFunction(value)) continue;\n      if (!onlyIfAbsent || !(property in destination))\n        destination[property] = value.methodize();\n    }\n  }\n\n  function findDOMClass(tagName) {\n    var klass;\n    var trans = {\n      \"OPTGROUP\": \"OptGroup\", \"TEXTAREA\": \"TextArea\", \"P\": \"Paragraph\",\n      \"FIELDSET\": \"FieldSet\", \"UL\": \"UList\", \"OL\": \"OList\", \"DL\": \"DList\",\n      \"DIR\": \"Directory\", \"H1\": \"Heading\", \"H2\": \"Heading\", \"H3\": \"Heading\",\n      \"H4\": \"Heading\", \"H5\": \"Heading\", \"H6\": \"Heading\", \"Q\": \"Quote\",\n      \"INS\": \"Mod\", \"DEL\": \"Mod\", \"A\": \"Anchor\", \"IMG\": \"Image\", \"CAPTION\":\n      \"TableCaption\", \"COL\": \"TableCol\", \"COLGROUP\": \"TableCol\", \"THEAD\":\n      \"TableSection\", \"TFOOT\": \"TableSection\", \"TBODY\": \"TableSection\", \"TR\":\n      \"TableRow\", \"TH\": \"TableCell\", \"TD\": \"TableCell\", \"FRAMESET\":\n      \"FrameSet\", \"IFRAME\": \"IFrame\"\n    };\n    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';\n    if (window[klass]) return window[klass];\n    klass = 'HTML' + tagName + 'Element';\n    if (window[klass]) return window[klass];\n    klass = 'HTML' + tagName.capitalize() + 'Element';\n    if (window[klass]) return window[klass];\n\n    var element = document.createElement(tagName),\n        proto = element['__proto__'] || element.constructor.prototype;\n\n    element = null;\n    return proto;\n  }\n\n  var elementPrototype = window.HTMLElement ? HTMLElement.prototype :\n   Element.prototype;\n\n  if (F.ElementExtensions) {\n    copy(Element.Methods, elementPrototype);\n    copy(Element.Methods.Simulated, elementPrototype, true);\n  }\n\n  if (F.SpecificElementExtensions) {\n    for (var tag in Element.Methods.ByTag) {\n      var klass = findDOMClass(tag);\n      if (Object.isUndefined(klass)) continue;\n      copy(T[tag], klass.prototype);\n    }\n  }\n\n  Object.extend(Element, Element.Methods);\n  delete Element.ByTag;\n\n  if (Element.extend.refresh) Element.extend.refresh();\n  Element.cache = { };\n};\n\n\ndocument.viewport = {\n\n  getDimensions: function() {\n    return { width: this.getWidth(), height: this.getHeight() };\n  },\n\n  getScrollOffsets: function() {\n    return Element._returnOffset(\n      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,\n      window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);\n  }\n};\n\n(function(viewport) {\n  var B = Prototype.Browser, doc = document, element, property = {};\n\n  function getRootElement() {\n    if (B.WebKit && !doc.evaluate)\n      return document;\n\n    if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)\n      return document.body;\n\n    return document.documentElement;\n  }\n\n  function define(D) {\n    if (!element) element = getRootElement();\n\n    property[D] = 'client' + D;\n\n    viewport['get' + D] = function() { return element[property[D]] };\n    return viewport['get' + D]();\n  }\n\n  viewport.getWidth  = define.curry('Width');\n\n  viewport.getHeight = define.curry('Height');\n})(document.viewport);\n\n\nElement.Storage = {\n  UID: 1\n};\n\nElement.addMethods({\n  getStorage: function(element) {\n    if (!(element = $(element))) return;\n\n    var uid;\n    if (element === window) {\n      uid = 0;\n    } else {\n      if (typeof element._prototypeUID === \"undefined\")\n        element._prototypeUID = Element.Storage.UID++;\n      uid = element._prototypeUID;\n    }\n\n    if (!Element.Storage[uid])\n      Element.Storage[uid] = $H();\n\n    return Element.Storage[uid];\n  },\n\n  store: function(element, key, value) {\n    if (!(element = $(element))) return;\n\n    if (arguments.length === 2) {\n      Element.getStorage(element).update(key);\n    } else {\n      Element.getStorage(element).set(key, value);\n    }\n\n    return element;\n  },\n\n  retrieve: function(element, key, defaultValue) {\n    if (!(element = $(element))) return;\n    var hash = Element.getStorage(element), value = hash.get(key);\n\n    if (Object.isUndefined(value)) {\n      hash.set(key, defaultValue);\n      value = defaultValue;\n    }\n\n    return value;\n  },\n\n  clone: function(element, deep) {\n    if (!(element = $(element))) return;\n    var clone = element.cloneNode(deep);\n    clone._prototypeUID = void 0;\n    if (deep) {\n      var descendants = Element.select(clone, '*'),\n          i = descendants.length;\n      while (i--) {\n        descendants[i]._prototypeUID = void 0;\n      }\n    }\n    return Element.extend(clone);\n  },\n\n  purge: function(element) {\n    if (!(element = $(element))) return;\n    purgeElement(element);\n\n    var descendants = element.getElementsByTagName('*'),\n     i = descendants.length;\n\n    while (i--) purgeElement(descendants[i]);\n\n    return null;\n  }\n});\n\n(function() {\n\n  function toDecimal(pctString) {\n    var match = pctString.match(/^(\\d+)%?$/i);\n    if (!match) return null;\n    return (Number(match[1]) / 100);\n  }\n\n  function getPixelValue(value, property) {\n    if (Object.isElement(value)) {\n      element = value;\n      value = element.getStyle(property);\n    }\n    if (value === null) {\n      return null;\n    }\n\n    if ((/^(?:-)?\\d+(\\.\\d+)?(px)?$/i).test(value)) {\n      return window.parseFloat(value);\n    }\n\n    if (/\\d/.test(value) && element.runtimeStyle) {\n      var style = element.style.left, rStyle = element.runtimeStyle.left;\n      element.runtimeStyle.left = element.currentStyle.left;\n      element.style.left = value || 0;\n      value = element.style.pixelLeft;\n      element.style.left = style;\n      element.runtimeStyle.left = rStyle;\n\n      return value;\n    }\n\n    if (value.include('%')) {\n      var decimal = toDecimal(value);\n      var whole;\n      if (property.include('left') || property.include('right') ||\n       property.include('width')) {\n        whole = $(element.parentNode).measure('width');\n      } else if (property.include('top') || property.include('bottom') ||\n       property.include('height')) {\n        whole = $(element.parentNode).measure('height');\n      }\n\n      return whole * decimal;\n    }\n\n    return 0;\n  }\n\n  function toCSSPixels(number) {\n    if (Object.isString(number) && number.endsWith('px')) {\n      return number;\n    }\n    return number + 'px';\n  }\n\n  function isDisplayed(element) {\n    var originalElement = element;\n    while (element && element.parentNode) {\n      var display = element.getStyle('display');\n      if (display === 'none') {\n        return false;\n      }\n      element = $(element.parentNode);\n    }\n    return true;\n  }\n\n  var hasLayout = Prototype.K;\n  if ('currentStyle' in document.documentElement) {\n    hasLayout = function(element) {\n      if (!element.currentStyle.hasLayout) {\n        element.style.zoom = 1;\n      }\n      return element;\n    };\n  }\n\n  function cssNameFor(key) {\n    if (key.include('border')) key = key + '-width';\n    return key.camelize();\n  }\n\n  Element.Layout = Class.create(Hash, {\n    initialize: function($super, element, preCompute) {\n      $super();\n      this.element = $(element);\n\n      Element.Layout.PROPERTIES.each( function(property) {\n        this._set(property, null);\n      }, this);\n\n      if (preCompute) {\n        this._preComputing = true;\n        this._begin();\n        Element.Layout.PROPERTIES.each( this._compute, this );\n        this._end();\n        this._preComputing = false;\n      }\n    },\n\n    _set: function(property, value) {\n      return Hash.prototype.set.call(this, property, value);\n    },\n\n    set: function(property, value) {\n      throw \"Properties of Element.Layout are read-only.\";\n    },\n\n    get: function($super, property) {\n      var value = $super(property);\n      return value === null ? this._compute(property) : value;\n    },\n\n    _begin: function() {\n      if (this._prepared) return;\n\n      var element = this.element;\n      if (isDisplayed(element)) {\n        this._prepared = true;\n        return;\n      }\n\n      var originalStyles = {\n        position:   element.style.position   || '',\n        width:      element.style.width      || '',\n        visibility: element.style.visibility || '',\n        display:    element.style.display    || ''\n      };\n\n      element.store('prototype_original_styles', originalStyles);\n\n      var position = element.getStyle('position'),\n       width = element.getStyle('width');\n\n      element.setStyle({\n        position:   'absolute',\n        visibility: 'hidden',\n        display:    'block'\n      });\n\n      var positionedWidth = element.getStyle('width');\n\n      var newWidth;\n      if (width && (positionedWidth === width)) {\n        newWidth = getPixelValue(width);\n      } else if (width && (position === 'absolute' || position === 'fixed')) {\n        newWidth = getPixelValue(width);\n      } else {\n        var parent = element.parentNode, pLayout = $(parent).getLayout();\n\n        newWidth = pLayout.get('width') -\n         this.get('margin-left') -\n         this.get('border-left') -\n         this.get('padding-left') -\n         this.get('padding-right') -\n         this.get('border-right') -\n         this.get('margin-right');\n      }\n\n      element.setStyle({ width: newWidth + 'px' });\n\n      this._prepared = true;\n    },\n\n    _end: function() {\n      var element = this.element;\n      var originalStyles = element.retrieve('prototype_original_styles');\n      element.store('prototype_original_styles', null);\n      element.setStyle(originalStyles);\n      this._prepared = false;\n    },\n\n    _compute: function(property) {\n      var COMPUTATIONS = Element.Layout.COMPUTATIONS;\n      if (!(property in COMPUTATIONS)) {\n        throw \"Property not found.\";\n      }\n      return this._set(property, COMPUTATIONS[property].call(this, this.element));\n    },\n\n    toObject: function() {\n      var args = $A(arguments);\n      var keys = (args.length === 0) ? Element.Layout.PROPERTIES :\n       args.join(' ').split(' ');\n      var obj = {};\n      keys.each( function(key) {\n        if (!Element.Layout.PROPERTIES.include(key)) return;\n        var value = this.get(key);\n        if (value != null) obj[key] = value;\n      }, this);\n      return obj;\n    },\n\n    toHash: function() {\n      var obj = this.toObject.apply(this, arguments);\n      return new Hash(obj);\n    },\n\n    toCSS: function() {\n      var args = $A(arguments);\n      var keys = (args.length === 0) ? Element.Layout.PROPERTIES :\n       args.join(' ').split(' ');\n      var css = {};\n\n      keys.each( function(key) {\n        if (!Element.Layout.PROPERTIES.include(key)) return;\n        if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;\n\n        var value = this.get(key);\n        if (value != null) css[cssNameFor(key)] = value + 'px';\n      }, this);\n      return css;\n    },\n\n    inspect: function() {\n      return \"#<Element.Layout>\";\n    }\n  });\n\n  Object.extend(Element.Layout, {\n    PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),\n\n    COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),\n\n    COMPUTATIONS: {\n      'height': function(element) {\n        if (!this._preComputing) this._begin();\n\n        var bHeight = this.get('border-box-height');\n        if (bHeight <= 0) return 0;\n\n        var bTop = this.get('border-top'),\n         bBottom = this.get('border-bottom');\n\n        var pTop = this.get('padding-top'),\n         pBottom = this.get('padding-bottom');\n\n        if (!this._preComputing) this._end();\n\n        return bHeight - bTop - bBottom - pTop - pBottom;\n      },\n\n      'width': function(element) {\n        if (!this._preComputing) this._begin();\n\n        var bWidth = this.get('border-box-width');\n        if (bWidth <= 0) return 0;\n\n        var bLeft = this.get('border-left'),\n         bRight = this.get('border-right');\n\n        var pLeft = this.get('padding-left'),\n         pRight = this.get('padding-right');\n\n        if (!this._preComputing) this._end();\n\n        return bWidth - bLeft - bRight - pLeft - pRight;\n      },\n\n      'padding-box-height': function(element) {\n        var height = this.get('height'),\n         pTop = this.get('padding-top'),\n         pBottom = this.get('padding-bottom');\n\n        return height + pTop + pBottom;\n      },\n\n      'padding-box-width': function(element) {\n        var width = this.get('width'),\n         pLeft = this.get('padding-left'),\n         pRight = this.get('padding-right');\n\n        return width + pLeft + pRight;\n      },\n\n      'border-box-height': function(element) {\n        return element.offsetHeight;\n      },\n\n      'border-box-width': function(element) {\n        return element.offsetWidth;\n      },\n\n      'margin-box-height': function(element) {\n        var bHeight = this.get('border-box-height'),\n         mTop = this.get('margin-top'),\n         mBottom = this.get('margin-bottom');\n\n        if (bHeight <= 0) return 0;\n\n        return bHeight + mTop + mBottom;\n      },\n\n      'margin-box-width': function(element) {\n        var bWidth = this.get('border-box-width'),\n         mLeft = this.get('margin-left'),\n         mRight = this.get('margin-right');\n\n        if (bWidth <= 0) return 0;\n\n        return bWidth + mLeft + mRight;\n      },\n\n      'top': function(element) {\n        var offset = element.positionedOffset();\n        return offset.top;\n      },\n\n      'bottom': function(element) {\n        var offset = element.positionedOffset(),\n         parent = element.getOffsetParent(),\n         pHeight = parent.measure('height');\n\n        var mHeight = this.get('border-box-height');\n\n        return pHeight - mHeight - offset.top;\n      },\n\n      'left': function(element) {\n        var offset = element.positionedOffset();\n        return offset.left;\n      },\n\n      'right': function(element) {\n        var offset = element.positionedOffset(),\n         parent = element.getOffsetParent(),\n         pWidth = parent.measure('width');\n\n        var mWidth = this.get('border-box-width');\n\n        return pWidth - mWidth - offset.left;\n      },\n\n      'padding-top': function(element) {\n        return getPixelValue(element, 'paddingTop');\n      },\n\n      'padding-bottom': function(element) {\n        return getPixelValue(element, 'paddingBottom');\n      },\n\n      'padding-left': function(element) {\n        return getPixelValue(element, 'paddingLeft');\n      },\n\n      'padding-right': function(element) {\n        return getPixelValue(element, 'paddingRight');\n      },\n\n      'border-top': function(element) {\n        return Object.isNumber(element.clientTop) ? element.clientTop :\n         getPixelValue(element, 'borderTopWidth');\n      },\n\n      'border-bottom': function(element) {\n        return Object.isNumber(element.clientBottom) ? element.clientBottom :\n         getPixelValue(element, 'borderBottomWidth');\n      },\n\n      'border-left': function(element) {\n        return Object.isNumber(element.clientLeft) ? element.clientLeft :\n         getPixelValue(element, 'borderLeftWidth');\n      },\n\n      'border-right': function(element) {\n        return Object.isNumber(element.clientRight) ? element.clientRight :\n         getPixelValue(element, 'borderRightWidth');\n      },\n\n      'margin-top': function(element) {\n        return getPixelValue(element, 'marginTop');\n      },\n\n      'margin-bottom': function(element) {\n        return getPixelValue(element, 'marginBottom');\n      },\n\n      'margin-left': function(element) {\n        return getPixelValue(element, 'marginLeft');\n      },\n\n      'margin-right': function(element) {\n        return getPixelValue(element, 'marginRight');\n      }\n    }\n  });\n\n  if ('getBoundingClientRect' in document.documentElement) {\n    Object.extend(Element.Layout.COMPUTATIONS, {\n      'right': function(element) {\n        var parent = hasLayout(element.getOffsetParent());\n        var rect = element.getBoundingClientRect(),\n         pRect = parent.getBoundingClientRect();\n\n        return (pRect.right - rect.right).round();\n      },\n\n      'bottom': function(element) {\n        var parent = hasLayout(element.getOffsetParent());\n        var rect = element.getBoundingClientRect(),\n         pRect = parent.getBoundingClientRect();\n\n        return (pRect.bottom - rect.bottom).round();\n      }\n    });\n  }\n\n  Element.Offset = Class.create({\n    initialize: function(left, top) {\n      this.left = left.round();\n      this.top  = top.round();\n\n      this[0] = this.left;\n      this[1] = this.top;\n    },\n\n    relativeTo: function(offset) {\n      return new Element.Offset(\n        this.left - offset.left,\n        this.top  - offset.top\n      );\n    },\n\n    inspect: function() {\n      return \"#<Element.Offset left: #{left} top: #{top}>\".interpolate(this);\n    },\n\n    toString: function() {\n      return \"[#{left}, #{top}]\".interpolate(this);\n    },\n\n    toArray: function() {\n      return [this.left, this.top];\n    }\n  });\n\n  function getLayout(element, preCompute) {\n    return new Element.Layout(element, preCompute);\n  }\n\n  function measure(element, property) {\n    return $(element).getLayout().get(property);\n  }\n\n  function getDimensions(element) {\n    var layout = $(element).getLayout();\n    return {\n      width:  layout.get('width'),\n      height: layout.get('height')\n    };\n  }\n\n  function getOffsetParent(element) {\n    if (isDetached(element)) return $(document.body);\n\n    var isInline = (Element.getStyle(element, 'display') === 'inline');\n    if (!isInline && element.offsetParent) return $(element.offsetParent);\n    if (element === document.body) return $(element);\n\n    while ((element = element.parentNode) && element !== document.body) {\n      if (Element.getStyle(element, 'position') !== 'static') {\n        return (element.nodeName === 'HTML') ? $(document.body) : $(element);\n      }\n    }\n\n    return $(document.body);\n  }\n\n\n  function cumulativeOffset(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      element = element.offsetParent;\n    } while (element);\n    return new Element.Offset(valueL, valueT);\n  }\n\n  function positionedOffset(element) {\n    var layout = element.getLayout();\n\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      element = element.offsetParent;\n      if (element) {\n        if (isBody(element)) break;\n        var p = Element.getStyle(element, 'position');\n        if (p !== 'static') break;\n      }\n    } while (element);\n\n    valueL -= layout.get('margin-top');\n    valueT -= layout.get('margin-left');\n\n    return new Element.Offset(valueL, valueT);\n  }\n\n  function cumulativeScrollOffset(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.scrollTop  || 0;\n      valueL += element.scrollLeft || 0;\n      element = element.parentNode;\n    } while (element);\n    return new Element.Offset(valueL, valueT);\n  }\n\n  function viewportOffset(forElement) {\n    var valueT = 0, valueL = 0, docBody = document.body;\n\n    var element = forElement;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      if (element.offsetParent == docBody &&\n        Element.getStyle(element, 'position') == 'absolute') break;\n    } while (element = element.offsetParent);\n\n    element = forElement;\n    do {\n      if (element != docBody) {\n        valueT -= element.scrollTop  || 0;\n        valueL -= element.scrollLeft || 0;\n      }\n    } while (element = element.parentNode);\n    return new Element.Offset(valueL, valueT);\n  }\n\n  function absolutize(element) {\n    element = $(element);\n\n    if (Element.getStyle(element, 'position') === 'absolute') {\n      return element;\n    }\n\n    var offsetParent = getOffsetParent(element);\n    var eOffset = element.viewportOffset(),\n     pOffset = offsetParent.viewportOffset();\n\n    var offset = eOffset.relativeTo(pOffset);\n    var layout = element.getLayout();\n\n    element.store('prototype_absolutize_original_styles', {\n      left:   element.getStyle('left'),\n      top:    element.getStyle('top'),\n      width:  element.getStyle('width'),\n      height: element.getStyle('height')\n    });\n\n    element.setStyle({\n      position: 'absolute',\n      top:    offset.top + 'px',\n      left:   offset.left + 'px',\n      width:  layout.get('width') + 'px',\n      height: layout.get('height') + 'px'\n    });\n\n    return element;\n  }\n\n  function relativize(element) {\n    element = $(element);\n    if (Element.getStyle(element, 'position') === 'relative') {\n      return element;\n    }\n\n    var originalStyles =\n     element.retrieve('prototype_absolutize_original_styles');\n\n    if (originalStyles) element.setStyle(originalStyles);\n    return element;\n  }\n\n  Element.addMethods({\n    getLayout:              getLayout,\n    measure:                measure,\n    getDimensions:          getDimensions,\n    getOffsetParent:        getOffsetParent,\n    cumulativeOffset:       cumulativeOffset,\n    positionedOffset:       positionedOffset,\n    cumulativeScrollOffset: cumulativeScrollOffset,\n    viewportOffset:         viewportOffset,\n    absolutize:             absolutize,\n    relativize:             relativize\n  });\n\n  function isBody(element) {\n    return element.nodeName.toUpperCase() === 'BODY';\n  }\n\n  function isDetached(element) {\n    return element !== document.body &&\n     !Element.descendantOf(element, document.body);\n  }\n\n  if ('getBoundingClientRect' in document.documentElement) {\n    Element.addMethods({\n      viewportOffset: function(element) {\n        element = $(element);\n        if (isDetached(element)) return new Element.Offset(0, 0);\n\n        var rect  = element.getBoundingClientRect(),\n         docEl = document.documentElement;\n        return new Element.Offset(rect.left - docEl.clientLeft,\n         rect.top - docEl.clientTop);\n      },\n\n      positionedOffset: function(element) {\n        element = $(element);\n        var parent = element.getOffsetParent();\n        if (isDetached(element)) return new Element.Offset(0, 0);\n\n        if (element.offsetParent &&\n         element.offsetParent.nodeName.toUpperCase() === 'HTML') {\n          return positionedOffset(element);\n        }\n\n        var eOffset = element.viewportOffset(),\n         pOffset = isBody(parent) ? viewportOffset(parent) :\n          parent.viewportOffset();\n        var retOffset = eOffset.relativeTo(pOffset);\n\n        var layout = element.getLayout();\n        var top  = retOffset.top  - layout.get('margin-top');\n        var left = retOffset.left - layout.get('margin-left');\n\n        return new Element.Offset(left, top);\n      }\n    });\n  }\n})();\nwindow.$$ = function() {\n  var expression = $A(arguments).join(', ');\n  return Prototype.Selector.select(expression, document);\n};\n\nPrototype.Selector = (function() {\n\n  function select() {\n    throw new Error('Method \"Prototype.Selector.select\" must be defined.');\n  }\n\n  function match() {\n    throw new Error('Method \"Prototype.Selector.match\" must be defined.');\n  }\n\n  function find(elements, expression, index) {\n    index = index || 0;\n    var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;\n\n    for (i = 0; i < length; i++) {\n      if (match(elements[i], expression) && index == matchIndex++) {\n        return Element.extend(elements[i]);\n      }\n    }\n  }\n\n  function extendElements(elements) {\n    for (var i = 0, length = elements.length; i < length; i++) {\n      Element.extend(elements[i]);\n    }\n    return elements;\n  }\n\n\n  var K = Prototype.K;\n\n  return {\n    select: select,\n    match: match,\n    find: find,\n    extendElements: (Element.extend === K) ? K : extendElements,\n    extendElement: Element.extend\n  };\n})();\nPrototype._original_property = window.Sizzle;\n/*!\n * Sizzle CSS Selector Engine - v1.0\n *  Copyright 2009, The Dojo Foundation\n *  Released under the MIT, BSD, and GPL Licenses.\n *  More information: http://sizzlejs.com/\n */\n(function(){\n\nvar chunker = /((?:\\((?:\\([^()]+\\)|[^()]+)+\\)|\\[(?:\\[[^[\\]]*\\]|['\"][^'\"]*['\"]|[^[\\]'\"]+)+\\]|\\\\.|[^ >+~,(\\[\\\\]+)+|[>+~])(\\s*,\\s*)?((?:.|\\r|\\n)*)/g,\n\tdone = 0,\n\ttoString = Object.prototype.toString,\n\thasDuplicate = false,\n\tbaseHasDuplicate = true;\n\n[0, 0].sort(function(){\n\tbaseHasDuplicate = false;\n\treturn 0;\n});\n\nvar Sizzle = function(selector, context, results, seed) {\n\tresults = results || [];\n\tvar origContext = context = context || document;\n\n\tif ( context.nodeType !== 1 && context.nodeType !== 9 ) {\n\t\treturn [];\n\t}\n\n\tif ( !selector || typeof selector !== \"string\" ) {\n\t\treturn results;\n\t}\n\n\tvar parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context),\n\t\tsoFar = selector;\n\n\twhile ( (chunker.exec(\"\"), m = chunker.exec(soFar)) !== null ) {\n\t\tsoFar = m[3];\n\n\t\tparts.push( m[1] );\n\n\t\tif ( m[2] ) {\n\t\t\textra = m[3];\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif ( parts.length > 1 && origPOS.exec( selector ) ) {\n\t\tif ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\n\t\t\tset = posProcess( parts[0] + parts[1], context );\n\t\t} else {\n\t\t\tset = Expr.relative[ parts[0] ] ?\n\t\t\t\t[ context ] :\n\t\t\t\tSizzle( parts.shift(), context );\n\n\t\t\twhile ( parts.length ) {\n\t\t\t\tselector = parts.shift();\n\n\t\t\t\tif ( Expr.relative[ selector ] )\n\t\t\t\t\tselector += parts.shift();\n\n\t\t\t\tset = posProcess( selector, set );\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\n\t\t\t\tExpr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\n\t\t\tvar ret = Sizzle.find( parts.shift(), context, contextXML );\n\t\t\tcontext = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];\n\t\t}\n\n\t\tif ( context ) {\n\t\t\tvar ret = seed ?\n\t\t\t\t{ expr: parts.pop(), set: makeArray(seed) } :\n\t\t\t\tSizzle.find( parts.pop(), parts.length === 1 && (parts[0] === \"~\" || parts[0] === \"+\") && context.parentNode ? context.parentNode : context, contextXML );\n\t\t\tset = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;\n\n\t\t\tif ( parts.length > 0 ) {\n\t\t\t\tcheckSet = makeArray(set);\n\t\t\t} else {\n\t\t\t\tprune = false;\n\t\t\t}\n\n\t\t\twhile ( parts.length ) {\n\t\t\t\tvar cur = parts.pop(), pop = cur;\n\n\t\t\t\tif ( !Expr.relative[ cur ] ) {\n\t\t\t\t\tcur = \"\";\n\t\t\t\t} else {\n\t\t\t\t\tpop = parts.pop();\n\t\t\t\t}\n\n\t\t\t\tif ( pop == null ) {\n\t\t\t\t\tpop = context;\n\t\t\t\t}\n\n\t\t\t\tExpr.relative[ cur ]( checkSet, pop, contextXML );\n\t\t\t}\n\t\t} else {\n\t\t\tcheckSet = parts = [];\n\t\t}\n\t}\n\n\tif ( !checkSet ) {\n\t\tcheckSet = set;\n\t}\n\n\tif ( !checkSet ) {\n\t\tthrow \"Syntax error, unrecognized expression: \" + (cur || selector);\n\t}\n\n\tif ( toString.call(checkSet) === \"[object Array]\" ) {\n\t\tif ( !prune ) {\n\t\t\tresults.push.apply( results, checkSet );\n\t\t} else if ( context && context.nodeType === 1 ) {\n\t\t\tfor ( var i = 0; checkSet[i] != null; i++ ) {\n\t\t\t\tif ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {\n\t\t\t\t\tresults.push( set[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor ( var i = 0; checkSet[i] != null; i++ ) {\n\t\t\t\tif ( checkSet[i] && checkSet[i].nodeType === 1 ) {\n\t\t\t\t\tresults.push( set[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tmakeArray( checkSet, results );\n\t}\n\n\tif ( extra ) {\n\t\tSizzle( extra, origContext, results, seed );\n\t\tSizzle.uniqueSort( results );\n\t}\n\n\treturn results;\n};\n\nSizzle.uniqueSort = function(results){\n\tif ( sortOrder ) {\n\t\thasDuplicate = baseHasDuplicate;\n\t\tresults.sort(sortOrder);\n\n\t\tif ( hasDuplicate ) {\n\t\t\tfor ( var i = 1; i < results.length; i++ ) {\n\t\t\t\tif ( results[i] === results[i-1] ) {\n\t\t\t\t\tresults.splice(i--, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n};\n\nSizzle.matches = function(expr, set){\n\treturn Sizzle(expr, null, null, set);\n};\n\nSizzle.find = function(expr, context, isXML){\n\tvar set, match;\n\n\tif ( !expr ) {\n\t\treturn [];\n\t}\n\n\tfor ( var i = 0, l = Expr.order.length; i < l; i++ ) {\n\t\tvar type = Expr.order[i], match;\n\n\t\tif ( (match = Expr.leftMatch[ type ].exec( expr )) ) {\n\t\t\tvar left = match[1];\n\t\t\tmatch.splice(1,1);\n\n\t\t\tif ( left.substr( left.length - 1 ) !== \"\\\\\" ) {\n\t\t\t\tmatch[1] = (match[1] || \"\").replace(/\\\\/g, \"\");\n\t\t\t\tset = Expr.find[ type ]( match, context, isXML );\n\t\t\t\tif ( set != null ) {\n\t\t\t\t\texpr = expr.replace( Expr.match[ type ], \"\" );\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( !set ) {\n\t\tset = context.getElementsByTagName(\"*\");\n\t}\n\n\treturn {set: set, expr: expr};\n};\n\nSizzle.filter = function(expr, set, inplace, not){\n\tvar old = expr, result = [], curLoop = set, match, anyFound,\n\t\tisXMLFilter = set && set[0] && isXML(set[0]);\n\n\twhile ( expr && set.length ) {\n\t\tfor ( var type in Expr.filter ) {\n\t\t\tif ( (match = Expr.match[ type ].exec( expr )) != null ) {\n\t\t\t\tvar filter = Expr.filter[ type ], found, item;\n\t\t\t\tanyFound = false;\n\n\t\t\t\tif ( curLoop == result ) {\n\t\t\t\t\tresult = [];\n\t\t\t\t}\n\n\t\t\t\tif ( Expr.preFilter[ type ] ) {\n\t\t\t\t\tmatch = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\n\n\t\t\t\t\tif ( !match ) {\n\t\t\t\t\t\tanyFound = found = true;\n\t\t\t\t\t} else if ( match === true ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( match ) {\n\t\t\t\t\tfor ( var i = 0; (item = curLoop[i]) != null; i++ ) {\n\t\t\t\t\t\tif ( item ) {\n\t\t\t\t\t\t\tfound = filter( item, match, i, curLoop );\n\t\t\t\t\t\t\tvar pass = not ^ !!found;\n\n\t\t\t\t\t\t\tif ( inplace && found != null ) {\n\t\t\t\t\t\t\t\tif ( pass ) {\n\t\t\t\t\t\t\t\t\tanyFound = true;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcurLoop[i] = false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if ( pass ) {\n\t\t\t\t\t\t\t\tresult.push( item );\n\t\t\t\t\t\t\t\tanyFound = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( found !== undefined ) {\n\t\t\t\t\tif ( !inplace ) {\n\t\t\t\t\t\tcurLoop = result;\n\t\t\t\t\t}\n\n\t\t\t\t\texpr = expr.replace( Expr.match[ type ], \"\" );\n\n\t\t\t\t\tif ( !anyFound ) {\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( expr == old ) {\n\t\t\tif ( anyFound == null ) {\n\t\t\t\tthrow \"Syntax error, unrecognized expression: \" + expr;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\told = expr;\n\t}\n\n\treturn curLoop;\n};\n\nvar Expr = Sizzle.selectors = {\n\torder: [ \"ID\", \"NAME\", \"TAG\" ],\n\tmatch: {\n\t\tID: /#((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)/,\n\t\tCLASS: /\\.((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)/,\n\t\tNAME: /\\[name=['\"]*((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)['\"]*\\]/,\n\t\tATTR: /\\[\\s*((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)\\s*(?:(\\S?=)\\s*(['\"]*)(.*?)\\3|)\\s*\\]/,\n\t\tTAG: /^((?:[\\w\\u00c0-\\uFFFF\\*-]|\\\\.)+)/,\n\t\tCHILD: /:(only|nth|last|first)-child(?:\\((even|odd|[\\dn+-]*)\\))?/,\n\t\tPOS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\))?(?=[^-]|$)/,\n\t\tPSEUDO: /:((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)(?:\\((['\"]*)((?:\\([^\\)]+\\)|[^\\2\\(\\)]*)+)\\2\\))?/\n\t},\n\tleftMatch: {},\n\tattrMap: {\n\t\t\"class\": \"className\",\n\t\t\"for\": \"htmlFor\"\n\t},\n\tattrHandle: {\n\t\thref: function(elem){\n\t\t\treturn elem.getAttribute(\"href\");\n\t\t}\n\t},\n\trelative: {\n\t\t\"+\": function(checkSet, part, isXML){\n\t\t\tvar isPartStr = typeof part === \"string\",\n\t\t\t\tisTag = isPartStr && !/\\W/.test(part),\n\t\t\t\tisPartStrNotTag = isPartStr && !isTag;\n\n\t\t\tif ( isTag && !isXML ) {\n\t\t\t\tpart = part.toUpperCase();\n\t\t\t}\n\n\t\t\tfor ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\n\t\t\t\tif ( (elem = checkSet[i]) ) {\n\t\t\t\t\twhile ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\n\n\t\t\t\t\tcheckSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?\n\t\t\t\t\t\telem || false :\n\t\t\t\t\t\telem === part;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( isPartStrNotTag ) {\n\t\t\t\tSizzle.filter( part, checkSet, true );\n\t\t\t}\n\t\t},\n\t\t\">\": function(checkSet, part, isXML){\n\t\t\tvar isPartStr = typeof part === \"string\";\n\n\t\t\tif ( isPartStr && !/\\W/.test(part) ) {\n\t\t\t\tpart = isXML ? part : part.toUpperCase();\n\n\t\t\t\tfor ( var i = 0, l = checkSet.length; i < l; i++ ) {\n\t\t\t\t\tvar elem = checkSet[i];\n\t\t\t\t\tif ( elem ) {\n\t\t\t\t\t\tvar parent = elem.parentNode;\n\t\t\t\t\t\tcheckSet[i] = parent.nodeName === part ? parent : false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( var i = 0, l = checkSet.length; i < l; i++ ) {\n\t\t\t\t\tvar elem = checkSet[i];\n\t\t\t\t\tif ( elem ) {\n\t\t\t\t\t\tcheckSet[i] = isPartStr ?\n\t\t\t\t\t\t\telem.parentNode :\n\t\t\t\t\t\t\telem.parentNode === part;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( isPartStr ) {\n\t\t\t\t\tSizzle.filter( part, checkSet, true );\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"\": function(checkSet, part, isXML){\n\t\t\tvar doneName = done++, checkFn = dirCheck;\n\n\t\t\tif ( !/\\W/.test(part) ) {\n\t\t\t\tvar nodeCheck = part = isXML ? part : part.toUpperCase();\n\t\t\t\tcheckFn = dirNodeCheck;\n\t\t\t}\n\n\t\t\tcheckFn(\"parentNode\", part, doneName, checkSet, nodeCheck, isXML);\n\t\t},\n\t\t\"~\": function(checkSet, part, isXML){\n\t\t\tvar doneName = done++, checkFn = dirCheck;\n\n\t\t\tif ( typeof part === \"string\" && !/\\W/.test(part) ) {\n\t\t\t\tvar nodeCheck = part = isXML ? part : part.toUpperCase();\n\t\t\t\tcheckFn = dirNodeCheck;\n\t\t\t}\n\n\t\t\tcheckFn(\"previousSibling\", part, doneName, checkSet, nodeCheck, isXML);\n\t\t}\n\t},\n\tfind: {\n\t\tID: function(match, context, isXML){\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && !isXML ) {\n\t\t\t\tvar m = context.getElementById(match[1]);\n\t\t\t\treturn m ? [m] : [];\n\t\t\t}\n\t\t},\n\t\tNAME: function(match, context, isXML){\n\t\t\tif ( typeof context.getElementsByName !== \"undefined\" ) {\n\t\t\t\tvar ret = [], results = context.getElementsByName(match[1]);\n\n\t\t\t\tfor ( var i = 0, l = results.length; i < l; i++ ) {\n\t\t\t\t\tif ( results[i].getAttribute(\"name\") === match[1] ) {\n\t\t\t\t\t\tret.push( results[i] );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn ret.length === 0 ? null : ret;\n\t\t\t}\n\t\t},\n\t\tTAG: function(match, context){\n\t\t\treturn context.getElementsByTagName(match[1]);\n\t\t}\n\t},\n\tpreFilter: {\n\t\tCLASS: function(match, curLoop, inplace, result, not, isXML){\n\t\t\tmatch = \" \" + match[1].replace(/\\\\/g, \"\") + \" \";\n\n\t\t\tif ( isXML ) {\n\t\t\t\treturn match;\n\t\t\t}\n\n\t\t\tfor ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\n\t\t\t\tif ( elem ) {\n\t\t\t\t\tif ( not ^ (elem.className && (\" \" + elem.className + \" \").indexOf(match) >= 0) ) {\n\t\t\t\t\t\tif ( !inplace )\n\t\t\t\t\t\t\tresult.push( elem );\n\t\t\t\t\t} else if ( inplace ) {\n\t\t\t\t\t\tcurLoop[i] = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t},\n\t\tID: function(match){\n\t\t\treturn match[1].replace(/\\\\/g, \"\");\n\t\t},\n\t\tTAG: function(match, curLoop){\n\t\t\tfor ( var i = 0; curLoop[i] === false; i++ ){}\n\t\t\treturn curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();\n\t\t},\n\t\tCHILD: function(match){\n\t\t\tif ( match[1] == \"nth\" ) {\n\t\t\t\tvar test = /(-?)(\\d*)n((?:\\+|-)?\\d*)/.exec(\n\t\t\t\t\tmatch[2] == \"even\" && \"2n\" || match[2] == \"odd\" && \"2n+1\" ||\n\t\t\t\t\t!/\\D/.test( match[2] ) && \"0n+\" + match[2] || match[2]);\n\n\t\t\t\tmatch[2] = (test[1] + (test[2] || 1)) - 0;\n\t\t\t\tmatch[3] = test[3] - 0;\n\t\t\t}\n\n\t\t\tmatch[0] = done++;\n\n\t\t\treturn match;\n\t\t},\n\t\tATTR: function(match, curLoop, inplace, result, not, isXML){\n\t\t\tvar name = match[1].replace(/\\\\/g, \"\");\n\n\t\t\tif ( !isXML && Expr.attrMap[name] ) {\n\t\t\t\tmatch[1] = Expr.attrMap[name];\n\t\t\t}\n\n\t\t\tif ( match[2] === \"~=\" ) {\n\t\t\t\tmatch[4] = \" \" + match[4] + \" \";\n\t\t\t}\n\n\t\t\treturn match;\n\t\t},\n\t\tPSEUDO: function(match, curLoop, inplace, result, not){\n\t\t\tif ( match[1] === \"not\" ) {\n\t\t\t\tif ( ( chunker.exec(match[3]) || \"\" ).length > 1 || /^\\w/.test(match[3]) ) {\n\t\t\t\t\tmatch[3] = Sizzle(match[3], null, null, curLoop);\n\t\t\t\t} else {\n\t\t\t\t\tvar ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\n\t\t\t\t\tif ( !inplace ) {\n\t\t\t\t\t\tresult.push.apply( result, ret );\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn match;\n\t\t},\n\t\tPOS: function(match){\n\t\t\tmatch.unshift( true );\n\t\t\treturn match;\n\t\t}\n\t},\n\tfilters: {\n\t\tenabled: function(elem){\n\t\t\treturn elem.disabled === false && elem.type !== \"hidden\";\n\t\t},\n\t\tdisabled: function(elem){\n\t\t\treturn elem.disabled === true;\n\t\t},\n\t\tchecked: function(elem){\n\t\t\treturn elem.checked === true;\n\t\t},\n\t\tselected: function(elem){\n\t\t\telem.parentNode.selectedIndex;\n\t\t\treturn elem.selected === true;\n\t\t},\n\t\tparent: function(elem){\n\t\t\treturn !!elem.firstChild;\n\t\t},\n\t\tempty: function(elem){\n\t\t\treturn !elem.firstChild;\n\t\t},\n\t\thas: function(elem, i, match){\n\t\t\treturn !!Sizzle( match[3], elem ).length;\n\t\t},\n\t\theader: function(elem){\n\t\t\treturn /h\\d/i.test( elem.nodeName );\n\t\t},\n\t\ttext: function(elem){\n\t\t\treturn \"text\" === elem.type;\n\t\t},\n\t\tradio: function(elem){\n\t\t\treturn \"radio\" === elem.type;\n\t\t},\n\t\tcheckbox: function(elem){\n\t\t\treturn \"checkbox\" === elem.type;\n\t\t},\n\t\tfile: function(elem){\n\t\t\treturn \"file\" === elem.type;\n\t\t},\n\t\tpassword: function(elem){\n\t\t\treturn \"password\" === elem.type;\n\t\t},\n\t\tsubmit: function(elem){\n\t\t\treturn \"submit\" === elem.type;\n\t\t},\n\t\timage: function(elem){\n\t\t\treturn \"image\" === elem.type;\n\t\t},\n\t\treset: function(elem){\n\t\t\treturn \"reset\" === elem.type;\n\t\t},\n\t\tbutton: function(elem){\n\t\t\treturn \"button\" === elem.type || elem.nodeName.toUpperCase() === \"BUTTON\";\n\t\t},\n\t\tinput: function(elem){\n\t\t\treturn /input|select|textarea|button/i.test(elem.nodeName);\n\t\t}\n\t},\n\tsetFilters: {\n\t\tfirst: function(elem, i){\n\t\t\treturn i === 0;\n\t\t},\n\t\tlast: function(elem, i, match, array){\n\t\t\treturn i === array.length - 1;\n\t\t},\n\t\teven: function(elem, i){\n\t\t\treturn i % 2 === 0;\n\t\t},\n\t\todd: function(elem, i){\n\t\t\treturn i % 2 === 1;\n\t\t},\n\t\tlt: function(elem, i, match){\n\t\t\treturn i < match[3] - 0;\n\t\t},\n\t\tgt: function(elem, i, match){\n\t\t\treturn i > match[3] - 0;\n\t\t},\n\t\tnth: function(elem, i, match){\n\t\t\treturn match[3] - 0 == i;\n\t\t},\n\t\teq: function(elem, i, match){\n\t\t\treturn match[3] - 0 == i;\n\t\t}\n\t},\n\tfilter: {\n\t\tPSEUDO: function(elem, match, i, array){\n\t\t\tvar name = match[1], filter = Expr.filters[ name ];\n\n\t\t\tif ( filter ) {\n\t\t\t\treturn filter( elem, i, match, array );\n\t\t\t} else if ( name === \"contains\" ) {\n\t\t\t\treturn (elem.textContent || elem.innerText || \"\").indexOf(match[3]) >= 0;\n\t\t\t} else if ( name === \"not\" ) {\n\t\t\t\tvar not = match[3];\n\n\t\t\t\tfor ( var i = 0, l = not.length; i < l; i++ ) {\n\t\t\t\t\tif ( not[i] === elem ) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t},\n\t\tCHILD: function(elem, match){\n\t\t\tvar type = match[1], node = elem;\n\t\t\tswitch (type) {\n\t\t\t\tcase 'only':\n\t\t\t\tcase 'first':\n\t\t\t\t\twhile ( (node = node.previousSibling) )  {\n\t\t\t\t\t\tif ( node.nodeType === 1 ) return false;\n\t\t\t\t\t}\n\t\t\t\t\tif ( type == 'first') return true;\n\t\t\t\t\tnode = elem;\n\t\t\t\tcase 'last':\n\t\t\t\t\twhile ( (node = node.nextSibling) )  {\n\t\t\t\t\t\tif ( node.nodeType === 1 ) return false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\tcase 'nth':\n\t\t\t\t\tvar first = match[2], last = match[3];\n\n\t\t\t\t\tif ( first == 1 && last == 0 ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tvar doneName = match[0],\n\t\t\t\t\t\tparent = elem.parentNode;\n\n\t\t\t\t\tif ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {\n\t\t\t\t\t\tvar count = 0;\n\t\t\t\t\t\tfor ( node = parent.firstChild; node; node = node.nextSibling ) {\n\t\t\t\t\t\t\tif ( node.nodeType === 1 ) {\n\t\t\t\t\t\t\t\tnode.nodeIndex = ++count;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tparent.sizcache = doneName;\n\t\t\t\t\t}\n\n\t\t\t\t\tvar diff = elem.nodeIndex - last;\n\t\t\t\t\tif ( first == 0 ) {\n\t\t\t\t\t\treturn diff == 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn ( diff % first == 0 && diff / first >= 0 );\n\t\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tID: function(elem, match){\n\t\t\treturn elem.nodeType === 1 && elem.getAttribute(\"id\") === match;\n\t\t},\n\t\tTAG: function(elem, match){\n\t\t\treturn (match === \"*\" && elem.nodeType === 1) || elem.nodeName === match;\n\t\t},\n\t\tCLASS: function(elem, match){\n\t\t\treturn (\" \" + (elem.className || elem.getAttribute(\"class\")) + \" \")\n\t\t\t\t.indexOf( match ) > -1;\n\t\t},\n\t\tATTR: function(elem, match){\n\t\t\tvar name = match[1],\n\t\t\t\tresult = Expr.attrHandle[ name ] ?\n\t\t\t\t\tExpr.attrHandle[ name ]( elem ) :\n\t\t\t\t\telem[ name ] != null ?\n\t\t\t\t\t\telem[ name ] :\n\t\t\t\t\t\telem.getAttribute( name ),\n\t\t\t\tvalue = result + \"\",\n\t\t\t\ttype = match[2],\n\t\t\t\tcheck = match[4];\n\n\t\t\treturn result == null ?\n\t\t\t\ttype === \"!=\" :\n\t\t\t\ttype === \"=\" ?\n\t\t\t\tvalue === check :\n\t\t\t\ttype === \"*=\" ?\n\t\t\t\tvalue.indexOf(check) >= 0 :\n\t\t\t\ttype === \"~=\" ?\n\t\t\t\t(\" \" + value + \" \").indexOf(check) >= 0 :\n\t\t\t\t!check ?\n\t\t\t\tvalue && result !== false :\n\t\t\t\ttype === \"!=\" ?\n\t\t\t\tvalue != check :\n\t\t\t\ttype === \"^=\" ?\n\t\t\t\tvalue.indexOf(check) === 0 :\n\t\t\t\ttype === \"$=\" ?\n\t\t\t\tvalue.substr(value.length - check.length) === check :\n\t\t\t\ttype === \"|=\" ?\n\t\t\t\tvalue === check || value.substr(0, check.length + 1) === check + \"-\" :\n\t\t\t\tfalse;\n\t\t},\n\t\tPOS: function(elem, match, i, array){\n\t\t\tvar name = match[2], filter = Expr.setFilters[ name ];\n\n\t\t\tif ( filter ) {\n\t\t\t\treturn filter( elem, i, match, array );\n\t\t\t}\n\t\t}\n\t}\n};\n\nvar origPOS = Expr.match.POS;\n\nfor ( var type in Expr.match ) {\n\tExpr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\\[]*\\])(?![^\\(]*\\))/.source );\n\tExpr.leftMatch[ type ] = new RegExp( /(^(?:.|\\r|\\n)*?)/.source + Expr.match[ type ].source );\n}\n\nvar makeArray = function(array, results) {\n\tarray = Array.prototype.slice.call( array, 0 );\n\n\tif ( results ) {\n\t\tresults.push.apply( results, array );\n\t\treturn results;\n\t}\n\n\treturn array;\n};\n\ntry {\n\tArray.prototype.slice.call( document.documentElement.childNodes, 0 );\n\n} catch(e){\n\tmakeArray = function(array, results) {\n\t\tvar ret = results || [];\n\n\t\tif ( toString.call(array) === \"[object Array]\" ) {\n\t\t\tArray.prototype.push.apply( ret, array );\n\t\t} else {\n\t\t\tif ( typeof array.length === \"number\" ) {\n\t\t\t\tfor ( var i = 0, l = array.length; i < l; i++ ) {\n\t\t\t\t\tret.push( array[i] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( var i = 0; array[i]; i++ ) {\n\t\t\t\t\tret.push( array[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t};\n}\n\nvar sortOrder;\n\nif ( document.documentElement.compareDocumentPosition ) {\n\tsortOrder = function( a, b ) {\n\t\tif ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {\n\t\t\tif ( a == b ) {\n\t\t\t\thasDuplicate = true;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tvar ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;\n\t\tif ( ret === 0 ) {\n\t\t\thasDuplicate = true;\n\t\t}\n\t\treturn ret;\n\t};\n} else if ( \"sourceIndex\" in document.documentElement ) {\n\tsortOrder = function( a, b ) {\n\t\tif ( !a.sourceIndex || !b.sourceIndex ) {\n\t\t\tif ( a == b ) {\n\t\t\t\thasDuplicate = true;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tvar ret = a.sourceIndex - b.sourceIndex;\n\t\tif ( ret === 0 ) {\n\t\t\thasDuplicate = true;\n\t\t}\n\t\treturn ret;\n\t};\n} else if ( document.createRange ) {\n\tsortOrder = function( a, b ) {\n\t\tif ( !a.ownerDocument || !b.ownerDocument ) {\n\t\t\tif ( a == b ) {\n\t\t\t\thasDuplicate = true;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tvar aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();\n\t\taRange.setStart(a, 0);\n\t\taRange.setEnd(a, 0);\n\t\tbRange.setStart(b, 0);\n\t\tbRange.setEnd(b, 0);\n\t\tvar ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);\n\t\tif ( ret === 0 ) {\n\t\t\thasDuplicate = true;\n\t\t}\n\t\treturn ret;\n\t};\n}\n\n(function(){\n\tvar form = document.createElement(\"div\"),\n\t\tid = \"script\" + (new Date).getTime();\n\tform.innerHTML = \"<a name='\" + id + \"'/>\";\n\n\tvar root = document.documentElement;\n\troot.insertBefore( form, root.firstChild );\n\n\tif ( !!document.getElementById( id ) ) {\n\t\tExpr.find.ID = function(match, context, isXML){\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && !isXML ) {\n\t\t\t\tvar m = context.getElementById(match[1]);\n\t\t\t\treturn m ? m.id === match[1] || typeof m.getAttributeNode !== \"undefined\" && m.getAttributeNode(\"id\").nodeValue === match[1] ? [m] : undefined : [];\n\t\t\t}\n\t\t};\n\n\t\tExpr.filter.ID = function(elem, match){\n\t\t\tvar node = typeof elem.getAttributeNode !== \"undefined\" && elem.getAttributeNode(\"id\");\n\t\t\treturn elem.nodeType === 1 && node && node.nodeValue === match;\n\t\t};\n\t}\n\n\troot.removeChild( form );\n\troot = form = null; // release memory in IE\n})();\n\n(function(){\n\n\tvar div = document.createElement(\"div\");\n\tdiv.appendChild( document.createComment(\"\") );\n\n\tif ( div.getElementsByTagName(\"*\").length > 0 ) {\n\t\tExpr.find.TAG = function(match, context){\n\t\t\tvar results = context.getElementsByTagName(match[1]);\n\n\t\t\tif ( match[1] === \"*\" ) {\n\t\t\t\tvar tmp = [];\n\n\t\t\t\tfor ( var i = 0; results[i]; i++ ) {\n\t\t\t\t\tif ( results[i].nodeType === 1 ) {\n\t\t\t\t\t\ttmp.push( results[i] );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tresults = tmp;\n\t\t\t}\n\n\t\t\treturn results;\n\t\t};\n\t}\n\n\tdiv.innerHTML = \"<a href='#'></a>\";\n\tif ( div.firstChild && typeof div.firstChild.getAttribute !== \"undefined\" &&\n\t\t\tdiv.firstChild.getAttribute(\"href\") !== \"#\" ) {\n\t\tExpr.attrHandle.href = function(elem){\n\t\t\treturn elem.getAttribute(\"href\", 2);\n\t\t};\n\t}\n\n\tdiv = null; // release memory in IE\n})();\n\nif ( document.querySelectorAll ) (function(){\n\tvar oldSizzle = Sizzle, div = document.createElement(\"div\");\n\tdiv.innerHTML = \"<p class='TEST'></p>\";\n\n\tif ( div.querySelectorAll && div.querySelectorAll(\".TEST\").length === 0 ) {\n\t\treturn;\n\t}\n\n\tSizzle = function(query, context, extra, seed){\n\t\tcontext = context || document;\n\n\t\tif ( !seed && context.nodeType === 9 && !isXML(context) ) {\n\t\t\ttry {\n\t\t\t\treturn makeArray( context.querySelectorAll(query), extra );\n\t\t\t} catch(e){}\n\t\t}\n\n\t\treturn oldSizzle(query, context, extra, seed);\n\t};\n\n\tfor ( var prop in oldSizzle ) {\n\t\tSizzle[ prop ] = oldSizzle[ prop ];\n\t}\n\n\tdiv = null; // release memory in IE\n})();\n\nif ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){\n\tvar div = document.createElement(\"div\");\n\tdiv.innerHTML = \"<div class='test e'></div><div class='test'></div>\";\n\n\tif ( div.getElementsByClassName(\"e\").length === 0 )\n\t\treturn;\n\n\tdiv.lastChild.className = \"e\";\n\n\tif ( div.getElementsByClassName(\"e\").length === 1 )\n\t\treturn;\n\n\tExpr.order.splice(1, 0, \"CLASS\");\n\tExpr.find.CLASS = function(match, context, isXML) {\n\t\tif ( typeof context.getElementsByClassName !== \"undefined\" && !isXML ) {\n\t\t\treturn context.getElementsByClassName(match[1]);\n\t\t}\n\t};\n\n\tdiv = null; // release memory in IE\n})();\n\nfunction dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\n\tvar sibDir = dir == \"previousSibling\" && !isXML;\n\tfor ( var i = 0, l = checkSet.length; i < l; i++ ) {\n\t\tvar elem = checkSet[i];\n\t\tif ( elem ) {\n\t\t\tif ( sibDir && elem.nodeType === 1 ){\n\t\t\t\telem.sizcache = doneName;\n\t\t\t\telem.sizset = i;\n\t\t\t}\n\t\t\telem = elem[dir];\n\t\t\tvar match = false;\n\n\t\t\twhile ( elem ) {\n\t\t\t\tif ( elem.sizcache === doneName ) {\n\t\t\t\t\tmatch = checkSet[elem.sizset];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( elem.nodeType === 1 && !isXML ){\n\t\t\t\t\telem.sizcache = doneName;\n\t\t\t\t\telem.sizset = i;\n\t\t\t\t}\n\n\t\t\t\tif ( elem.nodeName === cur ) {\n\t\t\t\t\tmatch = elem;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\telem = elem[dir];\n\t\t\t}\n\n\t\t\tcheckSet[i] = match;\n\t\t}\n\t}\n}\n\nfunction dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\n\tvar sibDir = dir == \"previousSibling\" && !isXML;\n\tfor ( var i = 0, l = checkSet.length; i < l; i++ ) {\n\t\tvar elem = checkSet[i];\n\t\tif ( elem ) {\n\t\t\tif ( sibDir && elem.nodeType === 1 ) {\n\t\t\t\telem.sizcache = doneName;\n\t\t\t\telem.sizset = i;\n\t\t\t}\n\t\t\telem = elem[dir];\n\t\t\tvar match = false;\n\n\t\t\twhile ( elem ) {\n\t\t\t\tif ( elem.sizcache === doneName ) {\n\t\t\t\t\tmatch = checkSet[elem.sizset];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\tif ( !isXML ) {\n\t\t\t\t\t\telem.sizcache = doneName;\n\t\t\t\t\t\telem.sizset = i;\n\t\t\t\t\t}\n\t\t\t\t\tif ( typeof cur !== \"string\" ) {\n\t\t\t\t\t\tif ( elem === cur ) {\n\t\t\t\t\t\t\tmatch = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\n\t\t\t\t\t\tmatch = elem;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\telem = elem[dir];\n\t\t\t}\n\n\t\t\tcheckSet[i] = match;\n\t\t}\n\t}\n}\n\nvar contains = document.compareDocumentPosition ?  function(a, b){\n\treturn a.compareDocumentPosition(b) & 16;\n} : function(a, b){\n\treturn a !== b && (a.contains ? a.contains(b) : true);\n};\n\nvar isXML = function(elem){\n\treturn elem.nodeType === 9 && elem.documentElement.nodeName !== \"HTML\" ||\n\t\t!!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== \"HTML\";\n};\n\nvar posProcess = function(selector, context){\n\tvar tmpSet = [], later = \"\", match,\n\t\troot = context.nodeType ? [context] : context;\n\n\twhile ( (match = Expr.match.PSEUDO.exec( selector )) ) {\n\t\tlater += match[0];\n\t\tselector = selector.replace( Expr.match.PSEUDO, \"\" );\n\t}\n\n\tselector = Expr.relative[selector] ? selector + \"*\" : selector;\n\n\tfor ( var i = 0, l = root.length; i < l; i++ ) {\n\t\tSizzle( selector, root[i], tmpSet );\n\t}\n\n\treturn Sizzle.filter( later, tmpSet );\n};\n\n\nwindow.Sizzle = Sizzle;\n\n})();\n\n;(function(engine) {\n  var extendElements = Prototype.Selector.extendElements;\n\n  function select(selector, scope) {\n    return extendElements(engine(selector, scope || document));\n  }\n\n  function match(element, selector) {\n    return engine.matches(selector, [element]).length == 1;\n  }\n\n  Prototype.Selector.engine = engine;\n  Prototype.Selector.select = select;\n  Prototype.Selector.match = match;\n})(Sizzle);\n\nwindow.Sizzle = Prototype._original_property;\ndelete Prototype._original_property;\n\nvar Form = {\n  reset: function(form) {\n    form = $(form);\n    form.reset();\n    return form;\n  },\n\n  serializeElements: function(elements, options) {\n    if (typeof options != 'object') options = { hash: !!options };\n    else if (Object.isUndefined(options.hash)) options.hash = true;\n    var key, value, submitted = false, submit = options.submit;\n\n    var data = elements.inject({ }, function(result, element) {\n      if (!element.disabled && element.name) {\n        key = element.name; value = $(element).getValue();\n        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&\n            submit !== false && (!submit || key == submit) && (submitted = true)))) {\n          if (key in result) {\n            if (!Object.isArray(result[key])) result[key] = [result[key]];\n            result[key].push(value);\n          }\n          else result[key] = value;\n        }\n      }\n      return result;\n    });\n\n    return options.hash ? data : Object.toQueryString(data);\n  }\n};\n\nForm.Methods = {\n  serialize: function(form, options) {\n    return Form.serializeElements(Form.getElements(form), options);\n  },\n\n  getElements: function(form) {\n    var elements = $(form).getElementsByTagName('*'),\n        element,\n        arr = [ ],\n        serializers = Form.Element.Serializers;\n    for (var i = 0; element = elements[i]; i++) {\n      arr.push(element);\n    }\n    return arr.inject([], function(elements, child) {\n      if (serializers[child.tagName.toLowerCase()])\n        elements.push(Element.extend(child));\n      return elements;\n    })\n  },\n\n  getInputs: function(form, typeName, name) {\n    form = $(form);\n    var inputs = form.getElementsByTagName('input');\n\n    if (!typeName && !name) return $A(inputs).map(Element.extend);\n\n    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {\n      var input = inputs[i];\n      if ((typeName && input.type != typeName) || (name && input.name != name))\n        continue;\n      matchingInputs.push(Element.extend(input));\n    }\n\n    return matchingInputs;\n  },\n\n  disable: function(form) {\n    form = $(form);\n    Form.getElements(form).invoke('disable');\n    return form;\n  },\n\n  enable: function(form) {\n    form = $(form);\n    Form.getElements(form).invoke('enable');\n    return form;\n  },\n\n  findFirstElement: function(form) {\n    var elements = $(form).getElements().findAll(function(element) {\n      return 'hidden' != element.type && !element.disabled;\n    });\n    var firstByIndex = elements.findAll(function(element) {\n      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;\n    }).sortBy(function(element) { return element.tabIndex }).first();\n\n    return firstByIndex ? firstByIndex : elements.find(function(element) {\n      return /^(?:input|select|textarea)$/i.test(element.tagName);\n    });\n  },\n\n  focusFirstElement: function(form) {\n    form = $(form);\n    form.findFirstElement().activate();\n    return form;\n  },\n\n  request: function(form, options) {\n    form = $(form), options = Object.clone(options || { });\n\n    var params = options.parameters, action = form.readAttribute('action') || '';\n    if (action.blank()) action = window.location.href;\n    options.parameters = form.serialize(true);\n\n    if (params) {\n      if (Object.isString(params)) params = params.toQueryParams();\n      Object.extend(options.parameters, params);\n    }\n\n    if (form.hasAttribute('method') && !options.method)\n      options.method = form.method;\n\n    return new Ajax.Request(action, options);\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\n\nForm.Element = {\n  focus: function(element) {\n    $(element).focus();\n    return element;\n  },\n\n  select: function(element) {\n    $(element).select();\n    return element;\n  }\n};\n\nForm.Element.Methods = {\n\n  serialize: function(element) {\n    element = $(element);\n    if (!element.disabled && element.name) {\n      var value = element.getValue();\n      if (value != undefined) {\n        var pair = { };\n        pair[element.name] = value;\n        return Object.toQueryString(pair);\n      }\n    }\n    return '';\n  },\n\n  getValue: function(element) {\n    element = $(element);\n    var method = element.tagName.toLowerCase();\n    return Form.Element.Serializers[method](element);\n  },\n\n  setValue: function(element, value) {\n    element = $(element);\n    var method = element.tagName.toLowerCase();\n    Form.Element.Serializers[method](element, value);\n    return element;\n  },\n\n  clear: function(element) {\n    $(element).value = '';\n    return element;\n  },\n\n  present: function(element) {\n    return $(element).value != '';\n  },\n\n  activate: function(element) {\n    element = $(element);\n    try {\n      element.focus();\n      if (element.select && (element.tagName.toLowerCase() != 'input' ||\n          !(/^(?:button|reset|submit)$/i.test(element.type))))\n        element.select();\n    } catch (e) { }\n    return element;\n  },\n\n  disable: function(element) {\n    element = $(element);\n    element.disabled = true;\n    return element;\n  },\n\n  enable: function(element) {\n    element = $(element);\n    element.disabled = false;\n    return element;\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Field = Form.Element;\n\nvar $F = Form.Element.Methods.getValue;\n\n/*--------------------------------------------------------------------------*/\n\nForm.Element.Serializers = {\n  input: function(element, value) {\n    switch (element.type.toLowerCase()) {\n      case 'checkbox':\n      case 'radio':\n        return Form.Element.Serializers.inputSelector(element, value);\n      default:\n        return Form.Element.Serializers.textarea(element, value);\n    }\n  },\n\n  inputSelector: function(element, value) {\n    if (Object.isUndefined(value)) return element.checked ? element.value : null;\n    else element.checked = !!value;\n  },\n\n  textarea: function(element, value) {\n    if (Object.isUndefined(value)) return element.value;\n    else element.value = value;\n  },\n\n  select: function(element, value) {\n    if (Object.isUndefined(value))\n      return this[element.type == 'select-one' ?\n        'selectOne' : 'selectMany'](element);\n    else {\n      var opt, currentValue, single = !Object.isArray(value);\n      for (var i = 0, length = element.length; i < length; i++) {\n        opt = element.options[i];\n        currentValue = this.optionValue(opt);\n        if (single) {\n          if (currentValue == value) {\n            opt.selected = true;\n            return;\n          }\n        }\n        else opt.selected = value.include(currentValue);\n      }\n    }\n  },\n\n  selectOne: function(element) {\n    var index = element.selectedIndex;\n    return index >= 0 ? this.optionValue(element.options[index]) : null;\n  },\n\n  selectMany: function(element) {\n    var values, length = element.length;\n    if (!length) return null;\n\n    for (var i = 0, values = []; i < length; i++) {\n      var opt = element.options[i];\n      if (opt.selected) values.push(this.optionValue(opt));\n    }\n    return values;\n  },\n\n  optionValue: function(opt) {\n    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\n\nAbstract.TimedObserver = Class.create(PeriodicalExecuter, {\n  initialize: function($super, element, frequency, callback) {\n    $super(callback, frequency);\n    this.element   = $(element);\n    this.lastValue = this.getValue();\n  },\n\n  execute: function() {\n    var value = this.getValue();\n    if (Object.isString(this.lastValue) && Object.isString(value) ?\n        this.lastValue != value : String(this.lastValue) != String(value)) {\n      this.callback(this.element, value);\n      this.lastValue = value;\n    }\n  }\n});\n\nForm.Element.Observer = Class.create(Abstract.TimedObserver, {\n  getValue: function() {\n    return Form.Element.getValue(this.element);\n  }\n});\n\nForm.Observer = Class.create(Abstract.TimedObserver, {\n  getValue: function() {\n    return Form.serialize(this.element);\n  }\n});\n\n/*--------------------------------------------------------------------------*/\n\nAbstract.EventObserver = Class.create({\n  initialize: function(element, callback) {\n    this.element  = $(element);\n    this.callback = callback;\n\n    this.lastValue = this.getValue();\n    if (this.element.tagName.toLowerCase() == 'form')\n      this.registerFormCallbacks();\n    else\n      this.registerCallback(this.element);\n  },\n\n  onElementEvent: function() {\n    var value = this.getValue();\n    if (this.lastValue != value) {\n      this.callback(this.element, value);\n      this.lastValue = value;\n    }\n  },\n\n  registerFormCallbacks: function() {\n    Form.getElements(this.element).each(this.registerCallback, this);\n  },\n\n  registerCallback: function(element) {\n    if (element.type) {\n      switch (element.type.toLowerCase()) {\n        case 'checkbox':\n        case 'radio':\n          Event.observe(element, 'click', this.onElementEvent.bind(this));\n          break;\n        default:\n          Event.observe(element, 'change', this.onElementEvent.bind(this));\n          break;\n      }\n    }\n  }\n});\n\nForm.Element.EventObserver = Class.create(Abstract.EventObserver, {\n  getValue: function() {\n    return Form.Element.getValue(this.element);\n  }\n});\n\nForm.EventObserver = Class.create(Abstract.EventObserver, {\n  getValue: function() {\n    return Form.serialize(this.element);\n  }\n});\n(function() {\n\n  var Event = {\n    KEY_BACKSPACE: 8,\n    KEY_TAB:       9,\n    KEY_RETURN:   13,\n    KEY_ESC:      27,\n    KEY_LEFT:     37,\n    KEY_UP:       38,\n    KEY_RIGHT:    39,\n    KEY_DOWN:     40,\n    KEY_DELETE:   46,\n    KEY_HOME:     36,\n    KEY_END:      35,\n    KEY_PAGEUP:   33,\n    KEY_PAGEDOWN: 34,\n    KEY_INSERT:   45,\n\n    cache: {}\n  };\n\n  var docEl = document.documentElement;\n  var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl\n    && 'onmouseleave' in docEl;\n\n  var _isButton;\n  if (Prototype.Browser.IE) {\n    var buttonMap = { 0: 1, 1: 4, 2: 2 };\n    _isButton = function(event, code) {\n      return event.button === buttonMap[code];\n    };\n  } else if (Prototype.Browser.WebKit) {\n    _isButton = function(event, code) {\n      switch (code) {\n        case 0: return event.which == 1 && !event.metaKey;\n        case 1: return event.which == 1 && event.metaKey;\n        default: return false;\n      }\n    };\n  } else {\n    _isButton = function(event, code) {\n      return event.which ? (event.which === code + 1) : (event.button === code);\n    };\n  }\n\n  function isLeftClick(event)   { return _isButton(event, 0) }\n\n  function isMiddleClick(event) { return _isButton(event, 1) }\n\n  function isRightClick(event)  { return _isButton(event, 2) }\n\n  function element(event) {\n    event = Event.extend(event);\n\n    var node = event.target, type = event.type,\n     currentTarget = event.currentTarget;\n\n    if (currentTarget && currentTarget.tagName) {\n      if (type === 'load' || type === 'error' ||\n        (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'\n          && currentTarget.type === 'radio'))\n            node = currentTarget;\n    }\n\n    if (node.nodeType == Node.TEXT_NODE)\n      node = node.parentNode;\n\n    return Element.extend(node);\n  }\n\n  function findElement(event, expression) {\n    var element = Event.element(event);\n    if (!expression) return element;\n    while (element) {\n      if (Object.isElement(element) && Prototype.Selector.match(element, expression)) {\n        return Element.extend(element);\n      }\n      element = element.parentNode;\n    }\n  }\n\n  function pointer(event) {\n    return { x: pointerX(event), y: pointerY(event) };\n  }\n\n  function pointerX(event) {\n    var docElement = document.documentElement,\n     body = document.body || { scrollLeft: 0 };\n\n    return event.pageX || (event.clientX +\n      (docElement.scrollLeft || body.scrollLeft) -\n      (docElement.clientLeft || 0));\n  }\n\n  function pointerY(event) {\n    var docElement = document.documentElement,\n     body = document.body || { scrollTop: 0 };\n\n    return  event.pageY || (event.clientY +\n       (docElement.scrollTop || body.scrollTop) -\n       (docElement.clientTop || 0));\n  }\n\n\n  function stop(event) {\n    Event.extend(event);\n    event.preventDefault();\n    event.stopPropagation();\n\n    event.stopped = true;\n  }\n\n  Event.Methods = {\n    isLeftClick: isLeftClick,\n    isMiddleClick: isMiddleClick,\n    isRightClick: isRightClick,\n\n    element: element,\n    findElement: findElement,\n\n    pointer: pointer,\n    pointerX: pointerX,\n    pointerY: pointerY,\n\n    stop: stop\n  };\n\n\n  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {\n    m[name] = Event.Methods[name].methodize();\n    return m;\n  });\n\n  if (Prototype.Browser.IE) {\n    function _relatedTarget(event) {\n      var element;\n      switch (event.type) {\n        case 'mouseover': element = event.fromElement; break;\n        case 'mouseout':  element = event.toElement;   break;\n        default: return null;\n      }\n      return Element.extend(element);\n    }\n\n    Object.extend(methods, {\n      stopPropagation: function() { this.cancelBubble = true },\n      preventDefault:  function() { this.returnValue = false },\n      inspect: function() { return '[object Event]' }\n    });\n\n    Event.extend = function(event, element) {\n      if (!event) return false;\n      if (event._extendedByPrototype) return event;\n\n      event._extendedByPrototype = Prototype.emptyFunction;\n      var pointer = Event.pointer(event);\n\n      Object.extend(event, {\n        target: event.srcElement || element,\n        relatedTarget: _relatedTarget(event),\n        pageX:  pointer.x,\n        pageY:  pointer.y\n      });\n\n      return Object.extend(event, methods);\n    };\n  } else {\n    Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;\n    Object.extend(Event.prototype, methods);\n    Event.extend = Prototype.K;\n  }\n\n  function _createResponder(element, eventName, handler) {\n    var registry = Element.retrieve(element, 'prototype_event_registry');\n\n    if (Object.isUndefined(registry)) {\n      CACHE.push(element);\n      registry = Element.retrieve(element, 'prototype_event_registry', $H());\n    }\n\n    var respondersForEvent = registry.get(eventName);\n    if (Object.isUndefined(respondersForEvent)) {\n      respondersForEvent = [];\n      registry.set(eventName, respondersForEvent);\n    }\n\n    if (respondersForEvent.pluck('handler').include(handler)) return false;\n\n    var responder;\n    if (eventName.include(\":\")) {\n      responder = function(event) {\n        if (Object.isUndefined(event.eventName))\n          return false;\n\n        if (event.eventName !== eventName)\n          return false;\n\n        Event.extend(event, element);\n        handler.call(element, event);\n      };\n    } else {\n      if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&\n       (eventName === \"mouseenter\" || eventName === \"mouseleave\")) {\n        if (eventName === \"mouseenter\" || eventName === \"mouseleave\") {\n          responder = function(event) {\n            Event.extend(event, element);\n\n            var parent = event.relatedTarget;\n            while (parent && parent !== element) {\n              try { parent = parent.parentNode; }\n              catch(e) { parent = element; }\n            }\n\n            if (parent === element) return;\n\n            handler.call(element, event);\n          };\n        }\n      } else {\n        responder = function(event) {\n          Event.extend(event, element);\n          handler.call(element, event);\n        };\n      }\n    }\n\n    responder.handler = handler;\n    respondersForEvent.push(responder);\n    return responder;\n  }\n\n  function _destroyCache() {\n    for (var i = 0, length = CACHE.length; i < length; i++) {\n      Event.stopObserving(CACHE[i]);\n      CACHE[i] = null;\n    }\n  }\n\n  var CACHE = [];\n\n  if (Prototype.Browser.IE)\n    window.attachEvent('onunload', _destroyCache);\n\n  if (Prototype.Browser.WebKit)\n    window.addEventListener('unload', Prototype.emptyFunction, false);\n\n\n  var _getDOMEventName = Prototype.K,\n      translations = { mouseenter: \"mouseover\", mouseleave: \"mouseout\" };\n\n  if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {\n    _getDOMEventName = function(eventName) {\n      return (translations[eventName] || eventName);\n    };\n  }\n\n  function observe(element, eventName, handler) {\n    element = $(element);\n\n    var responder = _createResponder(element, eventName, handler);\n\n    if (!responder) return element;\n\n    if (eventName.include(':')) {\n      if (element.addEventListener)\n        element.addEventListener(\"dataavailable\", responder, false);\n      else {\n        element.attachEvent(\"ondataavailable\", responder);\n        element.attachEvent(\"onfilterchange\", responder);\n      }\n    } else {\n      var actualEventName = _getDOMEventName(eventName);\n\n      if (element.addEventListener)\n        element.addEventListener(actualEventName, responder, false);\n      else\n        element.attachEvent(\"on\" + actualEventName, responder);\n    }\n\n    return element;\n  }\n\n  function stopObserving(element, eventName, handler) {\n    element = $(element);\n\n    var registry = Element.retrieve(element, 'prototype_event_registry');\n    if (!registry) return element;\n\n    if (!eventName) {\n      registry.each( function(pair) {\n        var eventName = pair.key;\n        stopObserving(element, eventName);\n      });\n      return element;\n    }\n\n    var responders = registry.get(eventName);\n    if (!responders) return element;\n\n    if (!handler) {\n      responders.each(function(r) {\n        stopObserving(element, eventName, r.handler);\n      });\n      return element;\n    }\n\n    var responder = responders.find( function(r) { return r.handler === handler; });\n    if (!responder) return element;\n\n    if (eventName.include(':')) {\n      if (element.removeEventListener)\n        element.removeEventListener(\"dataavailable\", responder, false);\n      else {\n        element.detachEvent(\"ondataavailable\", responder);\n        element.detachEvent(\"onfilterchange\",  responder);\n      }\n    } else {\n      var actualEventName = _getDOMEventName(eventName);\n      if (element.removeEventListener)\n        element.removeEventListener(actualEventName, responder, false);\n      else\n        element.detachEvent('on' + actualEventName, responder);\n    }\n\n    registry.set(eventName, responders.without(responder));\n\n    return element;\n  }\n\n  function fire(element, eventName, memo, bubble) {\n    element = $(element);\n\n    if (Object.isUndefined(bubble))\n      bubble = true;\n\n    if (element == document && document.createEvent && !element.dispatchEvent)\n      element = document.documentElement;\n\n    var event;\n    if (document.createEvent) {\n      event = document.createEvent('HTMLEvents');\n      event.initEvent('dataavailable', true, true);\n    } else {\n      event = document.createEventObject();\n      event.eventType = bubble ? 'ondataavailable' : 'onfilterchange';\n    }\n\n    event.eventName = eventName;\n    event.memo = memo || { };\n\n    if (document.createEvent)\n      element.dispatchEvent(event);\n    else\n      element.fireEvent(event.eventType, event);\n\n    return Event.extend(event);\n  }\n\n  Event.Handler = Class.create({\n    initialize: function(element, eventName, selector, callback) {\n      this.element   = $(element);\n      this.eventName = eventName;\n      this.selector  = selector;\n      this.callback  = callback;\n      this.handler   = this.handleEvent.bind(this);\n    },\n\n    start: function() {\n      Event.observe(this.element, this.eventName, this.handler);\n      return this;\n    },\n\n    stop: function() {\n      Event.stopObserving(this.element, this.eventName, this.handler);\n      return this;\n    },\n\n    handleEvent: function(event) {\n      var element = event.findElement(this.selector);\n      if (element) this.callback.call(this.element, event, element);\n    }\n  });\n\n  function on(element, eventName, selector, callback) {\n    element = $(element);\n    if (Object.isFunction(selector) && Object.isUndefined(callback)) {\n      callback = selector, selector = null;\n    }\n\n    return new Event.Handler(element, eventName, selector, callback).start();\n  }\n\n  Object.extend(Event, Event.Methods);\n\n  Object.extend(Event, {\n    fire:          fire,\n    observe:       observe,\n    stopObserving: stopObserving,\n    on:            on\n  });\n\n  Element.addMethods({\n    fire:          fire,\n\n    observe:       observe,\n\n    stopObserving: stopObserving,\n\n    on:            on\n  });\n\n  Object.extend(document, {\n    fire:          fire.methodize(),\n\n    observe:       observe.methodize(),\n\n    stopObserving: stopObserving.methodize(),\n\n    on:            on.methodize(),\n\n    loaded:        false\n  });\n\n  if (window.Event) Object.extend(window.Event, Event);\n  else window.Event = Event;\n})();\n\n(function() {\n  /* Support for the DOMContentLoaded event is based on work by Dan Webb,\n     Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */\n\n  var timer;\n\n  function fireContentLoadedEvent() {\n    if (document.loaded) return;\n    if (timer) window.clearTimeout(timer);\n    document.loaded = true;\n    document.fire('dom:loaded');\n  }\n\n  function checkReadyState() {\n    if (document.readyState === 'complete') {\n      document.stopObserving('readystatechange', checkReadyState);\n      fireContentLoadedEvent();\n    }\n  }\n\n  function pollDoScroll() {\n    try { document.documentElement.doScroll('left'); }\n    catch(e) {\n      timer = pollDoScroll.defer();\n      return;\n    }\n    fireContentLoadedEvent();\n  }\n\n  if (document.addEventListener) {\n    document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);\n  } else {\n    document.observe('readystatechange', checkReadyState);\n    if (window == top)\n      timer = pollDoScroll.defer();\n  }\n\n  Event.observe(window, 'load', fireContentLoadedEvent);\n})();\n\nElement.addMethods();\n\n/*------------------------------- DEPRECATED -------------------------------*/\n\nHash.toQueryString = Object.toQueryString;\n\nvar Toggle = { display: Element.toggle };\n\nElement.Methods.childOf = Element.Methods.descendantOf;\n\nvar Insertion = {\n  Before: function(element, content) {\n    return Element.insert(element, {before:content});\n  },\n\n  Top: function(element, content) {\n    return Element.insert(element, {top:content});\n  },\n\n  Bottom: function(element, content) {\n    return Element.insert(element, {bottom:content});\n  },\n\n  After: function(element, content) {\n    return Element.insert(element, {after:content});\n  }\n};\n\nvar $continue = new Error('\"throw $continue\" is deprecated, use \"return\" instead');\n\nvar Position = {\n  includeScrollOffsets: false,\n\n  prepare: function() {\n    this.deltaX =  window.pageXOffset\n                || document.documentElement.scrollLeft\n                || document.body.scrollLeft\n                || 0;\n    this.deltaY =  window.pageYOffset\n                || document.documentElement.scrollTop\n                || document.body.scrollTop\n                || 0;\n  },\n\n  within: function(element, x, y) {\n    if (this.includeScrollOffsets)\n      return this.withinIncludingScrolloffsets(element, x, y);\n    this.xcomp = x;\n    this.ycomp = y;\n    this.offset = Element.cumulativeOffset(element);\n\n    return (y >= this.offset[1] &&\n            y <  this.offset[1] + element.offsetHeight &&\n            x >= this.offset[0] &&\n            x <  this.offset[0] + element.offsetWidth);\n  },\n\n  withinIncludingScrolloffsets: function(element, x, y) {\n    var offsetcache = Element.cumulativeScrollOffset(element);\n\n    this.xcomp = x + offsetcache[0] - this.deltaX;\n    this.ycomp = y + offsetcache[1] - this.deltaY;\n    this.offset = Element.cumulativeOffset(element);\n\n    return (this.ycomp >= this.offset[1] &&\n            this.ycomp <  this.offset[1] + element.offsetHeight &&\n            this.xcomp >= this.offset[0] &&\n            this.xcomp <  this.offset[0] + element.offsetWidth);\n  },\n\n  overlap: function(mode, element) {\n    if (!mode) return 0;\n    if (mode == 'vertical')\n      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /\n        element.offsetHeight;\n    if (mode == 'horizontal')\n      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /\n        element.offsetWidth;\n  },\n\n\n  cumulativeOffset: Element.Methods.cumulativeOffset,\n\n  positionedOffset: Element.Methods.positionedOffset,\n\n  absolutize: function(element) {\n    Position.prepare();\n    return Element.absolutize(element);\n  },\n\n  relativize: function(element) {\n    Position.prepare();\n    return Element.relativize(element);\n  },\n\n  realOffset: Element.Methods.cumulativeScrollOffset,\n\n  offsetParent: Element.Methods.getOffsetParent,\n\n  page: Element.Methods.viewportOffset,\n\n  clone: function(source, target, options) {\n    options = options || { };\n    return Element.clonePosition(target, source, options);\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nif (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){\n  function iter(name) {\n    return name.blank() ? null : \"[contains(concat(' ', @class, ' '), ' \" + name + \" ')]\";\n  }\n\n  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?\n  function(element, className) {\n    className = className.toString().strip();\n    var cond = /\\s/.test(className) ? $w(className).map(iter).join('') : iter(className);\n    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];\n  } : function(element, className) {\n    className = className.toString().strip();\n    var elements = [], classNames = (/\\s/.test(className) ? $w(className) : null);\n    if (!classNames && !className) return elements;\n\n    var nodes = $(element).getElementsByTagName('*');\n    className = ' ' + className + ' ';\n\n    for (var i = 0, child, cn; child = nodes[i]; i++) {\n      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||\n          (classNames && classNames.all(function(name) {\n            return !name.toString().blank() && cn.include(' ' + name + ' ');\n          }))))\n        elements.push(Element.extend(child));\n    }\n    return elements;\n  };\n\n  return function(className, parentElement) {\n    return $(parentElement || document.body).getElementsByClassName(className);\n  };\n}(Element.Methods);\n\n/*--------------------------------------------------------------------------*/\n\nElement.ClassNames = Class.create();\nElement.ClassNames.prototype = {\n  initialize: function(element) {\n    this.element = $(element);\n  },\n\n  _each: function(iterator) {\n    this.element.className.split(/\\s+/).select(function(name) {\n      return name.length > 0;\n    })._each(iterator);\n  },\n\n  set: function(className) {\n    this.element.className = className;\n  },\n\n  add: function(classNameToAdd) {\n    if (this.include(classNameToAdd)) return;\n    this.set($A(this).concat(classNameToAdd).join(' '));\n  },\n\n  remove: function(classNameToRemove) {\n    if (!this.include(classNameToRemove)) return;\n    this.set($A(this).without(classNameToRemove).join(' '));\n  },\n\n  toString: function() {\n    return $A(this).join(' ');\n  }\n};\n\nObject.extend(Element.ClassNames.prototype, Enumerable);\n\n/*--------------------------------------------------------------------------*/\n\n(function() {\n  window.Selector = Class.create({\n    initialize: function(expression) {\n      this.expression = expression.strip();\n    },\n\n    findElements: function(rootElement) {\n      return Prototype.Selector.select(this.expression, rootElement);\n    },\n\n    match: function(element) {\n      return Prototype.Selector.match(element, this.expression);\n    },\n\n    toString: function() {\n      return this.expression;\n    },\n\n    inspect: function() {\n      return \"#<Selector: \" + this.expression + \">\";\n    }\n  });\n\n  Object.extend(Selector, {\n    matchElements: function(elements, expression) {\n      var match = Prototype.Selector.match,\n          results = [];\n\n      for (var i = 0, length = elements.length; i < length; i++) {\n        var element = elements[i];\n        if (match(element, expression)) {\n          results.push(Element.extend(element));\n        }\n      }\n      return results;\n    },\n\n    findElement: function(elements, expression, index) {\n      index = index || 0;\n      var matchIndex = 0, element;\n      for (var i = 0, length = elements.length; i < length; i++) {\n        element = elements[i];\n        if (Prototype.Selector.match(element, expression) && index === matchIndex++) {\n          return Element.extend(element);\n        }\n      }\n    },\n\n    findChildElements: function(element, expressions) {\n      var selector = expressions.toArray().join(', ');\n      return Prototype.Selector.select(selector, element || document);\n    }\n  });\n})();\n"
  },
  {
    "path": "test/apps/rails3/public/javascripts/rails.js",
    "content": "(function() {\n  // Technique from Juriy Zaytsev\n  // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/\n  function isEventSupported(eventName) {\n    var el = document.createElement('div');\n    eventName = 'on' + eventName;\n    var isSupported = (eventName in el);\n    if (!isSupported) {\n      el.setAttribute(eventName, 'return;');\n      isSupported = typeof el[eventName] == 'function';\n    }\n    el = null;\n    return isSupported;\n  }\n\n  function isForm(element) {\n    return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM'\n  }\n\n  function isInput(element) {\n    if (Object.isElement(element)) {\n      var name = element.nodeName.toUpperCase()\n      return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA'\n    }\n    else return false\n  }\n\n  var submitBubbles = isEventSupported('submit'),\n      changeBubbles = isEventSupported('change')\n\n  if (!submitBubbles || !changeBubbles) {\n    // augment the Event.Handler class to observe custom events when needed\n    Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(\n      function(init, element, eventName, selector, callback) {\n        init(element, eventName, selector, callback)\n        // is the handler being attached to an element that doesn't support this event?\n        if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||\n             (!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {\n          // \"submit\" => \"emulated:submit\"\n          this.eventName = 'emulated:' + this.eventName\n        }\n      }\n    )\n  }\n\n  if (!submitBubbles) {\n    // discover forms on the page by observing focus events which always bubble\n    document.on('focusin', 'form', function(focusEvent, form) {\n      // special handler for the real \"submit\" event (one-time operation)\n      if (!form.retrieve('emulated:submit')) {\n        form.on('submit', function(submitEvent) {\n          var emulated = form.fire('emulated:submit', submitEvent, true)\n          // if custom event received preventDefault, cancel the real one too\n          if (emulated.returnValue === false) submitEvent.preventDefault()\n        })\n        form.store('emulated:submit', true)\n      }\n    })\n  }\n\n  if (!changeBubbles) {\n    // discover form inputs on the page\n    document.on('focusin', 'input, select, texarea', function(focusEvent, input) {\n      // special handler for real \"change\" events\n      if (!input.retrieve('emulated:change')) {\n        input.on('change', function(changeEvent) {\n          input.fire('emulated:change', changeEvent, true)\n        })\n        input.store('emulated:change', true)\n      }\n    })\n  }\n\n  function handleRemote(element) {\n    var method, url, params;\n\n    var event = element.fire(\"ajax:before\");\n    if (event.stopped) return false;\n\n    if (element.tagName.toLowerCase() === 'form') {\n      method = element.readAttribute('method') || 'post';\n      url    = element.readAttribute('action');\n      params = element.serialize();\n    } else {\n      method = element.readAttribute('data-method') || 'get';\n      url    = element.readAttribute('href');\n      params = {};\n    }\n\n    new Ajax.Request(url, {\n      method: method,\n      parameters: params,\n      evalScripts: true,\n\n      onComplete:    function(request) { element.fire(\"ajax:complete\", request); },\n      onSuccess:     function(request) { element.fire(\"ajax:success\",  request); },\n      onFailure:     function(request) { element.fire(\"ajax:failure\",  request); }\n    });\n\n    element.fire(\"ajax:after\");\n  }\n\n  function handleMethod(element) {\n    var method = element.readAttribute('data-method'),\n        url = element.readAttribute('href'),\n        csrf_param = $$('meta[name=csrf-param]')[0],\n        csrf_token = $$('meta[name=csrf-token]')[0];\n\n    var form = new Element('form', { method: \"POST\", action: url, style: \"display: none;\" });\n    element.parentNode.insert(form);\n\n    if (method !== 'post') {\n      var field = new Element('input', { type: 'hidden', name: '_method', value: method });\n      form.insert(field);\n    }\n\n    if (csrf_param) {\n      var param = csrf_param.readAttribute('content'),\n          token = csrf_token.readAttribute('content'),\n          field = new Element('input', { type: 'hidden', name: param, value: token });\n      form.insert(field);\n    }\n\n    form.submit();\n  }\n\n\n  document.on(\"click\", \"*[data-confirm]\", function(event, element) {\n    var message = element.readAttribute('data-confirm');\n    if (!confirm(message)) event.stop();\n  });\n\n  document.on(\"click\", \"a[data-remote]\", function(event, element) {\n    if (event.stopped) return;\n    handleRemote(element);\n    event.stop();\n  });\n\n  document.on(\"click\", \"a[data-method]\", function(event, element) {\n    if (event.stopped) return;\n    handleMethod(element);\n    event.stop();\n  });\n\n  document.on(\"submit\", function(event) {\n    var element = event.findElement(),\n        message = element.readAttribute('data-confirm');\n    if (message && !confirm(message)) {\n      event.stop();\n      return false;\n    }\n\n    var inputs = element.select(\"input[type=submit][data-disable-with]\");\n    inputs.each(function(input) {\n      input.disabled = true;\n      input.writeAttribute('data-original-value', input.value);\n      input.value = input.readAttribute('data-disable-with');\n    });\n\n    var element = event.findElement(\"form[data-remote]\");\n    if (element) {\n      handleRemote(element);\n      event.stop();\n    }\n  });\n\n  document.on(\"ajax:after\", \"form\", function(event, element) {\n    var inputs = element.select(\"input[type=submit][disabled=true][data-disable-with]\");\n    inputs.each(function(input) {\n      input.value = input.readAttribute('data-original-value');\n      input.removeAttribute('data-original-value');\n      input.disabled = false;\n    });\n  });\n\n  Ajax.Responders.register({\n    onCreate: function(request) {\n      var csrf_meta_tag = $$('meta[name=csrf-token]')[0];\n\n      if (csrf_meta_tag) {\n        var header = 'X-CSRF-Token',\n            token = csrf_meta_tag.readAttribute('content');\n\n        if (!request.options.requestHeaders) {\n          request.options.requestHeaders = {};\n        }\n        request.options.requestHeaders[header] = token;\n      }\n    }\n  });\n})();\n"
  },
  {
    "path": "test/apps/rails3/public/robots.txt",
    "content": "# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file\n#\n# To ban all spiders from the entire site uncomment the next two lines:\n# User-Agent: *\n# Disallow: /\n"
  },
  {
    "path": "test/apps/rails3/public/stylesheets/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3/script/rails",
    "content": "#!/usr/bin/env ruby\n# This command will automatically be run when you run \"rails\" with Rails 3 gems installed from the root of your application.\n\nAPP_PATH = File.expand_path('../../config/application',  __FILE__)\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'rails/commands'\n"
  },
  {
    "path": "test/apps/rails3/test/functional/home_controller_test.rb",
    "content": "require 'test_helper'\n\nclass HomeControllerTest < ActionController::TestCase\n  test \"should get index\" do\n    get :index\n    assert_response :success\n  end\n\n  test \"should get test_params\" do\n    get :test_params\n    assert_response :success\n  end\n\n  test \"should get test_model\" do\n    get :test_model\n    assert_response :success\n  end\n\n  test \"should get test_cookie\" do\n    get :test_cookie\n    assert_response :success\n  end\n\n  test \"should get test_filter\" do\n    get :test_filter\n    assert_response :success\n  end\n\n  test \"should get test_file_access\" do\n    get :test_file_access\n    assert_response :success\n  end\n\n  test \"should get test_sql\" do\n    get :test_sql\n    assert_response :success\n  end\n\n  test \"should get test_command\" do\n    get :test_command\n    assert_response :success\n  end\n\n  test \"should get test_eval\" do\n    get :test_eval\n    assert_response :success\n  end\n\n  test \"should get test_redirect\" do\n    get :test_redirect\n    assert_response :success\n  end\n\n  test \"should get test_render\" do\n    get :test_render\n    assert_response :success\n  end\n\n  test \"should get test_mass_assignment\" do\n    get :test_mass_assignment\n    assert_response :success\n  end\n\n  test \"should get test_dynamic_render\" do\n    get :test_dynamic_render\n    assert_response :success\n  end\n\nend\n"
  },
  {
    "path": "test/apps/rails3/test/functional/other_controller_test.rb",
    "content": "require 'test_helper'\n\nclass OtherControllerTest < ActionController::TestCase\n  test \"should get test_locals\" do\n    get :test_locals\n    assert_response :success\n  end\n\n  test \"should get test_object\" do\n    get :test_object\n    assert_response :success\n  end\n\n  test \"should get test_collection\" do\n    get :test_collection\n    assert_response :success\n  end\n\n  test \"should get test_iteration\" do\n    get :test_iteration\n    assert_response :success\n  end\n\n  test \"should get test_send_file\" do\n    get :test_send_file\n    assert_response :success\n  end\n\nend\n"
  },
  {
    "path": "test/apps/rails3/test/performance/browsing_test.rb",
    "content": "require 'test_helper'\nrequire 'rails/performance_test_help'\n\n# Profiling results for each test method are written to tmp/performance.\nclass BrowsingTest < ActionDispatch::PerformanceTest\n  def test_homepage\n    get '/'\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3/test/test_helper.rb",
    "content": "ENV[\"RAILS_ENV\"] = \"test\"\nrequire File.expand_path('../../config/environment', __FILE__)\nrequire 'rails/test_help'\n\nclass ActiveSupport::TestCase\n  # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.\n  #\n  # Note: You'll currently still have to declare fixtures explicitly in integration tests\n  # -- they do not yet inherit this setting\n  fixtures :all\n\n  # Add more helper methods to be used by all tests here...\nend\n"
  },
  {
    "path": "test/apps/rails3/test/unit/helpers/home_helper_test.rb",
    "content": "require 'test_helper'\n\nclass HomeHelperTest < ActionView::TestCase\nend\n"
  },
  {
    "path": "test/apps/rails3/test/unit/helpers/other_helper_test.rb",
    "content": "require 'test_helper'\n\nclass OtherHelperTest < ActionView::TestCase\nend\n"
  },
  {
    "path": "test/apps/rails3/vendor/plugins/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.1/.gitignore",
    "content": ".bundle\ndb/*.sqlite3\nlog/*.log\ntmp/\n.sass-cache/\n"
  },
  {
    "path": "test/apps/rails3.1/Gemfile",
    "content": "source 'http://rubygems.org'\n\ngem 'rails', '3.1.0'\n\n# Bundle edge Rails instead:\n# gem 'rails',     :git => 'git://github.com/rails/rails.git'\n\ngem 'mysql'\n\ngem 'json'\n\ngem 'therubyracer'\n\ngem 'draper'\n\n# Gems used only for assets and not required\n# in production environments by default.\ngroup :assets do\n  gem 'sass-rails', \"  ~> 3.1.0\"\n  gem 'coffee-rails', \"~> 3.1.0\"\n  gem 'uglifier'\nend\n\ngem 'jquery-rails'\n\n# Use unicorn as the web server\n# gem 'unicorn'\n\n# Deploy with Capistrano\n# gem 'capistrano'\n\n# To use debugger\n# gem 'ruby-debug'\n\n"
  },
  {
    "path": "test/apps/rails3.1/README",
    "content": "== Welcome to Rails\n\nRails is a web-application framework that includes everything needed to create\ndatabase-backed web applications according to the Model-View-Control pattern.\n\nThis pattern splits the view (also called the presentation) into \"dumb\"\ntemplates that are primarily responsible for inserting pre-built data in between\nHTML tags. The model contains the \"smart\" domain objects (such as Account,\nProduct, Person, Post) that holds all the business logic and knows how to\npersist themselves to a database. The controller handles the incoming requests\n(such as Save New Account, Update Product, Show Post) by manipulating the model\nand directing data to the view.\n\nIn Rails, the model is handled by what's called an object-relational mapping\nlayer entitled Active Record. This layer allows you to present the data from\ndatabase rows as objects and embellish these data objects with business logic\nmethods. You can read more about Active Record in\nlink:files/vendor/rails/activerecord/README.html.\n\nThe controller and view are handled by the Action Pack, which handles both\nlayers by its two parts: Action View and Action Controller. These two layers\nare bundled in a single package due to their heavy interdependence. This is\nunlike the relationship between the Active Record and Action Pack that is much\nmore separate. Each of these packages can be used independently outside of\nRails. You can read more about Action Pack in\nlink:files/vendor/rails/actionpack/README.html.\n\n\n== Getting Started\n\n1. At the command prompt, create a new Rails application:\n       <tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)\n\n2. Change directory to <tt>myapp</tt> and start the web server:\n       <tt>cd myapp; rails server</tt> (run with --help for options)\n\n3. Go to http://localhost:3000/ and you'll see:\n       \"Welcome aboard: You're riding Ruby on Rails!\"\n\n4. Follow the guidelines to start developing your application. You can find\nthe following resources handy:\n\n* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html\n* Ruby on Rails Tutorial Book: http://www.railstutorial.org/\n\n\n== Debugging Rails\n\nSometimes your application goes wrong. Fortunately there are a lot of tools that\nwill help you debug it and get it back on the rails.\n\nFirst area to check is the application log files. Have \"tail -f\" commands\nrunning on the server.log and development.log. Rails will automatically display\ndebugging and runtime information to these files. Debugging info will also be\nshown in the browser on requests from 127.0.0.1.\n\nYou can also log your own messages directly into the log file from your code\nusing the Ruby logger class from inside your controllers. Example:\n\n  class WeblogController < ActionController::Base\n    def destroy\n      @weblog = Weblog.find(params[:id])\n      @weblog.destroy\n      logger.info(\"#{Time.now} Destroyed Weblog ID ##{@weblog.id}!\")\n    end\n  end\n\nThe result will be a message in your log file along the lines of:\n\n  Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!\n\nMore information on how to use the logger is at http://www.ruby-doc.org/core/\n\nAlso, Ruby documentation can be found at http://www.ruby-lang.org/. There are\nseveral books available online as well:\n\n* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)\n* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)\n\nThese two books will bring you up to speed on the Ruby language and also on\nprogramming in general.\n\n\n== Debugger\n\nDebugger support is available through the debugger command when you start your\nMongrel or WEBrick server with --debugger. This means that you can break out of\nexecution at any point in the code, investigate and change the model, and then,\nresume execution! You need to install ruby-debug to run the server in debugging\nmode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:\n\n  class WeblogController < ActionController::Base\n    def index\n      @posts = Post.all\n      debugger\n    end\n  end\n\nSo the controller will accept the action, run the first line, then present you\nwith a IRB prompt in the server window. Here you can do things like:\n\n  >> @posts.inspect\n  => \"[#<Post:0x14a6be8\n          @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,\n       #<Post:0x14a6620\n          @attributes={\"title\"=>\"Rails\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]\"\n  >> @posts.first.title = \"hello from a debugger\"\n  => \"hello from a debugger\"\n\n...and even better, you can examine how your runtime objects actually work:\n\n  >> f = @posts.first\n  => #<Post:0x13630c4 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>\n  >> f.\n  Display all 152 possibilities? (y or n)\n\nFinally, when you're ready to resume execution, you can enter \"cont\".\n\n\n== Console\n\nThe console is a Ruby shell, which allows you to interact with your\napplication's domain model. Here you'll have all parts of the application\nconfigured, just like it is when the application is running. You can inspect\ndomain models, change values, and save to the database. Starting the script\nwithout arguments will launch it in the development environment.\n\nTo start the console, run <tt>rails console</tt> from the application\ndirectory.\n\nOptions:\n\n* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications\n  made to the database.\n* Passing an environment name as an argument will load the corresponding\n  environment. Example: <tt>rails console production</tt>.\n\nTo reload your controllers and models after launching the console run\n<tt>reload!</tt>\n\nMore information about irb can be found at:\nlink:http://www.rubycentral.org/pickaxe/irb.html\n\n\n== dbconsole\n\nYou can go to the command line of your database directly through <tt>rails\ndbconsole</tt>. You would be connected to the database with the credentials\ndefined in database.yml. Starting the script without arguments will connect you\nto the development database. Passing an argument will connect you to a different\ndatabase, like <tt>rails dbconsole production</tt>. Currently works for MySQL,\nPostgreSQL and SQLite 3.\n\n== Description of Contents\n\nThe default directory structure of a generated Ruby on Rails application:\n\n  |-- app\n  |   |-- assets\n  |       |-- images\n  |       |-- javascripts\n  |       `-- stylesheets\n  |   |-- controllers\n  |   |-- helpers\n  |   |-- mailers\n  |   |-- models\n  |   `-- views\n  |       `-- layouts\n  |-- config\n  |   |-- environments\n  |   |-- initializers\n  |   `-- locales\n  |-- db\n  |-- doc\n  |-- lib\n  |   `-- tasks\n  |-- log\n  |-- public\n  |-- script\n  |-- test\n  |   |-- fixtures\n  |   |-- functional\n  |   |-- integration\n  |   |-- performance\n  |   `-- unit\n  |-- tmp\n  |   |-- cache\n  |   |-- pids\n  |   |-- sessions\n  |   `-- sockets\n  `-- vendor\n      |-- assets\n          `-- stylesheets\n      `-- plugins\n\napp\n  Holds all the code that's specific to this particular application.\n\napp/assets\n  Contains subdirectories for images, stylesheets, and JavaScript files.\n\napp/controllers\n  Holds controllers that should be named like weblogs_controller.rb for\n  automated URL mapping. All controllers should descend from\n  ApplicationController which itself descends from ActionController::Base.\n\napp/models\n  Holds models that should be named like post.rb. Models descend from\n  ActiveRecord::Base by default.\n\napp/views\n  Holds the template files for the view that should be named like\n  weblogs/index.html.erb for the WeblogsController#index action. All views use\n  eRuby syntax by default.\n\napp/views/layouts\n  Holds the template files for layouts to be used with views. This models the\n  common header/footer method of wrapping views. In your views, define a layout\n  using the <tt>layout :default</tt> and create a file named default.html.erb.\n  Inside default.html.erb, call <% yield %> to render the view using this\n  layout.\n\napp/helpers\n  Holds view helpers that should be named like weblogs_helper.rb. These are\n  generated for you automatically when using generators for controllers.\n  Helpers can be used to wrap functionality for your views into methods.\n\nconfig\n  Configuration files for the Rails environment, the routing map, the database,\n  and other dependencies.\n\ndb\n  Contains the database schema in schema.rb. db/migrate contains all the\n  sequence of Migrations for your schema.\n\ndoc\n  This directory is where your application documentation will be stored when\n  generated using <tt>rake doc:app</tt>\n\nlib\n  Application specific libraries. Basically, any kind of custom code that\n  doesn't belong under controllers, models, or helpers. This directory is in\n  the load path.\n\npublic\n  The directory available for the web server. Also contains the dispatchers and the\n  default HTML files. This should be set as the DOCUMENT_ROOT of your web\n  server.\n\nscript\n  Helper scripts for automation and generation.\n\ntest\n  Unit and functional tests along with fixtures. When using the rails generate\n  command, template test files will be generated for you and placed in this\n  directory.\n\nvendor\n  External libraries that the application depends on. Also includes the plugins\n  subdirectory. If the app has frozen rails, those gems also go here, under\n  vendor/rails/. This directory is in the load path.\n"
  },
  {
    "path": "test/apps/rails3.1/Rakefile",
    "content": "#!/usr/bin/env rake\n# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire File.expand_path('../config/application', __FILE__)\n\nRails31::Application.load_tasks\n"
  },
  {
    "path": "test/apps/rails3.1/app/assets/javascripts/application.js",
    "content": "// This is a manifest file that'll be compiled into including all the files listed below.\n// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically\n// be included in the compiled file accessible from http://example.com/assets/application.js\n// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the\n// the compiled file.\n//\n//= require jquery\n//= require jquery_ujs\n//= require_tree .\n"
  },
  {
    "path": "test/apps/rails3.1/app/assets/javascripts/users.js.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/\n"
  },
  {
    "path": "test/apps/rails3.1/app/assets/stylesheets/application.css",
    "content": "/*\n * This is a manifest file that'll automatically include all the stylesheets available in this directory\n * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at\n * the top of the compiled file, but it's generally better to create a new file per style scope.\n *= require_self\n *= require_tree . \n*/"
  },
  {
    "path": "test/apps/rails3.1/app/assets/stylesheets/scaffolds.css.scss",
    "content": "body {\n  background-color: #fff;\n  color: #333;\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 13px;\n  line-height: 18px; }\n\np, ol, ul, td {\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 13px;\n  line-height: 18px; }\n\npre {\n  background-color: #eee;\n  padding: 10px;\n  font-size: 11px; }\n\na {\n  color: #000;\n  &:visited {\n    color: #666; }\n  &:hover {\n    color: #fff;\n    background-color: #000; } }\n\ndiv {\n  &.field, &.actions {\n    margin-bottom: 10px; } }\n\n#notice {\n  color: green; }\n\n.field_with_errors {\n  padding: 2px;\n  background-color: red;\n  display: table; }\n\n#error_explanation {\n  width: 450px;\n  border: 2px solid red;\n  padding: 7px;\n  padding-bottom: 0;\n  margin-bottom: 20px;\n  background-color: #f0f0f0;\n  h2 {\n    text-align: left;\n    font-weight: bold;\n    padding: 5px 5px 5px 15px;\n    font-size: 12px;\n    margin: -7px;\n    margin-bottom: 0px;\n    background-color: #c00;\n    color: #fff; }\n  ul li {\n    font-size: 12px;\n    list-style: square; } }\n"
  },
  {
    "path": "test/apps/rails3.1/app/assets/stylesheets/users.css.scss",
    "content": "// Place all the styles related to the Users controller here.\n// They will automatically be included in application.css.\n// You can use Sass (SCSS) here: http://sass-lang.com/\n"
  },
  {
    "path": "test/apps/rails3.1/app/controllers/admin_controller.rb",
    "content": "class AdminController < ApplicationController\n  #Examples of skipping important filters with a blacklist instead of whitelist\n  skip_before_filter :login_required, :except => :do_admin_stuff\n  skip_filter :authenticate_user!, :except => :do_admin_stuff\n  skip_before_filter :require_user, :except => [:do_admin_stuff, :do_other_stuff]\n  before_filter -> { @thing = params[:t] }, only: [:use_lambda_filter]\n\n  def constantize_some_stuff\n    klass = params[:class].constantize\n    klass.new.do_bad_things\n\n    params[:class].safe_constantize.new(params[:arg])\n\n    Module.qualified_const_get(params[:const])\n\n    const_get(params[:arg])\n\n    some_method(params[:class]).constantize\n  end\n\n  def authenticate_user!\n    correct_password = \"7001337\"\n\n    authenticate_or_request_with_http_basic do |username, password|\n      username == \"foo\" && password == correct_password\n    end\n  end\n\n  def show_detailed_exceptions?\n    yeah_sure_they_are_an_admin_right? current_user\n  end\n\n  def make_system_calls\n    `#{\"blah #{why?}\"}`\n\n    # Some command injection of literals\n    # which should not raise warnings \n    or_input = if admin\n                 \"rm -rf\"\n               else\n                 :symbol\n               end\n\n    system \"cd / && #{or_input}\"\n    `cd / && #{or_input}`\n\n    system \"echo #{1}\"\n    exec \"nmap 192.168.#{1}.1\"\n    system @thing # no warning\n  end\n\n  def use_lambda_filter\n    eval @thing\n  end\n\n  def authenticate_token!\n    authenticate_token_or_basic do |username, password|\n      username == \"foo\"\n    end\n  end\n\n  def authenticate_token_or_basic(&block)\n    authenticate_or_request_with_http_basic(&block)\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.1/app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::Base\n  protect_from_forgery\nend\n"
  },
  {
    "path": "test/apps/rails3.1/app/controllers/mixins/user_mixin.rb",
    "content": "module UserMixin\n  #Test mixin action method with explicit template\n  def mixin_action\n    @dangerous_input = params[:bad]\n    render 'users/mixin_template'\n  end\n\n  #Test mixin action method with default template\n  def mixin_default\n    @dangerous_input = params[:bad]\n  end\n\n  def assign_if\n    @value = if something\n               this\n               that\n             end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.1/app/controllers/other_controller.rb",
    "content": "class OtherController < ApplicationController\n  def a\n    @a = params[:bad]\n  end\n\n  def b\n    @b = params[:bad]\n  end\n\n  def c\n    @c = params[:bad]\n  end\n\n  def d\n    @d = params[:bad]\n  end\n\n  def e\n    @e = params[:bad]\n  end\n\n  def f\n    @f = params[:bad]\n  end\n\n  def g\n    @g = params[:bad]\n  end\n\n  def test_partial1\n    @a = params[:bad!]\n    render :test_partial\n  end\n\n  def test_partial2\n    @b = params[:badder!]\n    render :test_partial\n  end\n\n  def test_string_interp\n    @user = User.find(current_user)\n    @greeting = \"Hello, #{greeted += 1; @user.name}!\"\n  end\n\n  def test_arel_table_access\n    User.where(User.arel_table[:id].eq(params[:some_id]))\n  end\n\n  def test_draper_redirect\n    redirect_to RecordDecorator.decorate(Record.where(:something => params[:access_key]).find(params[:id]))\n  end\n\n  def test_model_redirect_in_or\n    if something\n      user = User.find(params[:something])\n    else\n      user = User.find(params[:else])\n    end\n\n    redirect_to user\n  end\n\n  def test_sanitized_medium\n    sanitize something\n    @css = sanitize_css(some_css)\n  end\n\n  def test_deserialization\n    CSV.load params[:csv]\n\n    Marshal.load params[:object]\n\n    Marshal.restore User.find(1).cool_stored_thing\n  end\n\n  def test_model_in_haml\n    @user = User.new\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.1/app/controllers/users_controller.rb",
    "content": "class UsersController < ApplicationController\n  PASSWORD = \"superdupersecret\"\n  \n  http_basic_authenticate_with :name => \"superduperadmin\", :password => PASSWORD, :only => :create\n\n  # GET /users\n  # GET /users.json\n  def index\n    @users = User.all\n\n    respond_to do |format|\n      format.html # index.html.erb\n      format.json { render :json => @users }\n    end\n  end\n\n  # GET /users/1\n  # GET /users/1.json\n  def show\n    @user = User.find(params[:id])\n\n    respond_to do |format|\n      format.html # show.html.erb\n      format.json { render :json => @user }\n    end\n  end\n\n  # GET /users/new\n  # GET /users/new.json\n  def new\n    @user = User.new\n\n    respond_to do |format|\n      format.html # new.html.erb\n      format.json { render :json => @user }\n    end\n  end\n\n  # GET /users/1/edit\n  def edit\n    @user = User.find(params[:id])\n  end\n\n  # POST /users\n  # POST /users.json\n  def create\n    @user = User.new(params[:user], :without_protection => true)\n\n    respond_to do |format|\n      if @user.save\n        format.html { redirect_to @user, :notice => 'User was successfully created.' }\n        format.json { render :json => @user, :status => :created, :location => @user }\n      else\n        format.html { render :action => \"new\" }\n        format.json { render :json => @user.errors, :status => :unprocessable_entity }\n      end\n    end\n  end\n\n  # PUT /users/1\n  # PUT /users/1.json\n  def update\n    @user = User.find(params[:id])\n\n    respond_to do |format|\n      if @user.update_attributes(params[:user])\n        format.html { redirect_to @user, :notice => 'User was successfully updated.' }\n        format.json { head :ok }\n      else\n        format.html { render :action => \"edit\" }\n        format.json { render :json => @user.errors, :status => :unprocessable_entity }\n      end\n    end\n  end\n\n  # DELETE /users/1\n  # DELETE /users/1.json\n  def destroy\n    @user = User.find(params[:id])\n    @user.destroy\n\n    respond_to do |format|\n      format.html { redirect_to users_url }\n      format.json { head :ok }\n    end\n  end\n\n  def circular_render\n  end\n\n  skip_before_filter :verify_authenticity_token, :except => [:create, :edit]\n\n  def redirect_to_new_user\n    redirect_to User.new\n  end\n\n  def redirect_to_user_url\n    redirect_to User.find(1).url\n  end\n\n  def redirect_to_user_find_by\n    redirect_to User.find_by_name(params[:name])\n  end\n\n  def test_file_access_params\n    File.unlink(blah(params[:file]))\n    Pathname.readlines(\"blah/#{cookies[:file]}\")\n    File.delete(params[:file])\n    IO.read(User.find_by_name('bob').file_path)\n  end\n\n  def redirect_to_user_as_param\n    redirect_to blah(User.find(1)) #Don't warn\n  end\n\n  def redirect_to_association\n    redirect_to User.first.account #Don't warn\n  end\n\n  def redirect_to_safe_second_param\n    redirect_to :back, :notice => \"Go back, #{params[:user]}!\" #Don't warn\n  end\n\n  def test_simple_helper\n    @user = simple_helper\n  end\n\n  def test_less_simple_helpers\n    assign_ivar\n    @input = less_simple_helper\n    @other_thing = simple_helper_with_args(params[:x])\n  end\n\n  def test_assign_twice\n    assign_ivar\n  end\n\n  def update_all_users\n    #Unsafe\n    User.update_all params[:yaysql]\n    User.update_all \"name = 'Bob'\", \"name = '#{params[:name]}'\"\n    User.update_all \"old = TRUE\", [\"name = '#{params[:name]}' AND age > ?\", params[:age]]\n    User.update_all \"old = TRUE\", [\"name = ? AND age > ?\", params[:name], params[:age]], :order => params[:order]\n\n    User.where(:name => params[:name]).update_all(params[:update])\n    User.where(:admin => true).update_all(\"setting = #{params[:setting]}\")\n    User.where(:name => params[:name]).update_all([\"active = ?, age = #{params[:age]}\", params[:active]]).limit(1)\n\n    #Safe(ish)\n    User.update_all [\"name = ?\", params[:new_name]], [\"name = ?\", params[:old_name]]\n    User.update_all({:old => true}, [\"name = ? AND age > ?\", params[:name], params[:age]])\n    User.update_all({:admin => true}, { :name => params[:name] }, :limit => params[:limit])\n  end\n\n  def test_assign_if\n  end\n\n  private\n\n  def simple_helper\n    User.find(params[:id])\n  end\n\n  def less_simple_helper\n    params[:input]\n  end\n\n  def simple_helper_with_args arg\n    arg\n  end\n\n  def assign_ivar\n    @some_value = params[:badthing]\n  end\n\n  def pluck_something\n    User.pluck params[:column]\n  end\n\n  include UserMixin\n\n  before_filter :assign_if, :only => :test_assign_if\n\n  def redirect_merge\n    redirect_to params.merge(host: 'http://app.webthing.com/stuff', port: '80').except(:action, :controller, :auth_token)\n  end\n\n  def drape\n    @user = (params[:id] ? User.find(params[:id]) : current_user).decorate\n  end\n\n  def mass_again\n    User.new(stuff, :without_protection => true)\n  end\n\n  def dynamic_finders\n    User.find_by_name_and_password(params[:name], params[:pass])\n    User.find_by_reset_code(params[:code])\n    Product.find_by_guid(params[:guid].to_s) # No warn\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.1/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails3.1/app/helpers/users_helper.rb",
    "content": "module UsersHelper\nend\n"
  },
  {
    "path": "test/apps/rails3.1/app/mailers/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.1/app/models/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.1/app/models/account.rb",
    "content": "class Account < ActiveRecord::Base\n  validates :username, :length => 6..20, :format => /([a-z][0-9])+/i\n  validates :phone, :format => { :with => /(\\d{3})-(\\d{3})-(\\d{4})/, :on => :create }, :presence => true \n  validates :first_name, :format => /\\w+/\n  serialize :cc_info #safe from CVE-2013-0277\n  attr_accessible :blah_admin_blah\nend\n"
  },
  {
    "path": "test/apps/rails3.1/app/models/product.rb",
    "content": "class Product < ActiveRecord::Base\n  def test_find_order\n    #Should warn, no escaping done for :order\n    Product.find(:all, :order => params[:order])\n    Product.find(:all, :conditions => 'admin = 1', :order => \"name #{params[:order]}\")\n  end\n\n  def test_find_group\n    #Should warn, no escaping done for :group\n    Product.find(:all, :conditions => 'admin = 1', :group => params[:group])\n    Product.find(:all, :conditions => 'admin = 1', :group => \"something, #{params[:group]}\")\n  end\n\n  def test_find_having\n    #Should warn\n    Product.find(:first, :conditions => 'admin = 1', :having => \"x = #{params[:having]}\")\n\n    #Should not warn, hash values are escaped\n    Product.find(:first, :conditions => 'admin = 1', :having => { :x => params[:having]})\n\n    #Should not warn, properly interpolated\n    Product.find(:first, :conditions => ['name = ?', params[:name]], :having => [ 'x = ?', params[:having]])\n\n    #Should warn, not quite properly interpolated\n    Product.find(:first, :conditions => ['name = ?', params[:name]], :having => [ \"admin = ? and x = #{params[:having]}\", cookies[:admin]])\n    Product.find(:first, :conditions => ['name = ?', params[:name]], :having => [ \"admin = ? and x = '\" + params[:having] + \"'\", cookies[:admin]])\n  end\n\n  def test_find_joins\n    #Should not warn, string values are not going to have injection\n    Product.find(:first, :conditions => 'admin = 1', :joins => \"LEFT JOIN comments ON comments.post_id = id\")\n\n    #Should warn\n    Product.find(:first, :conditions => 'admin = 1', :joins => \"LEFT JOIN comments ON comments.#{params[:join]} = id\")\n\n    #Should not warn\n    Product.find(:first, :conditions => 'admin = 1', :joins => [:x, :y])\n\n    #Should warn\n    Product.find(:first, :conditions => 'admin = 1', :joins => [\"LEFT JOIN comments ON comments.#{params[:join]} = id\", :x, :y])\n  end\n\n  def test_find_select\n    #Should not warn, string values are not going to have injection\n    Product.find(:last, :conditions => 'admin = 1', :select => \"name\")\n\n    #Should warn\n    Product.find(:last, :conditions => 'admin = 1', :select => params[:column])\n    Product.find(:last, :conditions => 'admin = 1', :select => \"name, #{params[:column]}\")\n    Product.find(:last, :conditions => 'admin = 1', :select => \"name, \" + params[:column])\n  end\n\n  def test_find_from\n    #Should not warn, string values are not going to have injection\n    Product.find(:last, :conditions => 'admin = 1', :from => \"users\")\n\n    #Should warn\n    Product.find(:last, :conditions => 'admin = 1', :from => params[:table])\n    Product.find(:last, :conditions => 'admin = 1', :from => \"#{params[:table]}\")\n  end\n\n  def test_find_lock\n    #Should not warn\n    Product.find(:last, :conditions => 'admin = 1', :lock => true)\n\n    #Should warn\n    Product.find(:last, :conditions => 'admin = 1', :lock => params[:lock])\n    Product.find(:last, :conditions => 'admin = 1', :lock => \"LOCK #{params[:lock]}\")\n  end\n\n  def test_where\n    #Should not warn\n    Product.where(\"admin = 1\")\n    Product.where(\"admin = ?\", params[:admin])\n    Product.where([\"admin = ?\", params[:admin]])\n    Product.where([\"admin = :admin\", { :admin => params[:admin] }])\n    Product.where(:admin => params[:admin])\n\n    #Should warn\n    Product.where(\"admin = '#{params[:admin]}'\").first\n    Product.where([\"admin = ? AND user_name = #{@name}\", params[:admin]])\n  end\n\n  TOTALLY_SAFE = \"some safe string\"\n\n  def test_constant_interpolation\n    #Should not warn\n    Product.first(\"blah = #{TOTALLY_SAFE}\")\n  end\n\n  def test_local_interpolation\n    #Should warn, medium confidence\n    Product.first(\"blah = #{local_var}\")\n  end\n\n  def test_conditional_args_in_sql\n    #Should warn\n    Product.last(\"blah = '#{something ? params[:blah] : TOTALLY_SAFE}'\")\n\n    #Should not warn\n    Product.last(\"blah = '#{params[:blah] ? 1 : 0}'\")\n  end\n\n  def test_params_in_args\n    #Should warn\n    Product.last(\"blah = '#{something(params[:blah])}'\")\n  end\n\n  def test_params_to_i\n    #Should not warn\n    Product.last(\"blah = '#{params[:id].to_i}'\")\n  end\n\n  def test_more_if_statements\n    if some_condition\n      x = params[:x]\n    else\n      x = \"BLAH\"\n    end\n\n    y = if some_other_condition\n      params[:x]\n      \"blah\"\n    else\n      params[:y]\n      \"blah\"\n    end\n\n    #Should warn\n    Product.last(\"blah = '#{x}'\")\n\n    #Should not warn\n    Product.last(\"blah = '#{y}'\")\n    Product.where(\"blah = 1\").group(y)\n  end\n\n  def test_calculations\n    #Should warn\n    Product.calculate(:count, :all, :conditions => \"blah = '#{params[:blah]}'\")\n    Product.calculate(:sum, params[:column_name])  # Should warn - column name from params\n    Product.minimum(:price, :conditions => \"blah = #{params[:blach]}\")\n    Product.maximum(:price, :group => params[:columns])\n    Product.average(:price, :conditions => [\"blah = #{params[:columns]} and x = ?\", x])\n    Product.sum(params[:columns])\n  end\n\n  def test_select\n    #Should not warn\n    Product.select([:price, :sku])\n\n    #Should warn\n    Product.select params[:columns]\n  end\n\n  def test_conditional_in_options\n    x = params[:x] == y ? \"created_at ASC\" : \"created_at DESC\"\n    z = params[:y] == y ? \"safe\" : \"totally safe\"\n\n    #Should not warn\n    Product.all(:order => x, :having => z, :select => z, :from => z,\n                :group => z)\n  end\n\n  def test_or_interpolation\n    #Should not warn\n    Product.where(\"blah = #{1 or 2}\")\n  end\n\n  def test_params_to_f\n    #Should not warn\n    Product.last(\"blah = '#{params[:id].to_f}'\")\n  end\n\n  def test_interpolation_in_first_arg\n    Product.where(\"x = #{params[:x]} AND y = ?\", y)\n  end\n\n  def test_to_sql_interpolation\n    #Should not warn\n    prices = Produt.select(:price).where(\"created_at < :time\").to_sql\n\n    where(\"price IN (#{prices}) OR whatever\", :price => some_price)\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.1/app/models/some_model.rb",
    "content": "class SomeModel < @some_variable\nend\n"
  },
  {
    "path": "test/apps/rails3.1/app/models/user.rb",
    "content": "class User < ActiveRecord::Base\n  attr_accessible :name\n\n  scope :tall, lambda {|*args| where(\"height > '#{User.average_height}'\") }\n\n  scope :blah, where(\"thinger = '#{BLAH}'\") #No longer warns on constants\n\n  scope :dah, lambda {|*args| { :conditions => \"dah = '#{args[1]}'\"}}\n  \n  scope :phooey, :conditions => \"phoeey = '#{User.phooey}'\"\n\n  scope :this_is_safe, lambda { |name|\n    where(\"name = ?\", \"%#{name.downcase}%\")\n  }\n\n  scope :this_is_also_safe, where(\"name = ?\", \"%#{name.downcase}%\")\n\n  scope :should_not_warn, :conditions => [\"name = ?\", \"%#{name.downcase}%\"]\n\n  scope :unsafe_multiline_scope, lambda {\n    something = something_helper\n    where(\"something = #{something}\")\n  }\n\n  scope :all\n\n  belongs_to :account\n\n  attr_accessible :admin, :as => :admin\n\n  def self.sql_stuff parent_id\n    condition = parent_id.blank? ? \" IS NULL\" : \" = #{parent_id}\"\n    self.connection.select_values(\"SELECT max(id) FROM content_pages WHERE parent_content_page_id #{condition}\")[0].to_i\n    self.connection.select_values(\"SELECT max(id) FROM content_pages WHERE child_content_page_id #{child_id}\")[0].to_i\n\n    # Should not warn\n    User.where(\"#{table_name}.visibility = ?\" +\n               \" OR (#{table_name}.visibility = ? AND #{table_name}.id IN (\" +\n               \"SELECT DISTINCT a.id FROM #{table_name} a\" +\n               \" INNER JOIN #{User.table_name} m ON m.id = mr.member_id AND m.user_id = ?\" +\n               \" WHERE a.project_id IS NULL OR a.project_id = m.project_id))\" +\n               \" OR #{table_name}.user_id = ?\",\n                 stuff, stuff, user.id, user.id)\n  end\n\n  def self.safe_sql_using_quoted_table_name\n    where(\"#{User.quoted_table_name}.id = ?\", 1)\n  end\n\n  def self.more_safe_stuff\n    where(\"#{User.primary_key} = #{table_name_prefix}a.thing\")\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>Rails31</title>\n  <%= stylesheet_link_tag    \"application\" %>\n  <%= javascript_include_tag \"application\" %>\n  <%= csrf_meta_tags %>\n</head>\n<body>\n\n<%= yield %>\n\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/_partial.html.erb",
    "content": "<%= raw @a %>\n\n<%= raw @b %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/a.html.erb",
    "content": "<%= raw @a %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/b.html.erb",
    "content": "<%= raw @b %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/c.html.erb",
    "content": "<%= raw @c %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/d.html.erb",
    "content": "<%= raw @d %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/e.html.erb",
    "content": "<%= raw @e %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/f.html.erb",
    "content": "<%= raw @f %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/g.html.erb",
    "content": "<%= raw @g %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/test_model_in_haml.html.haml",
    "content": "%user\n  %footer\n    = @user.updated_at\n\n  %h1= @user.name\n\n  != @user.bio\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/test_partial.html.erb",
    "content": "<%= render 'partial' %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/test_select_tag.html.erb",
    "content": "<%= select_tag \"name\", options, :prompt => something_benign %>\n\n<%= select_tag \"name\", options, :prompt => \"Select #{params[:name]}\" %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/test_string_interp.html.erb",
    "content": "<%= raw @greeting %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/other/test_strip_tags.html.erb",
    "content": "<%= strip_tags params[:body] %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/_bio.html.erb",
    "content": "<%= user_bio %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/_circular.html.erb",
    "content": "<% @i = (@i ? @i + 1 : 2) %>\n<%= render :partial => \"/users/circular_too\" %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/_circular_too.html.erb",
    "content": "<%= render :partial => \"/users/circular\" %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/_form.html.erb",
    "content": "<%= form_for(@user) do |f| %>\n  <% if @user.errors.any? %>\n    <div id=\"error_explanation\">\n      <h2><%= pluralize(@user.errors.count, \"error\") %> prohibited this user from being saved:</h2>\n\n      <ul>\n      <% @user.errors.full_messages.each do |msg| %>\n        <li><%= msg %></li>\n      <% end %>\n      </ul>\n    </div>\n  <% end %>\n\n  <div class=\"field\">\n    <%= f.label :name %><br />\n    <%= f.text_field :name %>\n  </div>\n  <div class=\"field\">\n    <%= f.label :bio %><br />\n    <%= f.text_field :bio %>\n  </div>\n  <div class=\"field\">\n    <%= f.label :password %><br />\n    <%= f.text_field :password %>\n  </div>\n  <div class=\"field\">\n    <%= f.label :email %><br />\n    <%= f.text_field :email %>\n  </div>\n  <div class=\"field\">\n    <%= f.label :role %><br />\n    <%= f.text_field :role %>\n  </div>\n  <div class=\"field\">\n    <%= f.label :something %>\n  </div>\n  <div class=\"actions\">\n    <%= f.submit %>\n  </div>\n\n<% end %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/_test_layout.html.erb",
    "content": "<%= raw @something %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/_user.html.erb",
    "content": "<tr>\n  <td><%= user.name %></td>\n  <td><%= render 'bio', :locals => { :user_bio => raw(user.bio) } %>\n  <td><%= user.password %></td>\n  <td><%= user.email %></td>\n  <td><%= user.role %></td>\n  <td><%= link_to 'Show', user %></td>\n  <td><%= link_to 'Edit', edit_user_path(user) %></td>\n  <td><%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %></td>\n</tr>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/circular_render.html.erb",
    "content": "<%= render :partial => \"/users/circular\" %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/drape.html.erb",
    "content": "<%= link_to @user.name, @user %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/edit.html.erb",
    "content": "<h1>Editing user</h1>\n\n<%= select('post', 'author_id', \"<option value='#{@user.id}'>#{@user.name}</option>\") %>\n\n<%= render 'form' %>\n\n<%= link_to 'Show', @user %> |\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/index.html.erb",
    "content": "<h1>Listing users</h1>\n\n<table>\n  <tr>\n    <th>Name</th>\n    <th>Bio</th>\n    <th>Password</th>\n    <th>Email</th>\n    <th>Role</th>\n    <th></th>\n    <th></th>\n    <th></th>\n  </tr>\n\n  <%= render 'user', :collection => @users %>\n</table>\n\n<br />\n\n<%= link_to 'New User', new_user_path %>\n\n<%= @something = params[\"something_bad\"] %>\n<%= render :layout => \"test_layout\" %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/interpolated_value.html.haml",
    "content": ".escaped_thing\n  Hi #{params[:awesomeness]}\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/json_test.html.erb",
    "content": "<%= raw({:donkey => params[:donkey]}.to_json) %>"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/mixin_default.html.erb",
    "content": "<%= raw @dangerous_input %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/mixin_template.html.erb",
    "content": "<%= raw @dangerous_input %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/new.html.erb",
    "content": "<h1>New user</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/show.html.erb",
    "content": "<p id=\"notice\"><%= notice %></p>\n\n<p>\n  <b>Name:</b>\n  <%= @user.name %>\n</p>\n\n<p>\n  <b>Bio:</b>\n  <%= @user.bio %>\n</p>\n\n<p>\n  <b>Password:</b>\n  <%= @user.password %>\n</p>\n\n<p>\n  <b>Email:</b>\n  <%= @user.email %>\n</p>\n\n<p>\n  <b>Role:</b>\n  <%= @user.role %>\n</p>\n\n\n<%= link_to 'Edit', edit_user_path(@user) %> |\n<%= link_to 'Back', users_path %>\n\n<%= translate('some_html', :some => '<input>') %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/test_assign_if.html.erb",
    "content": "<%= @value %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/test_assign_twice.html.erb",
    "content": "<%= raw @some_value %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/test_less_simple_helpers.html.erb",
    "content": "<%= raw @input %>\n\n<%= raw @other_thing %>\n\n<%= raw @some_value %>\n"
  },
  {
    "path": "test/apps/rails3.1/app/views/users/test_simple_helper.html.erb",
    "content": "<%= raw @user %>\n"
  },
  {
    "path": "test/apps/rails3.1/config/application.rb",
    "content": "require File.expand_path('../boot', __FILE__)\n\nrequire 'rails/all'\n\nif defined?(Bundler)\n  # If you precompile assets before deploying to production, use this line\n  Bundler.require *Rails.groups(:assets => %w(development test))\n  # If you want your assets lazily compiled in production, use this line\n  # Bundler.require(:default, :assets, Rails.env)\nend\n\nmodule Rails31\n  class Application < Rails::Application\n    # Settings in config/environments/* take precedence over those specified here.\n    # Application configuration should go into files in config/initializers\n    # -- all .rb files in that directory are automatically loaded.\n\n    # Custom directories with classes and modules you want to be autoloadable.\n    # config.autoload_paths += %W(#{config.root}/extras)\n\n    # Only load the plugins named here, in the order given (default is alphabetical).\n    # :all can be used as a placeholder for all plugins not explicitly named.\n    # config.plugins = [ :exception_notification, :ssl_requirement, :all ]\n\n    # Activate observers that should always be running.\n    # config.active_record.observers = :cacher, :garbage_collector, :forum_observer\n\n    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.\n    # Run \"rake -D time\" for a list of tasks for finding time zone names. Default is UTC.\n    # config.time_zone = 'Central Time (US & Canada)'\n\n    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.\n    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]\n    # config.i18n.default_locale = :de\n\n    # Configure the default encoding used in templates for Ruby 1.9.\n    config.encoding = \"utf-8\"\n\n    # Configure sensitive parameters which will be filtered from the log file.\n    config.filter_parameters += [:password]\n\n    # Enable the asset pipeline\n    config.assets.enabled = true\n\n    # Version of your assets, change this if you want to expire all your assets\n    config.assets.version = '1.0'\n\n    config.active_record.whitelist_attributes = true\n  end\nend\n\nHaml::Template.options[:format] = :html5\n"
  },
  {
    "path": "test/apps/rails3.1/config/boot.rb",
    "content": "require 'rubygems'\n\n# Set up gems listed in the Gemfile.\nENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)\n\nrequire 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])\n"
  },
  {
    "path": "test/apps/rails3.1/config/database.yml",
    "content": "# SQLite version 3.x\n#   gem install sqlite3\n#\n#   Ensure the SQLite 3 gem is defined in your Gemfile\n#   gem 'sqlite3'\ndevelopment:\n  adapter: sqlite3\n  database: db/development.sqlite3\n  pool: 5\n  timeout: 5000\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  adapter: sqlite3\n  database: db/test.sqlite3\n  pool: 5\n  timeout: 5000\n\nproduction:\n  adapter: sqlite3\n  database: db/production.sqlite3\n  pool: 5\n  timeout: 5000\n"
  },
  {
    "path": "test/apps/rails3.1/config/environment.rb",
    "content": "# Load the rails application\nrequire File.expand_path('../application', __FILE__)\n\n# Initialize the rails application\nRails31::Application.initialize!\n"
  },
  {
    "path": "test/apps/rails3.1/config/environments/development.rb",
    "content": "Rails31::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # In the development environment your application's code is reloaded on\n  # every request.  This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes = false\n\n  # Log error messages when you accidentally call methods on nil.\n  config.whiny_nils = true\n\n  # Show full error reports and disable caching\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Don't care if the mailer can't send\n  config.action_mailer.raise_delivery_errors = false\n\n  # Print deprecation notices to the Rails logger\n  config.active_support.deprecation = :log\n\n  # Only use best-standards-support built into browsers\n  config.action_dispatch.best_standards_support = :builtin\n\n  # Do not compress assets\n  config.assets.compress = false\n\n  # Expands the lines which load the assets\n  config.assets.debug = true\nend\n"
  },
  {
    "path": "test/apps/rails3.1/config/environments/production.rb",
    "content": "Rails31::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # Code is not reloaded between requests\n  config.cache_classes = true\n\n  # Full error reports are disabled and caching is turned on\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Disable Rails's static asset server (Apache or nginx will already do this)\n  config.serve_static_assets = false\n\n  # Compress JavaScripts and CSS\n  config.assets.compress = true\n\n  # Don't fallback to assets pipeline if a precompiled asset is missed\n  config.assets.compile = false\n\n  # Generate digests for assets URLs\n  config.assets.digest = true\n\n  # Defaults to Rails.root.join(\"public/assets\")\n  # config.assets.manifest = YOUR_PATH\n\n  # Specifies the header that your server uses for sending files\n  # config.action_dispatch.x_sendfile_header = \"X-Sendfile\" # for apache\n  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  config.force_ssl = true\n\n  # See everything in the log (default is :info)\n  # config.log_level = :debug\n\n  # Use a different logger for distributed setups\n  # config.logger = SyslogLogger.new\n\n  # Use a different cache store in production\n  # config.cache_store = :mem_cache_store\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server\n  # config.action_controller.asset_host = \"http://assets.example.com\"\n\n  # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)\n  # config.assets.precompile += %w( search.js )\n\n  # Disable delivery errors, bad email addresses will be ignored\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable threaded mode\n  # config.threadsafe!\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation can not be found)\n  config.i18n.fallbacks = true\n\n  # Send deprecation notices to registered listeners\n  config.active_support.deprecation = :notify\nend\n"
  },
  {
    "path": "test/apps/rails3.1/config/environments/test.rb",
    "content": "Rails31::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # The test environment is used exclusively to run your application's\n  # test suite.  You never need to work with it otherwise.  Remember that\n  # your test database is \"scratch space\" for the test suite and is wiped\n  # and recreated between test runs.  Don't rely on the data there!\n  config.cache_classes = true\n\n  # Configure static asset server for tests with Cache-Control for performance\n  config.serve_static_assets = true\n  config.static_cache_control = \"public, max-age=3600\"\n\n  # Log error messages when you accidentally call methods on nil\n  config.whiny_nils = true\n\n  # Show full error reports and disable caching\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Raise exceptions instead of rendering exception templates\n  config.action_dispatch.show_exceptions = false\n\n  # Disable request forgery protection in test environment\n  config.action_controller.allow_forgery_protection    = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Use SQL instead of Active Record's schema dumper when creating the test database.\n  # This is necessary if your schema can't be completely dumped by the schema dumper,\n  # like if you have constraints or database-specific column types\n  # config.active_record.schema_format = :sql\n\n  # Print deprecation notices to the stderr\n  config.active_support.deprecation = :stderr\n\n  # Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets\n  config.assets.allow_debugging = true\nend\n"
  },
  {
    "path": "test/apps/rails3.1/config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "test/apps/rails3.1/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format\n# (all these examples are active by default):\n# ActiveSupport::Inflector.inflections do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n"
  },
  {
    "path": "test/apps/rails3.1/config/initializers/mime_type_fix.rb",
    "content": "require 'action_dispatch/http/mime_type' \n\nMime.const_set :LOOKUP, Hash.new { |h,k| \n    Mime::Type.new(k) unless k.blank? \n}\n"
  },
  {
    "path": "test/apps/rails3.1/config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n# Mime::Type.register_alias \"text/html\", :iphone\n"
  },
  {
    "path": "test/apps/rails3.1/config/initializers/secret_token.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n# Make sure the secret is at least 30 characters and all random,\n# no regular words or you'll be exposed to dictionary attacks.\nRails31::Application.config.secret_token = 'bc1889b8c46e86b3becee7c3abf7bb935b024b86eac43acb820478dc524417144a006e0edf8191d2d1017a6404922b7824e667c8c8656b60604c821e13283b72'\n"
  },
  {
    "path": "test/apps/rails3.1/config/initializers/session_store.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\nRails31::Application.config.session_store :cookie_store, :key => '_rails3.1_session'\n\n# Use the database for sessions instead of the cookie-based default,\n# which shouldn't be used to store highly confidential information\n# (create the session table with \"rails generate session_migration\")\n# Rails31::Application.config.session_store :active_record_store\n"
  },
  {
    "path": "test/apps/rails3.1/config/initializers/set_escape_json.rb",
    "content": "# this value will be overwritten in unset_escape_json.rb\nActiveSupport.escape_html_entities_in_json = true\n"
  },
  {
    "path": "test/apps/rails3.1/config/initializers/unset_escape_json.rb",
    "content": "# this overwrites the value set in set_escape_json\nActiveSupport.escape_html_entities_in_json = false"
  },
  {
    "path": "test/apps/rails3.1/config/initializers/wrap_parameters.rb",
    "content": "# Be sure to restart your server when you modify this file.\n#\n# This file contains settings for ActionController::ParamsWrapper which\n# is enabled by default.\n\n# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.\nActiveSupport.on_load(:action_controller) do\n  wrap_parameters :format => [:json]\nend\n\n# Disable root element in JSON by default.\nActiveSupport.on_load(:active_record) do\n  self.include_root_in_json = false\nend\n"
  },
  {
    "path": "test/apps/rails3.1/config/initializers/xml_parsing.rb",
    "content": "ActiveSupport::XmlMini.backend = \"REXML\"\n"
  },
  {
    "path": "test/apps/rails3.1/config/initializers/yaml_parsing.rb",
    "content": "ActiveSupport::XmlMini::PARSING.delete(\"symbol\") \nActiveSupport::XmlMini::PARSING.delete(\"yaml\") \n"
  },
  {
    "path": "test/apps/rails3.1/config/locales/en.yml",
    "content": "# Sample localization file for English. Add more files in this directory for other locales.\n# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/apps/rails3.1/config/routes.rb",
    "content": "Rails31::Application.routes.draw do\n  resources :users\n  match 'users/test_simple_helper' => \"users#test_simple_helper\"\n  match 'users/test_less_simple_helpers' => \"users#test_less_simple_helpers\"\n  match 'users/test_less_simple_helpers' => \"users#test_assign_twice\"\n\n  resources :users do\n    get 'mixin_action'\n    get 'mixin_default'\n  end\n\n  resources :other do\n    get :a\n    delete 'f'\n  end\n\n  controller :other do\n    get 'b'\n    post 'something' => 'c'\n    put 'dee', :to => :d\n    get 'test_partial1'\n    get 'test_partial2'\n    get 'test_string_interp'\n  end\n\n  match 'e', :to => 'other#e', :as => 'eeeee'\n\n  get 'g' => 'other#g'\n\n  match 'blah/:id', :action => 'blarg' \n\n  # The priority is based upon order of creation:\n  # first created -> highest priority.\n\n  # Sample of regular route:\n  #   match 'products/:id' => 'catalog#view'\n  # Keep in mind you can assign values other than :controller and :action\n\n  # Sample of named route:\n  #   match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase\n  # This route can be invoked with purchase_url(:id => product.id)\n\n  # Sample resource route (maps HTTP verbs to controller actions automatically):\n  #   resources :products\n\n  # Sample resource route with options:\n  #   resources :products do\n  #     member do\n  #       get 'short'\n  #       post 'toggle'\n  #     end\n  #\n  #     collection do\n  #       get 'sold'\n  #     end\n  #   end\n\n  # Sample resource route with sub-resources:\n  #   resources :products do\n  #     resources :comments, :sales\n  #     resource :seller\n  #   end\n\n  # Sample resource route with more complex sub-resources\n  #   resources :products do\n  #     resources :comments\n  #     resources :sales do\n  #       get 'recent', :on => :collection\n  #     end\n  #   end\n\n  # Sample resource route within a namespace:\n  #   namespace :admin do\n  #     # Directs /admin/products/* to Admin::ProductsController\n  #     # (app/controllers/admin/products_controller.rb)\n  #     resources :products\n  #   end\n\n  # You can have the root of your site routed with \"root\"\n  # just remember to delete public/index.html.\n  # root :to => 'welcome#index'\n\n  # See how all your routes lay out with \"rake routes\"\n\n  # This is a legacy wild controller route that's not recommended for RESTful applications.\n  # Note: This route will make all actions in every controller accessible via GET requests.\n  # match ':controller(/:action(/:id(.:format)))'\nend\n"
  },
  {
    "path": "test/apps/rails3.1/config.ru",
    "content": "# This file is used by Rack-based servers to start the application.\n\nrequire ::File.expand_path('../config/environment',  __FILE__)\nrun Rails31::Application\n"
  },
  {
    "path": "test/apps/rails3.1/db/migrate/20110908172338_create_users.rb",
    "content": "class CreateUsers < ActiveRecord::Migration\n  def change\n    create_table :users do |t|\n      t.string :name\n      t.string :bio\n      t.string :password\n      t.string :email\n      t.string :role\n\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.1/db/seeds.rb",
    "content": "# This file should contain all the record creation needed to seed the database with its default values.\n# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).\n#\n# Examples:\n#\n#   cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])\n#   Mayor.create(:name => 'Emanuel', :city => cities.first)\n"
  },
  {
    "path": "test/apps/rails3.1/doc/README_FOR_APP",
    "content": "Use this README file to introduce your application and point to useful places in the API for learning more.\nRun \"rake doc:app\" to generate API documentation for your models, controllers, helpers, and libraries.\n"
  },
  {
    "path": "test/apps/rails3.1/lib/alib.rb",
    "content": "class Alib < $SOME_CONSTANT\nend\n"
  },
  {
    "path": "test/apps/rails3.1/lib/assets/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.1/lib/somelib.rb",
    "content": "class MyLib\n  def test_negative_array_index\n    #This should not cause an error, but it used to\n    [][-1]\n    [-1][-1]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.1/lib/tasks/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.1/log/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.1/public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The page you were looking for doesn't exist (404)</title>\n  <style type=\"text/css\">\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <h1>The page you were looking for doesn't exist.</h1>\n    <p>You may have mistyped the address or the page may have moved.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails3.1/public/422.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The change you wanted was rejected (422)</title>\n  <style type=\"text/css\">\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <h1>The change you wanted was rejected.</h1>\n    <p>Maybe you tried to change something you didn't have access to.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails3.1/public/500.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>We're sorry, but something went wrong (500)</title>\n  <style type=\"text/css\">\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <h1>We're sorry, but something went wrong.</h1>\n    <p>We've been notified about this issue and we'll take a look at it shortly.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails3.1/public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Ruby on Rails: Welcome aboard</title>\n    <style type=\"text/css\" media=\"screen\">\n      body {\n        margin: 0;\n        margin-bottom: 25px;\n        padding: 0;\n        background-color: #f0f0f0;\n        font-family: \"Lucida Grande\", \"Bitstream Vera Sans\", \"Verdana\";\n        font-size: 13px;\n        color: #333;\n      }\n\n      h1 {\n        font-size: 28px;\n        color: #000;\n      }\n\n      a  {color: #03c}\n      a:hover {\n        background-color: #03c;\n        color: white;\n        text-decoration: none;\n      }\n\n\n      #page {\n        background-color: #f0f0f0;\n        width: 750px;\n        margin: 0;\n        margin-left: auto;\n        margin-right: auto;\n      }\n\n      #content {\n        float: left;\n        background-color: white;\n        border: 3px solid #aaa;\n        border-top: none;\n        padding: 25px;\n        width: 500px;\n      }\n\n      #sidebar {\n        float: right;\n        width: 175px;\n      }\n\n      #footer {\n        clear: both;\n      }\n\n      #header, #about, #getting-started {\n        padding-left: 75px;\n        padding-right: 30px;\n      }\n\n\n      #header {\n        background-image: url(\"/assets/rails.png\");\n        background-repeat: no-repeat;\n        background-position: top left;\n        height: 64px;\n      }\n      #header h1, #header h2 {margin: 0}\n      #header h2 {\n        color: #888;\n        font-weight: normal;\n        font-size: 16px;\n      }\n\n\n      #about h3 {\n        margin: 0;\n        margin-bottom: 10px;\n        font-size: 14px;\n      }\n\n      #about-content {\n        background-color: #ffd;\n        border: 1px solid #fc0;\n        margin-left: -55px;\n        margin-right: -10px;\n      }\n      #about-content table {\n        margin-top: 10px;\n        margin-bottom: 10px;\n        font-size: 11px;\n        border-collapse: collapse;\n      }\n      #about-content td {\n        padding: 10px;\n        padding-top: 3px;\n        padding-bottom: 3px;\n      }\n      #about-content td.name  {color: #555}\n      #about-content td.value {color: #000}\n\n      #about-content ul {\n        padding: 0;\n        list-style-type: none;\n      }\n\n      #about-content.failure {\n        background-color: #fcc;\n        border: 1px solid #f00;\n      }\n      #about-content.failure p {\n        margin: 0;\n        padding: 10px;\n      }\n\n\n      #getting-started {\n        border-top: 1px solid #ccc;\n        margin-top: 25px;\n        padding-top: 15px;\n      }\n      #getting-started h1 {\n        margin: 0;\n        font-size: 20px;\n      }\n      #getting-started h2 {\n        margin: 0;\n        font-size: 14px;\n        font-weight: normal;\n        color: #333;\n        margin-bottom: 25px;\n      }\n      #getting-started ol {\n        margin-left: 0;\n        padding-left: 0;\n      }\n      #getting-started li {\n        font-size: 18px;\n        color: #888;\n        margin-bottom: 25px;\n      }\n      #getting-started li h2 {\n        margin: 0;\n        font-weight: normal;\n        font-size: 18px;\n        color: #333;\n      }\n      #getting-started li p {\n        color: #555;\n        font-size: 13px;\n      }\n\n\n      #sidebar ul {\n        margin-left: 0;\n        padding-left: 0;\n      }\n      #sidebar ul h3 {\n        margin-top: 25px;\n        font-size: 16px;\n        padding-bottom: 10px;\n        border-bottom: 1px solid #ccc;\n      }\n      #sidebar li {\n        list-style-type: none;\n      }\n      #sidebar ul.links li {\n        margin-bottom: 5px;\n      }\n\n      .filename {\n        font-style: italic;\n      }\n    </style>\n    <script type=\"text/javascript\">\n      function about() {\n        info = document.getElementById('about-content');\n        if (window.XMLHttpRequest)\n          { xhr = new XMLHttpRequest(); }\n        else\n          { xhr = new ActiveXObject(\"Microsoft.XMLHTTP\"); }\n        xhr.open(\"GET\",\"rails/info/properties\",false);\n        xhr.send(\"\");\n        info.innerHTML = xhr.responseText;\n        info.style.display = 'block'\n      }\n    </script>\n  </head>\n  <body>\n    <div id=\"page\">\n      <div id=\"sidebar\">\n        <ul id=\"sidebar-items\">\n          <li>\n            <h3>Browse the documentation</h3>\n            <ul class=\"links\">\n              <li><a href=\"http://guides.rubyonrails.org/\">Rails Guides</a></li>\n              <li><a href=\"http://api.rubyonrails.org/\">Rails API</a></li>\n              <li><a href=\"http://www.ruby-doc.org/core/\">Ruby core</a></li>\n              <li><a href=\"http://www.ruby-doc.org/stdlib/\">Ruby standard library</a></li>\n            </ul>\n          </li>\n        </ul>\n      </div>\n\n      <div id=\"content\">\n        <div id=\"header\">\n          <h1>Welcome aboard</h1>\n          <h2>You&rsquo;re riding Ruby on Rails!</h2>\n        </div>\n\n        <div id=\"about\">\n          <h3><a href=\"rails/info/properties\" onclick=\"about(); return false\">About your application&rsquo;s environment</a></h3>\n          <div id=\"about-content\" style=\"display: none\"></div>\n        </div>\n\n        <div id=\"getting-started\">\n          <h1>Getting started</h1>\n          <h2>Here&rsquo;s how to get rolling:</h2>\n\n          <ol>\n            <li>\n              <h2>Use <code>rails generate</code> to create your models and controllers</h2>\n              <p>To see all available options, run it without parameters.</p>\n            </li>\n\n            <li>\n              <h2>Set up a default route and remove <span class=\"filename\">public/index.html</span></h2>\n              <p>Routes are set up in <span class=\"filename\">config/routes.rb</span>.</p>\n            </li>\n\n            <li>\n              <h2>Create your database</h2>\n              <p>Run <code>rake db:create</code> to create your database. If you're not using SQLite (the default), edit <span class=\"filename\">config/database.yml</span> with your username and password.</p>\n            </li>\n          </ol>\n        </div>\n      </div>\n\n      <div id=\"footer\">&nbsp;</div>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/apps/rails3.1/public/robots.txt",
    "content": "# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file\n#\n# To ban all spiders from the entire site uncomment the next two lines:\n# User-Agent: *\n# Disallow: /\n"
  },
  {
    "path": "test/apps/rails3.1/script/rails",
    "content": "#!/usr/bin/env ruby\n# This command will automatically be run when you run \"rails\" with Rails 3 gems installed from the root of your application.\n\nAPP_PATH = File.expand_path('../../config/application',  __FILE__)\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'rails/commands'\n"
  },
  {
    "path": "test/apps/rails3.1/test/fixtures/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.1/test/fixtures/users.yml",
    "content": "# Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html\n\none:\n  name: MyString\n  bio: MyString\n  password: MyString\n  email: MyString\n  role: MyString\n\ntwo:\n  name: MyString\n  bio: MyString\n  password: MyString\n  email: MyString\n  role: MyString\n"
  },
  {
    "path": "test/apps/rails3.1/test/functional/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.1/test/functional/users_controller_test.rb",
    "content": "require 'test_helper'\n\nclass UsersControllerTest < ActionController::TestCase\n  setup do\n    @user = users(:one)\n  end\n\n  test \"should get index\" do\n    get :index\n    assert_response :success\n    assert_not_nil assigns(:users)\n  end\n\n  test \"should get new\" do\n    get :new\n    assert_response :success\n  end\n\n  test \"should create user\" do\n    assert_difference('User.count') do\n      post :create, :user => @user.attributes\n    end\n\n    assert_redirected_to user_path(assigns(:user))\n  end\n\n  test \"should show user\" do\n    get :show, :id => @user.to_param\n    assert_response :success\n  end\n\n  test \"should get edit\" do\n    get :edit, :id => @user.to_param\n    assert_response :success\n  end\n\n  test \"should update user\" do\n    put :update, :id => @user.to_param, :user => @user.attributes\n    assert_redirected_to user_path(assigns(:user))\n  end\n\n  test \"should destroy user\" do\n    assert_difference('User.count', -1) do\n      delete :destroy, :id => @user.to_param\n    end\n\n    assert_redirected_to users_path\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.1/test/integration/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.1/test/performance/browsing_test.rb",
    "content": "require 'test_helper'\nrequire 'rails/performance_test_help'\n\nclass BrowsingTest < ActionDispatch::PerformanceTest\n  # Refer to the documentation for all available options\n  # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory]\n  #                          :output => 'tmp/performance', :formats => [:flat] }\n\n  def test_homepage\n    get '/'\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.1/test/test_helper.rb",
    "content": "ENV[\"RAILS_ENV\"] = \"test\"\nrequire File.expand_path('../../config/environment', __FILE__)\nrequire 'rails/test_help'\n\nclass ActiveSupport::TestCase\n  # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.\n  #\n  # Note: You'll currently still have to declare fixtures explicitly in integration tests\n  # -- they do not yet inherit this setting\n  fixtures :all\n\n  # Add more helper methods to be used by all tests here...\nend\n"
  },
  {
    "path": "test/apps/rails3.1/test/unit/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.1/test/unit/helpers/users_helper_test.rb",
    "content": "require 'test_helper'\n\nclass UsersHelperTest < ActionView::TestCase\nend\n"
  },
  {
    "path": "test/apps/rails3.1/test/unit/user_test.rb",
    "content": "require 'test_helper'\n\nclass UserTest < ActiveSupport::TestCase\n  # test \"the truth\" do\n  #   assert true\n  # end\nend\n"
  },
  {
    "path": "test/apps/rails3.1/vendor/assets/stylesheets/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.1/vendor/plugins/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.2/Gemfile",
    "content": "source 'https://rubygems.org'\n\ngem 'rails', '3.2.9.rc2'\n\n# Bundle edge Rails instead:\n# gem 'rails', :git => 'git://github.com/rails/rails.git'\n\ngem 'sqlite3'\n\ngem 'json'\n\n# Gems used only for assets and not required\n# in production environments by default.\ngroup :assets do\n  gem 'sass-rails',   '~> 3.2.3'\n  gem 'coffee-rails', '~> 3.2.1'\n\n  # See https://github.com/sstephenson/execjs#readme for more supported runtimes\n  # gem 'therubyracer', :platforms => :ruby\n\n  gem 'uglifier', '>= 1.0.3'\nend\n\ngem 'jquery-rails'\n\n# To use ActiveModel has_secure_password\n# gem 'bcrypt-ruby', '~> 3.0.0'\n\n# To use Jbuilder templates for JSON\n# gem 'jbuilder'\n\n# Use unicorn as the app server\n# gem 'unicorn'\n\n# Deploy with Capistrano\n# gem 'capistrano'\n\n# To use debugger\n# gem 'ruby-debug'\n"
  },
  {
    "path": "test/apps/rails3.2/README.rdoc",
    "content": "== Welcome to Rails\n\nRails is a web-application framework that includes everything needed to create\ndatabase-backed web applications according to the Model-View-Control pattern.\n\nThis pattern splits the view (also called the presentation) into \"dumb\"\ntemplates that are primarily responsible for inserting pre-built data in between\nHTML tags. The model contains the \"smart\" domain objects (such as Account,\nProduct, Person, Post) that holds all the business logic and knows how to\npersist themselves to a database. The controller handles the incoming requests\n(such as Save New Account, Update Product, Show Post) by manipulating the model\nand directing data to the view.\n\nIn Rails, the model is handled by what's called an object-relational mapping\nlayer entitled Active Record. This layer allows you to present the data from\ndatabase rows as objects and embellish these data objects with business logic\nmethods. You can read more about Active Record in\nlink:files/vendor/rails/activerecord/README.html.\n\nThe controller and view are handled by the Action Pack, which handles both\nlayers by its two parts: Action View and Action Controller. These two layers\nare bundled in a single package due to their heavy interdependence. This is\nunlike the relationship between the Active Record and Action Pack that is much\nmore separate. Each of these packages can be used independently outside of\nRails. You can read more about Action Pack in\nlink:files/vendor/rails/actionpack/README.html.\n\n\n== Getting Started\n\n1. At the command prompt, create a new Rails application:\n       <tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)\n\n2. Change directory to <tt>myapp</tt> and start the web server:\n       <tt>cd myapp; rails server</tt> (run with --help for options)\n\n3. Go to http://localhost:3000/ and you'll see:\n       \"Welcome aboard: You're riding Ruby on Rails!\"\n\n4. Follow the guidelines to start developing your application. You can find\nthe following resources handy:\n\n* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html\n* Ruby on Rails Tutorial Book: http://www.railstutorial.org/\n\n\n== Debugging Rails\n\nSometimes your application goes wrong. Fortunately there are a lot of tools that\nwill help you debug it and get it back on the rails.\n\nFirst area to check is the application log files. Have \"tail -f\" commands\nrunning on the server.log and development.log. Rails will automatically display\ndebugging and runtime information to these files. Debugging info will also be\nshown in the browser on requests from 127.0.0.1.\n\nYou can also log your own messages directly into the log file from your code\nusing the Ruby logger class from inside your controllers. Example:\n\n  class WeblogController < ActionController::Base\n    def destroy\n      @weblog = Weblog.find(params[:id])\n      @weblog.destroy\n      logger.info(\"#{Time.now} Destroyed Weblog ID ##{@weblog.id}!\")\n    end\n  end\n\nThe result will be a message in your log file along the lines of:\n\n  Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!\n\nMore information on how to use the logger is at http://www.ruby-doc.org/core/\n\nAlso, Ruby documentation can be found at http://www.ruby-lang.org/. There are\nseveral books available online as well:\n\n* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)\n* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)\n\nThese two books will bring you up to speed on the Ruby language and also on\nprogramming in general.\n\n\n== Debugger\n\nDebugger support is available through the debugger command when you start your\nMongrel or WEBrick server with --debugger. This means that you can break out of\nexecution at any point in the code, investigate and change the model, and then,\nresume execution! You need to install ruby-debug to run the server in debugging\nmode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:\n\n  class WeblogController < ActionController::Base\n    def index\n      @posts = Post.all\n      debugger\n    end\n  end\n\nSo the controller will accept the action, run the first line, then present you\nwith a IRB prompt in the server window. Here you can do things like:\n\n  >> @posts.inspect\n  => \"[#<Post:0x14a6be8\n          @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,\n       #<Post:0x14a6620\n          @attributes={\"title\"=>\"Rails\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]\"\n  >> @posts.first.title = \"hello from a debugger\"\n  => \"hello from a debugger\"\n\n...and even better, you can examine how your runtime objects actually work:\n\n  >> f = @posts.first\n  => #<Post:0x13630c4 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>\n  >> f.\n  Display all 152 possibilities? (y or n)\n\nFinally, when you're ready to resume execution, you can enter \"cont\".\n\n\n== Console\n\nThe console is a Ruby shell, which allows you to interact with your\napplication's domain model. Here you'll have all parts of the application\nconfigured, just like it is when the application is running. You can inspect\ndomain models, change values, and save to the database. Starting the script\nwithout arguments will launch it in the development environment.\n\nTo start the console, run <tt>rails console</tt> from the application\ndirectory.\n\nOptions:\n\n* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications\n  made to the database.\n* Passing an environment name as an argument will load the corresponding\n  environment. Example: <tt>rails console production</tt>.\n\nTo reload your controllers and models after launching the console run\n<tt>reload!</tt>\n\nMore information about irb can be found at:\nlink:http://www.rubycentral.org/pickaxe/irb.html\n\n\n== dbconsole\n\nYou can go to the command line of your database directly through <tt>rails\ndbconsole</tt>. You would be connected to the database with the credentials\ndefined in database.yml. Starting the script without arguments will connect you\nto the development database. Passing an argument will connect you to a different\ndatabase, like <tt>rails dbconsole production</tt>. Currently works for MySQL,\nPostgreSQL and SQLite 3.\n\n== Description of Contents\n\nThe default directory structure of a generated Ruby on Rails application:\n\n  |-- app\n  |   |-- assets\n  |       |-- images\n  |       |-- javascripts\n  |       `-- stylesheets\n  |   |-- controllers\n  |   |-- helpers\n  |   |-- mailers\n  |   |-- models\n  |   `-- views\n  |       `-- layouts\n  |-- config\n  |   |-- environments\n  |   |-- initializers\n  |   `-- locales\n  |-- db\n  |-- doc\n  |-- lib\n  |   `-- tasks\n  |-- log\n  |-- public\n  |-- script\n  |-- test\n  |   |-- fixtures\n  |   |-- functional\n  |   |-- integration\n  |   |-- performance\n  |   `-- unit\n  |-- tmp\n  |   |-- cache\n  |   |-- pids\n  |   |-- sessions\n  |   `-- sockets\n  `-- vendor\n      |-- assets\n          `-- stylesheets\n      `-- plugins\n\napp\n  Holds all the code that's specific to this particular application.\n\napp/assets\n  Contains subdirectories for images, stylesheets, and JavaScript files.\n\napp/controllers\n  Holds controllers that should be named like weblogs_controller.rb for\n  automated URL mapping. All controllers should descend from\n  ApplicationController which itself descends from ActionController::Base.\n\napp/models\n  Holds models that should be named like post.rb. Models descend from\n  ActiveRecord::Base by default.\n\napp/views\n  Holds the template files for the view that should be named like\n  weblogs/index.html.erb for the WeblogsController#index action. All views use\n  eRuby syntax by default.\n\napp/views/layouts\n  Holds the template files for layouts to be used with views. This models the\n  common header/footer method of wrapping views. In your views, define a layout\n  using the <tt>layout :default</tt> and create a file named default.html.erb.\n  Inside default.html.erb, call <% yield %> to render the view using this\n  layout.\n\napp/helpers\n  Holds view helpers that should be named like weblogs_helper.rb. These are\n  generated for you automatically when using generators for controllers.\n  Helpers can be used to wrap functionality for your views into methods.\n\nconfig\n  Configuration files for the Rails environment, the routing map, the database,\n  and other dependencies.\n\ndb\n  Contains the database schema in schema.rb. db/migrate contains all the\n  sequence of Migrations for your schema.\n\ndoc\n  This directory is where your application documentation will be stored when\n  generated using <tt>rake doc:app</tt>\n\nlib\n  Application specific libraries. Basically, any kind of custom code that\n  doesn't belong under controllers, models, or helpers. This directory is in\n  the load path.\n\npublic\n  The directory available for the web server. Also contains the dispatchers and the\n  default HTML files. This should be set as the DOCUMENT_ROOT of your web\n  server.\n\nscript\n  Helper scripts for automation and generation.\n\ntest\n  Unit and functional tests along with fixtures. When using the rails generate\n  command, template test files will be generated for you and placed in this\n  directory.\n\nvendor\n  External libraries that the application depends on. Also includes the plugins\n  subdirectory. If the app has frozen rails, those gems also go here, under\n  vendor/rails/. This directory is in the load path.\n"
  },
  {
    "path": "test/apps/rails3.2/Rakefile",
    "content": "#!/usr/bin/env rake\n# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire File.expand_path('../config/application', __FILE__)\n\nRails32::Application.load_tasks\n"
  },
  {
    "path": "test/apps/rails3.2/app/assets/javascripts/application.js",
    "content": "// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//\n// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,\n// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.\n//\n// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the\n// the compiled file.\n//\n// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD\n// GO AFTER THE REQUIRES BELOW.\n//\n//= require jquery\n//= require jquery_ujs\n//= require_tree .\n"
  },
  {
    "path": "test/apps/rails3.2/app/assets/javascripts/users.js.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/\n"
  },
  {
    "path": "test/apps/rails3.2/app/assets/stylesheets/application.css",
    "content": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below.\n *\n * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,\n * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.\n *\n * You're free to add application-wide styles to this file and they'll appear at the top of the\n * compiled file, but it's generally better to create a new file per style scope.\n *\n *= require_self\n *= require_tree .\n */\n"
  },
  {
    "path": "test/apps/rails3.2/app/assets/stylesheets/scaffolds.css.scss",
    "content": "body {\n  background-color: #fff;\n  color: #333;\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 13px;\n  line-height: 18px;\n}\n\np, ol, ul, td {\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 13px;\n  line-height: 18px;\n}\n\npre {\n  background-color: #eee;\n  padding: 10px;\n  font-size: 11px;\n}\n\na {\n  color: #000;\n  &:visited {\n    color: #666;\n  }\n  &:hover {\n    color: #fff;\n    background-color: #000;\n  }\n}\n\ndiv {\n  &.field, &.actions {\n    margin-bottom: 10px;\n  }\n}\n\n#notice {\n  color: green;\n}\n\n.field_with_errors {\n  padding: 2px;\n  background-color: red;\n  display: table;\n}\n\n#error_explanation {\n  width: 450px;\n  border: 2px solid red;\n  padding: 7px;\n  padding-bottom: 0;\n  margin-bottom: 20px;\n  background-color: #f0f0f0;\n  h2 {\n    text-align: left;\n    font-weight: bold;\n    padding: 5px 5px 5px 15px;\n    font-size: 12px;\n    margin: -7px;\n    margin-bottom: 0px;\n    background-color: #c00;\n    color: #fff;\n  }\n  ul li {\n    font-size: 12px;\n    list-style: square;\n  }\n}\n"
  },
  {
    "path": "test/apps/rails3.2/app/assets/stylesheets/users.css.scss",
    "content": "// Place all the styles related to the Users controller here.\n// They will automatically be included in application.css.\n// You can use Sass (SCSS) here: http://sass-lang.com/\n"
  },
  {
    "path": "test/apps/rails3.2/app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::Base\n  protect_from_forgery\nend\n"
  },
  {
    "path": "test/apps/rails3.2/app/controllers/exec_controller/command_dependency.rb",
    "content": "class ExecController\n  def inner_exec\n    system params[:user_input]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.2/app/controllers/exec_controller.rb",
    "content": "class ExecController < ApplicationController\n  require_dependency \"exec_controller/command_dependency\"\n\n  def outer_exec\n    system params[:user_input]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.2/app/controllers/removal_controller.rb",
    "content": "class RemovalController < ApplicationController\n  def change_lines\n    <<-X\n    this\n    method\n    is\n    here\n    for line\n    numbers\n    X\n  end\n\n  def remove_this\n    redirect_to params[:url]\n  end\n\n  def remove_this_too\n    @some_input = raw params[:input]\n    @some_other_input = Account.first.name\n\n    render 'removal/controller_removed'\n  end\n\n  def implicit_render\n    @bad_stuff = raw params[:bad_stuff]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.2/app/controllers/users_controller.rb",
    "content": "class UsersController < ApplicationController\n  include UserControllerMixin\n\n  # GET /users\n  # GET /users.json\n  def index\n    @users = User.all\n\n    respond_to do |format|\n      format.html # index.html.erb\n      format.json { render :json => @users }\n    end\n  end\n\n  # GET /users/1\n  # GET /users/1.json\n  def show\n    @user = User.find(params[:id])\n    @user_data = raw params[:user_data]\n\n    respond_to do |format|\n      format.html # show.html.erb\n      format.json { render :json => @user }\n    end\n  end\n\n  # GET /users/new\n  # GET /users/new.json\n  def new\n    @user = User.new\n\n    respond_to do |format|\n      format.html # new.html.erb\n      format.json { render :json => @user }\n    end\n  end\n\n  # GET /users/1/edit\n  def edit\n    @user = User.find(params[:id])\n  end\n\n  # POST /users\n  # POST /users.json\n  def create\n    @user = User.new(params[:user])\n\n    respond_to do |format|\n      if @user.save\n        format.html { redirect_to @user, :notice => 'User was successfully created.' }\n        format.json { render :json => @user, :status => :created, :location => @user }\n      else\n        format.html { render :action => \"new\" }\n        format.json { render :json => @user.errors, :status => :unprocessable_entity }\n      end\n    end\n  end\n\n  # PUT /users/1\n  # PUT /users/1.json\n  def update\n    @user = User.find(params[:id])\n\n    respond_to do |format|\n      if @user.update_attributes(params[:user])\n        format.html { redirect_to @user, :notice => 'User was successfully updated.' }\n        format.json { head :no_content }\n      else\n        format.html { render :action => \"edit\" }\n        format.json { render :json => @user.errors, :status => :unprocessable_entity }\n      end\n    end\n  end\n\n  # DELETE /users/1\n  # DELETE /users/1.json\n  def destroy\n    @user = User.find(params[:id])\n    @user.destroy\n\n    respond_to do |format|\n      format.html { redirect_to users_url }\n      format.json { head :no_content }\n    end\n  end\n\n  def slimming\n    @user = User.find(params[:id])\n    @query = params[:query]\n  end\n\n  def show_detailed_exceptions?\n    false # no warning\n  end\n\n  def render_text\n    render :text => \"oh noes my service\"\n  end\n\n  def test_symbol_dos\n    params[:x].to_sym # no warning because this is an optional check\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.2/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails3.2/app/helpers/users_helper.rb",
    "content": "module UsersHelper\nend\n"
  },
  {
    "path": "test/apps/rails3.2/app/models/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.2/app/models/account.rb",
    "content": "class Account < ActiveRecord::Base\n  attr_accessible :plan_id, :banned \nend\n"
  },
  {
    "path": "test/apps/rails3.2/app/models/multi_model.rb",
    "content": "module MultiModel\n  class Model1 < ActiveRecord::Base\n\n  \tdef model_exec\n  \t  system params[:user_input]\n  \tend\n\n  end\n\n  class Model2 < ActiveRecord::Base\n\n  \tdef model_exec\n  \t  system params[:user_input2]\n  \tend\n\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.2/app/models/no_protection.rb",
    "content": "class NoProtection < ActiveRecord::Base\n  # Leave this class empty for Rescanner tests\nend\n"
  },
  {
    "path": "test/apps/rails3.2/app/models/user/command_dependency.rb",
    "content": "class User\n  def inner_exec\n    system params[:user_input]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.2/app/models/user.rb",
    "content": "class User < ActiveRecord::Base\n  require_dependency \"user/command_dependency\"\n\n  attr_accessible :bio, :name, :account_id, :admin, :status_id\nend\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>Rails32</title>\n  <%= stylesheet_link_tag    \"application\", :media => \"all\" %>\n  <%= javascript_include_tag \"application\" %>\n  <%= csrf_meta_tags %>\n</head>\n<body>\n\n<%= yield %>\n\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/removal/_partial.html.erb",
    "content": "<%= raw @some_other_input %>\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/removal/controller_removed.html.erb",
    "content": "<%= @some_input %>\n\n<%= render 'partial' %>\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/removal/implicit_render.html.erb",
    "content": "\n<%= @bad_stuff %>\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/users/_form.html.erb",
    "content": "You: <span><%= about %></span>\n\n<%= form_for(@user) do |f| %>\n  <% if @user.errors.any? %>\n    <div id=\"error_explanation\">\n      <h2><%= pluralize(@user.errors.count, \"error\") %> prohibited this user from being saved:</h2>\n\n      <ul>\n      <% @user.errors.full_messages.each do |msg| %>\n        <li><%= msg %></li>\n      <% end %>\n      </ul>\n    </div>\n  <% end %>\n\n  <div class=\"field\">\n    <%= f.label :name %><br />\n    <%= f.text_field :name %>\n  </div>\n  <div class=\"field\">\n    <%= f.label :bio %><br />\n    <%= f.text_field :bio %>\n  </div>\n  <div class=\"actions\">\n    <%= f.submit %>\n  </div>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/users/_slimmer.html.slim",
    "content": "- if some_value\n  div\n    = params[:escaped]\n- else\n  span\n    == params[:unescaped]\n\np== @user.profile\n\n- if x\n  = params[:unescaped]\n- else\n  = params[:escaped]\n\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/users/edit.html.erb",
    "content": "<h1>Editing user</h1>\n\n<%= render 'form', :locals => { :about => raw(@user.bio) } %>\n\n<%= link_to 'Show', @user %> |\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/users/index.html.erb",
    "content": "<h1>Listing users</h1>\n\n<table>\n  <tr>\n    <th>Name</th>\n    <th>Bio</th>\n    <th></th>\n    <th></th>\n    <th></th>\n  </tr>\n\n<% @users.each do |user| %>\n  <tr>\n    <td><%= user.name %></td>\n    <td><%= user.bio %></td>\n    <td><%= link_to 'Show', user %></td>\n    <td><%= link_to 'Edit', edit_user_path(user) %></td>\n    <td><%= link_to 'Destroy', user, :method => :delete, :data => { :confirm => 'Are you sure?' } %></td>\n  </tr>\n<% end %>\n</table>\n\n<br />\n\n<%= link_to 'New User', new_user_path %>\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/users/mixed_in.html.erb",
    "content": "<%= raw @user.something %>\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/users/new.html.erb",
    "content": "<h1>New user</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/users/sanitized.html.erb",
    "content": "<style>\n  <%= sanitize_css params[:css] %>\n</style>\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/users/show.html.erb",
    "content": "<p id=\"notice\"><%= notice %></p>\n\n<p>\n  <b>Name:</b>\n  <%= @user.name %>\n</p>\n\n<p>\n  <b>Bio:</b>\n  <%= @user.bio %>\n</p>\n\n<p>\n  <b>Other Thing:</b>\n  <%= @user_data %>\n</p>\n\n\n<%= link_to 'Edit', edit_user_path(@user) %> |\n<%= link_to 'Back', users_path %>\n\n<script>\n  var thing = <%= raw params.to_json %>;\n</script>\n"
  },
  {
    "path": "test/apps/rails3.2/app/views/users/slimming.html.slim",
    "content": "#content\n  .container\n    h2 Search for: #{{@query}}\n    p== @user.name\n\n  == render 'slimmer'\n"
  },
  {
    "path": "test/apps/rails3.2/config/application.rb",
    "content": "require File.expand_path('../boot', __FILE__)\n\nrequire 'rails/all'\n\nif defined?(Bundler)\n  # If you precompile assets before deploying to production, use this line\n  Bundler.require(*Rails.groups(:assets => %w(development test)))\n  # If you want your assets lazily compiled in production, use this line\n  # Bundler.require(:default, :assets, Rails.env)\nend\n\nmodule Rails32\n  class Application < Rails::Application\n    # Settings in config/environments/* take precedence over those specified here.\n    # Application configuration should go into files in config/initializers\n    # -- all .rb files in that directory are automatically loaded.\n\n    # Custom directories with classes and modules you want to be autoloadable.\n    # config.autoload_paths += %W(#{config.root}/extras)\n\n    # Only load the plugins named here, in the order given (default is alphabetical).\n    # :all can be used as a placeholder for all plugins not explicitly named.\n    # config.plugins = [ :exception_notification, :ssl_requirement, :all ]\n\n    # Activate observers that should always be running.\n    # config.active_record.observers = :cacher, :garbage_collector, :forum_observer\n\n    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.\n    # Run \"rake -D time\" for a list of tasks for finding time zone names. Default is UTC.\n    # config.time_zone = 'Central Time (US & Canada)'\n\n    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.\n    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]\n    # config.i18n.default_locale = :de\n\n    # Configure the default encoding used in templates for Ruby 1.9.\n    config.encoding = \"utf-8\"\n\n    # Configure sensitive parameters which will be filtered from the log file.\n    config.filter_parameters += [:password]\n\n    # Enable escaping HTML in JSON.\n    config.active_support.escape_html_entities_in_json = true\n\n    # Use SQL instead of Active Record's schema dumper when creating the database.\n    # This is necessary if your schema can't be completely dumped by the schema dumper,\n    # like if you have constraints or database-specific column types\n    # config.active_record.schema_format = :sql\n\n    # Enforce whitelist mode for mass assignment.\n    # This will create an empty whitelist of attributes available for mass-assignment for all models\n    # in your app. As such, your models will need to explicitly whitelist or blacklist accessible\n    # parameters by using an attr_accessible or attr_protected declaration.\n    config.active_record.whitelist_attributes = false\n\n    # Enable the asset pipeline\n    config.assets.enabled = true\n\n    # Version of your assets, change this if you want to expire all your assets\n    config.assets.version = '1.0'\n\n    config.mess_things_up_in_prod = ActiveSupport::OrderedOptions.new\n\n    config.actually_a_hash = {}\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.2/config/boot.rb",
    "content": "require 'rubygems'\n\n# Set up gems listed in the Gemfile.\nENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)\n\nrequire 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])\n"
  },
  {
    "path": "test/apps/rails3.2/config/database.yml",
    "content": "# SQLite version 3.x\n#   gem install sqlite3\n#\n#   Ensure the SQLite 3 gem is defined in your Gemfile\n#   gem 'sqlite3'\ndevelopment:\n  adapter: sqlite3\n  database: db/development.sqlite3\n  pool: 5\n  timeout: 5000\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  adapter: sqlite3\n  database: db/test.sqlite3\n  pool: 5\n  timeout: 5000\n\nproduction:\n  adapter: sqlite3\n  database: db/production.sqlite3\n  pool: 5\n  timeout: 5000\n"
  },
  {
    "path": "test/apps/rails3.2/config/environment.rb",
    "content": "# Load the rails application\nrequire File.expand_path('../application', __FILE__)\n\n# Initialize the rails application\nRails32::Application.initialize!\n"
  },
  {
    "path": "test/apps/rails3.2/config/environments/development.rb",
    "content": "Rails32::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # In the development environment your application's code is reloaded on\n  # every request. This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes = false\n\n  # Log error messages when you accidentally call methods on nil.\n  config.whiny_nils = true\n\n  # Show full error reports and disable caching\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Don't care if the mailer can't send\n  config.action_mailer.raise_delivery_errors = false\n\n  # Print deprecation notices to the Rails logger\n  config.active_support.deprecation = :log\n\n  # Only use best-standards-support built into browsers\n  config.action_dispatch.best_standards_support = :builtin\n\n  # Raise exception on mass assignment protection for Active Record models\n  config.active_record.mass_assignment_sanitizer = :strict\n\n  # Log the query plan for queries taking more than this (works\n  # with SQLite, MySQL, and PostgreSQL)\n  config.active_record.auto_explain_threshold_in_seconds = 0.5\n\n  # Do not compress assets\n  config.assets.compress = false\n\n  # Expands the lines which load the assets\n  config.assets.debug = true\nend\n"
  },
  {
    "path": "test/apps/rails3.2/config/environments/production.rb",
    "content": "Rails32::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # Code is not reloaded between requests\n  config.cache_classes = true\n\n  # Full error reports are disabled and caching is turned on\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Disable Rails's static asset server (Apache or nginx will already do this)\n  config.serve_static_assets = false\n\n  # Compress JavaScripts and CSS\n  config.assets.compress = true\n\n  # Don't fallback to assets pipeline if a precompiled asset is missed\n  config.assets.compile = true\n\n  # Generate digests for assets URLs\n  config.assets.digest = true\n\n  # Defaults to nil and saved in location specified by config.assets.prefix\n  # config.assets.manifest = YOUR_PATH\n\n  # Specifies the header that your server uses for sending files\n  # config.action_dispatch.x_sendfile_header = \"X-Sendfile\" # for apache\n  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  # config.force_ssl = true\n\n  # See everything in the log (default is :info)\n  # config.log_level = :debug\n\n  # Prepend all log lines with the following tags\n  # config.log_tags = [ :subdomain, :uuid ]\n\n  # Use a different logger for distributed setups\n  # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)\n\n  # Use a different cache store in production\n  # config.cache_store = :mem_cache_store\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server\n  # config.action_controller.asset_host = \"http://assets.example.com\"\n\n  # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)\n  # config.assets.precompile += %w( search.js )\n\n  # Disable delivery errors, bad email addresses will be ignored\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable threaded mode\n  # config.threadsafe!\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation can not be found)\n  config.i18n.fallbacks = true\n\n  # Send deprecation notices to registered listeners\n  config.active_support.deprecation = :notify\n\n  # Log the query plan for queries taking more than this (works\n  # with SQLite, MySQL, and PostgreSQL)\n  # config.active_record.auto_explain_threshold_in_seconds = 0.5\n  config.active_record.whitelist_attributes = true\n\n  # These configs actually resolve to values from application.rb\n  # but that's atypical, so going to skip for now or else errors\n  config.mess_things_up_in_prod.right_here = :nope_ignored\n  config.actually_a_hash[:thing] = 'cool setting'\nend\n"
  },
  {
    "path": "test/apps/rails3.2/config/environments/test.rb",
    "content": "Rails32::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb\n\n  # The test environment is used exclusively to run your application's\n  # test suite. You never need to work with it otherwise. Remember that\n  # your test database is \"scratch space\" for the test suite and is wiped\n  # and recreated between test runs. Don't rely on the data there!\n  config.cache_classes = true\n\n  # Configure static asset server for tests with Cache-Control for performance\n  config.serve_static_assets = true\n  config.static_cache_control = \"public, max-age=3600\"\n\n  # Log error messages when you accidentally call methods on nil\n  config.whiny_nils = true\n\n  # Show full error reports and disable caching\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Raise exceptions instead of rendering exception templates\n  config.action_dispatch.show_exceptions = false\n\n  # Disable request forgery protection in test environment\n  config.action_controller.allow_forgery_protection    = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Raise exception on mass assignment protection for Active Record models\n  config.active_record.mass_assignment_sanitizer = :strict\n\n  # Print deprecation notices to the stderr\n  config.active_support.deprecation = :stderr\nend\n"
  },
  {
    "path": "test/apps/rails3.2/config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "test/apps/rails3.2/config/initializers/header_dos_protection.rb",
    "content": "# https://groups.google.com/d/msg/ruby-security-ann/A-ebV4WxzKg/KNPTbX8XAQUJ\nActiveSupport.on_load(:action_view) do \n  ActionView::LookupContext::DetailsKey.class_eval do \n    class << self \n      alias :old_get :get \n\n      def get(details) \n        if details[:formats] \n          details = details.dup \n          syms    = Set.new Mime::SET.symbols \n          details[:formats] = details[:formats].select { |v| \n            syms.include? v \n          } \n        end \n        old_get details \n      end \n    end \n  end \nend \n"
  },
  {
    "path": "test/apps/rails3.2/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format\n# (all these examples are active by default):\n# ActiveSupport::Inflector.inflections do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n#\n# These inflection rules are supported but not enabled by default:\n# ActiveSupport::Inflector.inflections do |inflect|\n#   inflect.acronym 'RESTful'\n# end\n"
  },
  {
    "path": "test/apps/rails3.2/config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n# Mime::Type.register_alias \"text/html\", :iphone\n"
  },
  {
    "path": "test/apps/rails3.2/config/initializers/secret_token.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n# Make sure the secret is at least 30 characters and all random,\n# no regular words or you'll be exposed to dictionary attacks.\nRails32::Application.config.secret_token = 'e721d0d7e8e912026b379d7219b5947da6a954f6c1b7c09ab7b44b873346ee17a780890e6d034fe6bd5ac52cced7b4ebe1971c3f34d0d1e735302b0bd4a0bd62'\n"
  },
  {
    "path": "test/apps/rails3.2/config/initializers/session_store.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\nRails32::Application.config.session_store :cookie_store, :key => '_rails3.2_session'\n\n# Use the database for sessions instead of the cookie-based default,\n# which shouldn't be used to store highly confidential information\n# (create the session table with \"rails generate session_migration\")\n# Rails32::Application.config.session_store :active_record_store\n"
  },
  {
    "path": "test/apps/rails3.2/config/initializers/wrap_parameters.rb",
    "content": "# Be sure to restart your server when you modify this file.\n#\n# This file contains settings for ActionController::ParamsWrapper which\n# is enabled by default.\n\n# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.\nActiveSupport.on_load(:action_controller) do\n  wrap_parameters :format => [:json]\nend\n\n# Disable root element in JSON by default.\nActiveSupport.on_load(:active_record) do\n  self.include_root_in_json = false\nend\n"
  },
  {
    "path": "test/apps/rails3.2/config/locales/en.yml",
    "content": "# Sample localization file for English. Add more files in this directory for other locales.\n# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/apps/rails3.2/config/routes.rb",
    "content": "Rails32::Application.routes.draw do\n  resources :users do\n    get 'mixed_in'\n\n    member do\n      put 'update_password' => redirect('/settings/update_password')\n    end\n  end\n\n  match 'remove' => 'removal#remove_this_too'\n  match 'implicit' => 'removal#implicit_render'\n\n  match 'exec' => 'exec#exec_this'\n\n  # Routes for 'Default Routes' tests\n  get \"/glob/*action\", controller: 'glob_get'\n  post '/glob_post/*action', controller: 'glob_post'\n  put 'glob_put/*action', controller: 'glob_put'\n  match \"/glob/*action\", controller: 'glob_match'\n  get \"/foo_get/:action\", controller: 'foo_get'\n  post \"/foo_post/:action\", controller: 'foo_post'\n  put 'foo_put/:action', controller: 'foo_put'\n  match '/bar/:action', controller: 'bar_match'\n\n  # The priority is based upon order of creation:\n  # first created -> highest priority.\n\n  # Sample of regular route:\n  #   match 'products/:id' => 'catalog#view'\n  # Keep in mind you can assign values other than :controller and :action\n\n  # Sample of named route:\n  #   match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase\n  # This route can be invoked with purchase_url(:id => product.id)\n\n  # Sample resource route (maps HTTP verbs to controller actions automatically):\n  #   resources :products\n\n  # Sample resource route with options:\n  #   resources :products do\n  #     member do\n  #       get 'short'\n  #       post 'toggle'\n  #     end\n  #\n  #     collection do\n  #       get 'sold'\n  #     end\n  #   end\n\n  # Sample resource route with sub-resources:\n  #   resources :products do\n  #     resources :comments, :sales\n  #     resource :seller\n  #   end\n\n  # Sample resource route with more complex sub-resources\n  #   resources :products do\n  #     resources :comments\n  #     resources :sales do\n  #       get 'recent', :on => :collection\n  #     end\n  #   end\n\n  # Sample resource route within a namespace:\n  #   namespace :admin do\n  #     # Directs /admin/products/* to Admin::ProductsController\n  #     # (app/controllers/admin/products_controller.rb)\n  #     resources :products\n  #   end\n\n  # You can have the root of your site routed with \"root\"\n  # just remember to delete public/index.html.\n  # root :to => 'welcome#index'\n\n  # See how all your routes lay out with \"rake routes\"\n\n  # This is a legacy wild controller route that's not recommended for RESTful applications.\n  # Note: This route will make all actions in every controller accessible via GET requests.\n  # match ':controller(/:action(/:id))(.:format)'\nend\n"
  },
  {
    "path": "test/apps/rails3.2/config.ru",
    "content": "# This file is used by Rack-based servers to start the application.\n\nrequire ::File.expand_path('../config/environment',  __FILE__)\nrun Rails32::Application\n"
  },
  {
    "path": "test/apps/rails3.2/lib/assets/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.2/lib/tasks/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails3.2/lib/user_controller_mixin.rb",
    "content": "module UserControllerMixin\n  def mixed_in\n    @user = User.find(params[:id])\n  end\n\n  def [] index\n  end\nend\n"
  },
  {
    "path": "test/apps/rails3.2/script/rails",
    "content": "#!/usr/bin/env ruby\n# This command will automatically be run when you run \"rails\" with Rails 3 gems installed from the root of your application.\n\nAPP_PATH = File.expand_path('../../config/application',  __FILE__)\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'rails/commands'\n"
  },
  {
    "path": "test/apps/rails4/.gitignore",
    "content": "# See http://help.github.com/ignore-files/ for more about ignoring files.\n#\n# If you find yourself ignoring temporary files generated by your text editor\n# or operating system, you probably want to add a global ignore instead:\n#   git config --global core.excludesfile '~/.gitignore_global'\n\n# Ignore bundler config.\n/.bundle\n\n# Ignore the default SQLite database.\n/db/*.sqlite3\n/db/*.sqlite3-journal\n\n# Ignore all logfiles and tempfiles.\n/log/*.log\n/tmp\n"
  },
  {
    "path": "test/apps/rails4/Gemfile",
    "content": "source 'https://rubygems.org'\n\n# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'\ngem 'rails', '4.0.0'\n\ngem 'pg'\n\ngem 'haml'\n\n# Gems used only for assets and not required\n# in production environments by default.\ngroup :assets do\n  gem 'sass-rails',   '~> 4.0.0'\n  gem 'coffee-rails', '~> 4.0.0'\n\n  # See https://github.com/sstephenson/execjs#readme for more supported runtimes\n  # gem 'therubyracer', platforms: :ruby\n\n  gem 'uglifier', '>= 1.0.3'\nend\n\ngem 'jquery-rails'\n\n# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks\ngem 'turbolinks'\n\n# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder\ngem 'jbuilder', '~> 1.0.1'\n\ngem people do\n  strange things\nend\n# To use ActiveModel has_secure_password\n# gem 'bcrypt-ruby', '~> 3.0.0'\n\n# Use unicorn as the app server\n# gem 'unicorn'\n\n# Deploy with Capistrano\n# gem 'capistrano', group: :development\n\n# To use debugger\n# gem 'debugger'\n"
  },
  {
    "path": "test/apps/rails4/README.rdoc",
    "content": "== README\n\nThis README would normally document whatever steps are necessary to get the\napplication up and running.\n\nThings you may want to cover:\n\n* Ruby version\n\n* System dependencies\n\n* Configuration\n\n* Database creation\n\n* Database initialization\n\n* How to run the test suite\n\n* Services (job queues, cache servers, search engines, etc.)\n\n* Deployment instructions\n\n* ...\n\n\nPlease feel free to use a different markup language if you do not plan to run\n<tt>rake doc:app</tt>.\n"
  },
  {
    "path": "test/apps/rails4/Rakefile",
    "content": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire File.expand_path('../config/application', __FILE__)\n\nRails4::Application.load_tasks\n"
  },
  {
    "path": "test/apps/rails4/app/api/api.rb",
    "content": "module API\n\n  def insecure_command_execution\n    Open3.capture2 \"ls #{params[:dir]}\"\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4/app/assets/javascripts/application.js",
    "content": "// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//\n// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,\n// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.\n//\n// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the\n// compiled file.\n//\n// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD\n// GO AFTER THE REQUIRES BELOW.\n//\n//= require jquery\n//= require jquery_ujs\n//= require turbolinks\n//= require_tree .\n"
  },
  {
    "path": "test/apps/rails4/app/assets/stylesheets/application.css",
    "content": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below.\n *\n * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,\n * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.\n *\n * You're free to add application-wide styles to this file and they'll appear at the top of the\n * compiled file, but it's generally better to create a new file per style scope.\n *\n *= require_self\n *= require_tree .\n */\n"
  },
  {
    "path": "test/apps/rails4/app/controllers/another_controller.rb",
    "content": "class AnotherController < ApplicationController\n  def overflow\n    @test = @test.where.all\n  end\n\n  before_filter do\n    eval params[:x]\n  end\n\n  skip_before_action :set_bad_thing, :except => [:also_use_bad_thing]\n\n  def use_bad_thing\n    # This should not warn, because the filter is skipped!\n    User.where(@bad_thing)\n  end\n\n  def also_use_bad_thing\n    `#{@bad_thing}`\n  end\n\n  def render_stuff\n    user_name = User.current_user.name\n\n    render :text => \"Welcome back, #{params[:name]}!}\"\n    render :text => \"Welcome back, #{user_name}!}\"\n    render :text => params[:q]\n    render :text => user_name\n\n    render :inline => \"<%= #{params[:name]} %>\"\n    render :inline => \"<%= #{user_name} %>\"\n\n    render :text => CGI.escapeHTML(params[:q]) # should not warn\n    render :text => \"Welcome back, #{CGI::escapeHTML(params[:name])}!}\" # should not warn\n    render :text => params[:q], :content_type => \"text/plain\" # should not warn\n  end\n\n  def use_params_in_regex\n    @x = something.match /#{params[:x]}/\n  end\n\n  def building_strings_for_sql\n    query = \"SELECT * FROM users WHERE\"\n\n    if params[:search].to_i == 1\n      query << \" role = 'admin'\"\n    else\n      query << \" role = 'admin' \" + params[:search]\n    end\n\n    begin\n      result = {:result => User.find_by_sql(query) }\n    rescue\n      result = {}\n    end\n\n    render json: result.as_json\n  end\n\n  def safe_renders\n      render params[:action]\n      render params[:controller]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4/app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::API\n  # Prevent CSRF attacks by raising an exception.\n  # For APIs, you may want to use :null_session instead.\n  # protect_from_forgery with: :exception\n\n  def show_detailed_exceptions?\n    true\n  end\n\n  def redirect_to_created_model\n    if create\n      @model = User.create\n      @model.save!\n      redirect_to @model\n    else\n      @model = User.create!\n      @model.save\n      redirect_to @model\n    end\n  end\n\n  def bypass_ssl_check\n    # Should warn on self.verify_mode = OpenSSL::SSL::VERIFY_NONE\n    self.verify_mode = OpenSSL::SSL::VERIFY_NONE\n  end\n\n  before_action :set_bad_thing\n\n  def set_bad_thing\n    @bad_thing = params[:x]\n  end\n\n  def wrong_redirect_only_path\n    redirect_to(params.bla.merge(:only_path => true, :display => nil))\n  end\n\n  def redirect_only_path_with_unsafe_hash\n    redirect_to(params.to_unsafe_hash.merge(:only_path => true, :display => nil))\n  end\n\n  def redirect_only_path_with_unsafe_h\n    redirect_to(params.to_unsafe_h.merge(:only_path => true, :display => nil))\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4/app/controllers/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/app/controllers/friendly_controller.rb",
    "content": "class FriendlyController\n  some_helper_thing do\n    @user = User.current_user\n  end\n\n  def find\n    @user = User.friendly.find(params[:id])\n    redirect_to @user\n  end\n\n  def some_user_thing\n    redirect_to @user.url\n  end\n\n  def try_and_send\n    User.stuff.try(:where, params[:query])\n    User.send(:from, params[:table]).all\n  end\n\n  def mass_assign_user\n    # Should warn about permit!\n    x = params.permit!\n    @user = User.new(x)\n  end\n\n  def mass_assign_protected_model\n    # Warns with medium confidence because Account uses attr_accessible\n    params.permit!\n    Account.new(params)\n  end\n\n  def permit_without_usage\n    # Warns with medium confidence because there is no mass assignment\n    params.permit!\n  end\n\n  def permit_after_usage\n    # Warns with medium confidence because permit! is called after mass assignment\n    User.new(params)\n    params.permit!\n  end\n\n  def sql_with_exec\n    User.connection.select_values <<-SQL\n      SELECT id FROM collection_items\n        WHERE id > #{last_collection_item.id}\n          AND collection_id IN (#{destinations.map { |d| d.id}.join(',')})\"\n    SQL\n\n    Account.connection.select_rows(\"select thing.id, count(*) from things_stuff toc\n                                     join things dotoc on (toc.id=dotoc.toc_id)\n                                     join things do on (dotoc.data_object_id=do.id)\n                                     join thing_entries dohe on do.id = dohe.data_object_id\n                                     where do.published=#{params[:published]} and dohe.visibility_id=#{something.id} group by toc.id\")\n  end\n\n  def redirect_to_some_places\n    if something\n      redirect_to params.merge(:host => \"example.com\") # Should not warn\n    elsif something_else\n      redirect_to params.merge(:host => User.canonical_url) # Should not warn\n    else\n      redirect_to params.merge(:host => params[:host]) # Should warn\n    end\n  end\n\n  def select_some_stuff\n    User.select(:name, params[:x])\n  end\n\n  def send_some_stuff\n    blah.send(params[:x]).to_json\n  end\n\n  private def private_some_stuff\n    eval params[:what_is_this_java?]\n  end\n\n  def where_hashes\n    User.where('stuff' => params[:stuff]) # no warning\n    User.where(params[:key] => params[:stuff]) # warn\n  end\n\n  def whitelistit\n    whitelist = [\"Post\", \"Comments\"]\n    whitelisted_class_name = whitelist.detect {|k| k == params[:a]}\n    if whitelisted_class_name.nil?\n      raise \"Nope!\"\n    else\n      whitelisted_class_name.constantize\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4/app/controllers/mixed_controller.rb",
    "content": "class MixedController < ApplicationController\n    include ProxyThing::Proxied\nend\n"
  },
  {
    "path": "test/apps/rails4/app/controllers/mixed_in_proxy.rb",
    "content": "module ProxyThing\n  class X; end\n\n  module Proxied\n    def self.included(controller)\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4/app/controllers/users_controller.rb",
    "content": "class UsersController < ApplicationController\n  def test_sql_sanitize\n    User.where(\"age > #{sanitize params[:age]}\")\n  end\n\n  before_action :set_page\n\n  prepend_before_action :safe_set_page, :only => :test_prepend_before_action\n  append_before_action :safe_set_page, :only => :test_append_before_action\n\n  skip_before_action :verify_authenticity_token, :except => [:unsafe_stuff]\n\n  def test_before_action\n    render @page\n  end\n\n  # Call safe_set_page then set_page\n  def test_prepend_before_action\n    render @page # should not be safe\n  end\n\n  # Call set_page then safe_set_page\n  def test_append_before_action\n    render @page # should be safe\n  end\n\n  def set_page\n    @page = params[:page]\n  end\n\n  def safe_set_page\n    @page = :cool_page_bro\n  end\n\n  def redirect_to_model\n    # None of these should warn in Rails 4\n    if stuff\n      redirect_to User.find_by(:name => params[:name])\n    elsif other_stuff\n      redirect_to User.find_by!(:name => params[:name])\n    else\n      redirect_to User.where(:stuff => 1).take\n    end\n  end\n\n  def find_by_stuff\n    User.find_by \"age > #{params[:age_limit]}\"\n    User.find_by! params[:user_search]\n  end\n\n  def symbolize_safe_parameters\n    params[:controller].to_sym\n    params[:action].intern && params[:controller][/([^\\/]+)$/].try(:to_sym)\n  end\n\n  def mass_assignment_bypass\n    User.create_with(params)  # high warning\n    User.create_with(params).create # high warning\n    User.create_with(params[:x].permit(:y)) # should not warn, workaround\n    something.create_with({}) # should not warn on hash literals\n    x.create_with(y(params))  # medium warning\n    y.create_with(x)          # weak warning\n  end\n\n  def email_finds\n    Email.find_by_id! params[:email][:id]\n  end\n\n  def case_statement\n    @x = case params[:x]\n         when :yes\n           \"yep\"\n         when :no\n           \"nope\"\n         else\n           \"dunno\"\n         end\n  end\n\n  def open_stuff\n    open(params[:url]) # remote code execution warning\n    Kernel.open(URI(params[:url])) # file access and RCE warning\n    open(\"#{params[:x]}/something/something\") # remote code execution warning\n    open(\"some_path/#{params[:x]}/something/something\") # file access warning\n  end\n\n  def eval_it\n    @x = eval(params[:x])\n  end\n\n  def session_key\n    session[params[:x]] = params[:y]\n    session[\"blah-#{params[:token]}\"] = user.thing\n  end\n\n  def hash_some_things\n    Digest::MD5.base64digest(params[:password])\n    Digest::HMAC.new('that', 'thing', Digest::SHA1)\n    Digest::SHA1.new.update(thing)\n    Digest::SHA1.digest(current_user.password + current_user.salt)[0,15]\n\n    OpenSSL::Digest::Digest.new('md5')\n    OpenSSL::Digest.new(\"SHA1\")\n    OpenSSL::Digest::MD5.digest(password)\n  end\n\n  def redirector\n    redirect_to current_user.place.find(params[:p])\n  end\n\n  def more_haml\n  end\n\n  def without\n    User.new({username: \"jjconti\", admin: false}, without_protection: true)\n  end\n\n  def permit_in_sql\n    User.find_by(params.permit(:OMG)) # Don't warn\n    User.find_by(params.permit(:OMG)[:OMG]) # Warn\n    User.where(\"#{params.permit(:OMG)}\") # Warn\n  end\n\n  def exists_with_to_s\n    User.exists? params[:x].to_s # Don't warn\n  end\n\n  def find_and_create_em\n    # These all call find_by(), which we already know is dangerous\n    User.find_or_create_by(params[:user])\n    User.find_or_create_by!(params[:user])\n    User.find_or_initialize_by(params[:user])\n  end\n\n  def email_find_by\n    Email.find_by id: params[:email][:id]\n    Email.find_by! id: params[:email][:id]\n  end\n\n  def haml_test; end\nend\n"
  },
  {
    "path": "test/apps/rails4/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails4/app/mailers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/app/models/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/app/models/account.rb",
    "content": "class Account < ActiveRecord::Base\n  attr_accessible :name, :account_id, :admin\n\n  def sql_it_up_yeah\n    connection.exec_update \"UPDATE `purchases` SET type = '#{self.type}' WHERE id = '#{self.id}'\"\n\n    sql = \"UPDATE #{self.class.table_name} SET latest_version = #{version} where id = #{self.id}\"\n    self.class.connection.execute sql\n  end\n\n  def self.more_sql_connection\n    self.connection.exec_query \"UPDATE `purchases` SET type = '#{self.type}' WHERE id = '#{self.id}'\"\n  end\n\n  def safe_sql_should_not_warn\n    self.class.connection.execute \"DESCRIBE  #{self.business_object.table_name}\"\n    connection.select_one \"SELECT * FROM somewhere WHERE x=#{connection.quote(params[:x])}\"\n    connection.execute \"DELETE FROM stuff WHERE id=#{self.id}\"\n  end\n\n  def lots_of_string_building_sql\n    sql =\n      'SELECT count(*) as account_count, '+\n      'FROM account_links stuff_links '+\n      \"WHERE account_links.stuff_id = #{@stuff.id} \"\n\n    if params[:more_ids]\n      sql += \" AND stuff IN \"+\n        \"(SELECT something_id \"+\n        \"FROM some_join_thing \"+\n        \"WHERE something_id IN (#{params[:more_ids].join(',')}))\"\n    end\n    sql += \"GROUP BY title, id \"\n    Account.connection.select_all(sql)\n  end\n\n  def self.get_all_countries(locale)\n    q = \"country_#{locale} ASC\".to_s\n    c = User.order(q)\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4/app/models/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/app/models/email.rb",
    "content": "class Email < ActiveRecord::Base\n  attr_accessible :email\n\n  belongs_to :user\n\n  EMAIL_REGEX = /^[a-z0-9]+@[a-z0-9]+\\.[a-z]+$/\n\n  validates_format_of :email, with: EMAIL_REGEX\n\n  scope :assigned_to_user, ->(user) {\n    task_table = User.table_name\n\n    joins(\"INNER JOIN #{task_table}\n          ON  #{task_table}.user_id = #{user.id}\n          AND (#{task_table}.type_id = #{table_name}.type_id)\n          AND (#{task_table}.manager_id = #{table_name}.manager_id)\n          \")\n  }\nend\n"
  },
  {
    "path": "test/apps/rails4/app/models/phone.rb",
    "content": "class Phone < ActiveRecord::Base\n  PHONE_NUMBER_REGEXP = %r{\n    \\A\n    +\\d+ # counter prefix\n    \\ * # space\n    \\(\\d+\\) # city code\n    \\ * # space\n    (\\d+-)*\\d+\n    \\z\n  }x\n  validates_format_of :number, with: PHONE_NUMBER_REGEXP\nend\n"
  },
  {
    "path": "test/apps/rails4/app/models/recursive/stack_level.rb",
    "content": "class Exception < Exception\nend\n\nclass DescendentException < Exception\nend\n\nclass ExceptionA < ExceptionB\nend\n\nclass ExceptionB < ExceptionA\nend\n"
  },
  {
    "path": "test/apps/rails4/app/models/user.rb",
    "content": "class User < ActiveRecord::Base\n  def test_sql_sanitize(x)\n    self.select(\"#{sanitize(x)}\")\n  end\n\n  scope :hits_by_ip, ->(ip,col=\"*\") { select(\"#{col}\").order(\"id DESC\") }\n\n  def arel_exists\n    where(User.where(User.arel_table[:object_id].eq(arel_table[:id])).exists)\n  end\n\n  def symbol_stuff\n    self.where(User.table_name.to_sym)\n  end\n\n  scope :sorted_by, ->(field, asc) {\n    asc = ['desc', 'asc'].include?(asc) ? asc : 'asc'\n\n    ordering = if field == 'extension'\n                 \"substring_index(#{table_name}.data_file_name, '.', -1) #{asc}\"\n               elsif SORTABLE_COLUMNS.include?(field)\n                 { field.to_sym => asc.to_sym }\n               end\n    order(ordering) # should not warn about `asc` interpolation\n  }\n\n  def much_arel # None of these should warn\n    group_recipient = User.joins(:group).where(User.arel_table[:message_id].eq Email.arel_table[:id])\n    group_with_special_property = group_recipient.where(:groups => { :private => false, :special_property => true })\n    Email.where(group_recipient.exists.not.or(group_with_special_property.exists))\n\n    User.select('users.id').joins(User.joins(:deal_purchases).join_sources).where(Email.arel_table[:created_at].gt(last_activity)).group('users.id')\n    User.where(User.joins(:group).where(User.arel_table[:message_id].eq arel_table[:id]))\n    User.from(User.all)\n    User.from(User.active.order(created_at: :desc).limit(30))\n    User.from(User.active.order(created_at: :desc))\n    User.from(User.project(users[:age].average.as(\"mean_age\")))\n    User.from(User.group(user[:user_id]).having(thing[:id].count.gt(5)))\n  end\n\n  def self.encrypt_pass password\n    Base64.encode64(Digest::MD5.hexdigest(password))\n  end\n\n  accepts_nested_attributes_for :something, allow_destroy: false, reject_if: proc { |attributes| stuff }\n\n  def more_symbol_stuff stuff\n    User.find(stuff).attributes.symbolize_keys # meh, don't warn\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4/app/views/_global_partial.html.erb",
    "content": "<%= render 'something' %>\n"
  },
  {
    "path": "test/apps/rails4/app/views/another/html_safe_is_not.html.erb",
    "content": "<%= params[:x].html_safe %>\n"
  },
  {
    "path": "test/apps/rails4/app/views/another/overflow.html.erb",
    "content": "<% @test.each do |i| %>\n  <%= i %>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails4/app/views/another/use_params_in_regex.html.erb",
    "content": "<%= @x %>\n"
  },
  {
    "path": "test/apps/rails4/app/views/another/various_xss.html.erb",
    "content": "<%= link_to 'stuff', cool_thing_url(params[:x]) %> Don't warn\n<%= link_to 'stuff', User.find(params[:id]).home_url %> Warn\n<%= link_to 'stuff', User.find(params[:id]).home_path %> Don't warn\n<%= link_to 'other stuff', home_path(User.find(params[:id])) %> Don't warn\n<%= raw(x(params[:x])) %> Warn with warning code 2 not 5\n<% form_for User.first do |t| %>\n  <%== t.select(things, User.new(params[:t]).stuff) %>\n<% end %>\n\n<%= link_to 'Bars', current_user.bars.find(params[:id]) %> Don't warn\n"
  },
  {
    "path": "test/apps/rails4/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>Rails4</title>\n  <%= stylesheet_link_tag    \"application\", media: \"all\", \"data-turbolinks-track\" => true %>\n  <%= javascript_include_tag \"application\", \"data-turbolinks-track\" => true %>\n  <%= csrf_meta_tags %>\n</head>\n<body>\n\n<%= yield %>\n\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails4/app/views/users/eval_it.html.erb",
    "content": "<%= @x %>\n"
  },
  {
    "path": "test/apps/rails4/app/views/users/haml_test.html.haml",
    "content": "#content\n  .some.stuff\n    %p= params[:x]\n  #innerstuff\n    %h1= raw params[:y]\n    =\" #{User.first.name.html_safe}\"\n    :javascript\n      var import_file_upload_id = \"#{j(params[:id1])}\";\n    :coffeescript\n      import_file_upload_id_coffee = \"#{j(params[:id2])}\"\n"
  },
  {
    "path": "test/apps/rails4/app/views/users/index.html.erb",
    "content": "<%= raw User.find(params[:id]).to_json %>\n\n<%= raw inside_something(User.find(params[:id]).to_json) %>\n\n<%= raw call_something(params).to_json %>\n\n<%= raw params[:stuff].to_json %>\n\n<%= number_to_currency(params[:cost], params[:currency]) %>\n\n<%= number_to_human(params[:cost], format: h(params[:format])) %> Should not warn\n\n<%= number_to_percentage(params[:cost], negative_format: params[:format]) %>\n\n<%= render Thing.new(content: render(partial: \"stuff\")) %>\n\n<%== params[:double] %>\n\n<%== params[:x] == 1 %>\n"
  },
  {
    "path": "test/apps/rails4/app/views/users/more_haml.html.haml",
    "content": "%body\n    :javascript\n        $(function() {\n            #{\n                # Ticket #9999\n                # my variable needs to be number 4\n             }\n             var myVar = 4;\n        });\n\n"
  },
  {
    "path": "test/apps/rails4/app/views/users/test_parse.html.erb",
    "content": "Testing double ==\n<%== %{t=\"#{stuff unless other? }\"} if current_user %>\n"
  },
  {
    "path": "test/apps/rails4/bin/rails",
    "content": "#!/usr/bin/env ruby\nAPP_PATH = File.expand_path('../../config/application',  __FILE__)\nrequire_relative '../config/boot'\nrequire 'rails/commands'\n"
  },
  {
    "path": "test/apps/rails4/bin/rake",
    "content": "#!/usr/bin/env ruby\nrequire_relative '../config/boot'\nrequire 'rake'\nRake.application.run\n"
  },
  {
    "path": "test/apps/rails4/config/application.rb",
    "content": "require File.expand_path('../boot', __FILE__)\n\nrequire 'rails/all'\n\n# Assets should be precompiled for production (so we don't need the gems loaded then)\nBundler.require(*Rails.groups(assets: %w(development test)))\n\nmodule Rails4\n  class Application < Rails::Application\n    # Settings in config/environments/* take precedence over those specified here.\n    # Application configuration should go into files in config/initializers\n    # -- all .rb files in that directory are automatically loaded.\n\n    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.\n    # Run \"rake -D time\" for a list of tasks for finding time zone names. Default is UTC.\n    # config.time_zone = 'Central Time (US & Canada)'\n\n    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.\n    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]\n    # config.i18n.default_locale = :de\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4/config/boot.rb",
    "content": "# Set up gems listed in the Gemfile.\nENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)\n\nrequire 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])\n"
  },
  {
    "path": "test/apps/rails4/config/brakeman.ignore",
    "content": "{\n  \"ignored_warnings\": [\n    {\n      \"warning_type\": \"Mass Assignment\",\n      \"warning_code\": 60,\n      \"fingerprint\": \"cd83ecf615b17f849ba28050e7faf1d54f218dfa9435c3f65f47cb378c18cf98\",\n      \"message\": \"Potentially dangerous attribute available for mass assignment\",\n      \"file\": \"app/models/account.rb\",\n      \"line\": null,\n      \"link\": \"http://brakemanscanner.org/docs/warning_types/mass_assignment/\",\n      \"code\": \":admin\",\n      \"render_path\": null,\n      \"location\": {\n        \"type\": \"model\",\n        \"model\": \"Account\"\n      },\n      \"user_input\": null,\n      \"confidence\": \"High\",\n      \"note\": \"skipping this for a test\"\n    },\n    {\n      \"warning_type\": \"Mass Assignment\",\n      \"warning_code\": 60,\n      \"fingerprint\": \"abcdef01234567890ba28050e7faf1d54f218dfa9435c3f65f47cb378c18cf98\",\n      \"message\": \"Potentially dangerous attribute available for mass assignment\",\n      \"file\": \"app/models/account.rb\",\n      \"line\": null,\n      \"link\": \"http://brakemanscanner.org/docs/warning_types/mass_assignment/\",\n      \"code\": \":admin\",\n      \"render_path\": null,\n      \"location\": {\n        \"type\": \"model\",\n        \"model\": \"Account\"\n      },\n      \"user_input\": null,\n      \"confidence\": \"High\",\n      \"note\": \"this error should be detected as obsolete\"\n    }\n  ],\n  \"brakeman_version\": \"2.3.1\"\n}\n"
  },
  {
    "path": "test/apps/rails4/config/brakeman.yml",
    "content": "---\n:run_all_checks: true\n:additional_libs_path:\n- app/api/\n:rails4: true\n"
  },
  {
    "path": "test/apps/rails4/config/database.yml",
    "content": "# SQLite version 3.x\n#   gem install sqlite3\n#\n#   Ensure the SQLite 3 gem is defined in your Gemfile\n#   gem 'sqlite3'\ndevelopment:\n  adapter: sqlite3\n  database: db/development.sqlite3\n  pool: 5\n  timeout: 5000\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  adapter: sqlite3\n  database: db/test.sqlite3\n  pool: 5\n  timeout: 5000\n\nproduction:\n  adapter: sqlite3\n  database: db/production.sqlite3\n  pool: 5\n  timeout: 5000\n"
  },
  {
    "path": "test/apps/rails4/config/environment.rb",
    "content": "# Load the rails application.\nrequire File.expand_path('../application', __FILE__)\n\n# Initialize the rails application.\nRails4::Application.initialize!\n"
  },
  {
    "path": "test/apps/rails4/config/environments/development.rb",
    "content": "Rails4::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # In the development environment your application's code is reloaded on\n  # every request. This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes = false\n\n  # Do not eager load code on boot.\n  config.eager_load = false\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Don't care if the mailer can't send.\n  config.action_mailer.raise_delivery_errors = false\n\n  # Print deprecation notices to the Rails logger.\n  config.active_support.deprecation = :log\n\n  # Raise an error on page load if there are pending migrations\n  config.active_record.migration_error = :page_load\n\n  # Debug mode disables concatenation and preprocessing of assets.\n  config.assets.debug = true\nend\n"
  },
  {
    "path": "test/apps/rails4/config/environments/production.rb",
    "content": "Rails4::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Code is not reloaded between requests.\n  config.cache_classes = true\n\n  # Eager load code on boot. This eager loads most of Rails and\n  # your application in memory, allowing both thread web servers\n  # and those relying on copy on write to perform better.\n  # Rake tasks automatically ignore this option for performance.\n  config.eager_load = true\n\n  # Full error reports are disabled and caching is turned on.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = true\n\n  # Enable Rack::Cache to put a simple HTTP cache in front of your application\n  # Add `rack-cache` to your Gemfile before enabling this.\n  # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.\n  # config.action_dispatch.rack_cache = true\n\n  # Disable Rails's static asset server (Apache or nginx will already do this).\n  config.serve_static_assets = true\n\n  # Compress JavaScripts and CSS.\n  config.assets.js_compressor  = :uglifier\n  # config.assets.css_compressor = :sass\n\n  # Whether to fallback to assets pipeline if a precompiled asset is missed.\n  config.assets.compile = false\n\n  # Generate digests for assets URLs.\n  config.assets.digest = true\n\n  # Version of your assets, change this if you want to expire all your assets.\n  config.assets.version = '1.0'\n\n  # Specifies the header that your server uses for sending files.\n  # config.action_dispatch.x_sendfile_header = \"X-Sendfile\" # for apache\n  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  # config.force_ssl = true\n\n  # Set to :debug to see everything in the log.\n  config.log_level = :info\n\n  # Prepend all log lines with the following tags.\n  # config.log_tags = [ :subdomain, :uuid ]\n\n  # Use a different logger for distributed setups.\n  # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)\n\n  # Use a different cache store in production.\n  # config.cache_store = :mem_cache_store\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server.\n  # config.action_controller.asset_host = \"http://assets.example.com\"\n\n  # Precompile additional assets.\n  # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.\n  # config.assets.precompile += %w( search.js )\n\n  # Ignore bad email addresses and do not raise email delivery errors.\n  # Set this to true and configure the email server for immediate delivery to raise delivery errors.\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation can not be found).\n  config.i18n.fallbacks = true\n\n  # Send deprecation notices to registered listeners.\n  config.active_support.deprecation = :notify\n\n  # Disable automatic flushing of the log to improve performance.\n  # config.autoflush_log = false\n\n  # Use default logging formatter so that PID and timestamp are not suppressed.\n  config.log_formatter = ::Logger::Formatter.new\nend\n"
  },
  {
    "path": "test/apps/rails4/config/environments/test.rb",
    "content": "Rails4::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # The test environment is used exclusively to run your application's\n  # test suite. You never need to work with it otherwise. Remember that\n  # your test database is \"scratch space\" for the test suite and is wiped\n  # and recreated between test runs. Don't rely on the data there!\n  config.cache_classes = true\n\n  # Do not eager load code on boot. This avoids loading your whole application\n  # just for the purpose of running a single test. If you are using a tool that\n  # preloads Rails for running tests, you may have to set it to true.\n  config.eager_load = false\n\n  # Configure static asset server for tests with Cache-Control for performance.\n  config.serve_static_assets = true\n  config.static_cache_control = \"public, max-age=3600\"\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Raise exceptions instead of rendering exception templates.\n  config.action_dispatch.show_exceptions = false\n\n  # Disable request forgery protection in test environment.\n  config.action_controller.allow_forgery_protection = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Print deprecation notices to the stderr.\n  config.active_support.deprecation = :stderr\nend\n"
  },
  {
    "path": "test/apps/rails4/config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "test/apps/rails4/config/initializers/filter_parameter_logging.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Configure sensitive parameters which will be filtered from the log file.\nRails.application.config.filter_parameters += [:password]\n"
  },
  {
    "path": "test/apps/rails4/config/initializers/i18n.rb",
    "content": "require 'i18n' \n\n# Override exception handler to more carefully html-escape missing-key results. \nclass HtmlSafeI18nExceptionHandler \n  Missing = I18n.const_defined?(:MissingTranslation) ? I18n::MissingTranslation : I18n::MissingTranslationData \n\n  def initialize(original_exception_handler) \n    @original_exception_handler = original_exception_handler \n  end \n\n  def call(exception, locale, key, options) \n    if exception.is_a?(Missing) && options[:rescue_format] == :html \n      keys = exception.keys.map { |k| Rack::Utils.escape_html k } \n      key = keys.last.to_s.gsub('_', ' ').gsub(/\\b('?[a-z])/) { $1.capitalize } \n      %(<span class=\"translation_missing\" title=\"translation missing: #{keys.join('.')}\">#{key}</span>) \n    else \n      @original_exception_handler.call(exception, locale, key, options) \n    end \n  end \nend \n"
  },
  {
    "path": "test/apps/rails4/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Inflections\n# are locale specific, and you may define rules for as many different\n# locales as you wish. All of these examples are active by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n\n# These inflection rules are supported but not enabled by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.acronym 'RESTful'\n# end\n"
  },
  {
    "path": "test/apps/rails4/config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n# Mime::Type.register_alias \"text/html\", :iphone\n"
  },
  {
    "path": "test/apps/rails4/config/initializers/secret_token.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n\n# Make sure the secret is at least 30 characters and all random,\n# no regular words or you'll be exposed to dictionary attacks.\n# You can use `rake secret` to generate a secure secret key.\n\n# Make sure your secret_key_base is kept private\n# if you're sharing your code publicly.\nRails4::Application.config.secret_key_base = '3d90f727dcc14992232b9461fac5d31cf2bc184854e0afd90ae67e0ae48f22b676ee2529c84d4c23bc2a9c7be6eeefcf202b91ccb8d04e7b87a85c852f6784d6'\n"
  },
  {
    "path": "test/apps/rails4/config/initializers/session_store.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\nRails4::Application.config.session_store :encrypted_cookie_store, key: '_rails4_session'\n"
  },
  {
    "path": "test/apps/rails4/config/initializers/wrap_parameters.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# This file contains settings for ActionController::ParamsWrapper which\n# is enabled by default.\n\n# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.\nActiveSupport.on_load(:action_controller) do\n  wrap_parameters format: [:json] if respond_to?(:wrap_parameters)\nend\n\n# To enable root element in JSON for ActiveRecord objects.\n# ActiveSupport.on_load(:active_record) do\n#  self.include_root_in_json = true\n# end\n"
  },
  {
    "path": "test/apps/rails4/config/locales/en.yml",
    "content": "# Files in the config/locales directory are used for internationalization\n# and are automatically loaded by Rails. If you want to use locales other\n# than English, add the necessary files in this directory.\n#\n# To use the locales, use `I18n.t`:\n#\n#     I18n.t 'hello'\n#\n# In views, this is aliased to just `t`:\n#\n#     <%= t('hello') %>\n#\n# To use a different locale, set it with `I18n.locale`:\n#\n#     I18n.locale = :es\n#\n# This would use the information in config/locales/es.yml.\n#\n# To learn more, please read the Rails Internationalization guide\n# available at http://guides.rubyonrails.org/i18n.html.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/apps/rails4/config/routes.rb",
    "content": "something.root\n\nRails4::Application.routes.draw do\n  resources not_a_symbol, :controller => :whatever\n\n  namespace also_not_a_symbol do\n    resource :thing\n  end\n\n  get ':controller/stuff/:id/:user_id'\n  # The priority is based upon order of creation: first created -> highest priority.\n  # See how all your routes lay out with \"rake routes\".\n\n  # You can have the root of your site routed with \"root\"\n  # root to: 'welcome#index'\n\n  # Example of regular route:\n  #   get 'products/:id' => 'catalog#view'\n\n  # Example of named route that can be invoked with purchase_url(id: product.id)\n  #   get 'products/:id/purchase' => 'catalog#purchase', as: :purchase\n\n  # Example resource route (maps HTTP verbs to controller actions automatically):\n  #   resources :products\n\n  # Example resource route with options:\n  #   resources :products do\n  #     member do\n  #       get 'short'\n  #       post 'toggle'\n  #     end\n  #\n  #     collection do\n  #       get 'sold'\n  #     end\n  #   end\n\n  # Example resource route with sub-resources:\n  #   resources :products do\n  #     resources :comments, :sales\n  #     resource :seller\n  #   end\n\n  # Example resource route with more complex sub-resources:\n  #   resources :products do\n  #     resources :comments\n  #     resources :sales do\n  #       get 'recent', on: :collection\n  #     end\n  #   end\n\n  # Example resource route within a namespace:\n  #   namespace :admin do\n  #     # Directs /admin/products/* to Admin::ProductsController\n  #     # (app/controllers/admin/products_controller.rb)\n  #     resources :products\n  #   end\nend\n"
  },
  {
    "path": "test/apps/rails4/config/secrets.yml",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key is used for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n\n# Make sure the secret is at least 30 characters and all random,\n# no regular words or you'll be exposed to dictionary attacks.\n# You can use `rake secret` to generate a secure secret key.\n\n# Make sure the secrets in this file are kept private\n# if you're sharing your code publicly.\n\ndevelopment:\n  secret_key_base: 4e0b21385c66e9e226bb066a8ca5a6fed0211228a81c1b986b9ec0f9719df67b1ddbeb435393121262493f171987318e5853bdfd9b7e1c17b3f6bc3a7c1fa8aa\n\ntest:\n  secret_key_base: d73778c248636d5540d8569e3cabf740b8ca85acc4fc5e4db5063386cbe9a68df3138ceb6b48fc7702300499a6a5626a5f7ba7649ca1f3c2c941cca128dd8c16\n\n# Do not keep production secrets in the repository,\n# instead read values from the environment.\nproduction:\n  secret_key_base: super_duper_secret_key\n"
  },
  {
    "path": "test/apps/rails4/config.ru",
    "content": "# This file is used by Rack-based servers to start the application.\n\nrequire ::File.expand_path('../config/environment',  __FILE__)\nrun Rails4::Application\n"
  },
  {
    "path": "test/apps/rails4/db/seeds.rb",
    "content": "# This file should contain all the record creation needed to seed the database with its default values.\n# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).\n#\n# Examples:\n#\n#   cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])\n#   Mayor.create(name: 'Emanuel', city: cities.first)\n"
  },
  {
    "path": "test/apps/rails4/external_checks/check_external_check_test.rb",
    "content": "require 'brakeman/checks/base_check'\n\n#Verify that checks external to the checks/ dir are added by the additional_checks_path options flag\nclass Brakeman::CheckExternalCheckTest < Brakeman::BaseCheck\n  Brakeman::Checks.add_optional self\n\n  @description = \"An external check that does nothing, used for testing\"\n\n  def run_check\n    tracker.find_call(target: nil, method: :call_shady_method).each do |result|\n      if user_input = has_immediate_user_input?(result[:call].first_arg)\n        warn result: result,\n          warning_type: \"Shady Call\",\n          warning_code: :custom_check,\n          message: \"Called something shady!\",\n          confidence: :high,\n          user_input: user_input,\n          :cwe_id => [-1]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4/lib/assets/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/lib/sweet_lib.rb",
    "content": "class SweetLib\n  def do_some_cool_stuff bad\n    `ls #{bad}`\n  end\n\n  def test_command_injection_in_lib\n    IO.popen(['ls', params[:id]]) #Should not warn\n    system(\"rm #{@bad}\") #Should warn about command injection\n  end\n\n  def test_net_http_start_ssl\n    Net::HTTP.start(uri.host, uri.port, :use_ssl => true, :verify_mode => OpenSSL::SSL::VERIFY_NONE)\n  end\n\n  def external_check_test\n    call_shady_method(params[:x])\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4/lib/tasks/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/lib/tasks/some_task.rb",
    "content": "class SomeTask\n  def some_task\n    # Should not warn because we are ignoring tasks\n    `#{x}`\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4/log/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The page you were looking for doesn't exist (404)</title>\n  <style>\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <h1>The page you were looking for doesn't exist.</h1>\n    <p>You may have mistyped the address or the page may have moved.</p>\n  </div>\n  <p>If you are the application owner check the logs for more information.</p>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails4/public/422.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The change you wanted was rejected (422)</title>\n  <style>\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <h1>The change you wanted was rejected.</h1>\n    <p>Maybe you tried to change something you didn't have access to.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails4/public/500.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>We're sorry, but something went wrong (500)</title>\n  <style>\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <h1>We're sorry, but something went wrong.</h1>\n  </div>\n  <p>If you are the application owner check the logs for more information.</p>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails4/public/robots.txt",
    "content": "# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file\n#\n# To ban all spiders from the entire site uncomment the next two lines:\n# User-agent: *\n# Disallow: /\n"
  },
  {
    "path": "test/apps/rails4/test/controllers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/test/fixtures/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/test/helpers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/test/integration/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/test/mailers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/test/models/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/test/test_helper.rb",
    "content": "ENV[\"RAILS_ENV\"] = \"test\"\nrequire File.expand_path('../../config/environment', __FILE__)\nrequire 'rails/test_help'\n\nclass ActiveSupport::TestCase\n  ActiveRecord::Migration.check_pending!\n\n  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.\n  #\n  # Note: You'll currently still have to declare fixtures explicitly in integration tests\n  # -- they do not yet inherit this setting\n  fixtures :all\n\n  # Add more helper methods to be used by all tests here...\nend\n"
  },
  {
    "path": "test/apps/rails4/vendor/assets/javascripts/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4/vendor/assets/stylesheets/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files for more about ignoring files.\n#\n# If you find yourself ignoring temporary files generated by your text editor\n# or operating system, you probably want to add a global ignore instead:\n#   git config --global core.excludesfile '~/.gitignore_global'\n\n# Ignore bundler config.\n/.bundle\n\n# Ignore the default SQLite database.\n/db/*.sqlite3\n/db/*.sqlite3-journal\n\n# Ignore all logfiles and tempfiles.\n/log/*.log\n/tmp\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/README.rdoc",
    "content": "== README\n\nThis README would normally document whatever steps are necessary to get the\napplication up and running.\n\nThings you may want to cover:\n\n* Ruby version\n\n* System dependencies\n\n* Configuration\n\n* Database creation\n\n* Database initialization\n\n* How to run the test suite\n\n* Services (job queues, cache servers, search engines, etc.)\n\n* Deployment instructions\n\n* ...\n\n\nPlease feel free to use a different markup language if you do not plan to run\n<tt>rake doc:app</tt>.\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/Rakefile",
    "content": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire File.expand_path('../config/application', __FILE__)\n\nRails.application.load_tasks\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/assets/images/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/assets/javascripts/application.js",
    "content": "// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//\n// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,\n// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.\n//\n// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the\n// compiled file.\n//\n// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details\n// about supported directives.\n//\n//= require jquery\n//= require jquery_ujs\n//= require turbolinks\n//= require_tree .\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/assets/stylesheets/application.css",
    "content": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below.\n *\n * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,\n * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.\n *\n * You're free to add application-wide styles to this file and they'll appear at the bottom of the\n * compiled file so the styles you add here take precedence over styles defined in any styles\n * defined in the other CSS/SCSS files in this directory. It is generally better to create a new\n * file per style scope.\n *\n *= require_tree .\n *= require_self\n */\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::Base\n  # Prevent CSRF attacks by raising an exception.\n  # For APIs, you may want to use :null_session instead.\n  protect_from_forgery with: :exception\nend\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/controllers/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/foo_team/controllers/api/foo_controller.rb",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/foo_team/models/foo.rb",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/foo_team/views/foo.html.erb",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/mailers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/models/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/models/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>Rails4NonStandardStructure</title>\n  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>\n  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>\n  <%= csrf_meta_tags %>\n</head>\n<body>\n\n<%= yield %>\n\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/bin/rails",
    "content": "#!/usr/bin/env ruby\nbegin\n  load File.expand_path(\"../spring\", __FILE__)\nrescue LoadError\nend\nAPP_PATH = File.expand_path('../../config/application',  __FILE__)\nrequire_relative '../config/boot'\nrequire 'rails/commands'\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/bin/rake",
    "content": "#!/usr/bin/env ruby\nbegin\n  load File.expand_path(\"../spring\", __FILE__)\nrescue LoadError\nend\nrequire_relative '../config/boot'\nrequire 'rake'\nRake.application.run\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/bin/spring",
    "content": "#!/usr/bin/env ruby\n\n# This file loads spring without using Bundler, in order to be fast\n# It gets overwritten when you run the `spring binstub` command\n\nunless defined?(Spring)\n  require \"rubygems\"\n  require \"bundler\"\n\n  if match = Bundler.default_lockfile.read.match(/^GEM$.*?^    (?:  )*spring \\((.*?)\\)$.*?^$/m)\n    ENV[\"GEM_PATH\"] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR)\n    ENV[\"GEM_HOME\"] = \"\"\n    Gem.paths = ENV\n\n    gem \"spring\", match[1]\n    require \"spring/binstub\"\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/application.rb",
    "content": "require File.expand_path('../boot', __FILE__)\n\nrequire 'rails/all'\n\n# Require the gems listed in Gemfile, including any gems\n# you've limited to :test, :development, or :production.\nBundler.require(*Rails.groups)\n\nmodule Rails4NonStandardStructure\n  class Application < Rails::Application\n    # Settings in config/environments/* take precedence over those specified here.\n    # Application configuration should go into files in config/initializers\n    # -- all .rb files in that directory are automatically loaded.\n\n    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.\n    # Run \"rake -D time\" for a list of tasks for finding time zone names. Default is UTC.\n    # config.time_zone = 'Central Time (US & Canada)'\n\n    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.\n    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]\n    # config.i18n.default_locale = :de\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/boot.rb",
    "content": "# Set up gems listed in the Gemfile.\nENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)\n\nrequire 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/database.yml",
    "content": "# SQLite version 3.x\n#   gem install sqlite3\n#\n#   Ensure the SQLite 3 gem is defined in your Gemfile\n#   gem 'sqlite3'\n#\ndefault: &default\n  adapter: sqlite3\n  pool: 5\n  timeout: 5000\n\ndevelopment:\n  <<: *default\n  database: db/development.sqlite3\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  <<: *default\n  database: db/test.sqlite3\n\nproduction:\n  <<: *default\n  database: db/production.sqlite3\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/environment.rb",
    "content": "# Load the Rails application.\nrequire File.expand_path('../application', __FILE__)\n\n# Initialize the Rails application.\nRails.application.initialize!\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/environments/development.rb",
    "content": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # In the development environment your application's code is reloaded on\n  # every request. This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes = false\n\n  # Do not eager load code on boot.\n  config.eager_load = false\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Don't care if the mailer can't send.\n  config.action_mailer.raise_delivery_errors = false\n\n  # Print deprecation notices to the Rails logger.\n  config.active_support.deprecation = :log\n\n  # Raise an error on page load if there are pending migrations.\n  config.active_record.migration_error = :page_load\n\n  # Debug mode disables concatenation and preprocessing of assets.\n  # This option may cause significant delays in view rendering with a large\n  # number of complex assets.\n  config.assets.debug = true\n\n  # Adds additional error checking when serving assets at runtime.\n  # Checks for improperly declared sprockets dependencies.\n  # Raises helpful error messages.\n  config.assets.raise_runtime_errors = true\n\n  # Raises error for missing translations\n  # config.action_view.raise_on_missing_translations = true\nend\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/environments/production.rb",
    "content": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Code is not reloaded between requests.\n  config.cache_classes = true\n\n  # Eager load code on boot. This eager loads most of Rails and\n  # your application in memory, allowing both threaded web servers\n  # and those relying on copy on write to perform better.\n  # Rake tasks automatically ignore this option for performance.\n  config.eager_load = true\n\n  # Full error reports are disabled and caching is turned on.\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Enable Rack::Cache to put a simple HTTP cache in front of your application\n  # Add `rack-cache` to your Gemfile before enabling this.\n  # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.\n  # config.action_dispatch.rack_cache = true\n\n  # Disable Rails's static asset server (Apache or nginx will already do this).\n  config.serve_static_assets = false\n\n  # Compress JavaScripts and CSS.\n  config.assets.js_compressor = :uglifier\n  # config.assets.css_compressor = :sass\n\n  # Do not fallback to assets pipeline if a precompiled asset is missed.\n  config.assets.compile = false\n\n  # Generate digests for assets URLs.\n  config.assets.digest = true\n\n  # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb\n\n  # Specifies the header that your server uses for sending files.\n  # config.action_dispatch.x_sendfile_header = \"X-Sendfile\" # for apache\n  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  # config.force_ssl = true\n\n  # Set to :debug to see everything in the log.\n  config.log_level = :info\n\n  # Prepend all log lines with the following tags.\n  # config.log_tags = [ :subdomain, :uuid ]\n\n  # Use a different logger for distributed setups.\n  # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)\n\n  # Use a different cache store in production.\n  # config.cache_store = :mem_cache_store\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server.\n  # config.action_controller.asset_host = \"http://assets.example.com\"\n\n  # Ignore bad email addresses and do not raise email delivery errors.\n  # Set this to true and configure the email server for immediate delivery to raise delivery errors.\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation cannot be found).\n  config.i18n.fallbacks = true\n\n  # Send deprecation notices to registered listeners.\n  config.active_support.deprecation = :notify\n\n  # Disable automatic flushing of the log to improve performance.\n  # config.autoflush_log = false\n\n  # Use default logging formatter so that PID and timestamp are not suppressed.\n  config.log_formatter = ::Logger::Formatter.new\n\n  # Do not dump schema after migrations.\n  config.active_record.dump_schema_after_migration = false\nend\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/environments/test.rb",
    "content": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # The test environment is used exclusively to run your application's\n  # test suite. You never need to work with it otherwise. Remember that\n  # your test database is \"scratch space\" for the test suite and is wiped\n  # and recreated between test runs. Don't rely on the data there!\n  config.cache_classes = true\n\n  # Do not eager load code on boot. This avoids loading your whole application\n  # just for the purpose of running a single test. If you are using a tool that\n  # preloads Rails for running tests, you may have to set it to true.\n  config.eager_load = false\n\n  # Configure static asset server for tests with Cache-Control for performance.\n  config.serve_static_assets  = true\n  config.static_cache_control = 'public, max-age=3600'\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Raise exceptions instead of rendering exception templates.\n  config.action_dispatch.show_exceptions = false\n\n  # Disable request forgery protection in test environment.\n  config.action_controller.allow_forgery_protection = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Print deprecation notices to the stderr.\n  config.active_support.deprecation = :stderr\n\n  # Raises error for missing translations\n  # config.action_view.raise_on_missing_translations = true\nend\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/initializers/assets.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Version of your assets, change this if you want to expire all your assets.\nRails.application.config.assets.version = '1.0'\n\n# Precompile additional assets.\n# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.\n# Rails.application.config.assets.precompile += %w( search.js )\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/initializers/cookies_serializer.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\nRails.application.config.action_dispatch.cookies_serializer = :json"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/initializers/filter_parameter_logging.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Configure sensitive parameters which will be filtered from the log file.\nRails.application.config.filter_parameters += [:password]\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Inflections\n# are locale specific, and you may define rules for as many different\n# locales as you wish. All of these examples are active by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n\n# These inflection rules are supported but not enabled by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.acronym 'RESTful'\n# end\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/initializers/session_store.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\nRails.application.config.session_store :cookie_store, key: '_rails4_non_standard_structure_session'\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/initializers/wrap_parameters.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# This file contains settings for ActionController::ParamsWrapper which\n# is enabled by default.\n\n# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.\nActiveSupport.on_load(:action_controller) do\n  wrap_parameters format: [:json] if respond_to?(:wrap_parameters)\nend\n\n# To enable root element in JSON for ActiveRecord objects.\n# ActiveSupport.on_load(:active_record) do\n#  self.include_root_in_json = true\n# end\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/locales/en.yml",
    "content": "# Files in the config/locales directory are used for internationalization\n# and are automatically loaded by Rails. If you want to use locales other\n# than English, add the necessary files in this directory.\n#\n# To use the locales, use `I18n.t`:\n#\n#     I18n.t 'hello'\n#\n# In views, this is aliased to just `t`:\n#\n#     <%= t('hello') %>\n#\n# To use a different locale, set it with `I18n.locale`:\n#\n#     I18n.locale = :es\n#\n# This would use the information in config/locales/es.yml.\n#\n# To learn more, please read the Rails Internationalization guide\n# available at http://guides.rubyonrails.org/i18n.html.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/routes.rb",
    "content": "Rails.application.routes.draw do\n  # The priority is based upon order of creation: first created -> highest priority.\n  # See how all your routes lay out with \"rake routes\".\n\n  # You can have the root of your site routed with \"root\"\n  # root 'welcome#index'\n\n  # Example of regular route:\n  #   get 'products/:id' => 'catalog#view'\n\n  # Example of named route that can be invoked with purchase_url(id: product.id)\n  #   get 'products/:id/purchase' => 'catalog#purchase', as: :purchase\n\n  # Example resource route (maps HTTP verbs to controller actions automatically):\n  #   resources :products\n\n  # Example resource route with options:\n  #   resources :products do\n  #     member do\n  #       get 'short'\n  #       post 'toggle'\n  #     end\n  #\n  #     collection do\n  #       get 'sold'\n  #     end\n  #   end\n\n  # Example resource route with sub-resources:\n  #   resources :products do\n  #     resources :comments, :sales\n  #     resource :seller\n  #   end\n\n  # Example resource route with more complex sub-resources:\n  #   resources :products do\n  #     resources :comments\n  #     resources :sales do\n  #       get 'recent', on: :collection\n  #     end\n  #   end\n\n  # Example resource route with concerns:\n  #   concern :toggleable do\n  #     post 'toggle'\n  #   end\n  #   resources :posts, concerns: :toggleable\n  #   resources :photos, concerns: :toggleable\n\n  # Example resource route within a namespace:\n  #   namespace :admin do\n  #     # Directs /admin/products/* to Admin::ProductsController\n  #     # (app/controllers/admin/products_controller.rb)\n  #     resources :products\n  #   end\nend\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config/secrets.yml",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key is used for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n\n# Make sure the secret is at least 30 characters and all random,\n# no regular words or you'll be exposed to dictionary attacks.\n# You can use `rake secret` to generate a secure secret key.\n\n# Make sure the secrets in this file are kept private\n# if you're sharing your code publicly.\n\ndevelopment:\n  secret_key_base: f7810c6ed73622446438bbeb969d38d98baf5e9135c33edff60c21921beafd14cad58e908fdb25a214803d0231c981a380fca97d7877707b73db75df26be04e8\n\ntest:\n  secret_key_base: ccc74872aaac828721f78621140e09c8d7186cab13887743429e2efe9ec4402e72adb2dd194a7507c13bf19bbbebf47b3570c729302fb18c706691de0e5dd979\n\n# Do not keep production secrets in the repository,\n# instead read values from the environment.\nproduction:\n  secret_key_base: <%= ENV[\"SECRET_KEY_BASE\"] %>\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/config.ru",
    "content": "# This file is used by Rack-based servers to start the application.\n\nrequire ::File.expand_path('../config/environment',  __FILE__)\nrun Rails.application\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/db/seeds.rb",
    "content": "# This file should contain all the record creation needed to seed the database with its default values.\n# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).\n#\n# Examples:\n#\n#   cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])\n#   Mayor.create(name: 'Emanuel', city: cities.first)\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/lib/assets/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/lib/tasks/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/log/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The page you were looking for doesn't exist (404)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  body {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>The page you were looking for doesn't exist.</h1>\n      <p>You may have mistyped the address or the page may have moved.</p>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/public/422.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The change you wanted was rejected (422)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  body {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>The change you wanted was rejected.</h1>\n      <p>Maybe you tried to change something you didn't have access to.</p>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/public/500.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>We're sorry, but something went wrong (500)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  body {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>We're sorry, but something went wrong.</h1>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/public/robots.txt",
    "content": "# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file\n#\n# To ban all spiders from the entire site uncomment the next two lines:\n# User-agent: *\n# Disallow: /\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/rails4test.gemspec",
    "content": "Gem::Specification.new do |s|\n  s.name        = \"rails4test\"\n  s.version     = \"0.0.1\"\n  s.summary     = \"Testing Brakeman with Rails 4\"\n  s.description = \"And this file is to test Brakeman's handling of gemspecs\"\n\n  s.add_dependency 'rails', '>= 4.1.8'\n  s.add_dependency 'haml'\nend\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/test/controllers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/test/fixtures/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/test/helpers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/test/integration/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/test/mailers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/test/models/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/test/test_helper.rb",
    "content": "ENV['RAILS_ENV'] ||= 'test'\nrequire File.expand_path('../../config/environment', __FILE__)\nrequire 'rails/test_help'\n\nclass ActiveSupport::TestCase\n  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.\n  fixtures :all\n\n  # Add more helper methods to be used by all tests here...\nend\n"
  },
  {
    "path": "test/apps/rails4_non_standard_structure/vendor/assets/javascripts/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_non_standard_structure/vendor/assets/stylesheets/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/README.rdoc",
    "content": "== README\n\nThis README would normally document whatever steps are necessary to get the\napplication up and running.\n\nThings you may want to cover:\n\n* Ruby version\n\n* System dependencies\n\n* Configuration\n\n* Database creation\n\n* Database initialization\n\n* How to run the test suite\n\n* Services (job queues, cache servers, search engines, etc.)\n\n* Deployment instructions\n\n* ...\n\n\nPlease feel free to use a different markup language if you do not plan to run\n<tt>rake doc:app</tt>.\n"
  },
  {
    "path": "test/apps/rails4_with_engines/Rakefile",
    "content": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire File.expand_path('../config/application', __FILE__)\n\nRails4::Application.load_tasks\n"
  },
  {
    "path": "test/apps/rails4_with_engines/alt_engines/admin_stuff/app/controllers/admin_controller.rb",
    "content": "class AdminController < ApplicationController\n  def debug\n    params[:class].classify.constantize.send(params[:meth])\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/alt_engines/admin_stuff/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/alt_engines/admin_stuff/app/views/admin/debug.html.erb",
    "content": "<%= raw params[:debug] %>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/app/assets/javascripts/application.js",
    "content": "// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//\n// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,\n// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.\n//\n// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the\n// compiled file.\n//\n// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD\n// GO AFTER THE REQUIRES BELOW.\n//\n//= require jquery\n//= require jquery_ujs\n//= require turbolinks\n//= require_tree .\n"
  },
  {
    "path": "test/apps/rails4_with_engines/app/assets/stylesheets/application.css",
    "content": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below.\n *\n * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,\n * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.\n *\n * You're free to add application-wide styles to this file and they'll appear at the top of the\n * compiled file, but it's generally better to create a new file per style scope.\n *\n *= require_self\n *= require_tree .\n */\n"
  },
  {
    "path": "test/apps/rails4_with_engines/app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::Base\n  # Prevent CSRF attacks by raising an exception.\n  # For APIs, you may want to use :null_session instead.\n  protect_from_forgery\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/app/controllers/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/app/mailers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/app/models/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/app/models/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>Rails4</title>\n  <%= stylesheet_link_tag    \"application\", media: \"all\", \"data-turbolinks-track\" => true %>\n  <%= javascript_include_tag \"application\", \"data-turbolinks-track\" => true %>\n  <%= csrf_meta_tags %>\n</head>\n<body>\n\n<%= yield %>\n\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/bin/rails",
    "content": "#!/usr/bin/env ruby\nAPP_PATH = File.expand_path('../../config/application',  __FILE__)\nrequire_relative '../config/boot'\nrequire 'rails/commands'\n"
  },
  {
    "path": "test/apps/rails4_with_engines/bin/rake",
    "content": "#!/usr/bin/env ruby\nrequire_relative '../config/boot'\nrequire 'rake'\nRake.application.run\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/application.rb",
    "content": "require File.expand_path('../boot', __FILE__)\n\nrequire 'rails/all'\n\n# Assets should be precompiled for production (so we don't need the gems loaded then)\nBundler.require(*Rails.groups(assets: %w(development test)))\n\nmodule Rails4\n  class Application < Rails::Application\n    # Settings in config/environments/* take precedence over those specified here.\n    # Application configuration should go into files in config/initializers\n    # -- all .rb files in that directory are automatically loaded.\n\n    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.\n    # Run \"rake -D time\" for a list of tasks for finding time zone names. Default is UTC.\n    # config.time_zone = 'Central Time (US & Canada)'\n\n    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.\n    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]\n    # config.i18n.default_locale = :de\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/boot.rb",
    "content": "# Set up gems listed in the Gemfile.\nENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)\n\nrequire 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/brakeman.yml",
    "content": "---\n:engine_paths:\n  - engines/user_removal\n  - alt_engines/*\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/database.yml",
    "content": "# SQLite version 3.x\n#   gem install sqlite3\n#\n#   Ensure the SQLite 3 gem is defined in your Gemfile\n#   gem 'sqlite3'\ndevelopment:\n  adapter: sqlite3\n  database: db/development.sqlite3\n  pool: 5\n  timeout: 5000\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  adapter: sqlite3\n  database: db/test.sqlite3\n  pool: 5\n  timeout: 5000\n\nproduction:\n  adapter: sqlite3\n  database: db/production.sqlite3\n  pool: 5\n  timeout: 5000\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/environment.rb",
    "content": "# Load the rails application.\nrequire File.expand_path('../application', __FILE__)\n\n# Initialize the rails application.\nRails4::Application.initialize!\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/environments/development.rb",
    "content": "Rails4::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # In the development environment your application's code is reloaded on\n  # every request. This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes = false\n\n  # Do not eager load code on boot.\n  config.eager_load = false\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Don't care if the mailer can't send.\n  config.action_mailer.raise_delivery_errors = false\n\n  # Print deprecation notices to the Rails logger.\n  config.active_support.deprecation = :log\n\n  # Raise an error on page load if there are pending migrations\n  config.active_record.migration_error = :page_load\n\n  # Debug mode disables concatenation and preprocessing of assets.\n  config.assets.debug = true\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/environments/production.rb",
    "content": "Rails4::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Code is not reloaded between requests.\n  config.cache_classes = true\n\n  # Eager load code on boot. This eager loads most of Rails and\n  # your application in memory, allowing both thread web servers\n  # and those relying on copy on write to perform better.\n  # Rake tasks automatically ignore this option for performance.\n  config.eager_load = true\n\n  # Full error reports are disabled and caching is turned on.\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Enable Rack::Cache to put a simple HTTP cache in front of your application\n  # Add `rack-cache` to your Gemfile before enabling this.\n  # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.\n  # config.action_dispatch.rack_cache = true\n\n  # Disable Rails's static asset server (Apache or nginx will already do this).\n  config.serve_static_assets = false\n\n  # Compress JavaScripts and CSS.\n  config.assets.js_compressor  = :uglifier\n  # config.assets.css_compressor = :sass\n\n  # Whether to fallback to assets pipeline if a precompiled asset is missed.\n  config.assets.compile = false\n\n  # Generate digests for assets URLs.\n  config.assets.digest = true\n\n  # Version of your assets, change this if you want to expire all your assets.\n  config.assets.version = '1.0'\n\n  # Specifies the header that your server uses for sending files.\n  # config.action_dispatch.x_sendfile_header = \"X-Sendfile\" # for apache\n  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  # config.force_ssl = true\n\n  # Set to :debug to see everything in the log.\n  config.log_level = :info\n\n  # Prepend all log lines with the following tags.\n  # config.log_tags = [ :subdomain, :uuid ]\n\n  # Use a different logger for distributed setups.\n  # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)\n\n  # Use a different cache store in production.\n  # config.cache_store = :mem_cache_store\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server.\n  # config.action_controller.asset_host = \"http://assets.example.com\"\n\n  # Precompile additional assets.\n  # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.\n  # config.assets.precompile += %w( search.js )\n\n  # Ignore bad email addresses and do not raise email delivery errors.\n  # Set this to true and configure the email server for immediate delivery to raise delivery errors.\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation can not be found).\n  config.i18n.fallbacks = true\n\n  # Send deprecation notices to registered listeners.\n  config.active_support.deprecation = :notify\n\n  # Enforce whitelist mode for mass assignment. (now used by protected_attributes gem)\n  # This will create an empty whitelist of attributes available for mass-assignment for all models\n  # in your app. As such, your models will need to explicitly whitelist or blacklist accessible\n  # parameters by using an attr_accessible or attr_protected declaration.\n  config.active_record.whitelist_attributes = false\n\n  # Disable automatic flushing of the log to improve performance.\n  # config.autoflush_log = false\n\n  # Use default logging formatter so that PID and timestamp are not suppressed.\n  config.log_formatter = ::Logger::Formatter.new\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/environments/test.rb",
    "content": "Rails4::Application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # The test environment is used exclusively to run your application's\n  # test suite. You never need to work with it otherwise. Remember that\n  # your test database is \"scratch space\" for the test suite and is wiped\n  # and recreated between test runs. Don't rely on the data there!\n  config.cache_classes = true\n\n  # Do not eager load code on boot. This avoids loading your whole application\n  # just for the purpose of running a single test. If you are using a tool that\n  # preloads Rails for running tests, you may have to set it to true.\n  config.eager_load = false\n\n  # Configure static asset server for tests with Cache-Control for performance.\n  config.serve_static_assets = true\n  config.static_cache_control = \"public, max-age=3600\"\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Raise exceptions instead of rendering exception templates.\n  config.action_dispatch.show_exceptions = false\n\n  # Disable request forgery protection in test environment.\n  config.action_controller.allow_forgery_protection = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Print deprecation notices to the stderr.\n  config.active_support.deprecation = :stderr\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/initializers/filter_parameter_logging.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Configure sensitive parameters which will be filtered from the log file.\nRails.application.config.filter_parameters += [:password]\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Inflections\n# are locale specific, and you may define rules for as many different\n# locales as you wish. All of these examples are active by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n\n# These inflection rules are supported but not enabled by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.acronym 'RESTful'\n# end\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n# Mime::Type.register_alias \"text/html\", :iphone\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/initializers/nested_attributes_bypass_fix.rb",
    "content": "module ActiveRecord\n  module NestedAttributes\n    private\n\n    def reject_new_record?(association_name, attributes)\n      will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)\n    end\n\n    def call_reject_if(association_name, attributes)\n      return false if will_be_destroyed?(association_name, attributes)\n\n      case callback = self.nested_attributes_options[association_name][:reject_if]\n      when Symbol\n        method(callback).arity == 0 ? send(callback) : send(callback, attributes)\n      when Proc\n        callback.call(attributes)\n      end\n    end\n\n    def will_be_destroyed?(association_name, attributes)\n      allow_destroy?(association_name) && has_destroy_flag?(attributes)\n    end\n\n    def allow_destroy?(association_name)\n      self.nested_attributes_options[association_name][:allow_destroy]\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/initializers/secret_token.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n\n# Make sure the secret is at least 30 characters and all random,\n# no regular words or you'll be exposed to dictionary attacks.\n# You can use `rake secret` to generate a secure secret key.\n\n# Make sure your secret_key_base is kept private\n# if you're sharing your code publicly.\nRails4::Application.config.secret_key_base = '3d90f727dcc14992232b9461fac5d31cf2bc184854e0afd90ae67e0ae48f22b676ee2529c84d4c23bc2a9c7be6eeefcf202b91ccb8d04e7b87a85c852f6784d6'\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/initializers/session_store.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\nRails4::Application.config.session_store :encrypted_cookie_store, key: '_rails4_session'\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/initializers/wrap_parameters.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# This file contains settings for ActionController::ParamsWrapper which\n# is enabled by default.\n\n# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.\nActiveSupport.on_load(:action_controller) do\n  wrap_parameters format: [:json] if respond_to?(:wrap_parameters)\nend\n\n# To enable root element in JSON for ActiveRecord objects.\n# ActiveSupport.on_load(:active_record) do\n#  self.include_root_in_json = true\n# end\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/locales/en.yml",
    "content": "# Files in the config/locales directory are used for internationalization\n# and are automatically loaded by Rails. If you want to use locales other\n# than English, add the necessary files in this directory.\n#\n# To use the locales, use `I18n.t`:\n#\n#     I18n.t 'hello'\n#\n# In views, this is aliased to just `t`:\n#\n#     <%= t('hello') %>\n#\n# To use a different locale, set it with `I18n.locale`:\n#\n#     I18n.locale = :es\n#\n# This would use the information in config/locales/es.yml.\n#\n# To learn more, please read the Rails Internationalization guide\n# available at http://guides.rubyonrails.org/i18n.html.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config/routes.rb",
    "content": "Rails4::Application.routes.draw do\n  # The priority is based upon order of creation: first created -> highest priority.\n  # See how all your routes lay out with \"rake routes\".\n\n  # You can have the root of your site routed with \"root\"\n  # root to: 'welcome#index'\n\n  # Example of regular route:\n  #   get 'products/:id' => 'catalog#view'\n\n  # Example of named route that can be invoked with purchase_url(id: product.id)\n  #   get 'products/:id/purchase' => 'catalog#purchase', as: :purchase\n\n  # Example resource route (maps HTTP verbs to controller actions automatically):\n  #   resources :products\n\n  # Example resource route with options:\n  #   resources :products do\n  #     member do\n  #       get 'short'\n  #       post 'toggle'\n  #     end\n  #\n  #     collection do\n  #       get 'sold'\n  #     end\n  #   end\n\n  # Example resource route with sub-resources:\n  #   resources :products do\n  #     resources :comments, :sales\n  #     resource :seller\n  #   end\n\n  # Example resource route with more complex sub-resources:\n  #   resources :products do\n  #     resources :comments\n  #     resources :sales do\n  #       get 'recent', on: :collection\n  #     end\n  #   end\n\n  # Example resource route within a namespace:\n  #   namespace :admin do\n  #     # Directs /admin/products/* to Admin::ProductsController\n  #     # (app/controllers/admin/products_controller.rb)\n  #     resources :products\n  #   end\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/config.ru",
    "content": "# This file is used by Rack-based servers to start the application.\n\nrequire ::File.expand_path('../config/environment',  __FILE__)\nrun Rails4::Application\n"
  },
  {
    "path": "test/apps/rails4_with_engines/db/seeds.rb",
    "content": "# This file should contain all the record creation needed to seed the database with its default values.\n# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).\n#\n# Examples:\n#\n#   cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])\n#   Mayor.create(name: 'Emanuel', city: cities.first)\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/assets/javascripts/users.js.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/assets/stylesheets/users.css.scss",
    "content": "// Place all the styles related to the Users controller here.\n// They will automatically be included in application.css.\n// You can use Sass (SCSS) here: http://sass-lang.com/\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/controllers/base_controller.rb",
    "content": "class BaseController < ActionController::Base\n  # missing protect_from_forgery call\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/controllers/removal_controller.rb",
    "content": "class RemovalController < ApplicationController\n  def change_lines\n    <<-X\n    this\n    method\n    is\n    here\n    for line\n    numbers\n    X\n  end\n\n  def remove_this\n    redirect_to params[:url]\n  end\n\n  def remove_this_too\n    @some_input = raw params[:input]\n    @some_other_input = Account.first.name\n\n    render 'removal/controller_removed'\n  end\n\n  def implicit_render\n    @bad_stuff = raw params[:bad_stuff]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/controllers/users_controller.rb",
    "content": "class UsersController < ApplicationController\n  include UserControllerMixin\n\n  # GET /users\n  # GET /users.json\n  def index\n    @users = User.all\n\n    respond_to do |format|\n      format.html # index.html.erb\n      format.json { render :json => @users }\n    end\n  end\n\n  # GET /users/1\n  # GET /users/1.json\n  def show\n    @user = User.find(params[:id])\n    @user_data = raw params[:user_data]\n\n    respond_to do |format|\n      format.html # show.html.erb\n      format.json { render :json => @user }\n    end\n  end\n\n  # GET /users/new\n  # GET /users/new.json\n  def new\n    @user = User.new\n\n    respond_to do |format|\n      format.html # new.html.erb\n      format.json { render :json => @user }\n    end\n  end\n\n  # GET /users/1/edit\n  def edit\n    @user = User.find(params[:id])\n  end\n\n  # POST /users\n  # POST /users.json\n  def create\n    @user = User.new(params[:user])\n\n    respond_to do |format|\n      if @user.save\n        format.html { redirect_to @user, :notice => 'User was successfully created.' }\n        format.json { render :json => @user, :status => :created, :location => @user }\n      else\n        format.html { render :action => \"new\" }\n        format.json { render :json => @user.errors, :status => :unprocessable_entity }\n      end\n    end\n  end\n\n  # PUT /users/1\n  # PUT /users/1.json\n  def update\n    @user = User.find(params[:id])\n\n    respond_to do |format|\n      if @user.update_attributes(params[:user])\n        format.html { redirect_to @user, :notice => 'User was successfully updated.' }\n        format.json { head :no_content }\n      else\n        format.html { render :action => \"edit\" }\n        format.json { render :json => @user.errors, :status => :unprocessable_entity }\n      end\n    end\n  end\n\n  # DELETE /users/1\n  # DELETE /users/1.json\n  def destroy\n    @user = User.find(params[:id])\n    @user.destroy\n\n    respond_to do |format|\n      format.html { redirect_to users_url }\n      format.json { head :no_content }\n    end\n  end\n\n  def slimming\n    @user = User.find(params[:id])\n    @query = params[:query]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/helpers/users_helper.rb",
    "content": "module UsersHelper\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/models/.gitkeep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/models/account.rb",
    "content": "class Account < ActiveRecord::Base\n  attr_accessible :plan_id, :banned \nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/models/no_protection.rb",
    "content": "class NoProtection < ActiveRecord::Base\n  # Leave this class empty for Rescanner tests\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/models/user.rb",
    "content": "class User < ActiveRecord::Base\n  attr_accessible :bio, :name, :account_id, :admin, :status_id \n\n  accepts_nested_attributes_for :something, allow_destroy: false, reject_if: proc { |attributes| stuff }\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/views/removal/_partial.html.erb",
    "content": "<%= raw @some_other_input %>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/views/removal/controller_removed.html.erb",
    "content": "<%= @some_input %>\n\n<%= render 'partial' %>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/views/removal/implicit_render.html.erb",
    "content": "\n<%= @bad_stuff %>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/views/users/_form.html.erb",
    "content": "You: <span><%= about %></span>\n\n<%= form_for(@user) do |f| %>\n  <% if @user.errors.any? %>\n    <div id=\"error_explanation\">\n      <h2><%= pluralize(@user.errors.count, \"error\") %> prohibited this user from being saved:</h2>\n\n      <ul>\n      <% @user.errors.full_messages.each do |msg| %>\n        <li><%= msg %></li>\n      <% end %>\n      </ul>\n    </div>\n  <% end %>\n\n  <div class=\"field\">\n    <%= f.label :name %><br />\n    <%= f.text_field :name %>\n  </div>\n  <div class=\"field\">\n    <%= f.label :bio %><br />\n    <%= f.text_field :bio %>\n  </div>\n  <div class=\"actions\">\n    <%= f.submit %>\n  </div>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/views/users/_slimmer.html.slim",
    "content": "- if some_value\n  div\n    = params[:escaped]\n- else\n  span\n    == params[:unescaped]\n\np== @user.profile\n\n- if x\n  = params[:unescaped]\n- else\n  = params[:escaped]\n\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/views/users/edit.html.erb",
    "content": "<h1>Editing user</h1>\n\n<%= render 'form', :locals => { :about => raw(@user.bio) } %>\n\n<%= link_to 'Show', @user %> |\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/views/users/index.html.erb",
    "content": "<h1>Listing users</h1>\n\n<table>\n  <tr>\n    <th>Name</th>\n    <th>Bio</th>\n    <th></th>\n    <th></th>\n    <th></th>\n  </tr>\n\n<% @users.each do |user| %>\n  <tr>\n    <td><%= user.name %></td>\n    <td><%= user.bio %></td>\n    <td><%= link_to 'Show', user %></td>\n    <td><%= link_to 'Edit', edit_user_path(user) %></td>\n    <td><%= link_to 'Destroy', user, :method => :delete, :data => { :confirm => 'Are you sure?' } %></td>\n  </tr>\n<% end %>\n</table>\n\n<br />\n\n<%= link_to 'New User', new_user_path %>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/views/users/mixed_in.html.erb",
    "content": "<%= raw @user.something %>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/views/users/new.html.erb",
    "content": "<h1>New user</h1>\n\n<%= render 'form' %>\n\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/views/users/sanitized.html.erb",
    "content": "<style>\n  <%= sanitize_css params[:css] %>\n</style>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/views/users/show.html.erb",
    "content": "<p id=\"notice\"><%= notice %></p>\n\n<p>\n  <b>Name:</b>\n  <%= @user.name %>\n</p>\n\n<p>\n  <b>Bio:</b>\n  <%= @user.bio %>\n</p>\n\n<p>\n  <b>Other Thing:</b>\n  <%= @user_data %>\n</p>\n\n<p>\n  <b>Stuff I like:</b>\n  <%= simple_format(@user.likes, :class => \"likes\") %>\n  <%= simple_format(\"some string\", :color => params[:color]) %>\n  <%= simple_format(\"some string\", :id => h(params[:color])) %> should not warn\n</p>\n\n\n<%= link_to 'Edit', edit_user_path(@user) %> |\n<%= link_to 'Back', users_path %>\n\n<script>\n  var thing = <%= raw params.to_json %>;\n</script>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/app/views/users/slimming.html.slim",
    "content": "#content\n  .container\n    h2 Search for: #{{@query}}\n    p== @user.name\n\n  == render 'slimmer'\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/config/routes.rb",
    "content": "Rails32::Application.routes.draw do\n  resources :users do\n    get 'mixed_in'\n  end\n  \n  match 'remove' => 'removal#remove_this_too'\n  match 'implicit' => 'removal#implicit_render'\n\n  # The priority is based upon order of creation:\n  # first created -> highest priority.\n\n  # Sample of regular route:\n  #   match 'products/:id' => 'catalog#view'\n  # Keep in mind you can assign values other than :controller and :action\n\n  # Sample of named route:\n  #   match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase\n  # This route can be invoked with purchase_url(:id => product.id)\n\n  # Sample resource route (maps HTTP verbs to controller actions automatically):\n  #   resources :products\n\n  # Sample resource route with options:\n  #   resources :products do\n  #     member do\n  #       get 'short'\n  #       post 'toggle'\n  #     end\n  #\n  #     collection do\n  #       get 'sold'\n  #     end\n  #   end\n\n  # Sample resource route with sub-resources:\n  #   resources :products do\n  #     resources :comments, :sales\n  #     resource :seller\n  #   end\n\n  # Sample resource route with more complex sub-resources\n  #   resources :products do\n  #     resources :comments\n  #     resources :sales do\n  #       get 'recent', :on => :collection\n  #     end\n  #   end\n\n  # Sample resource route within a namespace:\n  #   namespace :admin do\n  #     # Directs /admin/products/* to Admin::ProductsController\n  #     # (app/controllers/admin/products_controller.rb)\n  #     resources :products\n  #   end\n\n  # You can have the root of your site routed with \"root\"\n  # just remember to delete public/index.html.\n  # root :to => 'welcome#index'\n\n  # See how all your routes lay out with \"rake routes\"\n\n  # This is a legacy wild controller route that's not recommended for RESTful applications.\n  # Note: This route will make all actions in every controller accessible via GET requests.\n  # match ':controller(/:action(/:id))(.:format)'\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/engines/user_removal/lib/user_removal.rb",
    "content": "module UserRemoval\n  class Engine < Rails::Engine\n\n    initializer :assets do |config|\n      Rails.application.config.assets.precompile += Dir.glob(root.join('app/assets/stylesheets/**/*.css*')).collect {|f| f.gsub(%r{.*/app/assets/stylesheets/}, \"\").gsub(/\\.css.*/, '.css') }\n      Rails.application.config.assets.precompile += Dir.glob(root.join('app/assets/javascripts/**/*.js')).collect {|f| f.gsub(%r{.*/app/assets/javascripts/}, \"\") }\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/gems.rb",
    "content": "source 'https://rubygems.org'\n\n# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'\ngem 'rails', '4.0.0'\n\ngem 'sqlite3'\n\n# Gems used only for assets and not required\n# in production environments by default.\ngroup :assets do\n  gem 'sass-rails',   '~> 4.0.0'\n  gem 'coffee-rails', '~> 4.0.0'\n\n  # See https://github.com/sstephenson/execjs#readme for more supported runtimes\n  # gem 'therubyracer', platforms: :ruby\n\n  gem 'uglifier', '>= 1.0.3'\nend\n\ngem 'jquery-rails'\n\n# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks\ngem 'turbolinks'\n\n# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder\ngem 'jbuilder', '~> 1.0.1'\n\n# To use ActiveModel has_secure_password\n# gem 'bcrypt-ruby', '~> 3.0.0'\n\n# Use unicorn as the app server\n# gem 'unicorn'\n\n# Deploy with Capistrano\n# gem 'capistrano', group: :development\n\n# To use debugger\n# gem 'debugger'\n"
  },
  {
    "path": "test/apps/rails4_with_engines/lib/assets/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/lib/tasks/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/log/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The page you were looking for doesn't exist (404)</title>\n  <style>\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <h1>The page you were looking for doesn't exist.</h1>\n    <p>You may have mistyped the address or the page may have moved.</p>\n  </div>\n  <p>If you are the application owner check the logs for more information.</p>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/public/422.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The change you wanted was rejected (422)</title>\n  <style>\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <h1>The change you wanted was rejected.</h1>\n    <p>Maybe you tried to change something you didn't have access to.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/public/500.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>We're sorry, but something went wrong (500)</title>\n  <style>\n    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n    div.dialog {\n      width: 25em;\n      padding: 0 4em;\n      margin: 4em auto 0 auto;\n      border: 1px solid #ccc;\n      border-right-color: #999;\n      border-bottom-color: #999;\n    }\n    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <h1>We're sorry, but something went wrong.</h1>\n  </div>\n  <p>If you are the application owner check the logs for more information.</p>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails4_with_engines/public/robots.txt",
    "content": "# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file\n#\n# To ban all spiders from the entire site uncomment the next two lines:\n# User-agent: *\n# Disallow: /\n"
  },
  {
    "path": "test/apps/rails4_with_engines/script/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/test/controllers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/test/fixtures/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/test/helpers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/test/integration/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/test/mailers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/test/models/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/test/test_helper.rb",
    "content": "ENV[\"RAILS_ENV\"] = \"test\"\nrequire File.expand_path('../../config/environment', __FILE__)\nrequire 'rails/test_help'\n\nclass ActiveSupport::TestCase\n  ActiveRecord::Migration.check_pending!\n\n  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.\n  #\n  # Note: You'll currently still have to declare fixtures explicitly in integration tests\n  # -- they do not yet inherit this setting\n  fixtures :all\n\n  # Add more helper methods to be used by all tests here...\nend\n"
  },
  {
    "path": "test/apps/rails4_with_engines/vendor/assets/javascripts/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails4_with_engines/vendor/assets/stylesheets/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files for more about ignoring files.\n#\n# If you find yourself ignoring temporary files generated by your text editor\n# or operating system, you probably want to add a global ignore instead:\n#   git config --global core.excludesfile '~/.gitignore_global'\n\n# Ignore bundler config.\n/.bundle\n\n# Ignore the default SQLite database.\n/db/*.sqlite3\n/db/*.sqlite3-journal\n\n# Ignore all logfiles and tempfiles.\n/log/*\n/tmp/*\n!/log/.keep\n!/tmp/.keep\n\n# Ignore Byebug command history file.\n.byebug_history\n"
  },
  {
    "path": "test/apps/rails5/Gemfile",
    "content": "source 'https://rubygems.org'\n\n\n# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'\ngem 'rails', '>= 5.0.0.beta1', '< 5.1'\n# Use sqlite3 as the database for Active Record\ngem 'sqlite3'\n# Use Uglifier as compressor for JavaScript assets\ngem 'uglifier', '>= 1.3.0'\n# Use CoffeeScript for .coffee assets and views\ngem 'coffee-rails', '~> 4.1.0'\n# See https://github.com/rails/execjs#readme for more supported runtimes\n# gem 'therubyracer', platforms: :ruby\n\n# Use jquery as the JavaScript library\ngem 'jquery-rails'\n# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks\ngem 'turbolinks'\n# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder\ngem 'jbuilder', '~> 2.0'\n# Use Puma as the app server\ngem 'puma'\n\ngem 'actionpack-page_caching', '1.2.0'\n\n# Use ActiveModel has_secure_password\n# gem 'bcrypt', '~> 3.1.7'\n\n# Use Capistrano for deployment\n# gem 'capistrano-rails', group: :development\n\ngroup :development, :test do\n  # Call 'byebug' anywhere in the code to stop execution and get a debugger console\n  gem 'byebug'\nend\n\ngroup :development do\n  # Access an IRB console on exception pages or by using <%= console %> in views\n  gem 'web-console', '~> 3.0'\n  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring\n  gem 'spring'\nend\n\n# Windows does not include zoneinfo files, so bundle the tzinfo-data gem\ngem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]\n"
  },
  {
    "path": "test/apps/rails5/README.md",
    "content": "## README\n\nThis README would normally document whatever steps are necessary to get the\napplication up and running.\n\nThings you may want to cover:\n\n* Ruby version\n\n* System dependencies\n\n* Configuration\n\n* Database creation\n\n* Database initialization\n\n* How to run the test suite\n\n* Services (job queues, cache servers, search engines, etc.)\n\n* Deployment instructions\n\n* ...\n"
  },
  {
    "path": "test/apps/rails5/Rakefile",
    "content": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire File.expand_path('../config/application', __FILE__)\n\nRails.application.load_tasks\n"
  },
  {
    "path": "test/apps/rails5/app/assets/config/manifest.js",
    "content": "//= link_tree ../images\n//= link_directory ../javascripts .js\n//= link_directory ../stylesheets .css\n"
  },
  {
    "path": "test/apps/rails5/app/assets/images/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/app/assets/javascripts/application.js",
    "content": "// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//\n// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,\n// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.\n//\n// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the\n// compiled file. JavaScript code in this file should be added after the last require_* statement.\n//\n// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details\n// about supported directives.\n//\n//= require jquery\n//= require jquery_ujs\n//= require turbolinks\n//= require_tree .\n"
  },
  {
    "path": "test/apps/rails5/app/assets/javascripts/cable.coffee",
    "content": "# Action Cable provides the framework to deal with WebSockets in Rails. \n# You can generate new channels where WebSocket features live using the rails generate channel command.\n#\n# Turn on the cable connection by removing the comments after the require statements (and ensure it's also on in config/routes.rb).\n#\n#= require action_cable\n#= require_self\n#= require_tree ./channels\n#\n# @App ||= {}\n# App.cable = ActionCable.createConsumer()\n"
  },
  {
    "path": "test/apps/rails5/app/assets/javascripts/channels/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/app/assets/javascripts/users.coffee",
    "content": "# Place all the behaviors and hooks related to the matching controller here.\n# All this logic will automatically be available in application.js.\n# You can use CoffeeScript in this file: http://coffeescript.org/\n"
  },
  {
    "path": "test/apps/rails5/app/assets/stylesheets/application.css",
    "content": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below.\n *\n * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,\n * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.\n *\n * You're free to add application-wide styles to this file and they'll appear at the bottom of the\n * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS\n * files in this directory. Styles in this file should be added after the last require_* statement.\n * It is generally better to create a new file per style scope.\n *\n *= require_tree .\n *= require_self\n */\n"
  },
  {
    "path": "test/apps/rails5/app/assets/stylesheets/scaffold.css",
    "content": "body {\n  background-color: #fff;\n  color: #333;\n}\n\nbody, p, ol, ul, td {\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 13px;\n  line-height: 18px;\n  margin: 33px;\n}\n\npre {\n  background-color: #eee;\n  padding: 10px;\n  font-size: 11px;\n}\n\na {\n  color: #000;\n}\n\na:visited {\n  color: #666;\n}\n\na:hover {\n  color: #fff;\n  background-color: #000;\n}\n\nth {\n  padding-bottom: 5px;\n}\n\ntd {\n  padding-bottom: 7px;\n  padding-left: 5px;\n  padding-right: 5px;\n}\n\ndiv.field,\ndiv.actions {\n  margin-bottom: 10px;\n}\n\n#notice {\n  color: green;\n}\n\n.field_with_errors {\n  padding: 2px;\n  background-color: red;\n  display: table;\n}\n\n#error_explanation {\n  width: 450px;\n  border: 2px solid red;\n  padding: 7px;\n  padding-bottom: 0;\n  margin-bottom: 20px;\n  background-color: #f0f0f0;\n}\n\n#error_explanation h2 {\n  text-align: left;\n  font-weight: bold;\n  padding: 5px 5px 5px 15px;\n  font-size: 12px;\n  margin: -7px;\n  margin-bottom: 0;\n  background-color: #c00;\n  color: #fff;\n}\n\n#error_explanation ul li {\n  font-size: 12px;\n  list-style: square;\n}\n\nlabel {\n  display: block;\n}\n"
  },
  {
    "path": "test/apps/rails5/app/assets/stylesheets/users.css",
    "content": "/*\n  Place all the styles related to the matching controller here.\n  They will automatically be included in application.css.\n*/\n"
  },
  {
    "path": "test/apps/rails5/app/channels/application_cable/channel.rb",
    "content": "# Be sure to restart your server when you modify this file. Action Cable runs in an EventMachine loop that does not support auto reloading.\nmodule ApplicationCable\n  class Channel < ActionCable::Channel::Base\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/app/channels/application_cable/connection.rb",
    "content": "# Be sure to restart your server when you modify this file. Action Cable runs in an EventMachine loop that does not support auto reloading.\nmodule ApplicationCable\n  class Connection < ActionCable::Connection::Base\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::Base\n  # Prevent CSRF attacks by raising an exception.\n  # For APIs, you may want to use :null_session instead.\n  protect_from_forgery with: :exception\nend\n"
  },
  {
    "path": "test/apps/rails5/app/controllers/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/app/controllers/concerns/concerning.rb",
    "content": "module Concerning\n  extend ActiveSupport::Concern\n\n  included do\n    include Concerning\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/app/controllers/concerns/forgery_protection.rb",
    "content": "module ForgeryProtection\n  extend ActiveSupport::Concern\n\n  included do\n    protect_from_forgery with: :exception\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/app/controllers/file_controller.rb",
    "content": "class FileController < ApplicationController\n  def download_tempfile_with_params\n    send_file Tempfile.new([params[:file_name], \".txt\"])\n  end\n\n  def download_sanitized_with_params\n    send_file ActiveStorage::Filename.new(\"#{params[:file_name]}.jpg\").sanitized\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/app/controllers/mixed_controller.rb",
    "content": "class BaseController < ActionController::Base\n  # No protect_from_forgery call, but one mixed in\n  include ForgeryProtection\n  include Concerning\n\n  Statistics::AdminWithdrawal::BANK_LIST = [:deutsche, :boa, :jpm_chase, :cyprus]\n\n  def another_early_return\n    bank_name = params[:filename].first\n    unless Statistics::AdminWithdrawal::BANK_LIST.include?(bank_name)\n      flash[:alert] = 'Invalid filename'\n      redirect_to :back\n      return\n    end\n\n    Statistics::AdminWithdrawal.send(\"export_#{bank_name}_#inc!\")\n  end\n\n  def yet_another_early_return\n    scope_name = params[:scope].presence\n    fail ActiveRecord::RecordNotFound unless ['safe', 'also_safe'].include?(scope_name)\n    Model.public_send(scope_name)\n  end\n\n  def redirect_to_strong_params\n    redirect_to params.permit(:domain) # should warn\n    redirect_to params.permit(:page, :sort) # should not warn\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/app/controllers/users_controller.rb",
    "content": "class UsersController < ApplicationController\n  before_action :set_user, only: [:show, :edit, :update, :destroy]\n\n  # GET /users\n  # GET /users.json\n  def index\n    @users = User.all\n  end\n\n  # GET /users/1\n  # GET /users/1.json\n  def show\n  end\n\n  # GET /users/new\n  def new\n    @user = User.new\n  end\n\n  # GET /users/1/edit\n  def edit\n  end\n\n  # POST /users\n  # POST /users.json\n  def create\n    @user = User.new(user_params)\n\n    respond_to do |format|\n      if @user.save\n        format.html { redirect_to @user, notice: 'User was successfully created.' }\n        format.json { render :show, status: :created, location: @user }\n      else\n        format.html { render :new }\n        format.json { render json: @user.errors, status: :unprocessable_entity }\n      end\n    end\n  end\n\n  # PATCH/PUT /users/1\n  # PATCH/PUT /users/1.json\n  def update\n    respond_to do |format|\n      if @user.update(user_params)\n        format.html { redirect_to @user, notice: 'User was successfully updated.' }\n        format.json { render :show, status: :ok, location: @user }\n      else\n        format.html { render :edit }\n        format.json { render json: @user.errors, status: :unprocessable_entity }\n      end\n    end\n  end\n\n  # DELETE /users/1\n  # DELETE /users/1.json\n  def destroy\n    @user.destroy\n    respond_to do |format|\n      format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }\n      format.json { head :no_content }\n    end\n  end\n\n  private\n    # Use callbacks to share common setup or constraints between actions.\n    def set_user\n      @user = User.find(params[:id])\n    end\n\n    # Never trust parameters from the scary internet, only allow the white list through.\n    def user_params\n      params[:user]\n    end\n\n    def ruby_230\n      x&.send(params[:x])\n      a&.b ||= blah\n      params.permit!\n      User.new&.attributes = params\n    end\n\n    def symbol\n      params[:x].to_sym\n    end\n\n    def slice_then_permit\n      User.new(params.slice(:id).permit!)\n      User.find_by(params.slice(:id))\n      redirect_to params.slice(:back_to)\n    end\n\n    def nested_sql_interp \n      User.connection.execute(\"SELECT * FROM foo WHERE #{true ? \"bar = #{ActiveRecord::Base.connection.quote(true)}\" : \"bar = 0\"}\")\n    end\n\n    def arel_sql\n      Arel.sql(\"select #{params[:s]}\")\n    end\nend\n"
  },
  {
    "path": "test/apps/rails5/app/controllers/widget_controller.rb",
    "content": "class WidgetController < ApplicationController\n  def show\n  end\n\n  def dynamic_constant\n    identifier_class = params[:IdentifierClass]\n    namespace = identifier_class.constantize::IDENTIFIER_NAMESPACE # should warn\n  end\n\n  def render_thing\n    render params[:x].thing?\n  end\n\n  def render_inline\n    render :inline => \"<%= xss.html_safe %>\", :content_type => \"text/html\", :locals => { :xss => params[:xss] }\n  end\n\n  def sql_with_case\n    group_by_col =\n      case params[:group_by]\n      when 'brand'            then 'brand'\n      when 'year'             then 'YEAR(date_utc)'\n      when 'month'            then \"CONCAT(YEAR(date_utc), '-', LPAD(MONTH(date_utc), 2, '0'))\"\n      when 'week'             then \"CONCAT(YEAR(date_utc), '-', LPAD(WEEK(date_utc, 1), 2, '0'))\"\n      when 'day'              then \"DATE(date_utc)\"\n      else raise ArgumentError, 'Invalid group by value'\n      end\n\n    query = \"SELECT id, #{group_by_col} AS group_by_col, COUNT(*) FROM records\"\n\n    # No warnings\n    rows = User.connection.select_rows(query)\n  end\n\n  def sql_with_another_case\n    subset_clause = case script_subset\n                    when :greasyfork\n                      \"AND `sensitive` = false\"\n                    when :sleazyfork\n                      \"AND `sensitive` = true\"\n                    else\n                      \"\"\n                    end\n    sql =<<-EOF\n  SELECT\n    text, SUM(daily_installs) install_count, COUNT(s.id) script_count\n  FROM script_applies_tos\n  JOIN scripts s ON script_id = s.id\n  WHERE\n    domain\n    AND script_type_id = 1\n    AND script_delete_type_id IS NULL\n    AND !tld_extra\n    #{subset_clause}\n  GROUP BY text\n  ORDER BY text\n    EOF\n\n    # No warnings\n    by_sites = User.connection.select_rows(sql)\n  end\n\n  def render_with_case\n    # No warnings\n    case params[:switch_case_on_this]\n    when \"one\"\n      render partial: params[:switch_case_on_this], locals: { x: 1 }\n    when \"two\"\n      render partial: params[:switch_case_on_this], locals: { x: 2 }\n    end\n  end\n\n  def no_html\n    @x = params[:x].html_safe\n  end\n\n  def guard_with_return\n    goto = params[:goto]\n    event = params[:event]\n    return redirect_to user_path unless %w[comment subscribe].include?(goto)\n\n    redirect_to send(\"#{goto}_event_path\", event) # should not warn\n  end\n\n  def render_cookies\n    render inline: request.cookies[\"value\"]\n  end\n\n  def dangerous_permits\n    params.permit(:admin)\n    params.permit(:role_id)\n  end\n\n  def redirect_to_path\n    session = User.find_by_token params[:session]\n\n    if session\n      # proceed with extracting user context from session and more and redirect to the last path the user was shown to\n      login(session.user)\n      redirect_to session.user.current_path\n    else\n      redirect_to expired_or_invalid_session_path\n    end\n  end\n\n  def render_safely\n    slug = params[:slug].to_s\n    render slug if template_exists?(slug, 'pages')\n  end\n\n  def attributes\n  end\n\n  def haml_test\n  end\nend\n\nIDENTIFIER_NAMESPACE = 'apis'\n"
  },
  {
    "path": "test/apps/rails5/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails5/app/helpers/users_helper.rb",
    "content": "module UsersHelper\n  def bad_helper\n    eval(params[:x])\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/app/jobs/application_job.rb",
    "content": "class ApplicationJob < ActiveJob::Base\nend\n"
  },
  {
    "path": "test/apps/rails5/app/mailers/application_mailer.rb",
    "content": "class ApplicationMailer < ActionMailer::Base\n  default from: 'from@example.com'\n  layout 'mailer'\nend\n"
  },
  {
    "path": "test/apps/rails5/app/models/application_record.rb",
    "content": "class ApplicationRecord < ActiveRecord::Base\n  self.abstract_class = true\nend\n"
  },
  {
    "path": "test/apps/rails5/app/models/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/app/models/thing.rb",
    "content": "class Thing < ApplicationRecord\n  def self.self_and_descendants_for(id)\n    where(<<-SQL, id: id)\n      #{quoted_table_name}.#{quoted_primary_key} IN (\n        WITH RECURSIVE descendant_tree(#{quoted_primary_key}, path) AS (\n            SELECT #{quoted_primary_key}, ARRAY[#{quoted_primary_key}]\n            FROM #{quoted_table_name}\n            WHERE #{quoted_primary_key} = :id\n          UNION ALL\n            SELECT #{quoted_table_name}.#{quoted_primary_key}, descendant_tree.path || #{quoted_table_name}.#{quoted_primary_key}\n            FROM descendant_tree\n            JOIN #{quoted_table_name} ON #{quoted_table_name}.parent_id = descendant_tree.#{quoted_primary_key}\n            WHERE NOT #{quoted_table_name}.#{quoted_primary_key} = ANY(descendant_tree.path)\n        )\n        SELECT #{quoted_primary_key}\n        FROM descendant_tree\n        ORDER BY path\n      )\n     SQL\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/app/models/user.rb",
    "content": "class User < ApplicationRecord\n  def self.render_user_input\n    ERB.new(params).result\n  end\n\n  def self.evaluate_user_input\n    eval(params)\n  end\n\n  def evaluate_user_input\n    self.class.evaluate_user_input\n  end\n\n  def test_stuff\n    if Rails.env.test?\n      User.where(params)\n    end\n  end\n\n  has_many :things,\n    -> { where(Thing.canadian.where_values_hash) }\n\n  def self.all_that_jazz(user)\n    User.where(User.access_condition(user))\n  end\n\n  belongs_to :matched_user, class_name: 'User', optional: true\nend\n"
  },
  {
    "path": "test/apps/rails5/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Rails5</title>\n    <%= csrf_meta_tags %>\n    <%= action_cable_meta_tag %>\n\n    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>\n    <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>\n  </head>\n\n  <body>\n    <%= yield %>\n  </body>\n</html>\n"
  },
  {
    "path": "test/apps/rails5/app/views/layouts/mailer.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n    <style>\n      /* Email styles need to be inline */\n    </style>\n  </head>\n\n  <body>\n    <%= yield %>\n  </body>\n</html>\n"
  },
  {
    "path": "test/apps/rails5/app/views/layouts/mailer.text.erb",
    "content": "<%= yield %>\n"
  },
  {
    "path": "test/apps/rails5/app/views/layouts/users.html.erb",
    "content": "<% if @user %>\n  <%= @user.name.html_safe %>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails5/app/views/users/_form.html.erb",
    "content": "<%= form_for(user) do |f| %>\n  <% if user.errors.any? %>\n    <div id=\"error_explanation\">\n      <h2><%= pluralize(user.errors.count, \"error\") %> prohibited this user from being saved:</h2>\n\n      <ul>\n      <% user.errors.full_messages.each do |message| %>\n        <li><%= message %></li>\n      <% end %>\n      </ul>\n    </div>\n  <% end %>\n\n  <div class=\"actions\">\n    <%= f.submit %>\n  </div>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails5/app/views/users/edit.html.erb",
    "content": "<h1>Editing User</h1>\n\n<%= render 'form', user: @user %>\n\n<%= link_to 'Show', @user %> |\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails5/app/views/users/find_and_preserve.html.haml",
    "content": "= find_and_preserve do\n  %pre\n    :escaped\n      <p class=\"input-container input-container-5\">\n        <label for=\"f1\">Name</label>\n        <input class=\"input\" id=\"f1\" placeholder=\".input-container-5\" type=\"text\">\n      </p>\n      <p class=\"input-container input-container-10\">\n        <label for=\"f2\">Address</label>\n        <input class=\"input\" id=\"f2\" placeholder=\".input-container-10\" type=\"text\">\n      </p>\n"
  },
  {
    "path": "test/apps/rails5/app/views/users/if_thing.html.haml",
    "content": ":javascript\n  #{j(params[:a])} // Should not warn\n  #{j(params[:b]) unless params[:c]} // Should not warn\n"
  },
  {
    "path": "test/apps/rails5/app/views/users/index.html.erb",
    "content": "<p id=\"notice\"><%= notice %></p>\n\n<h1>Users</h1>\n\n<table>\n  <thead>\n    <tr>\n      <th colspan=\"3\"></th>\n    </tr>\n  </thead>\n\n  <tbody>\n    <% @users.each do |user| %>\n      <tr>\n        <td><%= link_to 'Show', user %></td>\n        <td><%= link_to 'Edit', edit_user_path(user) %></td>\n        <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>\n      </tr>\n    <% end %>\n  </tbody>\n</table>\n\n<br>\n\n<%= link_to 'New User', new_user_path %>\n<%= link_to 'slice', params.slice(:url) %>\n"
  },
  {
    "path": "test/apps/rails5/app/views/users/index.json.jbuilder",
    "content": "json.array!(@users) do |user|\n  json.extract! user, :id\n  json.url user_url(user, format: :json)\nend\n"
  },
  {
    "path": "test/apps/rails5/app/views/users/new.html.erb",
    "content": "<h1>New User</h1>\n\n<%= render 'form', user: @user %>\n\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails5/app/views/users/safe_call_params.html.haml",
    "content": ":javascript\n  factory.printing.copies = #{params[:copies]&.to_i || 1};\n"
  },
  {
    "path": "test/apps/rails5/app/views/users/sanitizing.html.erb",
    "content": "<%= raw sanitize(params[:x]) %>\n\n<%= strip_tags(params[:x]).html_safe %>\n"
  },
  {
    "path": "test/apps/rails5/app/views/users/show.html.erb",
    "content": "<p id=\"notice\"><%= notice %></p>\n\n<%= link_to 'Edit', edit_user_path(@user) %> |\n<%= link_to 'Back', users_path %>\n\n<%= link_to(\"good\", params.merge(:page => 2)) %>\n<%= link_to(\"xss\", url_for(params[:bad])) %>\n\n<%= link_to(image_tag(\"icons/twitter-gray.svg\"), sanitize(@user.home_page), target: \"_blank\") %>\n\n<%= link_to '', 'something_static', target: '_blank' %> No warning\n<%= link_to '', some_url, target: '_blank' %> Warn\n<%= link_to '', some_url, target: :_blank %> Warn\n<%= link_to some_url, target: '_blank' do -%>\n  Warn\n<% end %>\n<%= link_to some_url, target: :_blank do -%>\n  Warn\n<% end %>\n<%= link_to '', some_url, target: '_blank', rel: 'noopener' %> Weak warning\n<%= link_to '', some_url, target: '_blank', rel: 'noreferrer' %> Weak warning\n<%= link_to '', some_url, target: '_blank', rel: 'noopener noreferrer' %> No warning\n<%= link_to some_url, target: '_blank', rel: 'noopener noreferrer' do -%>\n  No Warning\n<% end %>\n<%= link_to '', target: '_blank' %> No warning\n"
  },
  {
    "path": "test/apps/rails5/app/views/users/show.json.jbuilder",
    "content": "json.extract! @user, :id, :created_at, :updated_at\n"
  },
  {
    "path": "test/apps/rails5/app/views/widget/attributes.html.haml",
    "content": "%a{\"data-text\" => \"#{params[:name]}\"} No warning\n\n%pre.ksd.margin= params[:blah]\n\n= @user = User.first\n\n\n= form_tag create_thing_path do\n  %p\n    Stuff\n  %ul\n    %li things\n    %li blah + #{@user.name.stuff_html.html_safe}\n    %li other stuff #{@user.name_with_stuff}\n    %li\n      asdoasd\n      %code arghlagb\n    %li and the things #{@user.name_with_stuff}\n\n= blah = capture do\n  %p= params[:x]\n\n= link_to stuff, blah\n"
  },
  {
    "path": "test/apps/rails5/app/views/widget/content_tag.html.erb",
    "content": "<%= content_tag(:div, \"hi\", title: params[:stuff].html_safe) %>\n\n<%= content_tag(:div, \"hi\", title: sanitize(params[:stuff])) %>\n"
  },
  {
    "path": "test/apps/rails5/app/views/widget/graphql.html.erb",
    "content": "<%graphql\n  fragment Thing on Thing {\n    thingHTML\n\n    ...Views::Things::ThingHeader::Thing\n  }\n%>\n"
  },
  {
    "path": "test/apps/rails5/app/views/widget/haml_test.html.haml",
    "content": "%li\n  = \"#{ params[:x] } No warning, escaped by default\"\n\n%p\n  :javascript\n    var x = #{ raw params[:y] }; // This better warn!\n\n\n= form_for [:admin, @user], :html => { :onsubmit => \"event.stop(); addField(this)\" } do |f|\n  %p\n    %label{:for=>\"page_field_name\"}= t('name')\n\n%pre.unobstrusive= something? ? @user.name : \"No warning, escaped\"\n\n%td #{link_to @user.description, edit_user_path(@user)} # No warning\n"
  },
  {
    "path": "test/apps/rails5/app/views/widget/no_html.haml",
    "content": "%h1= @x\n"
  },
  {
    "path": "test/apps/rails5/app/views/widget/show.html.erb",
    "content": "<%= params[:x].html_safe unless this_is_a_bad_idea? %>\n\n<%= link_to(\"Thing\", \"#{ENV['SOME_URL']}#{params[:x]}\") %>\n<%= link_to(\"Email!\", \"mailto:#{params[:x]}\") %>\n"
  },
  {
    "path": "test/apps/rails5/bin/rails",
    "content": "#!/usr/bin/env ruby\nbegin\n  load File.expand_path('../spring', __FILE__)\nrescue LoadError => e\n  raise unless e.message.include?('spring')\nend\nAPP_PATH = File.expand_path('../../config/application', __FILE__)\nrequire_relative '../config/boot'\nrequire 'rails/commands'\n"
  },
  {
    "path": "test/apps/rails5/bin/rake",
    "content": "#!/usr/bin/env ruby\nbegin\n  load File.expand_path('../spring', __FILE__)\nrescue LoadError => e\n  raise unless e.message.include?('spring')\nend\nrequire_relative '../config/boot'\nrequire 'rake'\nRake.application.run\n"
  },
  {
    "path": "test/apps/rails5/bin/setup",
    "content": "#!/usr/bin/env ruby\nrequire 'pathname'\nrequire 'fileutils'\ninclude FileUtils\n\n# path to your application root.\nAPP_ROOT = Pathname.new File.expand_path('../../', __FILE__)\n\ndef system!(*args)\n  system(*args) || abort(\"\\n== Command #{args} failed ==\")\nend\n\nchdir APP_ROOT do\n  # This script is a starting point to setup your application.\n  # Add necessary setup steps to this file.\n\n  puts '== Installing dependencies =='\n  system! 'gem install bundler --conservative'\n  system('bundle check') or system!('bundle install')\n\n  # puts \"\\n== Copying sample files ==\"\n  # unless File.exist?('config/database.yml')\n  #   cp 'config/database.yml.sample', 'config/database.yml'\n  # end\n\n  puts \"\\n== Preparing database ==\"\n  system! 'bin/rails db:setup'\n\n  puts \"\\n== Removing old logs and tempfiles ==\"\n  system! 'bin/rails log:clear tmp:clear'\n\n  puts \"\\n== Restarting application server ==\"\n  system! 'bin/rails restart'\nend\n"
  },
  {
    "path": "test/apps/rails5/bin/spring",
    "content": "#!/usr/bin/env ruby\n\n# This file loads spring without using Bundler, in order to be fast.\n# It gets overwritten when you run the `spring binstub` command.\n\nunless defined?(Spring)\n  require 'rubygems'\n  require 'bundler'\n\n  if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^    (?:  )*spring \\((.*?)\\)$.*?^$/m))\n    Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq }\n    gem 'spring', match[1]\n    require 'spring/binstub'\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/bin/update",
    "content": "#!/usr/bin/env ruby\nrequire 'pathname'\nrequire 'fileutils'\ninclude FileUtils\n\n# path to your application root.\nAPP_ROOT = Pathname.new File.expand_path('../../', __FILE__)\n\ndef system!(*args)\n  system(*args) || abort(\"\\n== Command #{args} failed ==\")\nend\n\nchdir APP_ROOT do\n  # This script is a way to update your development environment automatically.\n  # Add necessary update steps to this file.\n\n  puts '== Installing dependencies =='\n  system! 'gem install bundler --conservative'\n  system 'bundle check' or system! 'bundle install'\n\n  puts \"\\n== Updating database ==\"\n  system! 'bin/rails db:migrate'\n\n  puts \"\\n== Removing old logs and tempfiles ==\"\n  system! 'bin/rails log:clear tmp:clear'\n\n  puts \"\\n== Restarting application server ==\"\n  system! 'bin/rails restart'\nend\n"
  },
  {
    "path": "test/apps/rails5/config/application.rb",
    "content": "require File.expand_path('../boot', __FILE__)\n\nrequire 'rails/all'\n\n# Require the gems listed in Gemfile, including any gems\n# you've limited to :test, :development, or :production.\nBundler.require(*Rails.groups)\n\nmodule Rails5\n  class Application < Rails::Application\n    # Settings in config/environments/* take precedence over those specified here.\n    # Application configuration should go into files in config/initializers\n    # -- all .rb files in that directory are automatically loaded.\n\n    config.action_view.sanitized_allowed_tags = [\"select\", \"strong\", \"style\", \"b\"]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/config/boot.rb",
    "content": "ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)\n\nrequire 'bundler/setup' # Set up gems listed in the Gemfile.\n"
  },
  {
    "path": "test/apps/rails5/config/brakeman.yml",
    "content": "---\n:additional_checks_path:\n  - \"./test/apps/rails5/external_checks\"\n"
  },
  {
    "path": "test/apps/rails5/config/database.yml",
    "content": "# SQLite version 3.x\n#   gem install sqlite3\n#\n#   Ensure the SQLite 3 gem is defined in your Gemfile\n#   gem 'sqlite3'\n#\ndefault: &default\n  adapter: sqlite3\n  pool: 5\n  timeout: 5000\n\ndevelopment:\n  <<: *default\n  database: db/development.sqlite3\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  <<: *default\n  database: db/test.sqlite3\n\nproduction:\n  <<: *default\n  database: db/production.sqlite3\n"
  },
  {
    "path": "test/apps/rails5/config/environment.rb",
    "content": "# Load the Rails application.\nrequire File.expand_path('../application', __FILE__)\n\n# Initialize the Rails application.\nRails.application.initialize!\n"
  },
  {
    "path": "test/apps/rails5/config/environments/development.rb",
    "content": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # In the development environment your application's code is reloaded on\n  # every request. This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes = false\n\n  # Do not eager load code on boot.\n  config.eager_load = false\n\n  # Show full error reports.\n  config.consider_all_requests_local = true\n\n  # Enable/disable caching. By default caching is disabled.\n  if Rails.root.join('tmp/caching-dev.txt').exist?\n    config.action_controller.perform_caching = true\n    config.cache_store = :memory_store\n    config.public_file_server.headers = {\n      'Cache-Control' => 'public, max-age=172800'\n    }\n  else\n    config.action_controller.perform_caching = false\n    config.cache_store = :null_store\n  end\n\n  # Don't care if the mailer can't send.\n  config.action_mailer.raise_delivery_errors = false\n\n  # Print deprecation notices to the Rails logger.\n  config.active_support.deprecation = :log\n\n  # Raise an error on page load if there are pending migrations.\n  config.active_record.migration_error = :page_load\n\n  # Debug mode disables concatenation and preprocessing of assets.\n  # This option may cause significant delays in view rendering with a large\n  # number of complex assets.\n  config.assets.debug = true\n\n  # Asset digests allow you to set far-future HTTP expiration dates on all assets,\n  # yet still be able to expire them through the digest params.\n  config.assets.digest = true\n\n  # Adds additional error checking when serving assets at runtime.\n  # Checks for improperly declared sprockets dependencies.\n  # Raises helpful error messages.\n  config.assets.raise_runtime_errors = true\n\n  # Raises error for missing translations\n  # config.action_view.raise_on_missing_translations = true\n\n  # Use an evented file watcher to asynchronously detect changes in source code,\n  # routes, locales, etc. This feature depends on the listen gem.\n  # config.file_watcher = ActiveSupport::EventedFileUpdateChecker\nend\n"
  },
  {
    "path": "test/apps/rails5/config/environments/production.rb",
    "content": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Code is not reloaded between requests.\n  config.cache_classes = true\n\n  # Eager load code on boot. This eager loads most of Rails and\n  # your application in memory, allowing both threaded web servers\n  # and those relying on copy on write to perform better.\n  # Rake tasks automatically ignore this option for performance.\n  config.eager_load = true\n\n  # Full error reports are disabled and caching is turned on.\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Disable serving static files from the `/public` folder by default since\n  # Apache or NGINX already handles this.\n  config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?\n\n  # Compress JavaScripts and CSS.\n  config.assets.js_compressor = :uglifier\n  # config.assets.css_compressor = :sass\n\n  # Do not fallback to assets pipeline if a precompiled asset is missed.\n  config.assets.compile = true # CVE-2018-3760\n\n  # Asset digests allow you to set far-future HTTP expiration dates on all assets,\n  # yet still be able to expire them through the digest params.\n  config.assets.digest = true\n\n  # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server.\n  # config.action_controller.asset_host = 'http://assets.example.com'\n\n  # Specifies the header that your server uses for sending files.\n  # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache\n  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX\n\n  # Action Cable endpoint configuration\n  # config.action_cable.url = 'wss://example.com/cable'\n  # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\\/\\/example.*/ ]\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  # config.force_ssl = true\n\n  # Use the lowest log level to ensure availability of diagnostic information\n  # when problems arise.\n  config.log_level = :debug\n\n  # Prepend all log lines with the following tags.\n  # config.log_tags = [ :subdomain, :request_id ]\n\n  # Use a different logger for distributed setups.\n  # require 'syslog/logger'\n  # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')\n\n  # Use a different cache store in production.\n  # config.cache_store = :mem_cache_store\n\n  # Use a real queuing backend for Active Job (and separate queues per environment)\n  # config.active_job.queue_adapter     = :resque\n  # config.active_job.queue_name_prefix = \"rails5_#{Rails.env}\"\n\n  # Ignore bad email addresses and do not raise email delivery errors.\n  # Set this to true and configure the email server for immediate delivery to raise delivery errors.\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation cannot be found).\n  config.i18n.fallbacks = true\n\n  # Send deprecation notices to registered listeners.\n  config.active_support.deprecation = :notify\n\n  # Use default logging formatter so that PID and timestamp are not suppressed.\n  config.log_formatter = ::Logger::Formatter.new\n\n  # Do not dump schema after migrations.\n  config.active_record.dump_schema_after_migration = false\nend\n"
  },
  {
    "path": "test/apps/rails5/config/environments/test.rb",
    "content": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # The test environment is used exclusively to run your application's\n  # test suite. You never need to work with it otherwise. Remember that\n  # your test database is \"scratch space\" for the test suite and is wiped\n  # and recreated between test runs. Don't rely on the data there!\n  config.cache_classes = true\n\n  # Do not eager load code on boot. This avoids loading your whole application\n  # just for the purpose of running a single test. If you are using a tool that\n  # preloads Rails for running tests, you may have to set it to true.\n  config.eager_load = false\n\n  # Configure public file server for tests with Cache-Control for performance.\n  config.public_file_server.enabled = true\n  config.public_file_server.headers = {\n    'Cache-Control' => 'public, max-age=3600'\n  }\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Raise exceptions instead of rendering exception templates.\n  config.action_dispatch.show_exceptions = false\n\n  # Disable request forgery protection in test environment.\n  config.action_controller.allow_forgery_protection = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Randomize the order test cases are executed.\n  config.active_support.test_order = :random\n\n  # Print deprecation notices to the stderr.\n  config.active_support.deprecation = :stderr\n\n  # Raises error for missing translations\n  # config.action_view.raise_on_missing_translations = true\nend\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/active_record_belongs_to_required_by_default.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Require `belongs_to` associations by default. This is a new Rails 5.0 default, \n# so introduced as a config to ensure apps made with earlier versions of Rails aren't affected when upgrading.\nRails.application.config.active_record.belongs_to_required_by_default = true\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/application_controller_renderer.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# ApplicationController.renderer.defaults.merge!(\n#   http_host: 'example.org',\n#   https: false\n# )\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/assets.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Version of your assets, change this if you want to expire all your assets.\nRails.application.config.assets.version = '1.0'\n\n# Add additional assets to the asset load path\n# Rails.application.config.assets.paths << Emoji.images_path\n\n# Precompile additional assets.\n# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.\n# Rails.application.config.assets.precompile += %w( search.js )\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/callback_terminator.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Do not halt callback chains when a callback returns false. This is a new Rails 5.0 default, \n# so introduced as a config to ensure apps made with earlier versions of Rails aren't affected when upgrading.\nActiveSupport.halt_callback_chains_on_return_false = false\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/cookies_serializer.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# This is a new Rails 5.0 default, so introduced as a config to ensure apps made with earlier versions of Rails aren't affected when upgrading.\nRails.application.config.action_dispatch.cookies_serializer = :json\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/cors.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Avoid CORS issues when API is called from the frontend app.\n# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.\n\n# Read more: https://github.com/cyu/rack-cors\n\n# Rails.application.config.middleware.insert_before 0, Rack::Cors do\n#   allow do\n#     origins 'example.com'\n#\n#     resource '*',\n#       headers: :any,\n#       methods: [:get, :post, :put, :patch, :delete, :options, :head]\n#   end\n# end\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/filter_parameter_logging.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Configure sensitive parameters which will be filtered from the log file.\nRails.application.config.filter_parameters += [:password]\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Inflections\n# are locale specific, and you may define rules for as many different\n# locales as you wish. All of these examples are active by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n\n# These inflection rules are supported but not enabled by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.acronym 'RESTful'\n# end\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/request_forgery_protection.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Enable origin-checking CSRF mitigation.\nRails.application.config.action_controller.forgery_protection_origin_check = true\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/secrets.rb",
    "content": "DB_PASSWORD = \"sup3rs3cr37\"\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/session_store.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\nRails.application.config.session_store :cookie_store, key: '_rails5_session'\n"
  },
  {
    "path": "test/apps/rails5/config/initializers/wrap_parameters.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# This file contains settings for ActionController::ParamsWrapper which\n# is enabled by default.\n\n# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.\nActiveSupport.on_load(:action_controller) do\n  wrap_parameters format: [:json]\nend\n\n# To enable root element in JSON for ActiveRecord objects.\n# ActiveSupport.on_load(:active_record) do\n#   self.include_root_in_json = true\n# end\n"
  },
  {
    "path": "test/apps/rails5/config/locales/en.yml",
    "content": "# Files in the config/locales directory are used for internationalization\n# and are automatically loaded by Rails. If you want to use locales other\n# than English, add the necessary files in this directory.\n#\n# To use the locales, use `I18n.t`:\n#\n#     I18n.t 'hello'\n#\n# In views, this is aliased to just `t`:\n#\n#     <%= t('hello') %>\n#\n# To use a different locale, set it with `I18n.locale`:\n#\n#     I18n.locale = :es\n#\n# This would use the information in config/locales/es.yml.\n#\n# To learn more, please read the Rails Internationalization guide\n# available at http://guides.rubyonrails.org/i18n.html.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/apps/rails5/config/redis/cable.yml",
    "content": "# Action Cable uses Redis to administer connections, channels, and sending/receiving messages over the WebSocket.\nproduction:\n  url: redis://localhost:6379/1\n\ndevelopment:\n  url: redis://localhost:6379/2\n\ntest:\n  url: redis://localhost:6379/3\n"
  },
  {
    "path": "test/apps/rails5/config/routes.rb",
    "content": "Rails.application.routes.draw do\n  resources :users\n  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html\n\n  # Serve websocket cable requests in-process\n  # mount ActionCable.server => '/cable'\n  if Rails.env.test?\n    match '/:controller/:action'\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/config/secrets.yml",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key is used for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n\n# Make sure the secret is at least 30 characters and all random,\n# no regular words or you'll be exposed to dictionary attacks.\n# You can use `rake secret` to generate a secure secret key.\n\n# Make sure the secrets in this file are kept private\n# if you're sharing your code publicly.\n\ndevelopment:\n  secret_key_base: 12d3735e1cdb18ef2eca25f9a370028ac096ff273c5a889ed7a49047d5e30c9dc7fe095792a71b60c3f37dd80efaeda44db75e73c9f60813550c875eee7a241f\n\ntest:\n  secret_key_base: 446b08c3cdeccdaf9e8b247a2624d45218c5d429e8acde61ddd87aa7b9dd50973e49e6d94378cb4bcf08b7818a90abb044b5c8886f94de6970ade4a496df22f3\n\n# Do not keep production secrets in the repository,\n# instead read values from the environment.\nproduction:\n  secret_key_base: <%= ENV[\"SECRET_KEY_BASE\"] %>\n\n<% if Rails.root.join('config/ansible/secrets.yml').exist? %>\n<%= Rails.root.join('config/ansible/secrets.yml').read %>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails5/config.ru",
    "content": "# This file is used by Rack-based servers to start the application.\n\nrequire ::File.expand_path('../config/environment', __FILE__)\n\n# Action Cable uses EventMachine which requires that all classes are loaded in advance\nRails.application.eager_load!\nrequire 'action_cable/process/logging'\n\nrun Rails.application\n"
  },
  {
    "path": "test/apps/rails5/db/migrate/20160127223106_create_users.rb",
    "content": "class CreateUsers < ActiveRecord::Migration[5.0]\n  def change\n    create_table :users do |t|\n\n      t.timestamps\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/db/seeds.rb",
    "content": "# This file should contain all the record creation needed to seed the database with its default values.\n# The data can then be loaded with the rails db:seed (or created alongside the db with db:setup).\n#\n# Examples:\n#\n#   movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])\n#   Character.create(name: 'Luke', movie: movies.first)\n"
  },
  {
    "path": "test/apps/rails5/external_checks/check_external_check_test.rb",
    "content": "require 'brakeman/checks/base_check'\n\nclass Brakeman::CheckExternalCheckConfigTest < Brakeman::BaseCheck\n  Brakeman::Checks.add_optional self\n\n  @description = \"An external check for testing\"\n\n  def run_check\n    raise \"This should not have been loaded!\"\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/lib/a_lib.rb",
    "content": "class JustAClass\n  def do_sql_stuff\n    joins(\"INNER JOIN things ON id = #{params[:id]}\").\n    joins(\"INNER JOIN things ON stuff = 1\")\n  end\n\n  def divide_by_zero\n    whatever / 0 # warns\n\n    x = 100\n    y = x - 100\n    z = x / y # warns\n\n    1.0 / 0 # does not warn\n  end\n\n  def tempfile\n    FileUtils.move(params.permit(:my_upload => ([:upload])).dig(\"my_upload\", \"upload\").tempfile.path, \"/tmp/new_temp_file\")\n    FileUtils.move(params.permit(:my_upload => ([:upload])).dig(\"my_upload\", \"upload\").path, \"/tmp/new_temp_file\")\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/lib/assets/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/lib/lib.rb",
    "content": "class A\n  def b\n    $a, $b = a.b.c\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/lib/tasks/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/log/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The page you were looking for doesn't exist (404)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  body {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>The page you were looking for doesn't exist.</h1>\n      <p>You may have mistyped the address or the page may have moved.</p>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails5/public/422.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The change you wanted was rejected (422)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  body {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>The change you wanted was rejected.</h1>\n      <p>Maybe you tried to change something you didn't have access to.</p>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails5/public/500.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>We're sorry, but something went wrong (500)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  body {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body>\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>We're sorry, but something went wrong.</h1>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails5/public/robots.txt",
    "content": "# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file\n#\n# To ban all spiders from the entire site uncomment the next two lines:\n# User-agent: *\n# Disallow: /\n"
  },
  {
    "path": "test/apps/rails5/test/controllers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/test/controllers/users_controller_test.rb",
    "content": "require 'test_helper'\n\nclass UsersControllerTest < ActionDispatch::IntegrationTest\n  setup do\n    @user = users(:one)\n  end\n\n  test \"should get index\" do\n    get users_url\n    assert_response :success\n  end\n\n  test \"should get new\" do\n    get new_user_url\n    assert_response :success\n  end\n\n  test \"should create user\" do\n    assert_difference('User.count') do\n      post users_url, params: { user: {  } }\n    end\n\n    assert_redirected_to user_path(User.last)\n  end\n\n  test \"should show user\" do\n    get user_url(@user)\n    assert_response :success\n  end\n\n  test \"should get edit\" do\n    get edit_user_url(@user)\n    assert_response :success\n  end\n\n  test \"should update user\" do\n    patch user_url(@user), params: { user: {  } }\n    assert_redirected_to user_path(@user)\n  end\n\n  test \"should destroy user\" do\n    assert_difference('User.count', -1) do\n      delete user_url(@user)\n    end\n\n    assert_redirected_to users_path\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5/test/fixtures/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/test/fixtures/files/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/test/fixtures/users.yml",
    "content": "# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html\n\n# This model initially had no columns defined.  If you add columns to the\n# model remove the '{}' from the fixture names and add the columns immediately\n# below each fixture, per the syntax in the comments below\n#\none: {}\n# column: value\n#\ntwo: {}\n#  column: value\n"
  },
  {
    "path": "test/apps/rails5/test/helpers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/test/integration/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/test/mailers/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/test/models/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/test/models/user_test.rb",
    "content": "require 'test_helper'\n\nclass UserTest < ActiveSupport::TestCase\n  # test \"the truth\" do\n  #   assert true\n  # end\nend\n"
  },
  {
    "path": "test/apps/rails5/test/test_helper.rb",
    "content": "ENV['RAILS_ENV'] ||= 'test'\nrequire File.expand_path('../../config/environment', __FILE__)\nrequire 'rails/test_help'\n\nclass ActiveSupport::TestCase\n  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.\n  fixtures :all\n\n  # Add more helper methods to be used by all tests here...\nend\n"
  },
  {
    "path": "test/apps/rails5/tmp/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/vendor/assets/javascripts/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5/vendor/assets/stylesheets/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5.2/.ruby-version",
    "content": "2.3.1@something_weird\n"
  },
  {
    "path": "test/apps/rails5.2/Gemfile",
    "content": "source 'https://rubygems.org'\ngit_source(:github) { |repo| \"https://github.com/#{repo}.git\" }\n\nruby '2.3.1'\n\n# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'\ngem 'rails', '~> 5.2.0.beta2'\n# Use sqlite3 as the database for Active Record\ngem 'sqlite3'\n# Use Puma as the app server\ngem 'puma', '~> 3.11'\n# Use SCSS for stylesheets\ngem 'sass-rails', '~> 5.0'\n# Use Uglifier as compressor for JavaScript assets\ngem 'uglifier', '>= 1.3.0'\n# See https://github.com/rails/execjs#readme for more supported runtimes\n# gem 'mini_racer', platforms: :ruby\n\n# Use CoffeeScript for .coffee assets and views\ngem 'coffee-rails', '~> 4.2'\n# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks\ngem 'turbolinks', '~> 5'\n# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder\ngem 'jbuilder', '~> 2.5'\n# Use Redis adapter to run Action Cable in production\n# gem 'redis', '~> 4.0'\n# Use ActiveModel has_secure_password\n# gem 'bcrypt', '~> 3.1.7'\n\n# Use ActiveStorage variant\n# gem 'mini_magick', '~> 4.8'\n\n# Use Capistrano for deployment\n# gem 'capistrano-rails', group: :development\n\n# Reduces boot times through caching; required in config/boot.rb\ngem 'bootsnap', '>= 1.1.0', require: false\n\n# Specify haml 5 so we can test our warning (brakeman doesn't support haml 5 yet)\ngem 'haml', '~> 5.0.3'\n\ngroup :development, :test do\n  # Call 'byebug' anywhere in the code to stop execution and get a debugger console\n  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]\n  # Adds support for Capybara system testing and selenium driver\n  gem 'capybara', '~> 2.15'\n  gem 'selenium-webdriver'\n  # Easy installation and use of chromedriver to run system tests with Chrome\n  gem 'chromedriver-helper'\nend\n\ngroup :development do\n  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.\n  gem 'web-console', '>= 3.3.0'\n  gem 'listen', '>= 3.0.5', '< 3.2'\n  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring\n  gem 'spring'\n  gem 'spring-watcher-listen', '~> 2.0.0'\nend\n\n# Windows does not include zoneinfo files, so bundle the tzinfo-data gem\ngem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]\n\ngem \"slim\", \"~> 3.0.1\", require: [\"slim\", \"slim/smart\"]\n"
  },
  {
    "path": "test/apps/rails5.2/README.md",
    "content": "# README\n\nThis README would normally document whatever steps are necessary to get the\napplication up and running.\n\nThings you may want to cover:\n\n* Ruby version\n\n* System dependencies\n\n* Configuration\n\n* Database creation\n\n* Database initialization\n\n* How to run the test suite\n\n* Services (job queues, cache servers, search engines, etc.)\n\n* Deployment instructions\n\n* ...\n"
  },
  {
    "path": "test/apps/rails5.2/Rakefile",
    "content": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire_relative 'config/application'\n\nRails.application.load_tasks\n"
  },
  {
    "path": "test/apps/rails5.2/app/assets/config/manifest.js",
    "content": "//= link_tree ../images\n//= link_directory ../javascripts .js\n//= link_directory ../stylesheets .css\n"
  },
  {
    "path": "test/apps/rails5.2/app/assets/images/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5.2/app/assets/javascripts/application.js",
    "content": "// This is a manifest file that'll be compiled into application.js, which will include all the files\n// listed below.\n//\n// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's\n// vendor/assets/javascripts directory can be referenced here using a relative path.\n//\n// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the\n// compiled file. JavaScript code in this file should be added after the last require_* statement.\n//\n// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details\n// about supported directives.\n//\n//= require rails-ujs\n//= require activestorage\n//= require turbolinks\n//= require_tree .\n"
  },
  {
    "path": "test/apps/rails5.2/app/assets/javascripts/cable.js",
    "content": "// Action Cable provides the framework to deal with WebSockets in Rails.\n// You can generate new channels where WebSocket features live using the `rails generate channel` command.\n//\n//= require action_cable\n//= require_self\n//= require_tree ./channels\n\n(function() {\n  this.App || (this.App = {});\n\n  App.cable = ActionCable.createConsumer();\n\n}).call(this);\n"
  },
  {
    "path": "test/apps/rails5.2/app/assets/javascripts/channels/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5.2/app/assets/stylesheets/application.css",
    "content": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below.\n *\n * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's\n * vendor/assets/stylesheets directory can be referenced here using a relative path.\n *\n * You're free to add application-wide styles to this file and they'll appear at the bottom of the\n * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS\n * files in this directory. Styles in this file should be added after the last require_* statement.\n * It is generally better to create a new file per style scope.\n *\n *= require_tree .\n *= require_self\n */\n"
  },
  {
    "path": "test/apps/rails5.2/app/channels/application_cable/channel.rb",
    "content": "module ApplicationCable\n  class Channel < ActionCable::Channel::Base\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5.2/app/channels/application_cable/connection.rb",
    "content": "module ApplicationCable\n  class Connection < ActionCable::Connection::Base\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5.2/app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::Base\nend\n"
  },
  {
    "path": "test/apps/rails5.2/app/controllers/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5.2/app/controllers/users_controller.rb",
    "content": "class UsersController < ApplicationController\n  def index\n    index_params = params.permit(:name, friend_names: []).to_hash\n    User.where(index_params).qualify.all\n  end\n\n  def show\n    show_params = params.permit(:id, :name).to_hash.symbolize_keys\n    User.where(show_params).qualify.all\n  end\n\n  ALLOWED_FOOS = [:bar, :baz].freeze\n  def delete(foo)\n    unless ALLOWED_FOOS.include? foo\n      raise ArgumentError, \"Unexpected foo: #{foo}\"\n    end\n\n    Person.where(\"#{foo} >= 1\")\n  end\n\n  def safe_one(foo)\n    return if !ALLOWED_FOOS.include?(foo)\n\n    Person.where(\"#{foo} >= 1\")\n  end\n\n  def better_user_input_reporting\n    table = Something.selection.select { |x| some_condition? x }.map { |x| \"#{User.table_name}.#{x}\" } \n\n    # Should report SQLi, but not about User.table_name specifically\n    User.find_by_sql(\"SELECT #{\"#{table}.name\"} where name = #{params[:name]}\")\n  end\n\n  def splat_args \n    Person.where(*params[:foo]).qualify.all\n  end\n\n  def splat_kwargs\n    User.where(**params[:foo]).qualify.all\n  end\n\n  def one\n    @user = User.find(params[:id])\n  end\n\n  def two\n    @user = User.find(params[:id])\n  end\n\n  def some_api\n    Oj.load(params[:json]) # Unsafe by default\n    Oj.load(params[:json], mode: :object) # Unsafe, regardless of default\n    Oj.object_load(params[:json], mode: :strict) # Always unsafe, regardless of mode\n    Oj.load(params[:json], mode: :strict) # Safe\n  end\n\n  def not_not\n    si = ManualCSVImport.new(header_row: !!params[:header_row], archive: !!params[:archive])\n    @errors = [si.results[:invalid_info], si.results[:ignored_info]].flatten\n  end\n\n  def test_empty_partial_name\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5.2/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails5.2/app/helpers/users_helper.rb",
    "content": "module UsersHelper\nend\n"
  },
  {
    "path": "test/apps/rails5.2/app/jobs/application_job.rb",
    "content": "class ApplicationJob < ActiveJob::Base\nend\n"
  },
  {
    "path": "test/apps/rails5.2/app/jobs/delete_stuff_job.rb",
    "content": "class DeleteStuffJob < ApplicationJob\n  def perform file\n    `rm -rf #{file}`\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5.2/app/mailers/application_mailer.rb",
    "content": "class ApplicationMailer < ActionMailer::Base\n  default from: 'from@example.com'\n  layout 'mailer'\nend\n"
  },
  {
    "path": "test/apps/rails5.2/app/models/application_record.rb",
    "content": "class ApplicationRecord < ActiveRecord::Base\n  self.abstract_class = true\nend\n"
  },
  {
    "path": "test/apps/rails5.2/app/models/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5.2/app/models/user.rb",
    "content": "class User < ActiveRecord::Base\n  def not_something thing\n    where.not(\"blah == #{thing}\")\n  end\n  SUBQUERY_TABLE_ALIAS = \"my_table_alias\".freeze\n\n  # This is used inside a larger query by using `inner_query.to_sql`\n  def inner_query\n    self.class.\n      select(\"#{SUBQUERY_TABLE_ALIAS}.*\").\n      from(\"#{table_name} AS #{SUBQUERY_TABLE_ALIAS}\")\n  end\n\n  def singularize_safe_literal\n    [:fees, :fair].each do |type|\n      Money.new(articles.sum(\"calculated_#{type.to_s.singularize}_cents * quantity\"))\n    end\n  end\n\n  def foreign_key_thing\n    assoc_reflection = reflect_on_association(:foos)\n    foreign_key = assoc_reflection.foreign_key\n\n    User.joins(\"INNER JOIN <complex join involving custom SQL and #{foreign_key} interpolation>\")\n  end\n\n  def polymorphic_name_joins\n    MediaFile.joins(\n      \"JOIN #{table_name}\n        ON media_files.parent_type = '#{polymorphic_name}'\n        AND media_files.parent_id = #{table_name}.id\"\n    )\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/home/index.html.erb",
    "content": "<%= t(:my_translation, timeago: timeago(user.created_at)) %>\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Rails52</title>\n    <%= csrf_meta_tags %>\n\n    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>\n    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>\n  </head>\n\n  <body>\n    <%= yield %>\n  </body>\n</html>\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/layouts/mailer.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n    <style>\n      /* Email styles need to be inline */\n    </style>\n  </head>\n\n  <body>\n    <%= yield %>\n  </body>\n</html>\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/layouts/mailer.text.erb",
    "content": "<%= yield %>\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/users/_empty_partial_name.html.erb",
    "content": "<%= render(:partial => nested_partial) if nested_partial.present? %>\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/users/_foo.html.haml",
    "content": "= raw(d)\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/users/_foo2.html.haml",
    "content": "= raw(e)\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/users/kwsplat.html.haml",
    "content": "- known = { d: params[:x] }\n= render partial: \"foo\", locals: { **known, a: 1 }\n= render partial: \"foo2\", locals: { **unknown, e: params[:x] }\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/users/link.html.erb",
    "content": "<%= link_to params[:x] do %>\n  stuff\n<% end %>\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/users/not_not.html.erb",
    "content": "<%= @errors.join(\"<br/>\").html_safe %>\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/users/one.html.haml",
    "content": ":sass\n  #name\n    border: 3px dashed orange\n    color: #{raw @user.name}\n\n%p#name\n  %b Name:\n  = @user.name\n  = sanitize @user.bio, tags: ['style', :select]\n  = Rails::Html::SafeListSanitizer.new.sanitize(@user.description, tags: [\"select\", \"style\"])\n\n:coffeescript\n  $('h1').click ->\n  alert('You clicked on the headline')\n\n:markdown\n  # Jello!\n\n:sass\n  #div\n    font-weight: bold\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/users/smart.html.slim",
    "content": "p\n  Your credit card\n  strong will not\n  > be charged now.\n\nThis is a text\n  which spans\n  several lines.\n\nfooter\n  Copyright &copy; #{params[:x]} not xss?\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/users/test_empty_partial_name.html.erb",
    "content": "<%= render :partial => 'empty_partial_name', :locals => {:nested_partial => ''} %>\n"
  },
  {
    "path": "test/apps/rails5.2/app/views/users/two.html.slim",
    "content": "scss:\n  #someDiv\n    border: 3px dashed orange\n\njavascript:\n  var x = #{ raw @user.name }\n\ncoffee:\n  greeting = 'hi'\n"
  },
  {
    "path": "test/apps/rails5.2/bin/rails",
    "content": "#!/usr/bin/env ruby\nbegin\n  load File.expand_path('../spring', __FILE__)\nrescue LoadError => e\n  raise unless e.message.include?('spring')\nend\nAPP_PATH = File.expand_path('../config/application', __dir__)\nrequire_relative '../config/boot'\nrequire 'rails/commands'\n"
  },
  {
    "path": "test/apps/rails5.2/bin/rake",
    "content": "#!/usr/bin/env ruby\nbegin\n  load File.expand_path('../spring', __FILE__)\nrescue LoadError => e\n  raise unless e.message.include?('spring')\nend\nrequire_relative '../config/boot'\nrequire 'rake'\nRake.application.run\n"
  },
  {
    "path": "test/apps/rails5.2/bin/setup",
    "content": "#!/usr/bin/env ruby\nrequire 'fileutils'\ninclude FileUtils\n\n# path to your application root.\nAPP_ROOT = File.expand_path('..', __dir__)\n\ndef system!(*args)\n  system(*args) || abort(\"\\n== Command #{args} failed ==\")\nend\n\nchdir APP_ROOT do\n  # This script is a starting point to setup your application.\n  # Add necessary setup steps to this file.\n\n  puts '== Installing dependencies =='\n  system! 'gem install bundler --conservative'\n  system('bundle check') || system!('bundle install')\n\n  # Install JavaScript dependencies if using Yarn\n  # system('bin/yarn')\n\n  # puts \"\\n== Copying sample files ==\"\n  # unless File.exist?('config/database.yml')\n  #   cp 'config/database.yml.sample', 'config/database.yml'\n  # end\n\n  puts \"\\n== Preparing database ==\"\n  system! 'bin/rails db:setup'\n\n  puts \"\\n== Removing old logs and tempfiles ==\"\n  system! 'bin/rails log:clear tmp:clear'\n\n  puts \"\\n== Restarting application server ==\"\n  system! 'bin/rails restart'\nend\n"
  },
  {
    "path": "test/apps/rails5.2/bin/spring",
    "content": "#!/usr/bin/env ruby\n\n# This file loads spring without using Bundler, in order to be fast.\n# It gets overwritten when you run the `spring binstub` command.\n\nunless defined?(Spring)\n  require 'rubygems'\n  require 'bundler'\n\n  lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)\n  spring = lockfile.specs.detect { |spec| spec.name == \"spring\" }\n  if spring\n    Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path\n    gem 'spring', spring.version\n    require 'spring/binstub'\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5.2/bin/update",
    "content": "#!/usr/bin/env ruby\nrequire 'fileutils'\ninclude FileUtils\n\n# path to your application root.\nAPP_ROOT = File.expand_path('..', __dir__)\n\ndef system!(*args)\n  system(*args) || abort(\"\\n== Command #{args} failed ==\")\nend\n\nchdir APP_ROOT do\n  # This script is a way to update your development environment automatically.\n  # Add necessary update steps to this file.\n\n  puts '== Installing dependencies =='\n  system! 'gem install bundler --conservative'\n  system('bundle check') || system!('bundle install')\n\n  # Install JavaScript dependencies if using Yarn\n  # system('bin/yarn')\n\n  puts \"\\n== Updating database ==\"\n  system! 'bin/rails db:migrate'\n\n  puts \"\\n== Removing old logs and tempfiles ==\"\n  system! 'bin/rails log:clear tmp:clear'\n\n  puts \"\\n== Restarting application server ==\"\n  system! 'bin/rails restart'\nend\n"
  },
  {
    "path": "test/apps/rails5.2/bin/yarn",
    "content": "#!/usr/bin/env ruby\nAPP_ROOT = File.expand_path('..', __dir__)\nDir.chdir(APP_ROOT) do\n  begin\n    exec \"yarnpkg #{ARGV.join(' ')}\"\n  rescue Errno::ENOENT\n    $stderr.puts \"Yarn executable was not detected in the system.\"\n    $stderr.puts \"Download Yarn at https://yarnpkg.com/en/docs/install\"\n    exit 1\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5.2/config/application.rb",
    "content": "require_relative 'boot'\n\nrequire 'rails/all'\n\n# Require the gems listed in Gemfile, including any gems\n# you've limited to :test, :development, or :production.\nBundler.require(*Rails.groups)\n\nmodule Rails52\n  class Application < Rails::Application\n    # Initialize configuration defaults for originally generated Rails version.\n    config.load_defaults 5.2\n\n    # Settings in config/environments/* take precedence over those specified here.\n    # Application configuration should go into files in config/initializers\n    # -- all .rb files in that directory are automatically loaded.\n\n    # https://guides.rubyonrails.org/v7.0/configuring.html#config-action-controller-per-form-csrf-tokens\n    # config.action_controller.per_form_csrf_tokens = true\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5.2/config/boot.rb",
    "content": "ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)\n\nrequire 'bundler/setup' # Set up gems listed in the Gemfile.\nrequire 'bootsnap/setup' # Speed up boot time by caching expensive operations.\n"
  },
  {
    "path": "test/apps/rails5.2/config/cable.yml",
    "content": "development:\n  adapter: async\n\ntest:\n  adapter: async\n\nproduction:\n  adapter: redis\n  url: <%= ENV.fetch(\"REDIS_URL\") { \"redis://localhost:6379/1\" } %>\n  channel_prefix: rails5_2_production\n"
  },
  {
    "path": "test/apps/rails5.2/config/credentials.yml.enc",
    "content": "nlV8moQTYjDyR+oTWV1hJIFI6vPU6dhsBI4h7MDS6AFbUMa3bS9M/S1T3/Qj3R25HAYNuTsswB7sMgYlEELUdPCB2yxAekd8dKizHTBv23CGRplaLC198FC/VWf815SrjxmNlDMnuA9XxsUhvon7qTkCLOXKwsE1qQ8AOAwu4R86anJvdMyIiuvogRcgl6ePkdLe9thQiDw0Hr8CeiCs4AfzasU5Lk3pxVjlM59Va0ZrVXezlTMjajTeJtim9vEPIM0BBecgWzZySRCskA4L/xVwAEFWcerBoOyGMFoi7ZwmYkux/Q28oQUCq04iNmuLF4RMD75axhcD7o2ldML3k5O3mGIuYOi1dzHtibewGJlNjhBAcnapsZtbODGPM6Zrs79M137iQdQcpj83vEGFJ92u9xgQN74N--DrU0T/aJZtDsx0XU--cplS41E/sGK0o829mOPNdQ=="
  },
  {
    "path": "test/apps/rails5.2/config/database.yml",
    "content": "# SQLite version 3.x\n#   gem install sqlite3\n#\n#   Ensure the SQLite 3 gem is defined in your Gemfile\n#   gem 'sqlite3'\n#\ndefault: &default\n  adapter: sqlite3\n  pool: <%= ENV.fetch(\"RAILS_MAX_THREADS\") { 5 } %>\n  timeout: 5000\n\ndevelopment:\n  <<: *default\n  database: db/development.sqlite3\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  <<: *default\n  database: db/test.sqlite3\n\nproduction:\n  <<: *default\n  database: db/production.sqlite3\n"
  },
  {
    "path": "test/apps/rails5.2/config/environment.rb",
    "content": "# Load the Rails application.\nrequire_relative 'application'\n\n# Initialize the Rails application.\nRails.application.initialize!\n"
  },
  {
    "path": "test/apps/rails5.2/config/environments/development.rb",
    "content": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # In the development environment your application's code is reloaded on\n  # every request. This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes = false\n\n  # Do not eager load code on boot.\n  config.eager_load = false\n\n  # Show full error reports.\n  config.consider_all_requests_local = true\n\n  # Enable/disable caching. By default caching is disabled.\n  # Run rails dev:cache to toggle caching.\n  if Rails.root.join('tmp/caching-dev.txt').exist?\n    config.action_controller.perform_caching = true\n\n    config.cache_store = :memory_store\n    config.public_file_server.headers = {\n      'Cache-Control' => \"public, max-age=#{2.days.to_i}\"\n    }\n  else\n    config.action_controller.perform_caching = false\n\n    config.cache_store = :null_store\n  end\n\n  # Store uploaded files on the local file system (see config/storage.yml for options)\n  config.active_storage.service = :local\n\n  # Don't care if the mailer can't send.\n  config.action_mailer.raise_delivery_errors = false\n\n  config.action_mailer.perform_caching = false\n\n  # Print deprecation notices to the Rails logger.\n  config.active_support.deprecation = :log\n\n  # Raise an error on page load if there are pending migrations.\n  config.active_record.migration_error = :page_load\n\n  # Debug mode disables concatenation and preprocessing of assets.\n  # This option may cause significant delays in view rendering with a large\n  # number of complex assets.\n  config.assets.debug = true\n\n  # Suppress logger output for asset requests.\n  config.assets.quiet = true\n\n  # Raises error for missing translations\n  # config.action_view.raise_on_missing_translations = true\n\n  # Use an evented file watcher to asynchronously detect changes in source code,\n  # routes, locales, etc. This feature depends on the listen gem.\n  config.file_watcher = ActiveSupport::EventedFileUpdateChecker\nend\n"
  },
  {
    "path": "test/apps/rails5.2/config/environments/production.rb",
    "content": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Code is not reloaded between requests.\n  config.cache_classes = true\n\n  # Eager load code on boot. This eager loads most of Rails and\n  # your application in memory, allowing both threaded web servers\n  # and those relying on copy on write to perform better.\n  # Rake tasks automatically ignore this option for performance.\n  config.eager_load = true\n\n  # Full error reports are disabled and caching is turned on.\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Ensures that a master key has been made available in either ENV[\"RAILS_MASTER_KEY\"]\n  # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).\n  # config.require_master_key = true\n\n  # Disable serving static files from the `/public` folder by default since\n  # Apache or NGINX already handles this.\n  config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?\n\n  # Compress JavaScripts and CSS.\n  config.assets.js_compressor = :uglifier\n  # config.assets.css_compressor = :sass\n\n  # Do not fallback to assets pipeline if a precompiled asset is missed.\n  config.assets.compile = false\n\n  # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server.\n  # config.action_controller.asset_host = 'http://assets.example.com'\n\n  # Specifies the header that your server uses for sending files.\n  # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache\n  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX\n\n  # Store uploaded files on the local file system (see config/storage.yml for options)\n  config.active_storage.service = :local\n\n  # Mount Action Cable outside main process or domain\n  # config.action_cable.mount_path = nil\n  # config.action_cable.url = 'wss://example.com/cable'\n  # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\\/\\/example.*/ ]\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  config.force_ssl = false\n\n  # Use the lowest log level to ensure availability of diagnostic information\n  # when problems arise.\n  config.log_level = :debug\n\n  # Prepend all log lines with the following tags.\n  config.log_tags = [ :request_id ]\n\n  # Use a different cache store in production.\n  # config.cache_store = :mem_cache_store\n\n  # Use a real queuing backend for Active Job (and separate queues per environment)\n  # config.active_job.queue_adapter     = :resque\n  # config.active_job.queue_name_prefix = \"rails5_2_#{Rails.env}\"\n\n  config.action_mailer.perform_caching = false\n\n  # Ignore bad email addresses and do not raise email delivery errors.\n  # Set this to true and configure the email server for immediate delivery to raise delivery errors.\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation cannot be found).\n  config.i18n.fallbacks = true\n\n  # Send deprecation notices to registered listeners.\n  config.active_support.deprecation = :notify\n\n  # Use default logging formatter so that PID and timestamp are not suppressed.\n  config.log_formatter = ::Logger::Formatter.new\n\n  # Use a different logger for distributed setups.\n  # require 'syslog/logger'\n  # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')\n\n  if ENV[\"RAILS_LOG_TO_STDOUT\"].present?\n    logger           = ActiveSupport::Logger.new(STDOUT)\n    logger.formatter = config.log_formatter\n    config.logger    = ActiveSupport::TaggedLogging.new(logger)\n  end\n\n  # Do not dump schema after migrations.\n  config.active_record.dump_schema_after_migration = false\n\n  # https://guides.rubyonrails.org/v7.0/configuring.html#config-action-controller-default-protect-from-forgery\n  # config.action_controller.default_protect_from_forgery = true\nend\n"
  },
  {
    "path": "test/apps/rails5.2/config/environments/test.rb",
    "content": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # The test environment is used exclusively to run your application's\n  # test suite. You never need to work with it otherwise. Remember that\n  # your test database is \"scratch space\" for the test suite and is wiped\n  # and recreated between test runs. Don't rely on the data there!\n  config.cache_classes = true\n\n  # Do not eager load code on boot. This avoids loading your whole application\n  # just for the purpose of running a single test. If you are using a tool that\n  # preloads Rails for running tests, you may have to set it to true.\n  config.eager_load = false\n\n  # Configure public file server for tests with Cache-Control for performance.\n  config.public_file_server.enabled = true\n  config.public_file_server.headers = {\n    'Cache-Control' => \"public, max-age=#{1.hour.to_i}\"\n  }\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n\n  # Raise exceptions instead of rendering exception templates.\n  config.action_dispatch.show_exceptions = false\n\n  # Disable request forgery protection in test environment.\n  config.action_controller.allow_forgery_protection = false\n\n  # Store uploaded files on the local file system in a temporary directory\n  config.active_storage.service = :test\n\n  config.action_mailer.perform_caching = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Print deprecation notices to the stderr.\n  config.active_support.deprecation = :stderr\n\n  # Raises error for missing translations\n  # config.action_view.raise_on_missing_translations = true\nend\n"
  },
  {
    "path": "test/apps/rails5.2/config/initializers/application_controller_renderer.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# ActiveSupport::Reloader.to_prepare do\n#   ApplicationController.renderer.defaults.merge!(\n#     http_host: 'example.org',\n#     https: false\n#   )\n# end\n"
  },
  {
    "path": "test/apps/rails5.2/config/initializers/assets.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Version of your assets, change this if you want to expire all your assets.\nRails.application.config.assets.version = '1.0'\n\n# Add additional assets to the asset load path.\n# Rails.application.config.assets.paths << Emoji.images_path\n# Add Yarn node_modules folder to the asset load path.\nRails.application.config.assets.paths << Rails.root.join('node_modules')\n\n# Precompile additional assets.\n# application.js, application.css, and all non-JS/CSS in the app/assets\n# folder are already added.\n# Rails.application.config.assets.precompile += %w( admin.js admin.css )\n"
  },
  {
    "path": "test/apps/rails5.2/config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "test/apps/rails5.2/config/initializers/content_security_policy.rb",
    "content": "# Define an application-wide content security policy\n# For further information see the following documentation\n# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy\n\nRails.application.config.content_security_policy do |p|\n  p.default_src :self, :https\n  p.font_src    :self, :https, :data\n  p.img_src     :self, :https, :data\n  p.object_src  :none\n  p.script_src  :self, :https\n  p.style_src   :self, :https, :unsafe_inline\n\n  # Specify URI for violation reports\n  # p.report_uri \"/csp-violation-report-endpoint\"\nend\n\n# Report CSP violations to a specified URI\n# For further information see the following documentation:\n# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only\n# Rails.application.config.content_security_policy_report_only = true\n"
  },
  {
    "path": "test/apps/rails5.2/config/initializers/cookies_serializer.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Specify a serializer for the signed and encrypted cookie jars.\n# Valid options are :json, :marshal, and :hybrid.\nRails.application.config.action_dispatch.cookies_serializer = :hybrid\n\nmodule Custom\n  module Serializer\n  end\nend\n\nRails.application.config.action_dispatch.cookies_serializer = Custom::Serializer\n"
  },
  {
    "path": "test/apps/rails5.2/config/initializers/filter_parameter_logging.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Configure sensitive parameters which will be filtered from the log file.\nRails.application.config.filter_parameters += [:password]\n"
  },
  {
    "path": "test/apps/rails5.2/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Inflections\n# are locale specific, and you may define rules for as many different\n# locales as you wish. All of these examples are active by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n\n# These inflection rules are supported but not enabled by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.acronym 'RESTful'\n# end\n"
  },
  {
    "path": "test/apps/rails5.2/config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n"
  },
  {
    "path": "test/apps/rails5.2/config/initializers/oj.rb",
    "content": "# These are commented out by default and then turned on in test/tests/oj.rb for testing.\n#\n# Oj.mimic_JSON\n# Oj.default_options = { mode: :strict }\n"
  },
  {
    "path": "test/apps/rails5.2/config/initializers/wrap_parameters.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# This file contains settings for ActionController::ParamsWrapper which\n# is enabled by default.\n\n# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.\nActiveSupport.on_load(:action_controller) do\n  wrap_parameters format: [:json]\nend\n\n# To enable root element in JSON for ActiveRecord objects.\n# ActiveSupport.on_load(:active_record) do\n#   self.include_root_in_json = true\n# end\n"
  },
  {
    "path": "test/apps/rails5.2/config/locales/en.yml",
    "content": "# Files in the config/locales directory are used for internationalization\n# and are automatically loaded by Rails. If you want to use locales other\n# than English, add the necessary files in this directory.\n#\n# To use the locales, use `I18n.t`:\n#\n#     I18n.t 'hello'\n#\n# In views, this is aliased to just `t`:\n#\n#     <%= t('hello') %>\n#\n# To use a different locale, set it with `I18n.locale`:\n#\n#     I18n.locale = :es\n#\n# This would use the information in config/locales/es.yml.\n#\n# The following keys must be escaped otherwise they will not be retrieved by\n# the default I18n backend:\n#\n# true, false, on, off, yes, no\n#\n# Instead, surround them with single quotes.\n#\n# en:\n#   'true': 'foo'\n#\n# To learn more, please read the Rails Internationalization guide\n# available at http://guides.rubyonrails.org/i18n.html.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/apps/rails5.2/config/puma.rb",
    "content": "# Puma can serve each request in a thread from an internal thread pool.\n# The `threads` method setting takes two numbers: a minimum and maximum.\n# Any libraries that use thread pools should be configured to match\n# the maximum value specified for Puma. Default is set to 5 threads for minimum\n# and maximum; this matches the default thread size of Active Record.\n#\nthreads_count = ENV.fetch(\"RAILS_MAX_THREADS\") { 5 }\nthreads threads_count, threads_count\n\n# Specifies the `port` that Puma will listen on to receive requests; default is 3000.\n#\nport        ENV.fetch(\"PORT\") { 3000 }\n\n# Specifies the `environment` that Puma will run in.\n#\nenvironment ENV.fetch(\"RAILS_ENV\") { \"development\" }\n\n# Specifies the number of `workers` to boot in clustered mode.\n# Workers are forked webserver processes. If using threads and workers together\n# the concurrency of the application would be max `threads` * `workers`.\n# Workers do not work on JRuby or Windows (both of which do not support\n# processes).\n#\n# workers ENV.fetch(\"WEB_CONCURRENCY\") { 2 }\n\n# Use the `preload_app!` method when specifying a `workers` number.\n# This directive tells Puma to first boot the application and load code\n# before forking the application. This takes advantage of Copy On Write\n# process behavior so workers use less memory. If you use this option\n# you need to make sure to reconnect any threads in the `on_worker_boot`\n# block.\n#\n# preload_app!\n\n# If you are preloading your application and using Active Record, it's\n# recommended that you close any connections to the database before workers\n# are forked to prevent connection leakage.\n#\n# before_fork do\n#   ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord)\n# end\n\n# The code in the `on_worker_boot` will be called if you are using\n# clustered mode by specifying a number of `workers`. After each worker\n# process is booted, this block will be run. If you are using the `preload_app!`\n# option, you will want to use this block to reconnect to any threads\n# or connections that may have been created at application boot, as Ruby\n# cannot share connections between processes.\n#\n# on_worker_boot do\n#   ActiveRecord::Base.establish_connection if defined?(ActiveRecord)\n# end\n#\n\n# Allow puma to be restarted by `rails restart` command.\nplugin :tmp_restart\n"
  },
  {
    "path": "test/apps/rails5.2/config/routes.rb",
    "content": "Rails.application.routes.draw do\n  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html\nend\n"
  },
  {
    "path": "test/apps/rails5.2/config/secrets.yml",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key is used for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n\n# Make sure the secret is at least 30 characters and all random,\n# no regular words or you'll be exposed to dictionary attacks.\n# You can use `rake secret` to generate a secure secret key.\n\n# Make sure the secrets in this file are kept private\n# if you're sharing your code publicly.\n\ntest_anchor: &test_anchor\n  abc: 123\n\ndevelopment:\n  test_anchor: *test_anchor\n  secret_key_base: 12d3735e1cdb18ef2eca25f9a370028ac096ff273c5a889ed7a49047d5e30c9dc7fe095792a71b60c3f37dd80efaeda44db75e73c9f60813550c875eee7a241f\n\ntest:\n  test_anchor: *test_anchor\n  secret_key_base: 446b08c3cdeccdaf9e8b247a2624d45218c5d429e8acde61ddd87aa7b9dd50973e49e6d94378cb4bcf08b7818a90abb044b5c8886f94de6970ade4a496df22f3\n\n# Do not keep production secrets in the repository,\n# instead read values from the environment.\nproduction:\n  test_anchor: *test_anchor\n  secret_key_base: <%= ENV[\"SECRET_KEY_BASE\"] %>\n\n<% if Rails.root.join('config/ansible/secrets.yml').exist? %>\n<%= Rails.root.join('config/ansible/secrets.yml').read %>\n<% end %>\n\n\n"
  },
  {
    "path": "test/apps/rails5.2/config/spring.rb",
    "content": "%w[\n  .ruby-version\n  .rbenv-vars\n  tmp/restart.txt\n  tmp/caching-dev.txt\n].each { |path| Spring.watch(path) }\n"
  },
  {
    "path": "test/apps/rails5.2/config/storage.yml",
    "content": "test:\n  service: Disk\n  root: <%= Rails.root.join(\"tmp/storage\") %>\n\nlocal:\n  service: Disk\n  root: <%= Rails.root.join(\"storage\") %>\n\n# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)\n# amazon:\n#   service: S3\n#   access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>\n#   secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>\n#   region: us-east-1\n#   bucket: your_own_bucket\n\n# Remember not to checkin your GCS keyfile to a repository\n# google:\n#   service: GCS\n#   project: your_project\n#   keyfile: <%= Rails.root.join(\"path/to/gcs.keyfile\") %>\n#   bucket: your_own_bucket\n\n# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)\n# microsoft:\n#   service: AzureStorage\n#   path: your_azure_storage_path\n#   storage_account_name: your_account_name\n#   storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>\n#   container: your_container_name\n\n# mirror:\n#   service: Mirror\n#   primary: local\n#   mirrors: [ amazon, google, microsoft ]\n"
  },
  {
    "path": "test/apps/rails5.2/config.ru",
    "content": "# This file is used by Rack-based servers to start the application.\n\nrequire_relative 'config/environment'\n\nrun Rails.application\n"
  },
  {
    "path": "test/apps/rails5.2/db/migrate/20171208205700_create_active_storage_tables.active_storage.rb",
    "content": "# This migration comes from active_storage (originally 20170806125915)\nclass CreateActiveStorageTables < ActiveRecord::Migration[5.2]\n  def change\n    create_table :active_storage_blobs do |t|\n      t.string   :key,        null: false\n      t.string   :filename,   null: false\n      t.string   :content_type\n      t.text     :metadata\n      t.bigint   :byte_size,  null: false\n      t.string   :checksum,   null: false\n      t.datetime :created_at, null: false\n\n      t.index [ :key ], unique: true\n    end\n\n    create_table :active_storage_attachments do |t|\n      t.string     :name,     null: false\n      t.references :record,   null: false, polymorphic: true, index: false\n      t.references :blob,     null: false\n\n      t.datetime :created_at, null: false\n\n      t.index [ :record_type, :record_id, :name, :blob_id ], name: \"index_active_storage_attachments_uniqueness\", unique: true\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5.2/db/seeds.rb",
    "content": "# This file should contain all the record creation needed to seed the database with its default values.\n# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).\n#\n# Examples:\n#\n#   movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])\n#   Character.create(name: 'Luke', movie: movies.first)\n"
  },
  {
    "path": "test/apps/rails5.2/lib/assets/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5.2/lib/factory_bot.rb",
    "content": "FactoryBot.define do\n  factory :foo do\n    included { \"an attribute value\" }\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5.2/lib/initthing.rb",
    "content": "class InitThing\n  def initialize\n    @blah = \"some cool stuff\"\n  end\n\n  def use_it\n    `#{@blah}`\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5.2/lib/shell.rb",
    "content": "class ShellStuff\n  def initialize(one, two)\n    @one = Shellwords.shellescape(one)\n    @two = Shellwords.escape(two)\n  end\n\n  def run(ip)\n    ip = Shellwords.shellescape(ip)\n    `dig +short -x #{ip} @#{@one} -p #{@two}`\n    `command #{Shellwords.escape(@one)}`\n    `command #{Shellwords.join(@two)}`\n    `command #{Shellwords.shelljoin(@two)}`\n    `command #{@one.shellescape}`\n    `command #{@two.shelljoin}`\n  end\n\n  def backticks_target(path)\n    `echo #{path}`.chomp\n  end\n\n  def process_pid\n    # should not warn\n    `something #{Process.pid}`\n  end\n\n  def nested_system_interp\n    filename = Shellwords.escape(\"#{file_prefix}.txt\")\n    system \"echo #{filename}\"\n  end\n\n  def system_array_join\n    command = [\"ruby\", method_that_returns_user_input, \"--some-flag\"].join(\" \")\n    system(command)\n  end\n\n  def system_as_target\n    !system(\"echo #{foo}\")\n  end\n\n  def interpolated_conditional_safe\n    `echo #{\"foo\" if foo} bar`\n  end\n\n  def interpolated_ternary_safe\n    `echo #{foo ? \"bar\" : \"baz\"}`\n  end\n\n  def interpolated_conditional_dangerous\n    `echo #{bar if foo} baz`\n  end\n\n  def interpolated_ternary_dangerous\n    `echo #{foo ? \"bar\" : bar} baz`\n  end\n\n  COMMANDS = { foo: \"echo\", bar: \"cat\" }\n  MORE_COMMANDS = { foo: \"touch\" }\n\n  def safe(arg)\n    command = if Date.today.tuesday? # Some condition.\n                COMMANDS[arg]\n              else\n                MORE_COMMANDS[arg]\n              end\n\n    `#{command} file1.txt`\n  end\n\n  EXPRESSIONS = [\"users.email\", \"concat_ws(' ', users.first_name, users.last_name)\"]\n\n  def perform_commands\n    EXPRESSIONS.each { |exp| `echo #{exp}` }\n  end\n\n  def scopes(base_scope)\n    EXPRESSIONS.map { |exp| base_scope.where(\"#{exp} ILIKE '%foo%'\") }\n  end\n\n  def shell_escape_model\n    a = User.new\n    z = Shellwords.escape(a.z)\n    result, status = Open3.capture2e(\"ls\",\n                                     z)  # Should not warn\n\n    `ls #{z}` # Also should not warn\n  end\n\n  def file_constant_use\n    # __FILE__ should not change based on absolute path\n    `cp #{__FILE__} #{somewhere_else}`\n  end\n\n  def interpolated_in_percent_W\n    # Should not warn\n    system(*%W(foo bar #{value}))\n  end\n\n  def completely_external\n    system(foo) # We assume this is safe and do not warn.\n  end\n\n  def string_concatenation\n    system(\"echo \" + foo)\n  end\n\n  def escaped_string_concatenation\n    system(\"echo \" + Shellwords.escape(foo))\n  end\n\n  def safe_string_concatenation\n    system(\"echo \" + \"foo\")\n  end\n\n  def dash_c_dangerous_concatenation\n    system(\"bash\", \"-c\", \"echo \" + foo)\n  end\n\n  def dash_c_safe_concatenation\n    system(\"bash\", \"-c\", \"echo \" + Shellwords.escape(foo))\n  end\n\n  def popen_dash_c\n    IO.popen([\"bash\", \"-c\", params[:foo]]) {}\n  end\n\n  def popen_concatenation\n    IO.popen(\"ls \" + foo) do |ls_io|\n      ls_io.read\n    end\n  end\n\n  def open3_capture_stdin_data\n    u = User.new\n    # None of these should warn\n    Open3.capture2(\"cat\", stdin_data: \"User.z = #{u.z}\")\n    Open3.capture2e(\"cat\", stdin_data: \"User.z = #{u.z}\")\n    Open3.capture3(\"cat\", stdin_data: \"User.z = #{u.z}\")\n  end\n\n  def tempfile_create\n    # these should not warn\n    tempfile = Tempfile.create\n    `something -out #{tempfile.path}`\n\n    Tempfile.create do |tempfile|\n      system(\"something -out #{tempfile.path}\")\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails5.2/lib/tasks/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5.2/log/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5.2/package.json",
    "content": "{\n  \"name\": \"rails5_2\",\n  \"private\": true,\n  \"dependencies\": {}\n}\n"
  },
  {
    "path": "test/apps/rails5.2/public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The page you were looking for doesn't exist (404)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  .rails-default-error-page {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  .rails-default-error-page div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  .rails-default-error-page div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  .rails-default-error-page h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  .rails-default-error-page div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body class=\"rails-default-error-page\">\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>The page you were looking for doesn't exist.</h1>\n      <p>You may have mistyped the address or the page may have moved.</p>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails5.2/public/422.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>The change you wanted was rejected (422)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  .rails-default-error-page {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  .rails-default-error-page div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  .rails-default-error-page div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  .rails-default-error-page h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  .rails-default-error-page div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body class=\"rails-default-error-page\">\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>The change you wanted was rejected.</h1>\n      <p>Maybe you tried to change something you didn't have access to.</p>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails5.2/public/500.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>We're sorry, but something went wrong (500)</title>\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n  <style>\n  .rails-default-error-page {\n    background-color: #EFEFEF;\n    color: #2E2F30;\n    text-align: center;\n    font-family: arial, sans-serif;\n    margin: 0;\n  }\n\n  .rails-default-error-page div.dialog {\n    width: 95%;\n    max-width: 33em;\n    margin: 4em auto 0;\n  }\n\n  .rails-default-error-page div.dialog > div {\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #BBB;\n    border-top: #B00100 solid 4px;\n    border-top-left-radius: 9px;\n    border-top-right-radius: 9px;\n    background-color: white;\n    padding: 7px 12% 0;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n\n  .rails-default-error-page h1 {\n    font-size: 100%;\n    color: #730E15;\n    line-height: 1.5em;\n  }\n\n  .rails-default-error-page div.dialog > p {\n    margin: 0 0 1em;\n    padding: 1em;\n    background-color: #F7F7F7;\n    border: 1px solid #CCC;\n    border-right-color: #999;\n    border-left-color: #999;\n    border-bottom-color: #999;\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n    border-top-color: #DADADA;\n    color: #666;\n    box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);\n  }\n  </style>\n</head>\n\n<body class=\"rails-default-error-page\">\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <div>\n      <h1>We're sorry, but something went wrong.</h1>\n    </div>\n    <p>If you are the application owner check the logs for more information.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails5.2/public/robots.txt",
    "content": "# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file\n"
  },
  {
    "path": "test/apps/rails5.2/vendor/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails5.2/vendor/vendored_thing.rb",
    "content": "class Vendored\n  def vendor\n    `rm -rf #{stuff}`\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files for more about ignoring files.\n#\n# If you find yourself ignoring temporary files generated by your text editor\n# or operating system, you probably want to add a global ignore instead:\n#   git config --global core.excludesfile '~/.gitignore_global'\n\n# Ignore bundler config.\n/.bundle\n\n# Ignore the default SQLite database.\n/db/*.sqlite3\n/db/*.sqlite3-journal\n\n# Ignore all logfiles and tempfiles.\n/log/*\n/tmp/*\n!/log/.keep\n!/tmp/.keep\n\n# Ignore uploaded files in development.\n/storage/*\n!/storage/.keep\n\n/public/assets\n.byebug_history\n\n# Ignore master key for decrypting credentials and more.\n/config/master.key\n\n/public/packs\n/public/packs-test\n/node_modules\n/yarn-error.log\nyarn-debug.log*\n.yarn-integrity\n"
  },
  {
    "path": "test/apps/rails6/Gemfile",
    "content": "source 'https://rubygems.org'\ngit_source(:github) { |repo| \"https://github.com/#{repo}.git\" }\n\nruby '2.5.3'\n\n# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'\ngem 'rails', '~> 6.0.0.beta2'\n# Use sqlite3 as the database for Active Record\ngem 'sqlite3', '~> 1.3', '>= 1.3.6'\n# Use Puma as the app server\ngem 'puma', '~> 3.11'\n# Use SCSS for stylesheets\ngem 'sass-rails', '~> 5.0'\n# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker\ngem 'webpacker', '>= 4.0.0.rc.3'\n# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks\ngem 'turbolinks', '~> 5'\n# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder\ngem 'jbuilder', '~> 2.5'\n# Use Redis adapter to run Action Cable in production\n# gem 'redis', '~> 4.0'\n# Use Active Model has_secure_password\n# gem 'bcrypt', '~> 3.1.7'\n\n# Use Active Storage variant\n# gem 'image_processing', '~> 1.2'\n\n# Reduces boot times through caching; required in config/boot.rb\ngem 'bootsnap', '>= 1.4.1', require: false\n\ngem 'safe_yaml'\n\ngroup :development, :test do\n  # Call 'byebug' anywhere in the code to stop execution and get a debugger console\n  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]\nend\n\ngroup :development do\n  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.\n  gem 'web-console', '>= 3.3.0'\n  gem 'listen', '>= 3.0.5', '< 3.2'\n  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring\n  gem 'spring'\n  gem 'spring-watcher-listen', '~> 2.0.0'\nend\n\ngroup :test do\n  # Adds support for Capybara system testing and selenium driver\n  gem 'capybara', '>= 2.15'\n  gem 'selenium-webdriver'\n  # Easy installation and use of chromedriver to run system tests with Chrome\n  gem 'chromedriver-helper'\nend\n\n# Windows does not include zoneinfo files, so bundle the tzinfo-data gem\ngem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]\n"
  },
  {
    "path": "test/apps/rails6/Rakefile",
    "content": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire_relative 'config/application'\n\nRails.application.load_tasks\n"
  },
  {
    "path": "test/apps/rails6/another_lib_dir/some_lib.rb",
    "content": "class A\n  def something(thing)\n    `rm -rf #{thing}`\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/assets/config/manifest.js",
    "content": "//= link_tree ../images\n//= link_directory ../stylesheets .css\n"
  },
  {
    "path": "test/apps/rails6/app/assets/images/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails6/app/assets/stylesheets/application.css",
    "content": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below.\n *\n * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's\n * vendor/assets/stylesheets directory can be referenced here using a relative path.\n *\n * You're free to add application-wide styles to this file and they'll appear at the bottom of the\n * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS\n * files in this directory. Styles in this file should be added after the last require_* statement.\n * It is generally better to create a new file per style scope.\n *\n *= require_tree .\n *= require_self\n */\n"
  },
  {
    "path": "test/apps/rails6/app/assets/stylesheets/scaffolds.scss",
    "content": "body {\n  background-color: #fff;\n  color: #333;\n  margin: 33px;\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 13px;\n  line-height: 18px;\n}\n\np, ol, ul, td {\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size: 13px;\n  line-height: 18px;\n}\n\npre {\n  background-color: #eee;\n  padding: 10px;\n  font-size: 11px;\n}\n\na {\n  color: #000;\n\n  &:visited {\n    color: #666;\n  }\n\n  &:hover {\n    color: #fff;\n    background-color: #000;\n  }\n}\n\nth {\n  padding-bottom: 5px;\n}\n\ntd {\n  padding: 0 5px 7px;\n}\n\ndiv {\n  &.field, &.actions {\n    margin-bottom: 10px;\n  }\n}\n\n#notice {\n  color: green;\n}\n\n.field_with_errors {\n  padding: 2px;\n  background-color: red;\n  display: table;\n}\n\n#error_explanation {\n  width: 450px;\n  border: 2px solid red;\n  padding: 7px 7px 0;\n  margin-bottom: 20px;\n  background-color: #f0f0f0;\n\n  h2 {\n    text-align: left;\n    font-weight: bold;\n    padding: 5px 5px 5px 15px;\n    font-size: 12px;\n    margin: -7px -7px 0;\n    background-color: #c00;\n    color: #fff;\n  }\n\n  ul li {\n    font-size: 12px;\n    list-style: square;\n  }\n}\n\nlabel {\n  display: block;\n}\n"
  },
  {
    "path": "test/apps/rails6/app/assets/stylesheets/users.scss",
    "content": "// Place all the styles related to the Users controller here.\n// They will automatically be included in application.css.\n// You can use Sass (SCSS) here: http://sass-lang.com/\n"
  },
  {
    "path": "test/apps/rails6/app/channels/application_cable/channel.rb",
    "content": "module ApplicationCable\n  class Channel < ActionCable::Channel::Base\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/channels/application_cable/connection.rb",
    "content": "module ApplicationCable\n  class Connection < ActionCable::Connection::Base\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/components/base_component.rb",
    "content": "class BaseComponent\n  def render_in\n    \"Hello, world\"\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/components/test_component.rb",
    "content": "class TestComponent < BaseComponent\n  def initialize(prop)\n    @prop = prop\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/components/test_view_component.rb",
    "content": "class TestViewComponent < ViewComponent::Base\n  def initialize(prop)\n    @prop = prop\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/components/test_view_component_contrib.rb",
    "content": "class TestViewComponentContrib < ViewComponentContrib::Base\n  def initialize(prop)\n    @prop = prop\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/components/test_view_component_fully_qualified_ancestor.rb",
    "content": "class TestViewComponentFullyQualifiedAncestor < ::ViewComponent::Base\n  def initialize(prop)\n    @prop = prop\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/components/text_phlex_component.rb",
    "content": "class TestPhlexComponent < Phlex::HTML\n    def initialize(prop)\n      @prop = prop\n    end\n  end\n  "
  },
  {
    "path": "test/apps/rails6/app/controllers/accounts_controller.rb",
    "content": "class AccountsController < ApplicationController\n  def login\n    if request.get?\n      # Do something benign\n    else\n      # Do something sensitive because it's a POST\n      # but actually it could be a HEAD :(\n    end\n  end\n\n  def auth_something \n    # Does not warn because there is an elsif clause\n    if request.get?\n      # Do something benign\n    elsif request.post?\n      # Do something sensitive because it's a POST\n    end\n\n    if request.post?\n      # Do something sensitive because it's a POST\n    elsif request.get?\n      # Do something benign\n    end\n  end\n\n  def eval_something\n    eval(params[:x]).to_s\n  end\n\n  def index\n    params.values_at(:test).join(\"|\")\n  end\n\n  def tr_sql \n    Arel.sql(<<~SQL.tr(\"\\n\", \" \"))\n      CASE\n      WHEN #{user_params[:field]} IS NULL\n        OR TRIM(#{user_params[:field]}) = ''\n      THEN 'Untitled'\n      ELSE TRIM(#{user_params[:field]})\n      END\n    SQL\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::Base\nend\n"
  },
  {
    "path": "test/apps/rails6/app/controllers/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails6/app/controllers/groups_controller.rb",
    "content": "class GroupsController < ApplicationController\n  def new_group\n    @group = Group.find params[:id]\n    new_group = @group.dup\n    new_group.save!\n    redirect_to new_group\n  end\n\n  def render_commands\n    render text: `#{params.require('name')} some optional text`\n    render json: `#{params.require('name')} some optional text`\n    render(TestComponent.new(params.require('name')))\n    render(TestViewComponent.new(params.require('name')))\n    render(::TestViewComponent.new(params.require('name')))\n    render(TestViewComponentFullyQualifiedAncestor.new(params.require('name')))\n  end\n\n  def squish_sql\n    ActiveRecord::Base.connection.execute \"SELECT * FROM #{user_input}\".squish\n    ActiveRecord::Base.connection.execute \"SELECT * FROM #{user_input}\".strip\n  end\n\n  def show\n    template = params[:template]\n\n    # Test file allowlist\n    return redirect_to '/groups' unless FILE_LIST.include? template\n\n    render \"groups/#{template}\"\n  end\n\n  def permit_bang_path\n    redirect_to groups_path(params.permit!)\n  end\n\n  def permit_bang_slice\n    params.permit!.slice(:whatever)\n  end\n\n  def safeish_yaml_load\n    YAML.load(params[:yaml_stuff], safe: true)\n    YAML.load(params[:yaml_stuff], safe: false) # not safe\n    YAML.load(params[:yaml_stuff]) # not safe\n  end\n\n  def dynamic_method_invocations\n    params[:method].to_sym.to_proc.call(Kernel)\n    (params[:klass].to_s).method(params[:method]).(params[:argument])\n    Kernel.tap(&params[:method].to_sym)\n    User.method(\"#{User.first.some_method_thing}_stuff\")\n  end\n\n  def only_for_dev\n    if Rails.env.development?\n      eval(params[:x]) # should not warn\n    end\n  end\n\n  def scope_with_custom_sanitization\n    ActiveRecord::Base.connection.execute \"SELECT * FROM #{sanitize_s(user_input)}\"\n  end\n\n  def sanitize_s(input)\n    input\n  end\n\n  def test_rails6_sqli\n    User.select(\"stuff\").reselect(params[:columns])\n    User.where(\"x = 1\").rewhere(\"x = #{params[:x]}\")\n    User.pluck(params[:column]) # Warn in 6.0, not in 6.1\n    User.order(\"name #{params[:direction]}\") # Warn in 6.0, not in 6.1\n    User.order(:name).reorder(params[:column]) # Warn in 6.0, not in 6.1\n  end\n\n  # From https://github.com/presidentbeef/brakeman/issues/1492\n  def enum_include_check\n    status = \"#{params[:status]}\"\n    if Group.statuses.include? status\n      @status = status.to_sym\n      @countries = Group.send(@status) # Should not warn\n    else\n      redirect_to root_path, notice: 'Invalid status'\n    end\n  end\n\n  def render_phlex_component\n    render(TestPhlexComponent.new(params.require('name')))\n  end\n\n  def render_view_component_contrib\n    render(TestViewComponentContrib.new(params.require('name')))\n  end\n\n  # Test ViewComponent with with_content chain (issue #1832)\n  def render_view_component_with_content\n    render(TestViewComponent.new(params.require('name')).with_content(\"string\"))\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/controllers/users_controller.rb",
    "content": "class UsersController < ApplicationController\n  before_action :set_user, only: [:show, :edit, :update, :destroy]\n\n  # GET /users\n  # GET /users.json\n  def index\n    @users = User.all\n  end\n\n  # GET /users/1\n  # GET /users/1.json\n  def show\n  end\n\n  # GET /users/new\n  def new\n    @user = User.new\n  end\n\n  def edit\n    render :edit, locals: { some_name.to_sym => 'stuff' }\n  end\n\n  # POST /users\n  # POST /users.json\n  def create\n    @user = User.new(user_params)\n\n    respond_to do |format|\n      if @user.save\n        format.html { redirect_to @user, notice: 'User was successfully created.' }\n        format.json { render :show, status: :created, location: @user }\n      else\n        format.html { render :new }\n        format.json { render json: @user.errors, status: :unprocessable_entity }\n      end\n    end\n  end\n\n  # PATCH/PUT /users/1\n  # PATCH/PUT /users/1.json\n  def update\n    respond_to do |format|\n      if @user.update(user_params)\n        format.html { redirect_to @user, notice: 'User was successfully updated.' }\n        format.json { render :show, status: :ok, location: @user }\n      else\n        format.html { render :edit }\n        format.json { render json: @user.errors, status: :unprocessable_entity }\n      end\n    end\n  end\n\n  # DELETE /users/1\n  # DELETE /users/1.json\n  def destroy\n    @user.destroy\n    respond_to do |format|\n      format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }\n      format.json { head :no_content }\n    end\n  end\n\n  def destroy_them_all\n    @user.destroy_by(params[:user])\n    @user.delete_by(params[:user])\n  end\n\n  def dangerous_system_call\n    system(\"bash\", \"-c\", params[:script])\n  end\n\n  def dangerous_exec_call\n    shell = \"zsh\"\n    exec(shell, SHELL_FLAG, \"#{params[:script]} -e ./\")\n  end\n  SHELL_FLAG = \"-c\"\n\n  def safe_system_call\n    system(\"bash\", \"-c\", \"echo\", params[:argument])\n  end\n\n  def safe_system_call_without_shell_dash_c\n    system(\"echo\", \"-c\", params[:argument])\n  end\n\n  def example_redirect_to_request_params\n    redirect_to request.params\n  end\n\n  def permit_bang\n    # Both should warn\n    SomeService.new(params: params.permit!).instance_method\n    params.permit!.merge({ some: 'hash' })\n  end\n\n  private\n    # Use callbacks to share common setup or constraints between actions.\n    def set_user\n      @user = User.find(params[:id])\n    end\n\n    # Never trust parameters from the scary internet, only allow the white list through.\n    def user_params\n      params.require(:user).permit(:name)\n    end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails6/app/helpers/users_helper.rb",
    "content": "module UsersHelper\nend\n"
  },
  {
    "path": "test/apps/rails6/app/javascript/channels/consumer.js",
    "content": "// Action Cable provides the framework to deal with WebSockets in Rails.\n// You can generate new channels where WebSocket features live using the `rails generate channel` command.\n\nimport { createConsumer } from \"@rails/actioncable\"\n\nexport default createConsumer()\n"
  },
  {
    "path": "test/apps/rails6/app/javascript/channels/index.js",
    "content": "// Load all the channels within this directory and all subdirectories.\n// Channel files must be named *_channel.js.\n\nconst channels = require.context('.', true, /_channel\\.js$/)\nchannels.keys().forEach(channels)\n"
  },
  {
    "path": "test/apps/rails6/app/javascript/packs/application.js",
    "content": "// This file is automatically compiled by Webpack, along with any other files\n// present in this directory. You're encouraged to place your actual application logic in\n// a relevant structure within app/javascript and only use these pack files to reference\n// that code so it'll be compiled.\n\nrequire(\"@rails/ujs\").start()\nrequire(\"turbolinks\").start()\nrequire(\"@rails/activestorage\").start()\nrequire(\"channels\")\n"
  },
  {
    "path": "test/apps/rails6/app/jobs/application_job.rb",
    "content": "class ApplicationJob < ActiveJob::Base\n  # Automatically retry jobs that encountered a deadlock\n  # retry_on ActiveRecord::Deadlocked\n\n  # Most jobs are safe to ignore if the underlying records are no longer available\n  # discard_on ActiveJob::DeserializationError\nend\n"
  },
  {
    "path": "test/apps/rails6/app/mailers/application_mailer.rb",
    "content": "class ApplicationMailer < ActionMailer::Base\n  default from: 'from@example.com'\n  layout 'mailer'\nend\n"
  },
  {
    "path": "test/apps/rails6/app/models/application_record.rb",
    "content": "class ApplicationRecord < ActiveRecord::Base\n  self.abstract_class = true\nend\n"
  },
  {
    "path": "test/apps/rails6/app/models/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails6/app/models/group.rb",
    "content": "class Group < ApplicationRecord\n  def uuid_in_sql\n    ActiveRecord::Base.connection.exec_query(\"select * where x = #{User.uuid}\")\n  end\n\n  def date_in_sql\n    date = 30.days.ago\n    Arel.sql(\"created_at > '#{date}'\")\n  end\n\n  def ar_sanitize_sql_like(query)\n    query = ActiveRecord::Base.sanitize_sql_like(query) # escaped variable\n    Arel.sql(\"name ILIKE '%#{query}%'\")\n  end\n\n  def fetch_constant_hash_value(role_name)\n    roles = { admin: 1, moderator: 2 }.freeze\n    role = roles.fetch(role_name)\n    Arel.sql(\"role = '#{role}'\")\n  end\n\n  def use_simple_method\n    # No warning\n    self.where(\"thing = #{Group.simple_method}\")\n  end\n\n  def self.simple_method\n    \"Hello\"\n  end\n\n  enum status: { start: 0, stop: 2, in_process: 3 }\n\n  def use_enum\n    # No warning\n    self.where(\"thing IN #{Group.statuses.values_at(*[:start, :stop]).join(',')}\")\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/models/user.rb",
    "content": "class User < ApplicationRecord\n  STUFF = [\n    DEFAULT_PASSWORD = \"p@ssw3rd2\"\n  ]\n\n  def self.scope_with_strip_heredoc(name)\n    conditions = <<-SQL.strip_heredoc\n      name = '#{name}'\n    SQL\n\n    where(conditions)\n  end\n\n  def self.render_user_input\n    ERB.new(params)\n  end\n\n  def self.more_heredocs\n    ActiveRecord::Base.connection.delete <<~SQL.chomp\n      DELETE FROM #{table} WHERE updated_at < now() - interval '#{period}'\n    SQL\n  end\n\n  def recent_stuff\n    where(\"date > #{Date.today - 1}\")\n  end\n\n  enum state: [\"pending\", \"active\", \"archived\"]\n\n  def check_enum\n    where(\"state = #{User.states[\"pending\"]}\")\n  end\n\n  enum \"stuff_#{stuff}\": [:things]\n\n  def locale\n    User.where(\"lower(slug_#{I18n.locale.to_s.split(\"-\").first}) = :country_id\", country_id: params[:new_country_id]).first\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Rails6</title>\n    <%= csrf_meta_tags %>\n    <%= csp_meta_tag %>\n\n    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>\n    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>\n  </head>\n\n  <body>\n    <%= yield %>\n  </body>\n</html>\n"
  },
  {
    "path": "test/apps/rails6/app/views/layouts/mailer.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n    <style>\n      /* Email styles need to be inline */\n    </style>\n  </head>\n\n  <body>\n    <%= yield %>\n  </body>\n</html>\n"
  },
  {
    "path": "test/apps/rails6/app/views/layouts/mailer.text.erb",
    "content": "<%= yield %>\n"
  },
  {
    "path": "test/apps/rails6/app/views/users/_form.html.erb",
    "content": "<%= form_with(model: user, local: true) do |form| %>\n  <% if user.errors.any? %>\n    <div id=\"error_explanation\">\n      <h2><%= pluralize(user.errors.count, \"error\") %> prohibited this user from being saved:</h2>\n\n      <ul>\n      <% user.errors.full_messages.each do |message| %>\n        <li><%= message %></li>\n      <% end %>\n      </ul>\n    </div>\n  <% end %>\n\n  <div class=\"field\">\n    <%= form.label :name %>\n    <%= form.text_field :name %>\n  </div>\n\n  <div class=\"actions\">\n    <%= form.submit %>\n  </div>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails6/app/views/users/_user.json.jbuilder",
    "content": "json.extract! user, :id, :name, :created_at, :updated_at\njson.url user_url(user, format: :json)\n"
  },
  {
    "path": "test/apps/rails6/app/views/users/edit.html.erb",
    "content": "<h1>Editing User</h1>\n\n<%= render 'form', user: @user %>\n\n<%= link_to 'Show', @user %> |\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails6/app/views/users/index.html.erb",
    "content": "<p id=\"notice\"><%= notice %></p>\n\n<h1>Users</h1>\n\n<table>\n  <thead>\n    <tr>\n      <th>Name</th>\n      <th colspan=\"3\"></th>\n    </tr>\n  </thead>\n\n  <tbody>\n    <% @users.each do |user| %>\n      <tr>\n        <td><%= user.name %></td>\n        <td><%= link_to 'Show', user %></td>\n        <td><%= link_to 'Edit', edit_user_path(user) %></td>\n        <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>\n      </tr>\n    <% end %>\n  </tbody>\n</table>\n\n<br>\n\n<%= link_to 'New User', new_user_path %>\n"
  },
  {
    "path": "test/apps/rails6/app/views/users/index.json.jbuilder",
    "content": "json.array! @users, partial: 'users/user', as: :user\n"
  },
  {
    "path": "test/apps/rails6/app/views/users/new.html.erb",
    "content": "<h1>New User</h1>\n\n<%= render 'form', user: @user %>\n\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails6/app/views/users/show.html.erb",
    "content": "<p id=\"notice\"><%= notice %></p>\n\n<p>\n  <strong>Name:</strong>\n  <%= @user.name.html_safe %>\n  <%= @user.name.html_safe if x %>\n  <%= @user.name.html_safe unless x %>\n  <%= x ? @user.name.html_safe : y %>\n</p>\n\n<%= link_to 'Edit', edit_user_path(@user) %> |\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails6/app/views/users/show.json.jbuilder",
    "content": "json.partial! \"users/user\", user: @user\n"
  },
  {
    "path": "test/apps/rails6/app/widgets/widget.rb",
    "content": "class Widget < ApplicationRecord\n  def spin(direction)\n    where(\"direction = #{direction})\").first.spin\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/babel.config.js",
    "content": "module.exports = function(api) {\n  var validEnv = ['development', 'test', 'production']\n  var currentEnv = api.env()\n  var isDevelopmentEnv = api.env('development')\n  var isProductionEnv = api.env('production')\n  var isTestEnv = api.env('test')\n\n  if (!validEnv.includes(currentEnv)) {\n    throw new Error(\n      'Please specify a valid `NODE_ENV` or ' +\n        '`BABEL_ENV` environment variables. Valid values are \"development\", ' +\n        '\"test\", and \"production\". Instead, received: ' +\n        JSON.stringify(currentEnv) +\n        '.'\n    )\n  }\n\n  return {\n    presets: [\n      isTestEnv && [\n        require('@babel/preset-env').default,\n        {\n          targets: {\n            node: 'current'\n          }\n        }\n      ],\n      (isProductionEnv || isDevelopmentEnv) && [\n        require('@babel/preset-env').default,\n        {\n          forceAllTransforms: true,\n          useBuiltIns: 'entry',\n          modules: false,\n          exclude: ['transform-typeof-symbol']\n        }\n      ]\n    ].filter(Boolean),\n    plugins: [\n      require('babel-plugin-macros'),\n      require('@babel/plugin-syntax-dynamic-import').default,\n      isTestEnv && require('babel-plugin-dynamic-import-node'),\n      require('@babel/plugin-transform-destructuring').default,\n      [\n        require('@babel/plugin-proposal-class-properties').default,\n        {\n          loose: true\n        }\n      ],\n      [\n        require('@babel/plugin-proposal-object-rest-spread').default,\n        {\n          useBuiltIns: true\n        }\n      ],\n      [\n        require('@babel/plugin-transform-runtime').default,\n        {\n          helpers: false,\n          regenerator: true\n        }\n      ],\n      [\n        require('@babel/plugin-transform-regenerator').default,\n        {\n          async: false\n        }\n      ]\n    ].filter(Boolean)\n  }\n}\n"
  },
  {
    "path": "test/apps/rails6/config/application.rb",
    "content": "require_relative 'boot'\n\nrequire 'rails/all'\n\n# Require the gems listed in Gemfile, including any gems\n# you've limited to :test, :development, or :production.\nBundler.require(*Rails.groups)\n\nmodule Rails6\n  class Application < Rails::Application\n    # Initialize configuration defaults for originally generated Rails version.\n    config.load_defaults 6.0\n\n    # Settings in config/environments/* take precedence over those specified here.\n    # Application configuration can go into files in config/initializers\n    # -- all .rb files in that directory are automatically loaded after loading\n    # the framework and any gems in your application.\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/config/boot.rb",
    "content": "ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)\n\nrequire 'bundler/setup' # Set up gems listed in the Gemfile.\nrequire 'bootsnap/setup' # Speed up boot time by caching expensive operations.\n"
  },
  {
    "path": "test/apps/rails6/config/cable.yml",
    "content": "development:\n  adapter: async\n\ntest:\n  adapter: test\n\nproduction:\n  adapter: redis\n  url: <%= ENV.fetch(\"REDIS_URL\") { \"redis://localhost:6379/1\" } %>\n  channel_prefix: rails6_production\n"
  },
  {
    "path": "test/apps/rails6/config/credentials.yml.enc",
    "content": "b/xul5Q0qIjaK5d9W62i89WFeOjav+ubFRXhjdR2SuzPbzhT9xYyKpjRbeMbMz+j2zB4TM1vRxD3HJHF9izgwgMT8rnjA2U1nBLjxWqbTG/uKYfwIbMvKxeSbIGGdd/jmhqX1FkI9kjPh+UXbYp35Q/AuNnRVT4uYhKPhYkzED8YHla0zaGJw7IOe4v3OhEDXdYQJTDtuIU2tdS33L79/M/JCyjF1kMNfspf9imbYx/1JYNo4XmroAs6IYR+keuzYIIIb7kkLjaI61xGUoH0har7NIzPLbujGnAc+IIjPMf3sa2bmRVbTZgAkDcCmVO5bR1uGslroOyV/UEC74668doWZ304ywGlHxqi2f1/U2/t+Nhp562rDViy0QHiAgipKodv3Xn85BrQLIUvXBizVpf1WJgiCU2v5c7n--RZK7EAHLEtUv1WeQ--Bp+CMY4Ux5xBWji9MshqCA=="
  },
  {
    "path": "test/apps/rails6/config/database.yml",
    "content": "# SQLite version 3.x\n#   gem install sqlite3\n#\n#   Ensure the SQLite 3 gem is defined in your Gemfile\n#   gem 'sqlite3'\n#\ndefault: &default\n  adapter: sqlite3\n  pool: <%= ENV.fetch(\"RAILS_MAX_THREADS\") { 5 } %>\n  timeout: 5000\n\ndevelopment:\n  <<: *default\n  database: db/development.sqlite3\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  <<: *default\n  database: db/test.sqlite3\n\nproduction:\n  <<: *default\n  database: db/production.sqlite3\n"
  },
  {
    "path": "test/apps/rails6/config/environment.rb",
    "content": "# Load the Rails application.\nrequire_relative 'application'\n\n# A Dir.glob constant for testing\nFILE_LIST = Dir.glob(File.join(Rails.root, \"app\", \"views\", \"**\", \"*\")).select do |f|\n  f.end_with? \".html\"\nend\n\n# Initialize the Rails application.\nRails.application.initialize!\n"
  },
  {
    "path": "test/apps/rails6/config/environments/development.rb",
    "content": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # In the development environment your application's code is reloaded on\n  # every request. This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes = false\n\n  # Do not eager load code on boot.\n  config.eager_load = false\n\n  # Show full error reports.\n  config.consider_all_requests_local = true\n\n  # Enable/disable caching. By default caching is disabled.\n  # Run rails dev:cache to toggle caching.\n  if Rails.root.join('tmp', 'caching-dev.txt').exist?\n    config.action_controller.perform_caching = true\n    config.action_controller.enable_fragment_cache_logging = true\n\n    config.cache_store = :memory_store\n    config.public_file_server.headers = {\n      'Cache-Control' => \"public, max-age=#{2.days.to_i}\"\n    }\n  else\n    config.action_controller.perform_caching = false\n\n    config.cache_store = :null_store\n  end\n\n  # Store uploaded files on the local file system (see config/storage.yml for options).\n  config.active_storage.service = :local\n\n  # Don't care if the mailer can't send.\n  config.action_mailer.raise_delivery_errors = false\n\n  config.action_mailer.perform_caching = false\n\n  # Print deprecation notices to the Rails logger.\n  config.active_support.deprecation = :log\n\n  # Raise an error on page load if there are pending migrations.\n  config.active_record.migration_error = :page_load\n\n  # Highlight code that triggered database queries in logs.\n  config.active_record.verbose_query_logs = true\n\n  # Debug mode disables concatenation and preprocessing of assets.\n  # This option may cause significant delays in view rendering with a large\n  # number of complex assets.\n  config.assets.debug = true\n\n  # Suppress logger output for asset requests.\n  config.assets.quiet = true\n\n  # Raises error for missing translations.\n  # config.action_view.raise_on_missing_translations = true\n\n  # Use an evented file watcher to asynchronously detect changes in source code,\n  # routes, locales, etc. This feature depends on the listen gem.\n  config.file_watcher = ActiveSupport::EventedFileUpdateChecker\nend\n"
  },
  {
    "path": "test/apps/rails6/config/environments/production.rb",
    "content": "::Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Code is not reloaded between requests.\n  config.cache_classes = true\n\n  # Eager load code on boot. This eager loads most of Rails and\n  # your application in memory, allowing both threaded web servers\n  # and those relying on copy on write to perform better.\n  # Rake tasks automatically ignore this option for performance.\n  config.eager_load = true\n\n  # Full error reports are disabled and caching is turned on.\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Ensures that a master key has been made available in either ENV[\"RAILS_MASTER_KEY\"]\n  # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).\n  # config.require_master_key = true\n\n  # Disable serving static files from the `/public` folder by default since\n  # Apache or NGINX already handles this.\n  config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?\n\n  # Compress CSS using a preprocessor.\n  # config.assets.css_compressor = :sass\n\n  # Do not fallback to assets pipeline if a precompiled asset is missed.\n  config.assets.compile = false\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server.\n  # config.action_controller.asset_host = 'http://assets.example.com'\n\n  # Specifies the header that your server uses for sending files.\n  # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache\n  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX\n\n  # Store uploaded files on the local file system (see config/storage.yml for options).\n  config.active_storage.service = :local\n\n  # Mount Action Cable outside main process or domain.\n  # config.action_cable.mount_path = nil\n  # config.action_cable.url = 'wss://example.com/cable'\n  # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\\/\\/example.*/ ]\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  config.force_ssl = true\n\n  # Use the lowest log level to ensure availability of diagnostic information\n  # when problems arise.\n  config.log_level = :debug\n\n  # Prepend all log lines with the following tags.\n  config.log_tags = [ :request_id ]\n\n  # Use a different cache store in production.\n  # config.cache_store = :mem_cache_store\n\n  # Use a real queuing backend for Active Job (and separate queues per environment).\n  # config.active_job.queue_adapter     = :resque\n  # config.active_job.queue_name_prefix = \"rails6_production\"\n\n  config.action_mailer.perform_caching = false\n\n  # Ignore bad email addresses and do not raise email delivery errors.\n  # Set this to true and configure the email server for immediate delivery to raise delivery errors.\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation cannot be found).\n  config.i18n.fallbacks = true\n\n  # Send deprecation notices to registered listeners.\n  config.active_support.deprecation = :notify\n\n  # Use default logging formatter so that PID and timestamp are not suppressed.\n  config.log_formatter = ::Logger::Formatter.new\n\n  # Use a different logger for distributed setups.\n  # require 'syslog/logger'\n  # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')\n\n  if ENV[\"RAILS_LOG_TO_STDOUT\"].present?\n    logger           = ActiveSupport::Logger.new(STDOUT)\n    logger.formatter = config.log_formatter\n    config.logger    = ActiveSupport::TaggedLogging.new(logger)\n  end\n\n  # Do not dump schema after migrations.\n  config.active_record.dump_schema_after_migration = false\n\n  # Inserts middleware to perform automatic connection switching.\n  # The `database_selector` hash is used to pass options to the DatabaseSelector\n  # middleware. The `delay` is used to determine how long to wait after a write\n  # to send a subsequent read to the primary.\n  #\n  # The `database_resolver` class is used by the middleware to determine which\n  # database is appropriate to use based on the time delay.\n  #\n  # The `database_resolver_context` class is used by the middleware to set\n  # timestamps for the last write to the primary. The resolver uses the context\n  # class timestamps to determine how long to wait before reading from the\n  # replica.\n  #\n  # By default Rails will store a last write timestamp in the session. The\n  # DatabaseSelector middleware is designed as such you can define your own\n  # strategy for connection switching and pass that into the middleware through\n  # these configuration options.\n  # config.active_record.database_selector = { delay: 2.seconds }\n  # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver\n  # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session\n\n\n  # Stop messing up my JSON!\n  config.active_support.escape_html_entities_in_json = false\nend\n"
  },
  {
    "path": "test/apps/rails6/config/environments/test.rb",
    "content": "Rails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # The test environment is used exclusively to run your application's\n  # test suite. You never need to work with it otherwise. Remember that\n  # your test database is \"scratch space\" for the test suite and is wiped\n  # and recreated between test runs. Don't rely on the data there!\n  config.cache_classes = true\n\n  # Do not eager load code on boot. This avoids loading your whole application\n  # just for the purpose of running a single test. If you are using a tool that\n  # preloads Rails for running tests, you may have to set it to true.\n  config.eager_load = false\n\n  # Configure public file server for tests with Cache-Control for performance.\n  config.public_file_server.enabled = true\n  config.public_file_server.headers = {\n    'Cache-Control' => \"public, max-age=#{1.hour.to_i}\"\n  }\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n  config.cache_store = :null_store\n\n  # Raise exceptions instead of rendering exception templates.\n  config.action_dispatch.show_exceptions = false\n\n  # Disable request forgery protection in test environment.\n  config.action_controller.allow_forgery_protection = false\n\n  # Store uploaded files on the local file system in a temporary directory.\n  config.active_storage.service = :test\n\n  config.action_mailer.perform_caching = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Print deprecation notices to the stderr.\n  config.active_support.deprecation = :stderr\n\n  # Raises error for missing translations.\n  # config.action_view.raise_on_missing_translations = true\nend\n"
  },
  {
    "path": "test/apps/rails6/config/initializers/allow_all_parameters.rb",
    "content": "# Allows all parameters for StrongParameters\nActionController::Parameters.permit_all_parameters = true\n"
  },
  {
    "path": "test/apps/rails6/config/initializers/application_controller_renderer.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# ActiveSupport::Reloader.to_prepare do\n#   ApplicationController.renderer.defaults.merge!(\n#     http_host: 'example.org',\n#     https: false\n#   )\n# end\n"
  },
  {
    "path": "test/apps/rails6/config/initializers/assets.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Version of your assets, change this if you want to expire all your assets.\nRails.application.config.assets.version = '1.0'\n\n# Add additional assets to the asset load path.\n# Rails.application.config.assets.paths << Emoji.images_path\n# Add Yarn node_modules folder to the asset load path.\nRails.application.config.assets.paths << Rails.root.join('node_modules')\n\n# Precompile additional assets.\n# application.js, application.css, and all non-JS/CSS in the app/assets\n# folder are already added.\n# Rails.application.config.assets.precompile += %w( admin.js admin.css )\n"
  },
  {
    "path": "test/apps/rails6/config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!\n"
  },
  {
    "path": "test/apps/rails6/config/initializers/content_security_policy.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Define an application-wide content security policy\n# For further information see the following documentation\n# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy\n\n# Rails.application.config.content_security_policy do |policy|\n#   policy.default_src :self, :https\n#   policy.font_src    :self, :https, :data\n#   policy.img_src     :self, :https, :data\n#   policy.object_src  :none\n#   policy.script_src  :self, :https\n#   policy.style_src   :self, :https\n#   # If you are using webpack-dev-server then specify webpack-dev-server host\n#   policy.connect_src :self, :https, \"http://localhost:3035\", \"ws://localhost:3035\" if Rails.env.development?\n\n#   # Specify URI for violation reports\n#   # policy.report_uri \"/csp-violation-report-endpoint\"\n# end\n\n# If you are using UJS then enable automatic nonce generation\n# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }\n\n# Report CSP violations to a specified URI\n# For further information see the following documentation:\n# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only\n# Rails.application.config.content_security_policy_report_only = true\n"
  },
  {
    "path": "test/apps/rails6/config/initializers/cookies_serializer.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Specify a serializer for the signed and encrypted cookie jars.\n# Valid options are :json, :marshal, and :hybrid.\nRails.application.config.action_dispatch.cookies_serializer = :marshal\n"
  },
  {
    "path": "test/apps/rails6/config/initializers/filter_parameter_logging.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Configure sensitive parameters which will be filtered from the log file.\nRails.application.config.filter_parameters += [:password]\n"
  },
  {
    "path": "test/apps/rails6/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Inflections\n# are locale specific, and you may define rules for as many different\n# locales as you wish. All of these examples are active by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n\n# These inflection rules are supported but not enabled by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.acronym 'RESTful'\n# end\n"
  },
  {
    "path": "test/apps/rails6/config/initializers/json_escape.rb",
    "content": "# I like my HTML entities\nActiveSupport::JSON::Encoding.escape_html_entities_in_json = false\n"
  },
  {
    "path": "test/apps/rails6/config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n"
  },
  {
    "path": "test/apps/rails6/config/initializers/wrap_parameters.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# This file contains settings for ActionController::ParamsWrapper which\n# is enabled by default.\n\n# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.\nActiveSupport.on_load(:action_controller) do\n  wrap_parameters format: [:json]\nend\n\n# To enable root element in JSON for ActiveRecord objects.\n# ActiveSupport.on_load(:active_record) do\n#   self.include_root_in_json = true\n# end\n"
  },
  {
    "path": "test/apps/rails6/config/locales/en.yml",
    "content": "# Files in the config/locales directory are used for internationalization\n# and are automatically loaded by Rails. If you want to use locales other\n# than English, add the necessary files in this directory.\n#\n# To use the locales, use `I18n.t`:\n#\n#     I18n.t 'hello'\n#\n# In views, this is aliased to just `t`:\n#\n#     <%= t('hello') %>\n#\n# To use a different locale, set it with `I18n.locale`:\n#\n#     I18n.locale = :es\n#\n# This would use the information in config/locales/es.yml.\n#\n# The following keys must be escaped otherwise they will not be retrieved by\n# the default I18n backend:\n#\n# true, false, on, off, yes, no\n#\n# Instead, surround them with single quotes.\n#\n# en:\n#   'true': 'foo'\n#\n# To learn more, please read the Rails Internationalization guide\n# available at https://guides.rubyonrails.org/i18n.html.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/apps/rails6/config/puma.rb",
    "content": "# Puma can serve each request in a thread from an internal thread pool.\n# The `threads` method setting takes two numbers: a minimum and maximum.\n# Any libraries that use thread pools should be configured to match\n# the maximum value specified for Puma. Default is set to 5 threads for minimum\n# and maximum; this matches the default thread size of Active Record.\n#\nmax_threads_count = ENV.fetch(\"RAILS_MAX_THREADS\") { 5 }\nmin_threads_count = ENV.fetch(\"RAILS_MIN_THREADS\") { max_threads_count }\nthreads min_threads_count, max_threads_count\n\n# Specifies the `port` that Puma will listen on to receive requests; default is 3000.\n#\nport        ENV.fetch(\"PORT\") { 3000 }\n\n# Specifies the `environment` that Puma will run in.\n#\nenvironment ENV.fetch(\"RAILS_ENV\") { \"development\" }\n\n# Specifies the number of `workers` to boot in clustered mode.\n# Workers are forked web server processes. If using threads and workers together\n# the concurrency of the application would be max `threads` * `workers`.\n# Workers do not work on JRuby or Windows (both of which do not support\n# processes).\n#\n# workers ENV.fetch(\"WEB_CONCURRENCY\") { 2 }\n\n# Use the `preload_app!` method when specifying a `workers` number.\n# This directive tells Puma to first boot the application and load code\n# before forking the application. This takes advantage of Copy On Write\n# process behavior so workers use less memory.\n#\n# preload_app!\n\n# Allow puma to be restarted by `rails restart` command.\nplugin :tmp_restart\n"
  },
  {
    "path": "test/apps/rails6/config/routes.rb",
    "content": "Rails.application.routes.draw do\n  resources :users\n\n  # https://github.com/presidentbeef/brakeman/issues/1410\n  x = ->(args) {\n  }\nend\n"
  },
  {
    "path": "test/apps/rails6/config/spring.rb",
    "content": "Spring.watch(\n  \".ruby-version\",\n  \".rbenv-vars\",\n  \"tmp/restart.txt\",\n  \"tmp/caching-dev.txt\"\n)\n"
  },
  {
    "path": "test/apps/rails6/config/storage.yml",
    "content": "test:\n  service: Disk\n  root: <%= Rails.root.join(\"tmp/storage\") %>\n\nlocal:\n  service: Disk\n  root: <%= Rails.root.join(\"storage\") %>\n\n# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)\n# amazon:\n#   service: S3\n#   access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>\n#   secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>\n#   region: us-east-1\n#   bucket: your_own_bucket\n\n# Remember not to checkin your GCS keyfile to a repository\n# google:\n#   service: GCS\n#   project: your_project\n#   credentials: <%= Rails.root.join(\"path/to/gcs.keyfile\") %>\n#   bucket: your_own_bucket\n\n# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)\n# microsoft:\n#   service: AzureStorage\n#   storage_account_name: your_account_name\n#   storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>\n#   container: your_container_name\n\n# mirror:\n#   service: Mirror\n#   primary: local\n#   mirrors: [ amazon, google, microsoft ]\n"
  },
  {
    "path": "test/apps/rails6/config/webpack/development.js",
    "content": "process.env.NODE_ENV = process.env.NODE_ENV || 'development'\n\nconst environment = require('./environment')\n\nmodule.exports = environment.toWebpackConfig()\n"
  },
  {
    "path": "test/apps/rails6/config/webpack/environment.js",
    "content": "const { environment } = require('@rails/webpacker')\n\nmodule.exports = environment\n"
  },
  {
    "path": "test/apps/rails6/config/webpack/production.js",
    "content": "process.env.NODE_ENV = process.env.NODE_ENV || 'production'\n\nconst environment = require('./environment')\n\nmodule.exports = environment.toWebpackConfig()\n"
  },
  {
    "path": "test/apps/rails6/config/webpack/test.js",
    "content": "process.env.NODE_ENV = process.env.NODE_ENV || 'development'\n\nconst environment = require('./environment')\n\nmodule.exports = environment.toWebpackConfig()\n"
  },
  {
    "path": "test/apps/rails6/config/webpacker.yml",
    "content": "# Note: You must restart bin/webpack-dev-server for changes to take effect\n\ndefault: &default\n  source_path: app/javascript\n  source_entry_path: packs\n  public_root_path: public\n  public_output_path: packs\n  cache_path: tmp/cache/webpacker\n  check_yarn_integrity: false\n  webpack_compile_output: false\n\n  # Additional paths webpack should lookup modules\n  # ['app/assets', 'engine/foo/app/assets']\n  resolved_paths: []\n\n  # Reload manifest.json on all requests so we reload latest compiled packs\n  cache_manifest: false\n\n  # Extract and emit a css file\n  extract_css: false\n\n  static_assets_extensions:\n    - .jpg\n    - .jpeg\n    - .png\n    - .gif\n    - .tiff\n    - .ico\n    - .svg\n    - .eot\n    - .otf\n    - .ttf\n    - .woff\n    - .woff2\n\n  extensions:\n    - .mjs\n    - .js\n    - .sass\n    - .scss\n    - .css\n    - .module.sass\n    - .module.scss\n    - .module.css\n    - .png\n    - .svg\n    - .gif\n    - .jpeg\n    - .jpg\n\ndevelopment:\n  <<: *default\n  compile: true\n\n  # Verifies that versions and hashed value of the package contents in the project's package.json\n  check_yarn_integrity: true\n\n  # Reference: https://webpack.js.org/configuration/dev-server/\n  dev_server:\n    https: false\n    host: localhost\n    port: 3035\n    public: localhost:3035\n    hmr: false\n    # Inline should be set to true if using HMR\n    inline: true\n    overlay: true\n    compress: true\n    disable_host_check: true\n    use_local_ip: false\n    quiet: false\n    headers:\n      'Access-Control-Allow-Origin': '*'\n    watch_options:\n      ignored: '**/node_modules/**'\n\n\ntest:\n  <<: *default\n  compile: true\n\n  # Compile test packs to a separate directory\n  public_output_path: packs-test\n\nproduction:\n  <<: *default\n\n  # Production depends on precompilation of packs prior to booting for performance.\n  compile: false\n\n  # Extract and emit a css file\n  extract_css: true\n\n  # Cache manifest.json for performance\n  cache_manifest: true\n"
  },
  {
    "path": "test/apps/rails6/config.ru",
    "content": "# This file is used by Rack-based servers to start the application.\n\nrequire_relative 'config/environment'\n\nrun Rails.application\n"
  },
  {
    "path": "test/apps/rails6/lib/assets/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails6/lib/run_stuff.rb",
    "content": "class RunStuff\n  def run\n    Tempfile.open(\"cool_stuff.txt\") do |temp_file|\n      `cat #{temp_file.path}`\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/lib/tasks/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails6/lib/view_component/base.rb",
    "content": "module ViewComponent\n  class Base\n    # For testing 'known_renderable_class?' in CheckRender\n  end\nend\n"
  },
  {
    "path": "test/apps/rails6/package.json",
    "content": "{\n  \"name\": \"rails6\",\n  \"private\": true,\n  \"dependencies\": {\n    \"@rails/actioncable\": \"^6.0.0-alpha\",\n    \"@rails/activestorage\": \"^6.0.0-alpha\",\n    \"@rails/ujs\": \"^6.0.0-alpha\",\n    \"@rails/webpacker\": \"^4.0.2\",\n    \"turbolinks\": \"^5.2.0\"\n  },\n  \"version\": \"0.1.0\",\n  \"devDependencies\": {\n    \"webpack-dev-server\": \"^3.2.1\"\n  }\n}\n"
  },
  {
    "path": "test/apps/rails6/postcss.config.js",
    "content": "module.exports = {\n  plugins: [\n    require('postcss-import'),\n    require('postcss-flexbugs-fixes'),\n    require('postcss-preset-env')({\n      autoprefixer: {\n        flexbox: 'no-2009'\n      },\n      stage: 3\n    })\n  ]\n}\n"
  },
  {
    "path": "test/apps/rails7/MyGemfile",
    "content": "source \"https://rubygems.org\"\ngit_source(:github) { |repo| \"https://github.com/#{repo}.git\" }\n\nruby \"2.7.0\"\n\n# Bundle edge Rails instead: gem \"rails\", github: \"rails/rails\", branch: \"main\"\ngem \"rails\", \"~> 7.0.0\"\n\n# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]\ngem \"sprockets-rails\", \">= 3.4.1\"\n\n# Use sqlite3 as the database for Active Record\ngem \"sqlite3\", \"~> 1.4\"\n\n# Use the Puma web server [https://github.com/puma/puma]\ngem \"puma\", \"~> 5.0\"\n\n# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]\ngem \"importmap-rails\", \">= 0.9.2\"\n\n# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]\ngem \"turbo-rails\", \">= 0.9.0\"\n\n# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]\ngem \"stimulus-rails\", \">= 0.7.3\"\n\n# Build JSON APIs with ease [https://github.com/rails/jbuilder]\ngem \"jbuilder\", \"~> 2.11\"\n\n# Use Redis adapter to run Action Cable in production\n# gem \"redis\", \"~> 4.0\"\n\n# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis]\n# gem \"kredis\"\n\n# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]\n# gem \"bcrypt\", \"~> 3.1.7\"\n\n# Windows does not include zoneinfo files, so bundle the tzinfo-data gem\ngem \"tzinfo-data\", platforms: %i[ mingw mswin x64_mingw jruby ]\n\n# Reduces boot times through caching; required in config/boot.rb\ngem \"bootsnap\", \">= 1.4.4\", require: false\n\ngem 'ransack', '~>3.0.0'\n\n# Use Sass to process CSS\n# gem \"sassc-rails\", \"~> 2.1\"\n\n# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]\n# gem \"image_processing\", \"~> 1.2\"\n\ngroup :development, :test do\n  # See https://edgeguides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem\n  gem \"debug\", \">= 1.0.0\", platforms: %i[ mri mingw x64_mingw ]\nend\n\ngroup :development do\n  # Use console on exceptions pages [https://github.com/rails/web-console]\n  gem \"web-console\", \">= 4.1.0\"\n\n  # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler]\n  # gem \"rack-mini-profiler\", \">= 2.3.3\"\n\n  # Speed up commands on slow machines / big apps [https://github.com/rails/spring]\n  # gem \"spring\"\nend\n\ngroup :test do\n  # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]\n  gem \"capybara\", \">= 3.26\"\n  gem \"selenium-webdriver\", \">= 4.0.0\"\n  gem \"webdrivers\"\nend\n"
  },
  {
    "path": "test/apps/rails7/app/assets/config/manifest.js",
    "content": "//= link_tree ../images\n//= link_directory ../stylesheets .css\n//= link_tree ../../javascript .js\n//= link_tree ../../../vendor/javascript .js\n"
  },
  {
    "path": "test/apps/rails7/app/assets/images/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails7/app/assets/stylesheets/application.css",
    "content": "/*\n * This is a manifest file that'll be compiled into application.css, which will include all the files\n * listed below.\n *\n * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's\n * vendor/assets/stylesheets directory can be referenced here using a relative path.\n *\n * You're free to add application-wide styles to this file and they'll appear at the bottom of the\n * compiled file so the styles you add here take precedence over styles defined in any other CSS\n * files in this directory. Styles in this file should be added after the last require_* statement.\n * It is generally better to create a new file per style scope.\n *\n *= require_tree .\n *= require_self\n */\n"
  },
  {
    "path": "test/apps/rails7/app/channels/application_cable/channel.rb",
    "content": "module ApplicationCable\n  class Channel < ActionCable::Channel::Base\n  end\nend\n"
  },
  {
    "path": "test/apps/rails7/app/channels/application_cable/connection.rb",
    "content": "module ApplicationCable\n  class Connection < ActionCable::Connection::Base\n  end\nend\n"
  },
  {
    "path": "test/apps/rails7/app/controllers/admin_controller.rb",
    "content": "class AdminController < ApplicationController\n  def search_users\n    # Medium warning because it's probably an admin interface\n    User.ransack(params[:q])\n  end\n\n  # Test kwsplats in filter options\n  before_filter(**options) do |c|\n    x\n  end\n\n  # Test weird option doesn't cause an error\n  before_action :thing, if: -> { true }\nend\n"
  },
  {
    "path": "test/apps/rails7/app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::Base\n  def anonymouns_arguments(*, **, &)\n    Time.use_zone(*, **, &)\n  end\n\n  def hash_value_omission\n    x = 1\n    y = 2\n\n    {x:, y:}\n  end\n\n  def endless_method_definition(msg) = puts \"#{Time.now}: #{msg}\"\n\n  def pattern_matching_parenthesis_ommission\n    [0, 1] => _, x\n    {y: 2} => y:\n\n    {x:, y:}\n  end\n\n  def pattern_matching_non_local_variable_pin\n    {timestamp: Time.now} in {timestamp: ^(Time.new(2021)..Time.new(2022))}\n  end\n\n  def pathname_stuff\n    z = Pathname.new('a').join(params[:x], 'z').basename # should warn\n    something(z) # should not be a duplicate warning\n\n    Rails.root.join('a', 'b', \"#{params[:c]}\") # should warn\n  end\nend\n"
  },
  {
    "path": "test/apps/rails7/app/controllers/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails7/app/controllers/users_controller.rb",
    "content": "class UsersController < ApplicationController\n  def redirect_to_last!\n    redirect_to User.last!\n  end\n\n  def presence\n    # Don't warn... @field is either \"foo\" or nil\n    @field = params[:field].presence_in(%w[foo]) || raise(ActionController::BadRequest)\n    render \"admin2/fields/#{@field}\"\n  end\n\n  def redirect_param_with_fallback\n    redirect_to params[:redirect_url] || \"/\"\n  end\n\n  def redirect_url_from_param_with_fallback\n    redirect_to url_from(params[:redirect_url]) || \"/\"\n  end\n\n  def redirect_with_allow_host\n    redirect_to params[:x], allow_other_host: true # low confidence warning\n  end\n\n  def redirect_with_explicit_not_allow\n    redirect_to params[:x], allow_other_host: false # no warning\n  end\n\n  def redirect_back_with_fallback\n    redirect_back fallback_location: params[:x]\n  end\n\n  def redirect_back_or_to_with_fallback\n    redirect_back_or_to params[:x]\n  end\n\n  def redirect_back_or_to_with_fallback_disallow_host\n    redirect_back_or_to params[:x], allow_other_host: false # no warning\n  end\n\n  def search\n    User.ransack(params[:q])\n  end\n\n  def search_books\n    # Should not warn - search limited appropriately\n    Book.ransack(params[:q])\n\n    # Low confidence because no idea what `some_book` is\n    some_book.things.ransack(params[:q])\n  end\n\n  class << self\n    def just_here_for_test_coverage_thanks\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails7/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails7/app/javascript/application.js",
    "content": "// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails\nimport \"@hotwired/turbo-rails\"\nimport \"controllers\"\n"
  },
  {
    "path": "test/apps/rails7/app/javascript/controllers/application.js",
    "content": "import { Application } from \"@hotwired/stimulus\"\n\nconst application = Application.start()\n\n// Configure Stimulus development experience\napplication.debug = false\nwindow.Stimulus   = application\n\nexport { application }\n"
  },
  {
    "path": "test/apps/rails7/app/javascript/controllers/hello_controller.js",
    "content": "import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n  connect() {\n    this.element.textContent = \"Hello World!\"\n  }\n}\n"
  },
  {
    "path": "test/apps/rails7/app/javascript/controllers/index.js",
    "content": "// Import and register all your controllers from the importmap under controllers/*\n\nimport { application } from \"controllers/application\"\n\n// Eager load all controllers defined in the import map under controllers/**/*_controller\nimport { eagerLoadControllersFrom } from \"@hotwired/stimulus-loading\"\neagerLoadControllersFrom(\"controllers\", application)\n\n// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)\n// import { lazyLoadControllersFrom } from \"@hotwired/stimulus-loading\"\n// lazyLoadControllersFrom(\"controllers\", application)\n"
  },
  {
    "path": "test/apps/rails7/app/jobs/application_job.rb",
    "content": "class ApplicationJob < ActiveJob::Base\n  # Automatically retry jobs that encountered a deadlock\n  # retry_on ActiveRecord::Deadlocked\n\n  # Most jobs are safe to ignore if the underlying records are no longer available\n  # discard_on ActiveJob::DeserializationError\nend\n"
  },
  {
    "path": "test/apps/rails7/app/mailers/application_mailer.rb",
    "content": "class ApplicationMailer < ActionMailer::Base\n  default from: \"from@example.com\"\n  layout \"mailer\"\nend\n"
  },
  {
    "path": "test/apps/rails7/app/models/application_record.rb",
    "content": "class ApplicationRecord < ActiveRecord::Base\n  primary_abstract_class\nend\n"
  },
  {
    "path": "test/apps/rails7/app/models/book.rb",
    "content": "class Book < Thing\n  def self.ransackable_attributes(auth_object = nil)\n    [:author, :title]\n  end\nend\n"
  },
  {
    "path": "test/apps/rails7/app/models/concerns/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails7/app/models/thing.rb",
    "content": "class Thing < ApplicationRecord\n  class << self\n    def ransackable_associations(auth_object = nil)\n      []\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails7/app/models/user.rb",
    "content": "class User < ApplicationRecord\nend\n"
  },
  {
    "path": "test/apps/rails7/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Rails7</title>\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <%= csrf_meta_tags %>\n    <%= csp_meta_tag %>\n\n    <%= stylesheet_link_tag \"application\", \"data-turbo-track\": \"reload\" %>\n    <%= javascript_importmap_tags %>\n  </head>\n\n  <body>\n    <%= yield %>\n  </body>\n</html>\n"
  },
  {
    "path": "test/apps/rails7/app/views/layouts/mailer.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n    <style>\n      /* Email styles need to be inline */\n    </style>\n  </head>\n\n  <body>\n    <%= yield %>\n  </body>\n</html>\n"
  },
  {
    "path": "test/apps/rails7/app/views/layouts/mailer.text.erb",
    "content": "<%= yield %>\n"
  },
  {
    "path": "test/apps/rails7/config/application.rb",
    "content": "require_relative \"boot\"\n\nrequire \"rails/all\"\n\n# Require the gems listed in Gemfile, including any gems\n# you've limited to :test, :development, or :production.\nBundler.require(*Rails.groups)\n\nmodule Rails7\n  class Application < Rails::Application\n    # Initialize configuration defaults for originally generated Rails version.\n    config.load_defaults '7.0'\n\n    # Configuration for the application, engines, and railties goes here.\n    #\n    # These settings can be overridden in specific environments using the files\n    # in config/environments, which are processed later.\n    #\n    # config.time_zone = \"Central Time (US & Canada)\"\n    # config.eager_load_paths << Rails.root.join(\"extras\")\n\n    # Test with this off\n    config.action_controller.raise_on_open_redirects = false\n  end\nend\n"
  },
  {
    "path": "test/apps/rails7/config/boot.rb",
    "content": "ENV[\"BUNDLE_GEMFILE\"] ||= File.expand_path(\"../Gemfile\", __dir__)\n\nrequire \"bundler/setup\" # Set up gems listed in the Gemfile.\nrequire \"bootsnap/setup\" # Speed up boot time by caching expensive operations.\n"
  },
  {
    "path": "test/apps/rails7/config/cable.yml",
    "content": "development:\n  adapter: async\n\ntest:\n  adapter: test\n\nproduction:\n  adapter: redis\n  url: <%= ENV.fetch(\"REDIS_URL\") { \"redis://localhost:6379/1\" } %>\n  channel_prefix: rails7_production\n"
  },
  {
    "path": "test/apps/rails7/config/credentials.yml.enc",
    "content": "RPUdW3kTfs+b1n3IHxYV/61sD5RQDPjnXz3itzhf5fgWR/OMmzV6BLlq2InQ7ja4WaZ+9iqqCAJ/Z+FMyzTDCNnwt0KqIwVjkD9hrZcnP0WyKDt4d0kZLsKt7geymidFeIs6t8tO79sItn21GXa16ayVQ8Tj6YGD082E7cPGpbUTDOZqma+ohCGuejyrPEmpiE3S0MfxXF0Vg9zfX/s5G+394xz4U0nasdBvx3bqQdQh+FfjF03KUBSWVIYEZtI7xOmTMn2LmXvgEa9EgTI1w0dwrS1FqGi8Tm7kMo6w5/ZDDWwtCXL1BD41N5IEkAruT/KOdMvJ0Bq09DOE2Fy45KVAE5/iDaA/jFO8AMRmBu/DGPqENvHqyJOgF33QzhK4gMQcVyWdwJ6yJAMaUZ+R//vQE51o0hp15jBb--6Ew1EqhwGvVIZOCa--nwQu+e3pCVMFFcbvREqkGw=="
  },
  {
    "path": "test/apps/rails7/config/database.yml",
    "content": "# SQLite. Versions 3.8.0 and up are supported.\n#   gem install sqlite3\n#\n#   Ensure the SQLite 3 gem is defined in your Gemfile\n#   gem \"sqlite3\"\n#\ndefault: &default\n  adapter: sqlite3\n  pool: <%= ENV.fetch(\"RAILS_MAX_THREADS\") { 5 } %>\n  timeout: 5000\n\ndevelopment:\n  <<: *default\n  database: db/development.sqlite3\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  <<: *default\n  database: db/test.sqlite3\n\nproduction:\n  <<: *default\n  database: db/production.sqlite3\n"
  },
  {
    "path": "test/apps/rails7/config/environment.rb",
    "content": "# Load the Rails application.\nrequire_relative \"application\"\n\n# Initialize the Rails application.\nRails.application.initialize!\n"
  },
  {
    "path": "test/apps/rails7/config/environments/development.rb",
    "content": "require \"active_support/core_ext/integer/time\"\n\nRails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # In the development environment your application's code is reloaded any time\n  # it changes. This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.cache_classes = false\n\n  # Do not eager load code on boot.\n  config.eager_load = false\n\n  # Show full error reports.\n  config.consider_all_requests_local = true\n\n  # Enable server timing\n  config.server_timing = true\n\n  # Enable/disable caching. By default caching is disabled.\n  # Run rails dev:cache to toggle caching.\n  if Rails.root.join(\"tmp/caching-dev.txt\").exist?\n    config.action_controller.perform_caching = true\n    config.action_controller.enable_fragment_cache_logging = true\n\n    config.cache_store = :memory_store\n    config.public_file_server.headers = {\n      \"Cache-Control\" => \"public, max-age=#{2.days.to_i}\"\n    }\n  else\n    config.action_controller.perform_caching = false\n\n    config.cache_store = :null_store\n  end\n\n  # Store uploaded files on the local file system (see config/storage.yml for options).\n  config.active_storage.service = :local\n\n  # Don't care if the mailer can't send.\n  config.action_mailer.raise_delivery_errors = false\n\n  config.action_mailer.perform_caching = false\n\n  # Print deprecation notices to the Rails logger.\n  config.active_support.deprecation = :log\n\n  # Raise exceptions for disallowed deprecations.\n  config.active_support.disallowed_deprecation = :raise\n\n  # Tell Active Support which deprecation messages to disallow.\n  config.active_support.disallowed_deprecation_warnings = []\n\n  # Raise an error on page load if there are pending migrations.\n  config.active_record.migration_error = :page_load\n\n  # Highlight code that triggered database queries in logs.\n  config.active_record.verbose_query_logs = true\n\n  # Suppress logger output for asset requests.\n  config.assets.quiet = true\n\n  # Raises error for missing translations.\n  # config.i18n.raise_on_missing_translations = true\n\n  # Annotate rendered view with file names.\n  # config.action_view.annotate_rendered_view_with_filenames = true\n\n  # Uncomment if you wish to allow Action Cable access from any origin.\n  # config.action_cable.disable_request_forgery_protection = true\nend\n"
  },
  {
    "path": "test/apps/rails7/config/environments/production.rb",
    "content": "require \"active_support/core_ext/integer/time\"\n\nRails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Code is not reloaded between requests.\n  config.cache_classes = true\n\n  # Eager load code on boot. This eager loads most of Rails and\n  # your application in memory, allowing both threaded web servers\n  # and those relying on copy on write to perform better.\n  # Rake tasks automatically ignore this option for performance.\n  config.eager_load = true\n\n  # Full error reports are disabled and caching is turned on.\n  config.consider_all_requests_local       = false\n  config.action_controller.perform_caching = true\n\n  # Ensures that a master key has been made available in either ENV[\"RAILS_MASTER_KEY\"]\n  # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).\n  # config.require_master_key = true\n\n  # Disable serving static files from the `/public` folder by default since\n  # Apache or NGINX already handles this.\n  config.public_file_server.enabled = ENV[\"RAILS_SERVE_STATIC_FILES\"].present?\n\n  # Compress CSS using a preprocessor.\n  # config.assets.css_compressor = :sass\n\n  # Do not fallback to assets pipeline if a precompiled asset is missed.\n  config.assets.compile = false\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server.\n  # config.asset_host = \"http://assets.example.com\"\n\n  # Specifies the header that your server uses for sending files.\n  # config.action_dispatch.x_sendfile_header = \"X-Sendfile\" # for Apache\n  # config.action_dispatch.x_sendfile_header = \"X-Accel-Redirect\" # for NGINX\n\n  # Store uploaded files on the local file system (see config/storage.yml for options).\n  config.active_storage.service = :local\n\n  # Mount Action Cable outside main process or domain.\n  # config.action_cable.mount_path = nil\n  # config.action_cable.url = \"wss://example.com/cable\"\n  # config.action_cable.allowed_request_origins = [ \"http://example.com\", /http:\\/\\/example.*/ ]\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  # config.force_ssl = true\n\n  # Include generic and useful information about system operation, but avoid logging too much\n  # information to avoid inadvertent exposure of personally identifiable information (PII).\n  config.log_level = :info\n\n  # Prepend all log lines with the following tags.\n  config.log_tags = [ :request_id ]\n\n  # Use a different cache store in production.\n  # config.cache_store = :mem_cache_store\n\n  # Use a real queuing backend for Active Job (and separate queues per environment).\n  # config.active_job.queue_adapter     = :resque\n  # config.active_job.queue_name_prefix = \"rails7_production\"\n\n  config.action_mailer.perform_caching = false\n\n  # Ignore bad email addresses and do not raise email delivery errors.\n  # Set this to true and configure the email server for immediate delivery to raise delivery errors.\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation cannot be found).\n  config.i18n.fallbacks = true\n\n  # Don't log any deprecations.\n  config.active_support.report_deprecations = false\n\n  # Use default logging formatter so that PID and timestamp are not suppressed.\n  config.log_formatter = ::Logger::Formatter.new\n\n  # Use a different logger for distributed setups.\n  # require \"syslog/logger\"\n  # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new \"app-name\")\n\n  if ENV[\"RAILS_LOG_TO_STDOUT\"].present?\n    logger           = ActiveSupport::Logger.new(STDOUT)\n    logger.formatter = config.log_formatter\n    config.logger    = ActiveSupport::TaggedLogging.new(logger)\n  end\n\n  # Do not dump schema after migrations.\n  config.active_record.dump_schema_after_migration = false\n\n  # Inserts middleware to perform automatic connection switching.\n  # The `database_selector` hash is used to pass options to the DatabaseSelector\n  # middleware. The `delay` is used to determine how long to wait after a write\n  # to send a subsequent read to the primary.\n  #\n  # The `database_resolver` class is used by the middleware to determine which\n  # database is appropriate to use based on the time delay.\n  #\n  # The `database_resolver_context` class is used by the middleware to set\n  # timestamps for the last write to the primary. The resolver uses the context\n  # class timestamps to determine how long to wait before reading from the\n  # replica.\n  #\n  # By default Rails will store a last write timestamp in the session. The\n  # DatabaseSelector middleware is designed as such you can define your own\n  # strategy for connection switching and pass that into the middleware through\n  # these configuration options.\n  # config.active_record.database_selector = { delay: 2.seconds }\n  # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver\n  # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session\n\n  # Inserts middleware to perform automatic shard swapping. The `shard_selector` hash\n  # can be used to pass options to the `ShardSelector` middleware. The `lock` option is\n  # used to determine whether shard swapping should be prohibited for the request.\n  #\n  # The `shard_resolver` option is used by the middleware to determine which shard\n  # to switch to. The application must provide a mechanism for finding the shard name\n  # in a proc. See guides for an example.\n  # config.active_record.shard_selector = { lock: true }\n  # config.active_record.shard_resolver = ->(request) { Tenant.find_by!(host: request.host).shard }\nend\n"
  },
  {
    "path": "test/apps/rails7/config/environments/test.rb",
    "content": "require \"active_support/core_ext/integer/time\"\n\n# The test environment is used exclusively to run your application's\n# test suite. You never need to work with it otherwise. Remember that\n# your test database is \"scratch space\" for the test suite and is wiped\n# and recreated between test runs. Don't rely on the data there!\n\nRails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Turn false under Spring and add config.action_view.cache_template_loading = true\n  config.cache_classes = true\n\n  # Eager loading loads your whole application. When running a single test locally,\n  # this probably isn't necessary. It's a good idea to do in a continuous integration\n  # system, or in some way before deploying your code.\n  config.eager_load = ENV[\"CI\"].present?\n\n  # Configure public file server for tests with Cache-Control for performance.\n  config.public_file_server.enabled = true\n  config.public_file_server.headers = {\n    \"Cache-Control\" => \"public, max-age=#{1.hour.to_i}\"\n  }\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local       = true\n  config.action_controller.perform_caching = false\n  config.cache_store = :null_store\n\n  # Raise exceptions instead of rendering exception templates.\n  config.action_dispatch.show_exceptions = false\n\n  # Disable request forgery protection in test environment.\n  config.action_controller.allow_forgery_protection = false\n\n  # Store uploaded files on the local file system in a temporary directory.\n  config.active_storage.service = :test\n\n  config.action_mailer.perform_caching = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Print deprecation notices to the stderr.\n  config.active_support.deprecation = :stderr\n\n  # Raise exceptions for disallowed deprecations.\n  config.active_support.disallowed_deprecation = :raise\n\n  # Tell Active Support which deprecation messages to disallow.\n  config.active_support.disallowed_deprecation_warnings = []\n\n  # Raises error for missing translations.\n  # config.i18n.raise_on_missing_translations = true\n\n  # Annotate rendered view with file names.\n  # config.action_view.annotate_rendered_view_with_filenames = true\nend\n"
  },
  {
    "path": "test/apps/rails7/config/importmap.rb",
    "content": "# Pin npm packages by running ./bin/importmap\n\npin \"application\", preload: true\npin \"@hotwired/turbo-rails\", to: \"turbo.min.js\", preload: true\npin \"@hotwired/stimulus\", to: \"stimulus.min.js\", preload: true\npin \"@hotwired/stimulus-loading\", to: \"stimulus-loading.js\", preload: true\npin_all_from \"app/javascript/controllers\", under: \"controllers\"\n"
  },
  {
    "path": "test/apps/rails7/config/initializers/assets.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Version of your assets, change this if you want to expire all your assets.\nRails.application.config.assets.version = \"1.0\"\n\n# Add additional assets to the asset load path.\n# Rails.application.config.assets.paths << Emoji.images_path\n\n# Precompile additional assets.\n# application.js, application.css, and all non-JS/CSS in the app/assets\n# folder are already added.\n# Rails.application.config.assets.precompile += %w( admin.js admin.css )\n"
  },
  {
    "path": "test/apps/rails7/config/initializers/content_security_policy.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Define an application-wide content security policy\n# For further information see the following documentation\n# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy\n\n# Rails.application.configure do\n#   config.content_security_policy do |policy|\n#     policy.default_src :self, :https\n#     policy.font_src    :self, :https, :data\n#     policy.img_src     :self, :https, :data\n#     policy.object_src  :none\n#     policy.script_src  :self, :https\n#     policy.style_src   :self, :https\n#     # Specify URI for violation reports\n#     # policy.report_uri \"/csp-violation-report-endpoint\"\n#   end\n#\n#   # Generate session nonces for permitted importmap and inline scripts\n#   config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }\n#   config.content_security_policy_nonce_directives = %w(script-src)\n#\n#   # Report CSP violations to a specified URI. See:\n#   # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only\n#   # config.content_security_policy_report_only = true\n# end\n"
  },
  {
    "path": "test/apps/rails7/config/initializers/filter_parameter_logging.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Configure sensitive parameters which will be filtered from the log file.\nRails.application.config.filter_parameters += [\n  :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn\n]\n"
  },
  {
    "path": "test/apps/rails7/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Inflections\n# are locale specific, and you may define rules for as many different\n# locales as you wish. All of these examples are active by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.plural /^(ox)$/i, \"\\\\1en\"\n#   inflect.singular /^(ox)en/i, \"\\\\1\"\n#   inflect.irregular \"person\", \"people\"\n#   inflect.uncountable %w( fish sheep )\n# end\n\n# These inflection rules are supported but not enabled by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.acronym \"RESTful\"\n# end\n"
  },
  {
    "path": "test/apps/rails7/config/initializers/permissions_policy.rb",
    "content": "# Define an application-wide HTTP permissions policy. For further\n# information see https://developers.google.com/web/updates/2018/06/feature-policy\n#\n# Rails.application.config.permissions_policy do |f|\n#   f.camera      :none\n#   f.gyroscope   :none\n#   f.microphone  :none\n#   f.usb         :none\n#   f.fullscreen  :self\n#   f.payment     :self, \"https://secure.example.com\"\n# end\n"
  },
  {
    "path": "test/apps/rails7/config/initializers/sanitizers.rb",
    "content": "Rails::Html::SafeListSanitizer.allowed_tags = [\"select\", \"a\", \"style\"]\n"
  },
  {
    "path": "test/apps/rails7/config/locales/en.yml",
    "content": "# Files in the config/locales directory are used for internationalization\n# and are automatically loaded by Rails. If you want to use locales other\n# than English, add the necessary files in this directory.\n#\n# To use the locales, use `I18n.t`:\n#\n#     I18n.t \"hello\"\n#\n# In views, this is aliased to just `t`:\n#\n#     <%= t(\"hello\") %>\n#\n# To use a different locale, set it with `I18n.locale`:\n#\n#     I18n.locale = :es\n#\n# This would use the information in config/locales/es.yml.\n#\n# The following keys must be escaped otherwise they will not be retrieved by\n# the default I18n backend:\n#\n# true, false, on, off, yes, no\n#\n# Instead, surround them with single quotes.\n#\n# en:\n#   \"true\": \"foo\"\n#\n# To learn more, please read the Rails Internationalization guide\n# available at https://guides.rubyonrails.org/i18n.html.\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/apps/rails7/config/master.key",
    "content": "f5d5ab5ccde263f03a1221511f868f20"
  },
  {
    "path": "test/apps/rails7/config/puma.rb",
    "content": "# Puma can serve each request in a thread from an internal thread pool.\n# The `threads` method setting takes two numbers: a minimum and maximum.\n# Any libraries that use thread pools should be configured to match\n# the maximum value specified for Puma. Default is set to 5 threads for minimum\n# and maximum; this matches the default thread size of Active Record.\n#\nmax_threads_count = ENV.fetch(\"RAILS_MAX_THREADS\") { 5 }\nmin_threads_count = ENV.fetch(\"RAILS_MIN_THREADS\") { max_threads_count }\nthreads min_threads_count, max_threads_count\n\n# Specifies the `worker_timeout` threshold that Puma will use to wait before\n# terminating a worker in development environments.\n#\nworker_timeout 3600 if ENV.fetch(\"RAILS_ENV\", \"development\") == \"development\"\n\n# Specifies the `port` that Puma will listen on to receive requests; default is 3000.\n#\nport ENV.fetch(\"PORT\") { 3000 }\n\n# Specifies the `environment` that Puma will run in.\n#\nenvironment ENV.fetch(\"RAILS_ENV\") { \"development\" }\n\n# Specifies the `pidfile` that Puma will use.\npidfile ENV.fetch(\"PIDFILE\") { \"tmp/pids/server.pid\" }\n\n# Specifies the number of `workers` to boot in clustered mode.\n# Workers are forked web server processes. If using threads and workers together\n# the concurrency of the application would be max `threads` * `workers`.\n# Workers do not work on JRuby or Windows (both of which do not support\n# processes).\n#\n# workers ENV.fetch(\"WEB_CONCURRENCY\") { 2 }\n\n# Use the `preload_app!` method when specifying a `workers` number.\n# This directive tells Puma to first boot the application and load code\n# before forking the application. This takes advantage of Copy On Write\n# process behavior so workers use less memory.\n#\n# preload_app!\n\n# Allow puma to be restarted by `bin/rails restart` command.\nplugin :tmp_restart\n"
  },
  {
    "path": "test/apps/rails7/config/routes.rb",
    "content": "Rails.application.routes.draw do\n  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html\n\n  # Defines the root path route (\"/\")\n  # root \"articles#index\"\nend\n"
  },
  {
    "path": "test/apps/rails7/config/storage.yml",
    "content": "test:\n  service: Disk\n  root: <%= Rails.root.join(\"tmp/storage\") %>\n\nlocal:\n  service: Disk\n  root: <%= Rails.root.join(\"storage\") %>\n\n# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)\n# amazon:\n#   service: S3\n#   access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>\n#   secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>\n#   region: us-east-1\n#   bucket: your_own_bucket-<%= Rails.env %>\n\n# Remember not to checkin your GCS keyfile to a repository\n# google:\n#   service: GCS\n#   project: your_project\n#   credentials: <%= Rails.root.join(\"path/to/gcs.keyfile\") %>\n#   bucket: your_own_bucket-<%= Rails.env %>\n\n# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)\n# microsoft:\n#   service: AzureStorage\n#   storage_account_name: your_account_name\n#   storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>\n#   container: your_container_name-<%= Rails.env %>\n\n# mirror:\n#   service: Mirror\n#   primary: local\n#   mirrors: [ amazon, google, microsoft ]\n"
  },
  {
    "path": "test/apps/rails7/lib/assets/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails7/lib/some_lib.rb",
    "content": "class SomeLib\n  def some_rsa_encrypting\n    public_key = OpenSSL::PKey::RSA.new(\"grab the public 4096 bit key\")\n    encrypted = Base64.encode64(public_key.public_encrypt(payload.to_json)) # Weak padding mode default\n    public_key.private_decrypt(Base64.decode64(encrypted)) # Weak padding mode default\n  end\n\n  def some_more_rsa_padding_modes\n    public_key = OpenSSL::PKey::RSA.new(\"grab the public 4096 bit key\")\n    public_key.public_decrypt(data, OpenSSL::PKey::RSA::PKCS1_PADDING)\n    public_key.private_encrypt(data, OpenSSL::PKey::RSA::NO_PADDING)\n    public_key.private_encrypt(data, OpenSSL::PKey::RSA::SSLV23_PADDING)\n  end\n\n  def small_rsa_keys \n    OpenSSL::PKey::RSA.generate(512) # Very weak\n    OpenSSL::PKey::RSA.new(1024) # Weak\n    OpenSSL::PKey::RSA.new(2048) # Okay\n  end\n\n  def pky_api\n    weak_rsa = OpenSSL::PKey.generate_key(\"rsa\", rsa_keygen_bits: 1024) # Medium warning about key size\n    weak_encrypted = weak_rsa.encrypt(\"data\", \"rsa_padding_mode\" => \"pkcs1\")\n    weak_encrypted = weak_rsa.decrypt(\"data\", \"rsa_padding_mode\" => \"oaep\")\n    weak_signature_digest = weak_rsa.sign(\"SHA256\", \"data\", rsa_padding_mode: \"PKCS1\")\n    weak_rsa.verify(\"SHA256\", \"data\", rsa_padding_mode: \"none\")\n    weak_rsa.sign_raw(nil, \"data\", rsa_padding_mode: \"none\")\n    weak_rsa.verify_raw(nil, \"data\", rsa_padding_mode: \"none\")\n    weak_rsa.encrypt(\"data\") # default is also pkcs1\n  end\n\n  class << self\n    def self.x\n      # why\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails7/lib/tasks/.keep",
    "content": ""
  },
  {
    "path": "test/apps/rails8/Gemfile",
    "content": "source \"https://rubygems.org\"\n\n# Use main development branch of Rails\ngem \"rails\", \"~> 8.0.0.alpha\", github: \"rails/rails\", branch: \"main\"\n# The modern asset pipeline for Rails [https://github.com/rails/propshaft]\ngem \"propshaft\"\n# Use sqlite3 as the database for Active Record\ngem \"sqlite3\", \">= 1.4\"\n# Use the Puma web server [https://github.com/puma/puma]\ngem \"puma\", \">= 5.0\"\n# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]\ngem \"importmap-rails\"\n# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]\ngem \"turbo-rails\"\n# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]\ngem \"stimulus-rails\"\n# Build JSON APIs with ease [https://github.com/rails/jbuilder]\ngem \"jbuilder\"\n# Use Redis adapter to run Action Cable in production\ngem \"redis\", \">= 4.0.1\"\n\n# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis]\n# gem \"kredis\"\n\n# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]\n# gem \"bcrypt\", \"~> 3.1.7\"\n\n# Windows does not include zoneinfo files, so bundle the tzinfo-data gem\ngem \"tzinfo-data\", platforms: %i[ windows jruby ]\n\n# Reduces boot times through caching; required in config/boot.rb\ngem \"bootsnap\", require: false\n\n# Deploy this application anywhere as a Docker container [https://kamal-deploy.org]\n# gem \"kamal\", require: false\n\n# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]\n# gem \"image_processing\", \"~> 1.2\"\n\ngroup :development, :test do\n  # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem\n  gem \"debug\", platforms: %i[ mri windows ], require: \"debug/prelude\"\n\n  # Static analysis for security vulnerabilities [https://brakemanscanner.org/]\n  # gem \"brakeman\", require: false\n\n  # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/]\n  # gem \"rubocop-rails-omakase\", require: false\nend\n\ngroup :development do\n  # Use console on exceptions pages [https://github.com/rails/web-console]\n  gem \"web-console\"\nend\n"
  },
  {
    "path": "test/apps/rails8/app/assets/stylesheets/application.css",
    "content": "/* Application styles */\n"
  },
  {
    "path": "test/apps/rails8/app/channels/application_cable/channel.rb",
    "content": "module ApplicationCable\n  class Channel < ActionCable::Channel::Base\n  end\nend\n"
  },
  {
    "path": "test/apps/rails8/app/channels/application_cable/connection.rb",
    "content": "module ApplicationCable\n  class Connection < ActionCable::Connection::Base\n  end\nend\n"
  },
  {
    "path": "test/apps/rails8/app/controllers/application_controller.rb",
    "content": "class ApplicationController < ActionController::Base\n  # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.\n  allow_browser versions: :modern\n\n  def deserialize_it\n    Marshal.load(something) # Always warns\n  end\nend\n"
  },
  {
    "path": "test/apps/rails8/app/controllers/users_controller.rb",
    "content": "class UsersController < ApplicationController\n  before_action :set_user, only: %i[ show edit update destroy ]\n\n  # GET /users or /users.json\n  def index\n    @users = User.all\n  end\n\n  # GET /users/1 or /users/1.json\n  def show\n  end\n\n  # GET /users/new\n  def new\n    @user = User.new\n  end\n\n  # GET /users/1/edit\n  def edit\n  end\n\n  # POST /users or /users.json\n  def create\n    @user = User.new(user_params)\n\n    respond_to do |format|\n      if @user.save\n        format.html { redirect_to user_url(@user), notice: \"User was successfully created.\" }\n        format.json { render :show, status: :created, location: @user }\n      else\n        format.html { render :new, status: :unprocessable_entity }\n        format.json { render json: @user.errors, status: :unprocessable_entity }\n      end\n    end\n  end\n\n  # PATCH/PUT /users/1 or /users/1.json\n  def update\n    respond_to do |format|\n      if @user.update(user_params)\n        format.html { redirect_to user_url(@user), notice: \"User was successfully updated.\" }\n        format.json { render :show, status: :ok, location: @user }\n      else\n        format.html { render :edit, status: :unprocessable_entity }\n        format.json { render json: @user.errors, status: :unprocessable_entity }\n      end\n    end\n  end\n\n  # DELETE /users/1 or /users/1.json\n  def destroy\n    @user.destroy!\n\n    respond_to do |format|\n      format.html { redirect_to users_url, notice: \"User was successfully destroyed.\" }\n      format.json { head :no_content }\n    end\n  end\n\n  def things\n    @things = Thing.all\n    render 'things/index'\n  end\n\n  def permit_or\n    if params[:foo_uid].present?\n      field_name = :foo_uid\n      query = params.permit(:foo_uid)\n    elsif params[:model_id].present?\n      field_name = :model_id\n      query = { id: params.require(:model_id) }\n    end\n\n    Thing.find_by(query) if field_name\n  end\n\n  def stats_count\n     $stats.count(\"thing.#{variable}\", :tags => ({ :cool => \"stuff\" }))\n     not_ar_model.count(\"something - #{params[:x]}\")\n  end\n\n  private\n    # Use callbacks to share common setup or constraints between actions.\n    def set_user\n      @user = User.find(params[:id])\n    end\n\n    # Only allow a list of trusted parameters through.\n    def user_params\n      params.require(:user).permit(:name)\n    end\nend\n"
  },
  {
    "path": "test/apps/rails8/app/helpers/application_helper.rb",
    "content": "module ApplicationHelper\nend\n"
  },
  {
    "path": "test/apps/rails8/app/helpers/users_helper.rb",
    "content": "module UsersHelper\nend\n"
  },
  {
    "path": "test/apps/rails8/app/javascript/application.js",
    "content": "// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails\nimport \"@hotwired/turbo-rails\"\nimport \"controllers\"\n"
  },
  {
    "path": "test/apps/rails8/app/javascript/controllers/application.js",
    "content": "import { Application } from \"@hotwired/stimulus\"\n\nconst application = Application.start()\n\n// Configure Stimulus development experience\napplication.debug = false\nwindow.Stimulus   = application\n\nexport { application }\n"
  },
  {
    "path": "test/apps/rails8/app/javascript/controllers/hello_controller.js",
    "content": "import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n  connect() {\n    this.element.textContent = \"Hello World!\"\n  }\n}\n"
  },
  {
    "path": "test/apps/rails8/app/javascript/controllers/index.js",
    "content": "// Import and register all your controllers from the importmap under controllers/*\n\nimport { application } from \"controllers/application\"\n\n// Eager load all controllers defined in the import map under controllers/**/*_controller\nimport { eagerLoadControllersFrom } from \"@hotwired/stimulus-loading\"\neagerLoadControllersFrom(\"controllers\", application)\n\n// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)\n// import { lazyLoadControllersFrom } from \"@hotwired/stimulus-loading\"\n// lazyLoadControllersFrom(\"controllers\", application)\n"
  },
  {
    "path": "test/apps/rails8/app/jobs/application_job.rb",
    "content": "class ApplicationJob < ActiveJob::Base\n  # Automatically retry jobs that encountered a deadlock\n  # retry_on ActiveRecord::Deadlocked\n\n  # Most jobs are safe to ignore if the underlying records are no longer available\n  # discard_on ActiveJob::DeserializationError\nend\n"
  },
  {
    "path": "test/apps/rails8/app/mailers/application_mailer.rb",
    "content": "class ApplicationMailer < ActionMailer::Base\n  default from: \"from@example.com\"\n  layout \"mailer\"\nend\n"
  },
  {
    "path": "test/apps/rails8/app/models/application_record.rb",
    "content": "class ApplicationRecord < ActiveRecord::Base\n  primary_abstract_class\nend\n"
  },
  {
    "path": "test/apps/rails8/app/models/thing.rb",
    "content": "class Thing < ApplicationRecord\nend\n"
  },
  {
    "path": "test/apps/rails8/app/models/user.rb",
    "content": "class User < ApplicationRecord\nend\n"
  },
  {
    "path": "test/apps/rails8/app/views/layouts/application.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title><%= content_for(:title) || \"My App\" %></title>\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n    <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n    <%= csrf_meta_tags %>\n    <%= csp_meta_tag %>\n\n    <%= yield :head %>\n\n    <link rel=\"manifest\" href=\"/manifest.json\">\n    <link rel=\"icon\" href=\"/icon.png\" type=\"image/png\">\n    <link rel=\"icon\" href=\"/icon.svg\" type=\"image/svg+xml\">\n    <link rel=\"apple-touch-icon\" href=\"/icon.png\">\n    \n    <%= stylesheet_link_tag :all, \"data-turbo-track\": \"reload\" %>\n    <%= javascript_importmap_tags %>\n  </head>\n\n  <body>\n    <%= yield %>\n  </body>\n</html>\n"
  },
  {
    "path": "test/apps/rails8/app/views/layouts/mailer.html.erb",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n    <style>\n      /* Email styles need to be inline */\n    </style>\n  </head>\n\n  <body>\n    <%= yield %>\n  </body>\n</html>\n"
  },
  {
    "path": "test/apps/rails8/app/views/layouts/mailer.text.erb",
    "content": "<%= yield %>\n"
  },
  {
    "path": "test/apps/rails8/app/views/pwa/manifest.json.erb",
    "content": "{\n  \"name\": \"MyApp\",\n  \"icons\": [\n    {\n      \"src\": \"/icon.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"512x512\"\n    },\n    {\n      \"src\": \"/icon.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"512x512\",\n      \"purpose\": \"maskable\"\n    }\n  ],\n  \"start_url\": \"/\",\n  \"display\": \"standalone\",\n  \"scope\": \"/\",\n  \"description\": \"MyApp.\",\n  \"theme_color\": \"red\",\n  \"background_color\": \"red\"\n}\n"
  },
  {
    "path": "test/apps/rails8/app/views/pwa/service-worker.js",
    "content": "// Add a service worker for processing Web Push notifications:\n//\n// self.addEventListener(\"push\", async (event) => {\n//   const { title, options } = await event.data.json()\n//   event.waitUntil(self.registration.showNotification(title, options))\n// })\n// \n// self.addEventListener(\"notificationclick\", function(event) {\n//   event.notification.close()\n//   event.waitUntil(\n//     clients.matchAll({ type: \"window\" }).then((clientList) => {\n//       for (let i = 0; i < clientList.length; i++) {\n//         let client = clientList[i]\n//         let clientPath = (new URL(client.url)).pathname\n// \n//         if (clientPath == event.notification.data.path && \"focus\" in client) {\n//           return client.focus()\n//         }\n//       }\n// \n//       if (clients.openWindow) {\n//         return clients.openWindow(event.notification.data.path)\n//       }\n//     })\n//   )\n// })\n"
  },
  {
    "path": "test/apps/rails8/app/views/things/_thing.html.erb",
    "content": "<%= raw thing.name %>\n"
  },
  {
    "path": "test/apps/rails8/app/views/things/index.html.erb",
    "content": "<%= render @things %>\n"
  },
  {
    "path": "test/apps/rails8/app/views/users/_form.html.erb",
    "content": "<%= form_with(model: user) do |form| %>\n  <% if user.errors.any? %>\n    <div style=\"color: red\">\n      <h2><%= pluralize(user.errors.count, \"error\") %> prohibited this user from being saved:</h2>\n\n      <ul>\n        <% user.errors.each do |error| %>\n          <li><%= error.full_message %></li>\n        <% end %>\n      </ul>\n    </div>\n  <% end %>\n\n  <div>\n    <%= form.label :name, style: \"display: block\" %>\n    <%= form.text_field :name %>\n  </div>\n\n  <div>\n    <%= form.submit %>\n  </div>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails8/app/views/users/_user.html.erb",
    "content": "<div id=\"<%= dom_id user %>\">\n  <p>\n    <strong>Name:</strong>\n    <%= user.name.html_safe %>\n  </p>\n\n</div>\n"
  },
  {
    "path": "test/apps/rails8/app/views/users/_user.json.jbuilder",
    "content": "json.extract! user, :id, :name, :created_at, :updated_at\njson.url user_url(user, format: :json)\n"
  },
  {
    "path": "test/apps/rails8/app/views/users/dom_id.haml",
    "content": ".test-list__item{ id: dom_id(User.first) }\n"
  },
  {
    "path": "test/apps/rails8/app/views/users/edit.html.erb",
    "content": "<% content_for :title, \"Editing user\" %>\n\n<h1>Editing user</h1>\n\n<%= render \"form\", user: @user %>\n\n<br>\n\n<div>\n  <%= link_to \"Show this user\", @user %> |\n  <%= link_to \"Back to users\", users_path %>\n</div>\n"
  },
  {
    "path": "test/apps/rails8/app/views/users/index.html.erb",
    "content": "<p style=\"color: green\"><%= notice %></p>\n\n<% content_for :title, \"Users\" %>\n\n<h1>Users</h1>\n\n<div id=\"users\">\n  <% @users.each do |user| %>\n    <%= render user %>\n    <p>\n      <%= link_to \"Show this user\", user %>\n    </p>\n  <% end %>\n</div>\n\n<%= link_to \"New user\", new_user_path %>\n"
  },
  {
    "path": "test/apps/rails8/app/views/users/index.json.jbuilder",
    "content": "json.array! @users, partial: \"users/user\", as: :user\n"
  },
  {
    "path": "test/apps/rails8/app/views/users/new.html.erb",
    "content": "<% content_for :title, \"New user\" %>\n\n<h1>New user</h1>\n\n<%= render \"form\", user: @user %>\n\n<br>\n\n<div>\n  <%= link_to \"Back to users\", users_path %>\n</div>\n"
  },
  {
    "path": "test/apps/rails8/app/views/users/show.html.erb",
    "content": "<p style=\"color: green\"><%= notice %></p>\n\n<%= render @user %>\n\n<div>\n  <%= link_to \"Edit this user\", edit_user_path(@user) %> |\n  <%= link_to \"Back to users\", users_path %>\n\n  <%= button_to \"Destroy this user\", @user, method: :delete %>\n</div>\n"
  },
  {
    "path": "test/apps/rails8/app/views/users/show.json.jbuilder",
    "content": "json.partial! \"users/user\", user: @user\n"
  },
  {
    "path": "test/apps/rails8/bin/brakeman",
    "content": "#!/usr/bin/env ruby\nrequire \"rubygems\"\nrequire \"bundler/setup\"\n\nARGV.unshift(\"--ensure-latest\")\n\nload Gem.bin_path(\"brakeman\", \"brakeman\")\n"
  },
  {
    "path": "test/apps/rails8/bin/importmap",
    "content": "#!/usr/bin/env ruby\n\nrequire_relative \"../config/application\"\nrequire \"importmap/commands\"\n"
  },
  {
    "path": "test/apps/rails8/bin/kamal",
    "content": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\n#\n# This file was generated by Bundler.\n#\n# The application 'kamal' is installed as part of a gem, and\n# this file is here to facilitate running it.\n#\n\nENV[\"BUNDLE_GEMFILE\"] ||= File.expand_path(\"../Gemfile\", __dir__)\n\nbundle_binstub = File.expand_path(\"bundle\", __dir__)\n\nif File.file?(bundle_binstub)\n  if File.read(bundle_binstub, 300).include?(\"This file was generated by Bundler\")\n    load(bundle_binstub)\n  else\n    abort(\"Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.\nReplace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.\")\n  end\nend\n\nrequire \"rubygems\"\nrequire \"bundler/setup\"\n\nload Gem.bin_path(\"kamal\", \"kamal\")\n"
  },
  {
    "path": "test/apps/rails8/bin/rails",
    "content": "#!/usr/bin/env ruby\nAPP_PATH = File.expand_path(\"../config/application\", __dir__)\nrequire_relative \"../config/boot\"\nrequire \"rails/commands\"\n"
  },
  {
    "path": "test/apps/rails8/bin/rake",
    "content": "#!/usr/bin/env ruby\nrequire_relative \"../config/boot\"\nrequire \"rake\"\nRake.application.run\n"
  },
  {
    "path": "test/apps/rails8/bin/rubocop",
    "content": "#!/usr/bin/env ruby\nrequire \"rubygems\"\nrequire \"bundler/setup\"\n\n# explicit rubocop config increases performance slightly while avoiding config confusion.\nARGV.unshift(\"--config\", File.expand_path(\"../.rubocop.yml\", __dir__))\n\nload Gem.bin_path(\"rubocop\", \"rubocop\")\n"
  },
  {
    "path": "test/apps/rails8/bin/setup",
    "content": "#!/usr/bin/env ruby\nrequire \"fileutils\"\n\nAPP_ROOT = File.expand_path(\"..\", __dir__)\nAPP_NAME = \"my_app\"\n\ndef system!(*args)\n  system(*args, exception: true)\nend\n\nFileUtils.chdir APP_ROOT do\n  # This script is a way to set up or update your development environment automatically.\n  # This script is idempotent, so that you can run it at any time and get an expectable outcome.\n  # Add necessary setup steps to this file.\n\n  puts \"== Installing dependencies ==\"\n  system! \"gem install bundler --conservative\"\n  system(\"bundle check\") || system!(\"bundle install\")\n\n  # puts \"\\n== Copying sample files ==\"\n  # unless File.exist?(\"config/database.yml\")\n  #   FileUtils.cp \"config/database.yml.sample\", \"config/database.yml\"\n  # end\n\n  puts \"\\n== Preparing database ==\"\n  system! \"bin/rails db:prepare\"\n\n  puts \"\\n== Removing old logs and tempfiles ==\"\n  system! \"bin/rails log:clear tmp:clear\"\n\n  puts \"\\n== Restarting application server ==\"\n  system! \"bin/rails restart\"\n\n  # puts \"\\n== Configuring puma-dev ==\"\n  # system \"ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}\"\n  # system \"curl -Is https://#{APP_NAME}.test/up | head -n 1\"\nend\n"
  },
  {
    "path": "test/apps/rails8/config/application.rb",
    "content": "require_relative \"boot\"\n\nrequire \"rails\"\n# Pick the frameworks you want:\nrequire \"active_model/railtie\"\nrequire \"active_job/railtie\"\nrequire \"active_record/railtie\"\nrequire \"active_storage/engine\"\nrequire \"action_controller/railtie\"\nrequire \"action_mailer/railtie\"\nrequire \"action_mailbox/engine\"\nrequire \"action_text/engine\"\nrequire \"action_view/railtie\"\nrequire \"action_cable/engine\"\n# require \"rails/test_unit/railtie\"\n\n# Require the gems listed in Gemfile, including any gems\n# you've limited to :test, :development, or :production.\nBundler.require(*Rails.groups)\n\nclass MyApp::Application < Rails::Application\n  # Initialize configuration defaults for originally generated Rails version.\n  config.load_defaults 8.0\n\n  # Please, add to the `ignore` list any other `lib` subdirectories that do\n  # not contain `.rb` files, or that should not be reloaded or eager loaded.\n  # Common ones are `templates`, `generators`, or `middleware`, for example.\n  config.autoload_lib(ignore: %w[assets tasks])\n\n  # Configuration for the application, engines, and railties goes here.\n  #\n  # These settings can be overridden in specific environments using the files\n  # in config/environments, which are processed later.\n  #\n  # config.time_zone = \"Central Time (US & Canada)\"\n  # config.eager_load_paths << Rails.root.join(\"extras\")\n\n  # Don't generate system test files.\n  config.generators.system_tests = nil\nend\n"
  },
  {
    "path": "test/apps/rails8/config/boot.rb",
    "content": "ENV[\"BUNDLE_GEMFILE\"] ||= File.expand_path(\"../Gemfile\", __dir__)\n\nrequire \"bundler/setup\" # Set up gems listed in the Gemfile.\nrequire \"bootsnap/setup\" # Speed up boot time by caching expensive operations.\n"
  },
  {
    "path": "test/apps/rails8/config/cable.yml",
    "content": "development:\n  adapter: redis\n  url: redis://localhost:6379/1\n\ntest:\n  adapter: test\n\nproduction:\n  adapter: redis\n  url: <%= ENV.fetch(\"REDIS_URL\") { \"redis://localhost:6379/1\" } %>\n  channel_prefix: my_app_production\n"
  },
  {
    "path": "test/apps/rails8/config/credentials.yml.enc",
    "content": "H0D18ePAXpz1hsN50y92mMXWQ81JfVpO4AcGAmekoYyhfKnV/axh7XnWCbo2QfCsMqtA3RFlexqE458SmUhC0ELC4XUKbf4YLlDZU3hPdC4AszUkqdZO5XxI9UT2HD77577sYni+mbbxpOESqMr7IvTDEpYvre79afPFBbodif8WEm6fQJVvQT4QbYsuRa1Rxg+cvtBh0MZZEhR2qGahs8b0xyHqKtRBts8NtDaDkM8E9nDQ6rm6/D6/cc9kLmLSvOrlLZcEZc999CyZKOwZKVETuGGGLWeGSL3XBMZdSI7lopzwHBSXn4bcYfuUI1gysxaIBu8X0Z/3Fb/dAIj3gpyE9kVJkC+82EoPpfrrNTu4W2FYxGX7PG+l58BBcpGCpzUOrW3KlOA6pPtwb9swkwlFunxl--I/0189+5PL944kTB--etq+j498EBV5ynkttoEY3w=="
  },
  {
    "path": "test/apps/rails8/config/database.yml",
    "content": "# SQLite. Versions 3.8.0 and up are supported.\n#   gem install sqlite3\n#\n#   Ensure the SQLite 3 gem is defined in your Gemfile\n#   gem \"sqlite3\"\n#\ndefault: &default\n  adapter: sqlite3\n  pool: <%= ENV.fetch(\"RAILS_MAX_THREADS\") { 5 } %>\n  timeout: 5000\n\ndevelopment:\n  <<: *default\n  database: storage/development.sqlite3\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  <<: *default\n  database: storage/test.sqlite3\n\n\n# Store production database in the storage/ directory, which by default\n# is mounted as a persistent Docker volume in config/deploy.yml.\nproduction:\n  <<: *default\n  database: storage/production.sqlite3\n"
  },
  {
    "path": "test/apps/rails8/config/deploy.yml",
    "content": "# Name of your application. Used to uniquely configure containers.\nservice: my_app\n\n# Name of the container image.\nimage: your-user/my_app\n\n# Deploy to these servers.\nservers:\n  web:\n    - 192.168.0.1\n  # job:\n  #   hosts:\n  #     - 192.168.0.1\n  #   cmd: bin/solid_queue work\n\n# Credentials for your image host.\nregistry:\n  # Specify the registry server, if you're not using Docker Hub\n  # server: registry.digitalocean.com / ghcr.io / ...\n  username: your-user\n\n  # Always use an access token rather than real password when possible.\n  password:\n    - KAMAL_REGISTRY_PASSWORD\n\n# Inject ENV variables into containers (secrets come from .env).\n# Remember to run `kamal env push` after making changes!\nenv:\n  secret:\n    - RAILS_MASTER_KEY\n  # clear:\n  #   DB_HOST: 192.168.0.2\n\n\n# Use a persistent storage volume for sqlite database files and local Active Storage files.\n# Recommended to change this to a mounted volume path that is backed up off server.\nvolumes:\n  - \"my_app_storage:/rails/storage\"\n\n\n# Bridge fingerprinted assets, like JS and CSS, between versions to avoid\n# hitting 404 on in-flight requests. Combines all files from new and old\n# version inside the asset_path.\nasset_path: /rails/public/assets\n\n# Use a different ssh user than root\n# ssh:\n#   user: app\n\n# Configure builder setup (defaults to multi-arch images).\n# builder:\n#   # Build same-arch image locally (use for x86->x86)\n#   multiarch: false\n#\n#   # Build diff-arch image via remote server\n#   remote:\n#     arch: amd64\n#     host: ssh://app@192.168.0.1\n#\n#   args:\n#     RUBY_VERSION: ruby-3.3.0\n#   secrets:\n#     - GITHUB_TOKEN\n#     - RAILS_MASTER_KEY\n\n# Use accessory services (secrets come from .env).\n# accessories:\n#   db:\n#     image: mysql:8.0\n#     host: 192.168.0.2\n#     port: 3306\n#     env:\n#       clear:\n#         MYSQL_ROOT_HOST: '%'\n#       secret:\n#         - MYSQL_ROOT_PASSWORD\n#     files:\n#       - config/mysql/production.cnf:/etc/mysql/my.cnf\n#       - db/production.sql:/docker-entrypoint-initdb.d/setup.sql\n#     directories:\n#       - data:/var/lib/mysql\n#   redis:\n#     image: redis:7.0\n#     host: 192.168.0.2\n#     port: 6379\n#     directories:\n#       - data:/data\n"
  },
  {
    "path": "test/apps/rails8/config/environment.rb",
    "content": "# Load the Rails application.\nrequire_relative \"application\"\n\n# Initialize the Rails application.\nRails.application.initialize!\n"
  },
  {
    "path": "test/apps/rails8/config/environments/development.rb",
    "content": "require \"active_support/core_ext/integer/time\"\n\nRails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # In the development environment your application's code is reloaded any time\n  # it changes. This slows down response time but is perfect for development\n  # since you don't have to restart the web server when you make code changes.\n  config.enable_reloading = true\n\n  # Do not eager load code on boot.\n  config.eager_load = false\n\n  # Show full error reports.\n  config.consider_all_requests_local = true\n\n  # Enable server timing.\n  config.server_timing = true\n\n  # Enable/disable caching. By default caching is disabled.\n  # Run rails dev:cache to toggle caching.\n  if Rails.root.join(\"tmp/caching-dev.txt\").exist?\n    config.action_controller.perform_caching = true\n    config.action_controller.enable_fragment_cache_logging = true\n\n    config.cache_store = :memory_store\n    config.public_file_server.headers = { \"Cache-Control\" => \"public, max-age=#{2.days.to_i}\" }\n  else\n    config.action_controller.perform_caching = false\n\n    config.cache_store = :null_store\n  end\n\n  # Store uploaded files on the local file system (see config/storage.yml for options).\n  config.active_storage.service = :local\n\n  # Don't care if the mailer can't send.\n  config.action_mailer.raise_delivery_errors = false\n\n  config.action_mailer.perform_caching = false\n\n  config.action_mailer.default_url_options = { host: \"localhost\", port: 3000 }\n\n  # Print deprecation notices to the Rails logger.\n  config.active_support.deprecation = :log\n\n  # Raise an error on page load if there are pending migrations.\n  config.active_record.migration_error = :page_load\n\n  # Highlight code that triggered database queries in logs.\n  config.active_record.verbose_query_logs = true\n\n  # Highlight code that enqueued background job in logs.\n  config.active_job.verbose_enqueue_logs = true\n\n\n  # Raises error for missing translations.\n  # config.i18n.raise_on_missing_translations = true\n\n  # Annotate rendered view with file names.\n  config.action_view.annotate_rendered_view_with_filenames = true\n\n  # Uncomment if you wish to allow Action Cable access from any origin.\n  # config.action_cable.disable_request_forgery_protection = true\n\n  # Raise error when a before_action's only/except options reference missing actions.\n  config.action_controller.raise_on_missing_callback_actions = true\n\n  # Apply autocorrection by RuboCop to files generated by `bin/rails generate`.\n  # config.generators.apply_rubocop_autocorrect_after_generate!\nend\n"
  },
  {
    "path": "test/apps/rails8/config/environments/production.rb",
    "content": "require \"active_support/core_ext/integer/time\"\n\nRails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # Code is not reloaded between requests.\n  config.enable_reloading = false\n\n  # Eager load code on boot. This eager loads most of Rails and\n  # your application in memory, allowing both threaded web servers\n  # and those relying on copy on write to perform better.\n  # Rake tasks automatically ignore this option for performance.\n  config.eager_load = true\n\n  # Full error reports are disabled and caching is turned on.\n  config.consider_all_requests_local = false\n  config.action_controller.perform_caching = true\n\n  # Ensures that a master key has been made available in ENV[\"RAILS_MASTER_KEY\"], config/master.key, or an environment\n  # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files).\n  # config.require_master_key = true\n\n  # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead.\n  # config.public_file_server.enabled = false\n\n  # Enable serving of images, stylesheets, and JavaScripts from an asset server.\n  # config.asset_host = \"http://assets.example.com\"\n\n  # Specifies the header that your server uses for sending files.\n  # config.action_dispatch.x_sendfile_header = \"X-Sendfile\" # for Apache\n  # config.action_dispatch.x_sendfile_header = \"X-Accel-Redirect\" # for NGINX\n\n  # Store uploaded files on the local file system (see config/storage.yml for options).\n  config.active_storage.service = :local\n\n  # Mount Action Cable outside main process or domain.\n  # config.action_cable.mount_path = nil\n  # config.action_cable.url = \"wss://example.com/cable\"\n  # config.action_cable.allowed_request_origins = [ \"http://example.com\", /http:\\/\\/example.*/ ]\n\n  # Assume all access to the app is happening through a SSL-terminating reverse proxy.\n  # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies.\n  # config.assume_ssl = true\n\n  # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.\n  config.force_ssl = true\n  \n  # Skip http-to-https redirect for the default health check endpoint.\n  # config.ssl_options = { redirect: { exclude: ->(request) { request.path == \"/up\" } } }\n\n  # Log to STDOUT by default\n  config.logger = ActiveSupport::Logger.new(STDOUT)\n    .tap  { |logger| logger.formatter = ::Logger::Formatter.new }\n    .then { |logger| ActiveSupport::TaggedLogging.new(logger) }\n\n  # Prepend all log lines with the following tags.\n  config.log_tags = [ :request_id ]\n\n  # \"info\" includes generic and useful information about system operation, but avoids logging too much\n  # information to avoid inadvertent exposure of personally identifiable information (PII). If you\n  # want to log everything, set the level to \"debug\".\n  config.log_level = ENV.fetch(\"RAILS_LOG_LEVEL\", \"info\")\n\n  # Use a different cache store in production.\n  # config.cache_store = :mem_cache_store\n\n  # Use a real queuing backend for Active Job (and separate queues per environment).\n  # config.active_job.queue_adapter = :resque\n  # config.active_job.queue_name_prefix = \"my_app_production\"\n\n  config.action_mailer.perform_caching = false\n\n  # Ignore bad email addresses and do not raise email delivery errors.\n  # Set this to true and configure the email server for immediate delivery to raise delivery errors.\n  # config.action_mailer.raise_delivery_errors = false\n\n  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to\n  # the I18n.default_locale when a translation cannot be found).\n  config.i18n.fallbacks = true\n\n  # Don't log any deprecations.\n  config.active_support.report_deprecations = false\n\n  # Do not dump schema after migrations.\n  config.active_record.dump_schema_after_migration = false\n\n  # Enable DNS rebinding protection and other `Host` header attacks.\n  # config.hosts = [\n  #   \"example.com\",     # Allow requests from example.com\n  #   /.*\\.example\\.com/ # Allow requests from subdomains like `www.example.com`\n  # ]\n  # Skip DNS rebinding protection for the default health check endpoint.\n  # config.host_authorization = { exclude: ->(request) { request.path == \"/up\" } }\nend\n"
  },
  {
    "path": "test/apps/rails8/config/environments/test.rb",
    "content": "require \"active_support/core_ext/integer/time\"\n\n# The test environment is used exclusively to run your application's\n# test suite. You never need to work with it otherwise. Remember that\n# your test database is \"scratch space\" for the test suite and is wiped\n# and recreated between test runs. Don't rely on the data there!\n\nRails.application.configure do\n  # Settings specified here will take precedence over those in config/application.rb.\n\n  # While tests run files are not watched, reloading is not necessary.\n  config.enable_reloading = false\n\n  # Eager loading loads your entire application. When running a single test locally,\n  # this is usually not necessary, and can slow down your test suite. However, it's\n  # recommended that you enable it in continuous integration systems to ensure eager\n  # loading is working properly before deploying your code.\n  config.eager_load = ENV[\"CI\"].present?\n\n  # Configure public file server for tests with Cache-Control for performance.\n  config.public_file_server.headers = { \"Cache-Control\" => \"public, max-age=#{1.hour.to_i}\" }\n\n  # Show full error reports and disable caching.\n  config.consider_all_requests_local = true\n  config.action_controller.perform_caching = false\n  config.cache_store = :null_store\n\n  # Render exception templates for rescuable exceptions and raise for other exceptions.\n  config.action_dispatch.show_exceptions = :rescuable\n\n  # Disable request forgery protection in test environment.\n  config.action_controller.allow_forgery_protection = false\n\n  # Store uploaded files on the local file system in a temporary directory.\n  config.active_storage.service = :test\n\n  config.action_mailer.perform_caching = false\n\n  # Tell Action Mailer not to deliver emails to the real world.\n  # The :test delivery method accumulates sent emails in the\n  # ActionMailer::Base.deliveries array.\n  config.action_mailer.delivery_method = :test\n\n  # Unlike controllers, the mailer instance doesn't have any context about the\n  # incoming request so you'll need to provide the :host parameter yourself.\n  config.action_mailer.default_url_options = { host: \"www.example.com\" }\n\n  # Print deprecation notices to the stderr.\n  config.active_support.deprecation = :stderr\n\n  # Raises error for missing translations.\n  # config.i18n.raise_on_missing_translations = true\n\n  # Annotate rendered view with file names.\n  # config.action_view.annotate_rendered_view_with_filenames = true\n\n  # Raise error when a before_action's only/except options reference missing actions.\n  config.action_controller.raise_on_missing_callback_actions = true\nend\n"
  },
  {
    "path": "test/apps/rails8/config/importmap.rb",
    "content": "# Pin npm packages by running ./bin/importmap\n\npin \"application\"\npin \"@hotwired/turbo-rails\", to: \"turbo.min.js\"\npin \"@hotwired/stimulus\", to: \"stimulus.min.js\"\npin \"@hotwired/stimulus-loading\", to: \"stimulus-loading.js\"\npin_all_from \"app/javascript/controllers\", under: \"controllers\"\n"
  },
  {
    "path": "test/apps/rails8/config/initializers/assets.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Version of your assets, change this if you want to expire all your assets.\nRails.application.config.assets.version = \"1.0\"\n\n# Add additional assets to the asset load path.\n# Rails.application.config.assets.paths << Emoji.images_path\n"
  },
  {
    "path": "test/apps/rails8/config/initializers/content_security_policy.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Define an application-wide content security policy.\n# See the Securing Rails Applications Guide for more information:\n# https://guides.rubyonrails.org/security.html#content-security-policy-header\n\n# Rails.application.configure do\n#   config.content_security_policy do |policy|\n#     policy.default_src :self, :https\n#     policy.font_src    :self, :https, :data\n#     policy.img_src     :self, :https, :data\n#     policy.object_src  :none\n#     policy.script_src  :self, :https\n#     policy.style_src   :self, :https\n#     # Specify URI for violation reports\n#     # policy.report_uri \"/csp-violation-report-endpoint\"\n#   end\n#\n#   # Generate session nonces for permitted importmap, inline scripts, and inline styles.\n#   config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }\n#   config.content_security_policy_nonce_directives = %w(script-src style-src)\n#\n#   # Report violations without enforcing the policy.\n#   # config.content_security_policy_report_only = true\n# end\n"
  },
  {
    "path": "test/apps/rails8/config/initializers/filter_parameter_logging.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.\n# Use this to limit dissemination of sensitive information.\n# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.\nRails.application.config.filter_parameters += [\n  :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn\n]\n"
  },
  {
    "path": "test/apps/rails8/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format. Inflections\n# are locale specific, and you may define rules for as many different\n# locales as you wish. All of these examples are active by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.plural /^(ox)$/i, \"\\\\1en\"\n#   inflect.singular /^(ox)en/i, \"\\\\1\"\n#   inflect.irregular \"person\", \"people\"\n#   inflect.uncountable %w( fish sheep )\n# end\n\n# These inflection rules are supported but not enabled by default:\n# ActiveSupport::Inflector.inflections(:en) do |inflect|\n#   inflect.acronym \"RESTful\"\n# end\n"
  },
  {
    "path": "test/apps/rails8/config/initializers/permissions_policy.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Define an application-wide HTTP permissions policy. For further\n# information see: https://developers.google.com/web/updates/2018/06/feature-policy\n\n# Rails.application.config.permissions_policy do |policy|\n#   policy.camera      :none\n#   policy.gyroscope   :none\n#   policy.microphone  :none\n#   policy.usb         :none\n#   policy.fullscreen  :self\n#   policy.payment     :self, \"https://secure.example.com\"\n# end\n"
  },
  {
    "path": "test/apps/rails8/config/locales/en.yml",
    "content": "# Files in the config/locales directory are used for internationalization and\n# are automatically loaded by Rails. If you want to use locales other than\n# English, add the necessary files in this directory.\n#\n# To use the locales, use `I18n.t`:\n#\n#     I18n.t \"hello\"\n#\n# In views, this is aliased to just `t`:\n#\n#     <%= t(\"hello\") %>\n#\n# To use a different locale, set it with `I18n.locale`:\n#\n#     I18n.locale = :es\n#\n# This would use the information in config/locales/es.yml.\n#\n# To learn more about the API, please read the Rails Internationalization guide\n# at https://guides.rubyonrails.org/i18n.html.\n#\n# Be aware that YAML interprets the following case-insensitive strings as\n# booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings\n# must be quoted to be interpreted as strings. For example:\n#\n#     en:\n#       \"yes\": yup\n#       enabled: \"ON\"\n\nen:\n  hello: \"Hello world\"\n"
  },
  {
    "path": "test/apps/rails8/config/master.key",
    "content": "14cad5d3e84a5abfc93a8d61873a7f66"
  },
  {
    "path": "test/apps/rails8/config/puma.rb",
    "content": "# This configuration file will be evaluated by Puma. The top-level methods that\n# are invoked here are part of Puma's configuration DSL. For more information\n# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html.\n\n# Puma starts a configurable number of processes (workers) and each process\n# serves each request in a thread from an internal thread pool.\n#\n# The ideal number of threads per worker depends both on how much time the\n# application spends waiting for IO operations and on how much you wish to\n# prioritize throughput over latency.\n#\n# As a rule of thumb, increasing the number of threads will increase how much\n# traffic a given process can handle (throughput), but due to CRuby's\n# Global VM Lock (GVL) it has diminishing returns and will degrade the\n# response time (latency) of the application.\n#\n# The default is set to 3 threads as it's deemed a decent compromise between\n# throughput and latency for the average Rails application.\n#\n# Any libraries that use a connection pool or another resource pool should\n# be configured to provide at least as many connections as the number of\n# threads. This includes Active Record's `pool` parameter in `database.yml`.\nthreads_count = ENV.fetch(\"RAILS_MAX_THREADS\", 3)\nthreads threads_count, threads_count\n\n# Specifies the `environment` that Puma will run in.\nrails_env = ENV.fetch(\"RAILS_ENV\", \"development\")\nenvironment rails_env\n\ncase rails_env\nwhen \"production\"\n  # If you are running more than 1 thread per process, the workers count\n  # should be equal to the number of processors (CPU cores) in production.\n  #\n  # Automatically detect the number of available processors in production.\n  require \"concurrent-ruby\"\n  workers_count = Integer(ENV.fetch(\"WEB_CONCURRENCY\") { Concurrent.available_processor_count })\n  workers workers_count if workers_count > 1\n\n  preload_app!\nwhen \"development\"\n  # Specifies a very generous `worker_timeout` so that the worker\n  # isn't killed by Puma when suspended by a debugger.\n  worker_timeout 3600\nend\n\n# Specifies the `port` that Puma will listen on to receive requests; default is 3000.\nport ENV.fetch(\"PORT\", 3000)\n\n# Allow puma to be restarted by `bin/rails restart` command.\nplugin :tmp_restart\n\n# Only use a pidfile when requested\npidfile ENV[\"PIDFILE\"] if ENV[\"PIDFILE\"]\n"
  },
  {
    "path": "test/apps/rails8/config/routes.rb",
    "content": "Rails.application.routes.draw do\n  resources :users\n  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html\n\n  # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.\n  # Can be used by load balancers and uptime monitors to verify that the app is live.\n  get \"up\" => \"rails/health#show\", as: :rails_health_check\n\n  # Render dynamic PWA files from app/views/pwa/*\n  get \"service-worker\" => \"rails/pwa#service_worker\", as: :pwa_service_worker\n  get \"manifest\" => \"rails/pwa#manifest\", as: :pwa_manifest\n\n  # Defines the root path route (\"/\")\n  # root \"posts#index\"\nend\n"
  },
  {
    "path": "test/apps/rails8/config/storage.yml",
    "content": "test:\n  service: Disk\n  root: <%= Rails.root.join(\"tmp/storage\") %>\n\nlocal:\n  service: Disk\n  root: <%= Rails.root.join(\"storage\") %>\n\n# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)\n# amazon:\n#   service: S3\n#   access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>\n#   secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>\n#   region: us-east-1\n#   bucket: your_own_bucket-<%= Rails.env %>\n\n# Remember not to checkin your GCS keyfile to a repository\n# google:\n#   service: GCS\n#   project: your_project\n#   credentials: <%= Rails.root.join(\"path/to/gcs.keyfile\") %>\n#   bucket: your_own_bucket-<%= Rails.env %>\n\n# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)\n# microsoft:\n#   service: AzureStorage\n#   storage_account_name: your_account_name\n#   storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>\n#   container: your_container_name-<%= Rails.env %>\n\n# mirror:\n#   service: Mirror\n#   primary: local\n#   mirrors: [ amazon, google, microsoft ]\n"
  },
  {
    "path": "test/apps/rails8/config.ru",
    "content": "# This file is used by Rack-based servers to start the application.\n\nrequire_relative \"config/environment\"\n\nrun Rails.application\nRails.application.load_server\n"
  },
  {
    "path": "test/apps/rails8/lib/evals.rb",
    "content": "class Evals\n  def evals(something)\n    instance_eval \"plain string - no warning\" \n    instance_eval \"interpolated #{string} - warning\"\n    instance_eval anything_else # no warning\n\n    eval anything # warning\n\n    self.class.class_eval do\n      # no warning\n    end\n\n    if [1, 2, 3].include? code\n      eval code # no warning\n    end\n\n    eval \"interpolate #{something}\"\n  end\n\n  def safe_strings\n    [\"good\", \"fine\"].each do |suffix|\n      class_eval <<-METHODS\n        def method_that_is_#{suffix}\n          puts suffix\n        end\n      METHODS\n    end\n  end\n\n  class << self\n    def defs_eval(string)\n      eval(\"foo #{string}\")\n    end\n  end\n\n  def Object.object_defs_eval(string)\n    eval(\"foo #{string}\")\n  end\n\n  def @ivar.ivar_def_eval(string)\n    eval(\"foo #{string}\")\n  end\n\n  lvar = Object.new\n  def lvar.lvar_def_eval(string)\n    eval(\"foo #{string}\")\n  end\nend\n"
  },
  {
    "path": "test/apps/rails8/lib/masgn.rb",
    "content": "def test_masgn_recursion\n  r = lambda {\n    x, q = r\n  }\n\n  y, z = r\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/Gemfile",
    "content": "source 'http://rubygems.org'\n\ngem 'rails', '2.3.14'\ngem 'json', '1.1.0'\ngem 'sqlite3'\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/README",
    "content": "This is a test application which uses the rails_xss plugin\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/Rakefile",
    "content": "# Add your own tasks in files placed in lib/tasks ending in .rake,\n# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.\n\nrequire(File.join(File.dirname(__FILE__), 'config', 'boot'))\n\nrequire 'rake'\nrequire 'rake/testtask'\nrequire 'rake/rdoctask'\n\nrequire 'tasks/rails'\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/controllers/application_controller.rb",
    "content": "# Filters added to this controller apply to all controllers in the application.\n# Likewise, all the methods added will be available for all controllers.\n\nclass ApplicationController < ActionController::Base\n  helper :all # include all helpers, all the time\n  #protect_from_forgery # See ActionController::RequestForgeryProtection for details\n\n  before_filter :current_user\n\n  def current_user\n    return @current_user if @current_user\n\n    user_id = session[:user_id] || cookies[:user_id]\n\n    if user_id\n      @current_user = User.find(user_id)\n    end\n  end\n\n  def require_logged_in\n    unless @current_user\n      redirect_to '/login', :page => request.path\n    end\n  end\n\n  def require_admin\n    unless @current_user and @current_user.admin?\n      render :text => \"Access denied\"\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/controllers/posts_controller.rb",
    "content": "class PostsController < ApplicationController\n  before_filter :require_logged_in, :except => [:index, :show]\n  \n  # GET /posts\n  # GET /posts.xml\n  def index\n    @posts = Post.all(:conditions => { :in_reply_to => nil })\n\n    respond_to do |format|\n      format.html # index.html.erb\n      format.xml  { render :xml => @posts }\n    end\n  end\n\n  # GET /posts/1\n  # GET /posts/1.xml\n  def show\n    @post = Post.find(params[:id])\n    @user = User.find(@post.user_id)\n\n    respond_to do |format|\n      format.html # show.html.erb\n      format.xml  { render :xml => @post }\n    end\n  end\n\n  # GET /posts/new\n  # GET /posts/new.xml\n  def new\n    @post = Post.new\n    @post.in_reply_to = params[:in_reply_to]\n\n    respond_to do |format|\n      format.html # new.html.erb\n      format.xml  { render :xml => @post }\n    end\n  end\n\n  # GET /posts/1/edit\n  def edit\n    @post = Post.find(params[:id])\n  end\n\n  # POST /posts\n  # POST /posts.xml\n  def create\n    @post = Post.new(params[:post])\n    @post.user_id = @current_user.id\n\n    respond_to do |format|\n      if @post.save\n        format.html { redirect_to(@post, :notice => 'Post was successfully created.') }\n        format.xml  { render :xml => @post, :status => :created, :location => @post }\n      else\n        format.html { render :action => \"new\" }\n        format.xml  { render :xml => @post.errors, :status => :unprocessable_entity }\n      end\n    end\n  end\n\n  # PUT /posts/1\n  # PUT /posts/1.xml\n  def update\n    @post = Post.find(params[:id])\n\n    respond_to do |format|\n      if @post.update_attributes(params[:post])\n        format.html { redirect_to(@post, :notice => 'Post was successfully updated.') }\n        format.xml  { head :ok }\n      else\n        format.html { render :action => \"edit\" }\n        format.xml  { render :xml => @post.errors, :status => :unprocessable_entity }\n      end\n    end\n  end\n\n  # DELETE /posts/1\n  # DELETE /posts/1.xml\n  def destroy\n    @post = Post.find(params[:id])\n    @post.destroy\n\n    respond_to do |format|\n      format.html { redirect_to(posts_url) }\n      format.xml  { head :ok }\n    end\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/controllers/users_controller.rb",
    "content": "class UsersController < ApplicationController\n  before_filter :require_logged_in, :only => [:edit, :destroy, :update]\n\n  # GET /users\n  # GET /users.xml\n  def index\n    @users = User.all\n\n    respond_to do |format|\n      format.html # index.html.erb\n      format.xml  { render :xml => @users }\n    end\n  end\n\n  # GET /users/1\n  # GET /users/1.xml\n  def show\n    @evil_input = params[:of_doom]\n    @user = User.find(params[:id])\n\n    respond_to do |format|\n      format.html # show.html.erb\n      format.xml  { render :xml => @user }\n    end\n  end\n\n  # GET /users/new\n  # GET /users/new.xml\n  def new\n    @user = User.new\n\n    respond_to do |format|\n      format.html # new.html.erb\n      format.xml  { render :xml => @user }\n    end\n  end\n\n  # GET /users/1/edit\n  def edit\n    @user = User.find(params[:id])\n    @user.password = nil\n  end\n\n  # POST /users\n  # POST /users.xml\n  def create\n    @user = User.new(params[:user])\n    @user.password = `echo #{params[:user][:password]} | shasum`\n    @user.user_name.downcase!\n\n    respond_to do |format|\n      if User.find(:all, :conditions => { :user_name => params[:user][:user_name] }).empty? and @user.save\n        session[:user_id] = @user.id\n        format.html { redirect_to(@user, :notice => 'User was successfully created.') }\n        format.xml  { render :xml => @user, :status => :created, :location => @user }\n      else\n        @user = User.new\n        format.html { render :action => \"new\" }\n        format.xml  { render :xml => @user.errors, :status => :unprocessable_entity }\n      end\n    end\n  end\n\n  # PUT /users/1\n  # PUT /users/1.xml\n  def update\n    @user = User.find(params[:id])\n    @user.password = `echo #{params[:user][:password]} | shasum`\n\n    respond_to do |format|\n      if @user.update_attributes(params[:user])\n        format.html { redirect_to(@user, :notice => 'User was successfully updated.') }\n        format.xml  { head :ok }\n      else\n        format.html { render :action => \"edit\" }\n        format.xml  { render :xml => @user.errors, :status => :unprocessable_entity }\n      end\n    end\n  end\n\n  # DELETE /users/1\n  # DELETE /users/1.xml\n  def destroy\n    @user = User.find(params[:id])\n    @user.destroy\n\n    respond_to do |format|\n      format.html { redirect_to(users_url) }\n      format.xml  { head :ok }\n    end\n  end\n\n  def login\n    if current_user\n      redirect_to params.update(:action => :index)\n    else\n      render :notice => \"Could not login\"\n    end\n  end\n\n  def login_user\n    password = `echo #{params[:user][:password]} | shasum`\n\n    $stderr.puts \"password: #{password}\"\n\n    user = User.find(:first, :conditions => { :user_name => params[:user][:user_name].downcase, :password => password }) \n\n    if user.nil?\n      redirect_to '/login'\n    else\n      session[:user_id] = user.id\n\n      redirect_to :controller => :posts, :action => :index \n    end\n  end\n\n  def logout\n    session[:user_id] = nil\n    redirect_to :action => :login\n  end\n\n  def search\n  end\n\n  def results\n    @users = User.all(:conditions => \"display_name like '%#{params[:query]}%'\")\n  end\n\n  def to_json\n\n  end\n\n  def delete_them_all\n    if User.connection.select_value(\"SELECT * from users WHERE name='#{params[:name]}'\").nil? #should warn\n      User.connection.execute(\"TRUNCATE users\") #shouldn't warn\n    end\n  end\n\n  def test_sanitize\n    @x = params[:x]\n  end\n\n  def string_mass\n    User.new(\"stuff\")\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/helpers/application_helper.rb",
    "content": "# Methods added to this helper will be available to all templates in the application.\nmodule ApplicationHelper\n  def authorized?\n    (@user and @current_user == @user[:id]) or (@current_user and @current_user.admin?)\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/helpers/posts_helper.rb",
    "content": "module PostsHelper\n  def author_of? post\n    @current_user and post.user_id == @current_user.id\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/helpers/users_helper.rb",
    "content": "module UsersHelper\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/models/post.rb",
    "content": "class Post < ActiveRecord::Base\n  belongs_to :user\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/models/user.rb",
    "content": "class User < ActiveRecord::Base\n  has_many :posts\n\n  validates_uniqueness_of :user_name\n  validates_format_of :user_name, :with => /^\\w+$/\n  validates_length_of :user_name, :maximum => 10\n  validates_format_of :display_name, :with => /^(\\w|\\s)+$/\n  validates_presence_of :user_name, :display_name, :password\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/layouts/posts.html.erb",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n       \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n  <meta http-equiv=\"content-type\" content=\"text/html;charset=UTF-8\" />\n  <title>Users: <%= controller.action_name %></title>\n  <%= stylesheet_link_tag 'scaffold' %>\n</head>\n<body>\n\n<p style=\"color: green\"><%= notice %></p>\n\n<%= yield %>\n\n<br />\n<hr />\n<ul>\n  <li><%= link_to 'Posts', posts_url %></li>\n  <li><%= link_to 'Users', users_url %></li>\n<% if @current_user %>\n  <li><%= link_to 'Logout', '/logout' %></li>\n<% else %>\n  <li><%= link_to 'Login', '/login' %></li>\n<% end %>\n</ul>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/layouts/users.html.erb",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n       \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n  <meta http-equiv=\"content-type\" content=\"text/html;charset=UTF-8\" />\n  <title>Users: <%= controller.action_name %></title>\n  <%= stylesheet_link_tag 'scaffold' %>\n</head>\n<body>\n\n<p style=\"color: green\"><%= notice %></p>\n\n<%= yield %>\n\n<br />\n<hr />\n<ul>\n  <li><%= link_to 'Posts', posts_url %></li>\n  <li><%= link_to 'Users', users_url %></li>\n  <li><%= link_to 'Search Users', '/search' %></li>\n<% if @current_user %>\n  <li><%= link_to 'Edit User', :controller => 'users', :action => 'edit', :id => @current_user %>\n  <li><%= link_to 'Logout', '/logout' %></li>\n<% else %>\n  <li><%= link_to 'Login', '/login' %></li>\n<% end %>\n</ul>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/posts/_show.html.erb",
    "content": "<p>\n  <b>User:</b>\n  <%=h @post.user_id %>\n</p>\n\n<p>\n  <b>Title:</b>\n  <%=h @post.title %>\n</p>\n\n<p>\n  <b>Body:</b>\n  <%=h @post.body %>\n</p>\n\n<% if @user == @post.user_id %>\n<%= link_to 'Edit', edit_post_path(@post) %> |\n<% end %>\n<%= link_to 'Back', posts_path %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/posts/edit.html.erb",
    "content": "<h1>Editing post</h1>\n\n<% form_for(@post) do |f| %>\n  <%= f.error_messages %>\n\n  <p>\n    <%= f.label :user_id %><br />\n    <%= f.text_field :user_id %>\n  </p>\n  <p>\n    <%= f.label :title %><br />\n    <%= f.text_field :title %>\n  </p>\n  <p>\n    <%= f.label :body %><br />\n    <%= f.text_field :body %>\n  </p>\n  <p>\n    <%= f.submit 'Update' %>\n  </p>\n<% end %>\n\n<%= link_to 'Show', @post %> |\n<%= link_to 'Back', posts_path %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/posts/index.html.erb",
    "content": "<h1>Topics</h1>\n\n<table>\n  <% unless @posts.empty? %>\n    <tr>\n      <th>User</th>\n      <th>Title</th>\n      <th></th>\n    </tr>\n  <% end %>\n  <% @posts.each do |post| %>\n    <tr>\n      <td><%=h User.find(post.user_id).user_name %></td>\n      <td><%=h post.title %></td>\n      <td><%= link_to 'View', post %></td>\n      <% if author_of? post %>\n        <td><%= link_to 'Edit', edit_post_path(post) %></td>\n        <td><%= link_to 'Delete', post, :method => :delete %></td>\n      <% end %>\n    </tr>\n  <% end %>\n</table>\n\n<br />\n\n<%= link_to 'New post', new_post_path %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/posts/new.html.erb",
    "content": "<h1>New post</h1>\n\n<% form_for(@post) do |f| %>\n  <%= f.error_messages %>\n  <p>\n    <%= f.label :title %><br />\n    <%= f.text_field :title %>\n  </p>\n  <p>\n    <%= f.label :body %><br />\n    <%= f.text_field :body %>\n  </p>\n  <p>\n    <%= f.hidden_field :in_reply_to %>\n  </p>\n  <p>\n    <%= f.submit 'Create' %>\n  </p>\n<% end %>\n\n<%= link_to 'Back', posts_path %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/posts/show.html.erb",
    "content": "<p>\n  <b>User:</b>\n  <%= User.find(@post.user_id).user_name %>\n</p>\n\n<p>\n  <b>Title:</b>\n  <%=h @post.title %>\n</p>\n\n<p>\n  <b>Body:</b>\n  <%=h @post.body %>\n</p>\n\n<% if @user == @post.user_id %>\n<%= link_to 'Edit', edit_post_path(@post) %> |\n<% end %>\n<%= link_to 'Back', posts_path %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/posts/show_topic.html.erb",
    "content": "<p>\n  <b>User:</b>\n  <%=h @post.user_id %>\n</p>\n\n<p>\n  <b>Title:</b>\n  <%=h @post.title %>\n</p>\n\n<p>\n  <b>Body:</b>\n  <%=h @post.body %>\n</p>\n\n<% if @user == @post.user_id %>\n<%= link_to 'Edit', edit_post_path(@post) %> |\n<% end %>\n<%= link_to 'Back', posts_path %>\n\n<%= render :partial => 'show', :collection => @posts %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/users/_user.html.erb",
    "content": "<p>\n  <%= link_to user.display_name, user %>\n</p>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/users/edit.html.erb",
    "content": "<h1>Edit Account</h1>\n\n<% form_for(@user) do |f| %>\n  <%= f.error_messages %>\n\n  <p>\n    <%= f.label :display_name %><br />\n    <%= f.text_field :display_name %>\n  </p>\n  <p>\n    <%= f.label :password %><br />\n    <%= f.password_field :password %>\n  </p>\n  <% if @current_user and @current_user.admin? %>\n  <p>\n    <%= f.label :admin %><br />\n    <%= f.check_box :admin %><br />\n  </p>\n  <% end %>\n  <p>\n    <%= f.submit 'Save' %>\n  </p>\n<% end %>\n\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/users/index.html.erb",
    "content": "<h1>Listing users</h1>\n\n<table>\n<% @users.each do |user| %>\n  <tr>\n    <td><%= raw link_to user.display_name, user %></td>\n    <% if @current_user and (user.id == @current_user.id or @current_user.admin?) %>\n    <td>| <%= link_to 'Edit', edit_user_path(user) %></td>\n    <td><%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %></td>\n    <% end %>\n  </tr>\n<% end %>\n</table>\n\n<br />\n\n<%= link_to 'New user', new_user_path %>\n\n<%= select('post', 'author_id', \"<option value='#{params[:id]}'>#{params[:name]}</option>\") %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/users/login.html.erb",
    "content": "<h1>Login</h1>\n\n<% form_for(:user, :url => { :action => :login_user }) do |f| %>\n  <%= f.error_messages %>\n\n  <p>\n    <%= f.label :user_name %><br />\n    <%= f.text_field :user_name %>\n  </p>\n  <p>\n  <p>\n    <%= f.label :password %><br />\n    <%= f.password_field :password %>\n    <%= hidden_field_tag :page, params[:page] %>\n  </p>\n  <p>\n    <%= f.submit 'Login' %>\n  </p>\n<% end %>\n\n<%= link_to 'Create new user', new_user_path %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/users/new.html.erb",
    "content": "<h1>New user</h1>\n\n<% form_for(@user) do |f| %>\n  <%= f.error_messages %>\n\n  <p>\n    <%= f.label :display_name %><br />\n    <%= f.text_field :display_name %>\n  </p>\n  <p>\n    <%= f.label :user_name %><br />\n    <%= f.text_field :user_name %>\n  </p>\n  <p>\n    <%= f.label :password %><br />\n    <%= f.password_field :password %>\n  </p>\n  <p>\n    <%= f.submit 'Create' %>\n  </p>\n<% end %>\n\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/users/results.html.erb",
    "content": "<p>\nResults for <b><%= params[:query] %></b>:\n</p>\n<% if @users.empty? %>\n  No results.\n<% else %>\n  <% @users.each do |user| %>\n    <%= render user, :object => user %>\n  <% end %>\n<% end %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/users/search.html.erb",
    "content": "<% form_tag '/results' do %>\n  Find user: <%= text_field_tag :query %>\n  <%= submit_tag 'Search' %>\n<% end %> \n</form>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/users/show.html.erb",
    "content": "<p>\n  <b>Display name:</b>\n  <%=h @user.display_name %>\n</p>\n\n<p>\n  <b>User name:</b>\n  <%=h @user.user_name %>\n</p>\n\n<p>\n  <b>Bio:</b>\n  <%= raw @user.bio %>\n</p>\n<p>\n  <b>Bad:</b> <%= @evil_input %>\n</p>\n\n<%= strip_tags @user.profile %>\n\n<% if @current_user and (@current_user.id == @user.id or @current_user.admin?) %>\n<%= link_to 'Edit', edit_user_path(@user) %> |\n<% end %>\n<%= link_to 'Back', users_path %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/users/test_sanitize.html.erb",
    "content": "<%= sanitize params[:x] %>\n<%= @x %>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/app/views/users/to_json.html.erb",
    "content": "<%= raw({:asdf => params[:asdf]}.to_json) %>"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/boot.rb",
    "content": "# Don't change this file!\n# Configure your app in config/environment.rb and config/environments/*.rb\n\nRAILS_ROOT = \"#{File.dirname(__FILE__)}/..\" unless defined?(RAILS_ROOT)\n\nmodule Rails\n  class << self\n    def boot!\n      unless booted?\n        preinitialize\n        pick_boot.run\n      end\n    end\n\n    def booted?\n      defined? Rails::Initializer\n    end\n\n    def pick_boot\n      (vendor_rails? ? VendorBoot : GemBoot).new\n    end\n\n    def vendor_rails?\n      File.exist?(\"#{RAILS_ROOT}/vendor/rails\")\n    end\n\n    def preinitialize\n      load(preinitializer_path) if File.exist?(preinitializer_path)\n    end\n\n    def preinitializer_path\n      \"#{RAILS_ROOT}/config/preinitializer.rb\"\n    end\n  end\n\n  class Boot\n    def run\n      load_initializer\n      Rails::Initializer.run(:set_load_path)\n    end\n  end\n\n  class VendorBoot < Boot\n    def load_initializer\n      require \"#{RAILS_ROOT}/vendor/rails/railties/lib/initializer\"\n      Rails::Initializer.run(:install_gem_spec_stubs)\n      Rails::GemDependency.add_frozen_gem_path\n    end\n  end\n\n  class GemBoot < Boot\n    def load_initializer\n      self.class.load_rubygems\n      load_rails_gem\n      require 'initializer'\n    end\n\n    def load_rails_gem\n      if version = self.class.gem_version\n        gem 'rails', version\n      else\n        gem 'rails'\n      end\n    rescue Gem::LoadError => load_error\n      if load_error.message =~ /Could not find RubyGem rails/\n        STDERR.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)\n        exit 1\n      else\n        raise\n      end\n    end\n\n    class << self\n      def rubygems_version\n        Gem::RubyGemsVersion rescue nil\n      end\n\n      def gem_version\n        if defined? RAILS_GEM_VERSION\n          RAILS_GEM_VERSION\n        elsif ENV.include?('RAILS_GEM_VERSION')\n          ENV['RAILS_GEM_VERSION']\n        else\n          parse_gem_version(read_environment_rb)\n        end\n      end\n\n      def load_rubygems\n        min_version = '1.3.2'\n        require 'rubygems'\n        unless rubygems_version >= min_version\n          $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)\n          exit 1\n        end\n\n      rescue LoadError\n        $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)\n        exit 1\n      end\n\n      def parse_gem_version(text)\n        $1 if text =~ /^[^#]*RAILS_GEM_VERSION\\s*=\\s*[\"']([!~<>=]*\\s*[\\d.]+)[\"']/\n      end\n\n      private\n        def read_environment_rb\n          File.read(\"#{RAILS_ROOT}/config/environment.rb\")\n        end\n    end\n  end\nend\n\n# All that for this:\nRails.boot!\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/database.yml",
    "content": "# SQLite version 3.x\n#   gem install sqlite3-ruby (not necessary on OS X Leopard)\ndevelopment:\n  adapter: sqlite3\n  database: db/development.sqlite3\n  pool: 5\n  timeout: 5000\n\n# Warning: The database defined as \"test\" will be erased and\n# re-generated from your development database when you run \"rake\".\n# Do not set this db to the same as development or production.\ntest:\n  adapter: sqlite3\n  database: db/test.sqlite3\n  pool: 5\n  timeout: 5000\n\nproduction:\n  adapter: sqlite3\n  database: db/production.sqlite3\n  pool: 5\n  timeout: 5000\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/environment.rb",
    "content": "# Be sure to restart your server when you modify this file\n\n# Specifies gem version of Rails to use when vendor/rails is not present\nRAILS_GEM_VERSION = '2.3.14' unless defined? RAILS_GEM_VERSION\n\n# Bootstrap the Rails environment, frameworks, and default configuration\nrequire File.join(File.dirname(__FILE__), 'boot')\n\nRails::Initializer.run do |config|\n  # Settings in config/environments/* take precedence over those specified here.\n  # Application configuration should go into files in config/initializers\n  # -- all .rb files in that directory are automatically loaded.\n\n  # Add additional load paths for your own custom dirs\n  # config.autoload_paths += %W( #{RAILS_ROOT}/extras )\n\n  # Specify gems that this application depends on and have them installed with rake gems:install\n  # config.gem \"bj\"\n  # config.gem \"hpricot\", :version => '0.6', :source => \"http://code.whytheluckystiff.net\"\n  # config.gem \"sqlite3-ruby\", :lib => \"sqlite3\"\n  # config.gem \"aws-s3\", :lib => \"aws/s3\"\n\n  # Only load the plugins named here, in the order given (default is alphabetical).\n  # :all can be used as a placeholder for all plugins not explicitly named\n  # config.plugins = [ :exception_notification, :ssl_requirement, :all ]\n\n  # Skip frameworks you're not going to use. To use Rails without a database,\n  # you must remove the Active Record framework.\n  # config.frameworks -= [ :active_record, :active_resource, :action_mailer ]\n\n  # Activate observers that should always be running\n  # config.active_record.observers = :cacher, :garbage_collector, :forum_observer\n\n  # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.\n  # Run \"rake -D time\" for a list of tasks for finding time zone names.\n  config.time_zone = 'UTC'\n\n  # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.\n  # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]\n  # config.i18n.default_locale = :de\nend"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/environments/development.rb",
    "content": "# Settings specified here will take precedence over those in config/environment.rb\n\n# In the development environment your application's code is reloaded on\n# every request.  This slows down response time but is perfect for development\n# since you don't have to restart the webserver when you make code changes.\nconfig.cache_classes = false\n\n# Log error messages when you accidentally call methods on nil.\nconfig.whiny_nils = true\n\n# Show full error reports and disable caching\nconfig.action_controller.consider_all_requests_local = true\nconfig.action_view.debug_rjs                         = true\nconfig.action_controller.perform_caching             = false\n\n# Don't care if the mailer can't send\nconfig.action_mailer.raise_delivery_errors = false"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/environments/production.rb",
    "content": "# Settings specified here will take precedence over those in config/environment.rb\n\n# The production environment is meant for finished, \"live\" apps.\n# Code is not reloaded between requests\nconfig.cache_classes = true\n\n# Full error reports are disabled and caching is turned on\nconfig.action_controller.consider_all_requests_local = false\nconfig.action_controller.perform_caching             = true\nconfig.action_view.cache_template_loading            = true\n\n# See everything in the log (default is :info)\n# config.log_level = :debug\n\n# Use a different logger for distributed setups\n# config.logger = SyslogLogger.new\n\n# Use a different cache store in production\n# config.cache_store = :mem_cache_store\n\n# Enable serving of images, stylesheets, and javascripts from an asset server\n# config.action_controller.asset_host = \"http://assets.example.com\"\n\n# Disable delivery errors, bad email addresses will be ignored\n# config.action_mailer.raise_delivery_errors = false\n\n# Enable threaded mode\n# config.threadsafe!"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/environments/test.rb",
    "content": "# Settings specified here will take precedence over those in config/environment.rb\n\n# The test environment is used exclusively to run your application's\n# test suite.  You never need to work with it otherwise.  Remember that\n# your test database is \"scratch space\" for the test suite and is wiped\n# and recreated between test runs.  Don't rely on the data there!\nconfig.cache_classes = true\n\n# Log error messages when you accidentally call methods on nil.\nconfig.whiny_nils = true\n\n# Show full error reports and disable caching\nconfig.action_controller.consider_all_requests_local = true\nconfig.action_controller.perform_caching             = false\nconfig.action_view.cache_template_loading            = true\n\n# Disable request forgery protection in test environment\nconfig.action_controller.allow_forgery_protection    = false\n\n# Tell Action Mailer not to deliver emails to the real world.\n# The :test delivery method accumulates sent emails in the\n# ActionMailer::Base.deliveries array.\nconfig.action_mailer.delivery_method = :test\n\n# Use SQL instead of Active Record's schema dumper when creating the test database.\n# This is necessary if your schema can't be completely dumped by the schema dumper,\n# like if you have constraints or database-specific column types\n# config.active_record.schema_format = :sql"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/initializers/backtrace_silencers.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.\n# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }\n\n# You can also remove all the silencers if you're trying do debug a problem that might steem from framework code.\n# Rails.backtrace_cleaner.remove_silencers!"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/initializers/cookie_verification_secret.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key for verifying the integrity of signed cookies.\n# If you change this key, all old signed cookies will become invalid!\n# Make sure the secret is at least 30 characters and all random, \n# no regular words or you'll be exposed to dictionary attacks.\nActionController::Base.cookie_verifier_secret = 'b87b8a9b6268a2ef25ba27ec0c70f7bccf759d862cfe788de36c8db96bb9bfacf77057c49b1a4cc4936aff465883a395b9310a3264e1123e751190f38181456d';\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/initializers/inflections.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new inflection rules using the following format \n# (all these examples are active by default):\n# ActiveSupport::Inflector.inflections do |inflect|\n#   inflect.plural /^(ox)$/i, '\\1en'\n#   inflect.singular /^(ox)en/i, '\\1'\n#   inflect.irregular 'person', 'people'\n#   inflect.uncountable %w( fish sheep )\n# end\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/initializers/json_parsing.rb",
    "content": "ActiveSupport::JSON.backend = \"JSONGem\"\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/initializers/mime_types.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Add new mime types for use in respond_to blocks:\n# Mime::Type.register \"text/richtext\", :rtf\n# Mime::Type.register_alias \"text/html\", :iphone\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/initializers/new_rails_defaults.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# These settings change the behavior of Rails 2 apps and will be defaults\n# for Rails 3. You can remove this initializer when Rails 3 is released.\n\nif defined?(ActiveRecord)\n  # Include Active Record class name as root for JSON serialized output.\n  ActiveRecord::Base.include_root_in_json = true\n\n  # Store the full class name (including module namespace) in STI type column.\n  ActiveRecord::Base.store_full_sti_class = true\nend\n\nActionController::Routing.generate_best_match = false\n\n# Use ISO 8601 format for JSON serialized times and dates.\nActiveSupport.use_standard_json_time_format = true\n\n# Don't escape HTML entities in JSON, leave that for the #json_escape helper.\n# if you're including raw json in an HTML page.\nActiveSupport.escape_html_entities_in_json = false"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/initializers/session_store.rb",
    "content": "# Be sure to restart your server when you modify this file.\n\n# Your secret key for verifying cookie session data integrity.\n# If you change this key, all old sessions will become invalid!\n# Make sure the secret is at least 30 characters and all random, \n# no regular words or you'll be exposed to dictionary attacks.\nActionController::Base.session = {\n  :key         => '_forums_session',\n  :secret      => 'a2b5bb679742890a7af48afb795fc54b17b19670a6ebd713bfaa25c11988cf8619c7138b32913a825dfc37876aa011a195260feaa7219b37c1797762668eba8d'\n}\n\n# Use the database for sessions instead of the cookie-based default,\n# which shouldn't be used to store highly confidential information\n# (create the session table with \"rake db:sessions:create\")\n# ActionController::Base.session_store = :active_record_store\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/initializers/single_quote_workaround.rb",
    "content": "class ERB\n  module Util\n\n    if \"html_safe exists\".respond_to?(:html_safe)\n      def html_escape(s)\n        s = s.to_s\n        if s.html_safe?\n          s\n        else\n          Rack::Utils.escape_html(s).html_safe\n        end\n      end\n    else\n      def html_escape(s)\n        s = s.to_s\n        Rack::Utils.escape_html(s).html_safe\n      end\n    end\n\n    remove_method :h\n    alias h html_escape\n\n    class << self\n      remove_method :html_escape\n      remove_method :h\n    end\n\n    module_function :html_escape\n    module_function :h\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/initializers/yaml_parsing.rb",
    "content": "#Enable YAML parsing (bad)\nActionController::Base.param_parsers[Mime::YAML] = :yaml\n\n#Disable YAML in XML (good)\nActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING.delete('symbol') \nActiveSupport::CoreExtensions::Hash::Conversions::XML_PARSING.delete('yaml') \n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/locales/en.yml",
    "content": "# Sample localization file for English. Add more files in this directory for other locales.\n# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.\n\nen:\n  hello: \"Hello world\""
  },
  {
    "path": "test/apps/rails_with_xss_plugin/config/routes.rb",
    "content": "ActionController::Routing::Routes.draw do |map|\n  map.resources :posts\n  map.resources :users\n \n  map.connect 'login', :controller => 'users', :action => 'login' \n  map.connect 'logout', :controller => 'users', :action => 'logout' \n \n  map.connect 'results', :controller => 'users', :action => 'results'\n  map.connect 'search', :controller => 'users', :action => 'search'\n \n\n  # The priority is based upon order of creation: first created -> highest priority.\n\n  # Sample of regular route:\n  #   map.connect 'products/:id', :controller => 'catalog', :action => 'view'\n  # Keep in mind you can assign values other than :controller and :action\n\n  # Sample of named route:\n  #   map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'\n  # This route can be invoked with purchase_url(:id => product.id)\n\n  # Sample resource route (maps HTTP verbs to controller actions automatically):\n  #   map.resources :products\n\n  # Sample resource route with options:\n  #   map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get }\n\n  # Sample resource route with sub-resources:\n  #   map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller\n  \n  # Sample resource route with more complex sub-resources\n  #   map.resources :products do |products|\n  #     products.resources :comments\n  #     products.resources :sales, :collection => { :recent => :get }\n  #   end\n\n  # Sample resource route within a namespace:\n  #   map.namespace :admin do |admin|\n  #     # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb)\n  #     admin.resources :products\n  #   end\n\n  # You can have the root of your site routed with map.root -- just remember to delete public/index.html.\n  # map.root :controller => \"welcome\"\n\n  # See how all your routes lay out with \"rake routes\"\n\n  # Install the default routes as the lowest priority.\n  # Note: These default routes make all actions in every controller accessible via GET requests. You should\n  # consider removing or commenting them out if you're using named routes and resources.\n  map.root :controller => \"posts\"\n  map.connect ':controller/:action/:id'\n  map.connect ':controller/:action/:id.:format'\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/db/migrate/20120312064721_create_users.rb",
    "content": "class CreateUsers < ActiveRecord::Migration\n  def self.up\n    create_table :users do |t|\n      t.string :display_name\n      t.string :user_name\n      t.string :signature\n      t.string :profile\n      t.string :password\n      t.boolean :admin\n\n      t.timestamps\n    end\n  end\n\n  def self.down\n    drop_table :users\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/db/migrate/20120312065023_create_posts.rb",
    "content": "class CreatePosts < ActiveRecord::Migration\n  def self.up\n    create_table :posts do |t|\n      t.integer :user_id\n      t.string :title\n      t.string :body\n      t.integer :in_reply_to\n\n      t.timestamps\n    end\n  end\n\n  def self.down\n    drop_table :posts\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/db/schema.rb",
    "content": "# This file is auto-generated from the current state of the database. Instead of editing this file, \n# please use the migrations feature of Active Record to incrementally modify your database, and\n# then regenerate this schema definition.\n#\n# Note that this schema.rb definition is the authoritative source for your database schema. If you need\n# to create the application database on another system, you should be using db:schema:load, not running\n# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations\n# you'll amass, the slower it'll run and the greater likelihood for issues).\n#\n# It's strongly recommended to check this file into your version control system.\n\nActiveRecord::Schema.define(:version => 20120312065023) do\n\n  create_table \"posts\", :force => true do |t|\n    t.integer  \"user_id\"\n    t.string   \"title\"\n    t.string   \"body\"\n    t.integer  \"in_reply_to\"\n    t.datetime \"created_at\"\n    t.datetime \"updated_at\"\n  end\n\n  create_table \"users\", :force => true do |t|\n    t.string   \"display_name\"\n    t.string   \"user_name\"\n    t.string   \"signature\"\n    t.string   \"profile\"\n    t.string   \"password\"\n    t.boolean  \"admin\"\n    t.datetime \"created_at\"\n    t.datetime \"updated_at\"\n  end\n\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/db/seeds.rb",
    "content": "# This file should contain all the record creation needed to seed the database with its default values.\n# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).\n#\n# Examples:\n#   \n#   cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])\n#   Major.create(:name => 'Daley', :city => cities.first)\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/doc/README_FOR_APP",
    "content": "Use this README file to introduce your application and point to useful places in the API for learning more.\nRun \"rake doc:app\" to generate API documentation for your models, controllers, helpers, and libraries.\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/public/404.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n       \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n\n<head>\n  <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />\n  <title>The page you were looking for doesn't exist (404)</title>\n\t<style type=\"text/css\">\n\t\tbody { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n\t\tdiv.dialog {\n\t\t\twidth: 25em;\n\t\t\tpadding: 0 4em;\n\t\t\tmargin: 4em auto 0 auto;\n\t\t\tborder: 1px solid #ccc;\n\t\t\tborder-right-color: #999;\n\t\t\tborder-bottom-color: #999;\n\t\t}\n\t\th1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n\t</style>\n</head>\n\n<body>\n  <!-- This file lives in public/404.html -->\n  <div class=\"dialog\">\n    <h1>The page you were looking for doesn't exist.</h1>\n    <p>You may have mistyped the address or the page may have moved.</p>\n  </div>\n</body>\n</html>"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/public/422.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n       \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n\n<head>\n  <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />\n  <title>The change you wanted was rejected (422)</title>\n\t<style type=\"text/css\">\n\t\tbody { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n\t\tdiv.dialog {\n\t\t\twidth: 25em;\n\t\t\tpadding: 0 4em;\n\t\t\tmargin: 4em auto 0 auto;\n\t\t\tborder: 1px solid #ccc;\n\t\t\tborder-right-color: #999;\n\t\t\tborder-bottom-color: #999;\n\t\t}\n\t\th1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n\t</style>\n</head>\n\n<body>\n  <!-- This file lives in public/422.html -->\n  <div class=\"dialog\">\n    <h1>The change you wanted was rejected.</h1>\n    <p>Maybe you tried to change something you didn't have access to.</p>\n  </div>\n</body>\n</html>"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/public/500.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n       \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n\n<head>\n  <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />\n  <title>We're sorry, but something went wrong (500)</title>\n\t<style type=\"text/css\">\n\t\tbody { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }\n\t\tdiv.dialog {\n\t\t\twidth: 25em;\n\t\t\tpadding: 0 4em;\n\t\t\tmargin: 4em auto 0 auto;\n\t\t\tborder: 1px solid #ccc;\n\t\t\tborder-right-color: #999;\n\t\t\tborder-bottom-color: #999;\n\t\t}\n\t\th1 { font-size: 100%; color: #f00; line-height: 1.5em; }\n\t</style>\n</head>\n\n<body>\n  <!-- This file lives in public/500.html -->\n  <div class=\"dialog\">\n    <h1>We're sorry, but something went wrong.</h1>\n    <p>We've been notified about this issue and we'll take a look at it shortly.</p>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/public/javascripts/application.js",
    "content": "// Place your application-specific JavaScript functions and classes here\n// This file is automatically included by javascript_include_tag :defaults\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/public/javascripts/controls.js",
    "content": "// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n//           (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)\n//           (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)\n// Contributors:\n//  Richard Livsey\n//  Rahul Bhargava\n//  Rob Wills\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\n// Autocompleter.Base handles all the autocompletion functionality\n// that's independent of the data source for autocompletion. This\n// includes drawing the autocompletion menu, observing keyboard\n// and mouse events, and similar.\n//\n// Specific autocompleters need to provide, at the very least,\n// a getUpdatedChoices function that will be invoked every time\n// the text inside the monitored textbox changes. This method\n// should get the text for which to provide autocompletion by\n// invoking this.getToken(), NOT by directly accessing\n// this.element.value. This is to allow incremental tokenized\n// autocompletion. Specific auto-completion logic (AJAX, etc)\n// belongs in getUpdatedChoices.\n//\n// Tokenized incremental autocompletion is enabled automatically\n// when an autocompleter is instantiated with the 'tokens' option\n// in the options parameter, e.g.:\n// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });\n// will incrementally autocomplete with a comma as the token.\n// Additionally, ',' in the above example can be replaced with\n// a token array, e.g. { tokens: [',', '\\n'] } which\n// enables autocompletion on multiple tokens. This is most\n// useful when one of the tokens is \\n (a newline), as it\n// allows smart autocompletion after linebreaks.\n\nif(typeof Effect == 'undefined')\n  throw(\"controls.js requires including script.aculo.us' effects.js library\");\n\nvar Autocompleter = { };\nAutocompleter.Base = Class.create({\n  baseInitialize: function(element, update, options) {\n    element          = $(element);\n    this.element     = element;\n    this.update      = $(update);\n    this.hasFocus    = false;\n    this.changed     = false;\n    this.active      = false;\n    this.index       = 0;\n    this.entryCount  = 0;\n    this.oldElementValue = this.element.value;\n\n    if(this.setOptions)\n      this.setOptions(options);\n    else\n      this.options = options || { };\n\n    this.options.paramName    = this.options.paramName || this.element.name;\n    this.options.tokens       = this.options.tokens || [];\n    this.options.frequency    = this.options.frequency || 0.4;\n    this.options.minChars     = this.options.minChars || 1;\n    this.options.onShow       = this.options.onShow ||\n      function(element, update){\n        if(!update.style.position || update.style.position=='absolute') {\n          update.style.position = 'absolute';\n          Position.clone(element, update, {\n            setHeight: false,\n            offsetTop: element.offsetHeight\n          });\n        }\n        Effect.Appear(update,{duration:0.15});\n      };\n    this.options.onHide = this.options.onHide ||\n      function(element, update){ new Effect.Fade(update,{duration:0.15}) };\n\n    if(typeof(this.options.tokens) == 'string')\n      this.options.tokens = new Array(this.options.tokens);\n    // Force carriage returns as token delimiters anyway\n    if (!this.options.tokens.include('\\n'))\n      this.options.tokens.push('\\n');\n\n    this.observer = null;\n\n    this.element.setAttribute('autocomplete','off');\n\n    Element.hide(this.update);\n\n    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));\n    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));\n  },\n\n  show: function() {\n    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);\n    if(!this.iefix &&\n      (Prototype.Browser.IE) &&\n      (Element.getStyle(this.update, 'position')=='absolute')) {\n      new Insertion.After(this.update,\n       '<iframe id=\"' + this.update.id + '_iefix\" '+\n       'style=\"display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);\" ' +\n       'src=\"javascript:false;\" frameborder=\"0\" scrolling=\"no\"></iframe>');\n      this.iefix = $(this.update.id+'_iefix');\n    }\n    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);\n  },\n\n  fixIEOverlapping: function() {\n    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});\n    this.iefix.style.zIndex = 1;\n    this.update.style.zIndex = 2;\n    Element.show(this.iefix);\n  },\n\n  hide: function() {\n    this.stopIndicator();\n    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);\n    if(this.iefix) Element.hide(this.iefix);\n  },\n\n  startIndicator: function() {\n    if(this.options.indicator) Element.show(this.options.indicator);\n  },\n\n  stopIndicator: function() {\n    if(this.options.indicator) Element.hide(this.options.indicator);\n  },\n\n  onKeyPress: function(event) {\n    if(this.active)\n      switch(event.keyCode) {\n       case Event.KEY_TAB:\n       case Event.KEY_RETURN:\n         this.selectEntry();\n         Event.stop(event);\n       case Event.KEY_ESC:\n         this.hide();\n         this.active = false;\n         Event.stop(event);\n         return;\n       case Event.KEY_LEFT:\n       case Event.KEY_RIGHT:\n         return;\n       case Event.KEY_UP:\n         this.markPrevious();\n         this.render();\n         Event.stop(event);\n         return;\n       case Event.KEY_DOWN:\n         this.markNext();\n         this.render();\n         Event.stop(event);\n         return;\n      }\n     else\n       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||\n         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;\n\n    this.changed = true;\n    this.hasFocus = true;\n\n    if(this.observer) clearTimeout(this.observer);\n      this.observer =\n        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);\n  },\n\n  activate: function() {\n    this.changed = false;\n    this.hasFocus = true;\n    this.getUpdatedChoices();\n  },\n\n  onHover: function(event) {\n    var element = Event.findElement(event, 'LI');\n    if(this.index != element.autocompleteIndex)\n    {\n        this.index = element.autocompleteIndex;\n        this.render();\n    }\n    Event.stop(event);\n  },\n\n  onClick: function(event) {\n    var element = Event.findElement(event, 'LI');\n    this.index = element.autocompleteIndex;\n    this.selectEntry();\n    this.hide();\n  },\n\n  onBlur: function(event) {\n    // needed to make click events working\n    setTimeout(this.hide.bind(this), 250);\n    this.hasFocus = false;\n    this.active = false;\n  },\n\n  render: function() {\n    if(this.entryCount > 0) {\n      for (var i = 0; i < this.entryCount; i++)\n        this.index==i ?\n          Element.addClassName(this.getEntry(i),\"selected\") :\n          Element.removeClassName(this.getEntry(i),\"selected\");\n      if(this.hasFocus) {\n        this.show();\n        this.active = true;\n      }\n    } else {\n      this.active = false;\n      this.hide();\n    }\n  },\n\n  markPrevious: function() {\n    if(this.index > 0) this.index--;\n      else this.index = this.entryCount-1;\n    this.getEntry(this.index).scrollIntoView(true);\n  },\n\n  markNext: function() {\n    if(this.index < this.entryCount-1) this.index++;\n      else this.index = 0;\n    this.getEntry(this.index).scrollIntoView(false);\n  },\n\n  getEntry: function(index) {\n    return this.update.firstChild.childNodes[index];\n  },\n\n  getCurrentEntry: function() {\n    return this.getEntry(this.index);\n  },\n\n  selectEntry: function() {\n    this.active = false;\n    this.updateElement(this.getCurrentEntry());\n  },\n\n  updateElement: function(selectedElement) {\n    if (this.options.updateElement) {\n      this.options.updateElement(selectedElement);\n      return;\n    }\n    var value = '';\n    if (this.options.select) {\n      var nodes = $(selectedElement).select('.' + this.options.select) || [];\n      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);\n    } else\n      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');\n\n    var bounds = this.getTokenBounds();\n    if (bounds[0] != -1) {\n      var newValue = this.element.value.substr(0, bounds[0]);\n      var whitespace = this.element.value.substr(bounds[0]).match(/^\\s+/);\n      if (whitespace)\n        newValue += whitespace[0];\n      this.element.value = newValue + value + this.element.value.substr(bounds[1]);\n    } else {\n      this.element.value = value;\n    }\n    this.oldElementValue = this.element.value;\n    this.element.focus();\n\n    if (this.options.afterUpdateElement)\n      this.options.afterUpdateElement(this.element, selectedElement);\n  },\n\n  updateChoices: function(choices) {\n    if(!this.changed && this.hasFocus) {\n      this.update.innerHTML = choices;\n      Element.cleanWhitespace(this.update);\n      Element.cleanWhitespace(this.update.down());\n\n      if(this.update.firstChild && this.update.down().childNodes) {\n        this.entryCount =\n          this.update.down().childNodes.length;\n        for (var i = 0; i < this.entryCount; i++) {\n          var entry = this.getEntry(i);\n          entry.autocompleteIndex = i;\n          this.addObservers(entry);\n        }\n      } else {\n        this.entryCount = 0;\n      }\n\n      this.stopIndicator();\n      this.index = 0;\n\n      if(this.entryCount==1 && this.options.autoSelect) {\n        this.selectEntry();\n        this.hide();\n      } else {\n        this.render();\n      }\n    }\n  },\n\n  addObservers: function(element) {\n    Event.observe(element, \"mouseover\", this.onHover.bindAsEventListener(this));\n    Event.observe(element, \"click\", this.onClick.bindAsEventListener(this));\n  },\n\n  onObserverEvent: function() {\n    this.changed = false;\n    this.tokenBounds = null;\n    if(this.getToken().length>=this.options.minChars) {\n      this.getUpdatedChoices();\n    } else {\n      this.active = false;\n      this.hide();\n    }\n    this.oldElementValue = this.element.value;\n  },\n\n  getToken: function() {\n    var bounds = this.getTokenBounds();\n    return this.element.value.substring(bounds[0], bounds[1]).strip();\n  },\n\n  getTokenBounds: function() {\n    if (null != this.tokenBounds) return this.tokenBounds;\n    var value = this.element.value;\n    if (value.strip().empty()) return [-1, 0];\n    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);\n    var offset = (diff == this.oldElementValue.length ? 1 : 0);\n    var prevTokenPos = -1, nextTokenPos = value.length;\n    var tp;\n    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {\n      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);\n      if (tp > prevTokenPos) prevTokenPos = tp;\n      tp = value.indexOf(this.options.tokens[index], diff + offset);\n      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;\n    }\n    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);\n  }\n});\n\nAutocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {\n  var boundary = Math.min(newS.length, oldS.length);\n  for (var index = 0; index < boundary; ++index)\n    if (newS[index] != oldS[index])\n      return index;\n  return boundary;\n};\n\nAjax.Autocompleter = Class.create(Autocompleter.Base, {\n  initialize: function(element, update, url, options) {\n    this.baseInitialize(element, update, options);\n    this.options.asynchronous  = true;\n    this.options.onComplete    = this.onComplete.bind(this);\n    this.options.defaultParams = this.options.parameters || null;\n    this.url                   = url;\n  },\n\n  getUpdatedChoices: function() {\n    this.startIndicator();\n\n    var entry = encodeURIComponent(this.options.paramName) + '=' +\n      encodeURIComponent(this.getToken());\n\n    this.options.parameters = this.options.callback ?\n      this.options.callback(this.element, entry) : entry;\n\n    if(this.options.defaultParams)\n      this.options.parameters += '&' + this.options.defaultParams;\n\n    new Ajax.Request(this.url, this.options);\n  },\n\n  onComplete: function(request) {\n    this.updateChoices(request.responseText);\n  }\n});\n\n// The local array autocompleter. Used when you'd prefer to\n// inject an array of autocompletion options into the page, rather\n// than sending out Ajax queries, which can be quite slow sometimes.\n//\n// The constructor takes four parameters. The first two are, as usual,\n// the id of the monitored textbox, and id of the autocompletion menu.\n// The third is the array you want to autocomplete from, and the fourth\n// is the options block.\n//\n// Extra local autocompletion options:\n// - choices - How many autocompletion choices to offer\n//\n// - partialSearch - If false, the autocompleter will match entered\n//                    text only at the beginning of strings in the\n//                    autocomplete array. Defaults to true, which will\n//                    match text at the beginning of any *word* in the\n//                    strings in the autocomplete array. If you want to\n//                    search anywhere in the string, additionally set\n//                    the option fullSearch to true (default: off).\n//\n// - fullSsearch - Search anywhere in autocomplete array strings.\n//\n// - partialChars - How many characters to enter before triggering\n//                   a partial match (unlike minChars, which defines\n//                   how many characters are required to do any match\n//                   at all). Defaults to 2.\n//\n// - ignoreCase - Whether to ignore case when autocompleting.\n//                 Defaults to true.\n//\n// It's possible to pass in a custom function as the 'selector'\n// option, if you prefer to write your own autocompletion logic.\n// In that case, the other options above will not apply unless\n// you support them.\n\nAutocompleter.Local = Class.create(Autocompleter.Base, {\n  initialize: function(element, update, array, options) {\n    this.baseInitialize(element, update, options);\n    this.options.array = array;\n  },\n\n  getUpdatedChoices: function() {\n    this.updateChoices(this.options.selector(this));\n  },\n\n  setOptions: function(options) {\n    this.options = Object.extend({\n      choices: 10,\n      partialSearch: true,\n      partialChars: 2,\n      ignoreCase: true,\n      fullSearch: false,\n      selector: function(instance) {\n        var ret       = []; // Beginning matches\n        var partial   = []; // Inside matches\n        var entry     = instance.getToken();\n        var count     = 0;\n\n        for (var i = 0; i < instance.options.array.length &&\n          ret.length < instance.options.choices ; i++) {\n\n          var elem = instance.options.array[i];\n          var foundPos = instance.options.ignoreCase ?\n            elem.toLowerCase().indexOf(entry.toLowerCase()) :\n            elem.indexOf(entry);\n\n          while (foundPos != -1) {\n            if (foundPos == 0 && elem.length != entry.length) {\n              ret.push(\"<li><strong>\" + elem.substr(0, entry.length) + \"</strong>\" +\n                elem.substr(entry.length) + \"</li>\");\n              break;\n            } else if (entry.length >= instance.options.partialChars &&\n              instance.options.partialSearch && foundPos != -1) {\n              if (instance.options.fullSearch || /\\s/.test(elem.substr(foundPos-1,1))) {\n                partial.push(\"<li>\" + elem.substr(0, foundPos) + \"<strong>\" +\n                  elem.substr(foundPos, entry.length) + \"</strong>\" + elem.substr(\n                  foundPos + entry.length) + \"</li>\");\n                break;\n              }\n            }\n\n            foundPos = instance.options.ignoreCase ?\n              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :\n              elem.indexOf(entry, foundPos + 1);\n\n          }\n        }\n        if (partial.length)\n          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));\n        return \"<ul>\" + ret.join('') + \"</ul>\";\n      }\n    }, options || { });\n  }\n});\n\n// AJAX in-place editor and collection editor\n// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).\n\n// Use this if you notice weird scrolling problems on some browsers,\n// the DOM might be a bit confused when this gets called so do this\n// waits 1 ms (with setTimeout) until it does the activation\nField.scrollFreeActivate = function(field) {\n  setTimeout(function() {\n    Field.activate(field);\n  }, 1);\n};\n\nAjax.InPlaceEditor = Class.create({\n  initialize: function(element, url, options) {\n    this.url = url;\n    this.element = element = $(element);\n    this.prepareOptions();\n    this._controls = { };\n    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!\n    Object.extend(this.options, options || { });\n    if (!this.options.formId && this.element.id) {\n      this.options.formId = this.element.id + '-inplaceeditor';\n      if ($(this.options.formId))\n        this.options.formId = '';\n    }\n    if (this.options.externalControl)\n      this.options.externalControl = $(this.options.externalControl);\n    if (!this.options.externalControl)\n      this.options.externalControlOnly = false;\n    this._originalBackground = this.element.getStyle('background-color') || 'transparent';\n    this.element.title = this.options.clickToEditText;\n    this._boundCancelHandler = this.handleFormCancellation.bind(this);\n    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);\n    this._boundFailureHandler = this.handleAJAXFailure.bind(this);\n    this._boundSubmitHandler = this.handleFormSubmission.bind(this);\n    this._boundWrapperHandler = this.wrapUp.bind(this);\n    this.registerListeners();\n  },\n  checkForEscapeOrReturn: function(e) {\n    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;\n    if (Event.KEY_ESC == e.keyCode)\n      this.handleFormCancellation(e);\n    else if (Event.KEY_RETURN == e.keyCode)\n      this.handleFormSubmission(e);\n  },\n  createControl: function(mode, handler, extraClasses) {\n    var control = this.options[mode + 'Control'];\n    var text = this.options[mode + 'Text'];\n    if ('button' == control) {\n      var btn = document.createElement('input');\n      btn.type = 'submit';\n      btn.value = text;\n      btn.className = 'editor_' + mode + '_button';\n      if ('cancel' == mode)\n        btn.onclick = this._boundCancelHandler;\n      this._form.appendChild(btn);\n      this._controls[mode] = btn;\n    } else if ('link' == control) {\n      var link = document.createElement('a');\n      link.href = '#';\n      link.appendChild(document.createTextNode(text));\n      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;\n      link.className = 'editor_' + mode + '_link';\n      if (extraClasses)\n        link.className += ' ' + extraClasses;\n      this._form.appendChild(link);\n      this._controls[mode] = link;\n    }\n  },\n  createEditField: function() {\n    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());\n    var fld;\n    if (1 >= this.options.rows && !/\\r|\\n/.test(this.getText())) {\n      fld = document.createElement('input');\n      fld.type = 'text';\n      var size = this.options.size || this.options.cols || 0;\n      if (0 < size) fld.size = size;\n    } else {\n      fld = document.createElement('textarea');\n      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);\n      fld.cols = this.options.cols || 40;\n    }\n    fld.name = this.options.paramName;\n    fld.value = text; // No HTML breaks conversion anymore\n    fld.className = 'editor_field';\n    if (this.options.submitOnBlur)\n      fld.onblur = this._boundSubmitHandler;\n    this._controls.editor = fld;\n    if (this.options.loadTextURL)\n      this.loadExternalText();\n    this._form.appendChild(this._controls.editor);\n  },\n  createForm: function() {\n    var ipe = this;\n    function addText(mode, condition) {\n      var text = ipe.options['text' + mode + 'Controls'];\n      if (!text || condition === false) return;\n      ipe._form.appendChild(document.createTextNode(text));\n    };\n    this._form = $(document.createElement('form'));\n    this._form.id = this.options.formId;\n    this._form.addClassName(this.options.formClassName);\n    this._form.onsubmit = this._boundSubmitHandler;\n    this.createEditField();\n    if ('textarea' == this._controls.editor.tagName.toLowerCase())\n      this._form.appendChild(document.createElement('br'));\n    if (this.options.onFormCustomization)\n      this.options.onFormCustomization(this, this._form);\n    addText('Before', this.options.okControl || this.options.cancelControl);\n    this.createControl('ok', this._boundSubmitHandler);\n    addText('Between', this.options.okControl && this.options.cancelControl);\n    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');\n    addText('After', this.options.okControl || this.options.cancelControl);\n  },\n  destroy: function() {\n    if (this._oldInnerHTML)\n      this.element.innerHTML = this._oldInnerHTML;\n    this.leaveEditMode();\n    this.unregisterListeners();\n  },\n  enterEditMode: function(e) {\n    if (this._saving || this._editing) return;\n    this._editing = true;\n    this.triggerCallback('onEnterEditMode');\n    if (this.options.externalControl)\n      this.options.externalControl.hide();\n    this.element.hide();\n    this.createForm();\n    this.element.parentNode.insertBefore(this._form, this.element);\n    if (!this.options.loadTextURL)\n      this.postProcessEditField();\n    if (e) Event.stop(e);\n  },\n  enterHover: function(e) {\n    if (this.options.hoverClassName)\n      this.element.addClassName(this.options.hoverClassName);\n    if (this._saving) return;\n    this.triggerCallback('onEnterHover');\n  },\n  getText: function() {\n    return this.element.innerHTML.unescapeHTML();\n  },\n  handleAJAXFailure: function(transport) {\n    this.triggerCallback('onFailure', transport);\n    if (this._oldInnerHTML) {\n      this.element.innerHTML = this._oldInnerHTML;\n      this._oldInnerHTML = null;\n    }\n  },\n  handleFormCancellation: function(e) {\n    this.wrapUp();\n    if (e) Event.stop(e);\n  },\n  handleFormSubmission: function(e) {\n    var form = this._form;\n    var value = $F(this._controls.editor);\n    this.prepareSubmission();\n    var params = this.options.callback(form, value) || '';\n    if (Object.isString(params))\n      params = params.toQueryParams();\n    params.editorId = this.element.id;\n    if (this.options.htmlResponse) {\n      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);\n      Object.extend(options, {\n        parameters: params,\n        onComplete: this._boundWrapperHandler,\n        onFailure: this._boundFailureHandler\n      });\n      new Ajax.Updater({ success: this.element }, this.url, options);\n    } else {\n      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n      Object.extend(options, {\n        parameters: params,\n        onComplete: this._boundWrapperHandler,\n        onFailure: this._boundFailureHandler\n      });\n      new Ajax.Request(this.url, options);\n    }\n    if (e) Event.stop(e);\n  },\n  leaveEditMode: function() {\n    this.element.removeClassName(this.options.savingClassName);\n    this.removeForm();\n    this.leaveHover();\n    this.element.style.backgroundColor = this._originalBackground;\n    this.element.show();\n    if (this.options.externalControl)\n      this.options.externalControl.show();\n    this._saving = false;\n    this._editing = false;\n    this._oldInnerHTML = null;\n    this.triggerCallback('onLeaveEditMode');\n  },\n  leaveHover: function(e) {\n    if (this.options.hoverClassName)\n      this.element.removeClassName(this.options.hoverClassName);\n    if (this._saving) return;\n    this.triggerCallback('onLeaveHover');\n  },\n  loadExternalText: function() {\n    this._form.addClassName(this.options.loadingClassName);\n    this._controls.editor.disabled = true;\n    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n    Object.extend(options, {\n      parameters: 'editorId=' + encodeURIComponent(this.element.id),\n      onComplete: Prototype.emptyFunction,\n      onSuccess: function(transport) {\n        this._form.removeClassName(this.options.loadingClassName);\n        var text = transport.responseText;\n        if (this.options.stripLoadedTextTags)\n          text = text.stripTags();\n        this._controls.editor.value = text;\n        this._controls.editor.disabled = false;\n        this.postProcessEditField();\n      }.bind(this),\n      onFailure: this._boundFailureHandler\n    });\n    new Ajax.Request(this.options.loadTextURL, options);\n  },\n  postProcessEditField: function() {\n    var fpc = this.options.fieldPostCreation;\n    if (fpc)\n      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();\n  },\n  prepareOptions: function() {\n    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);\n    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);\n    [this._extraDefaultOptions].flatten().compact().each(function(defs) {\n      Object.extend(this.options, defs);\n    }.bind(this));\n  },\n  prepareSubmission: function() {\n    this._saving = true;\n    this.removeForm();\n    this.leaveHover();\n    this.showSaving();\n  },\n  registerListeners: function() {\n    this._listeners = { };\n    var listener;\n    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {\n      listener = this[pair.value].bind(this);\n      this._listeners[pair.key] = listener;\n      if (!this.options.externalControlOnly)\n        this.element.observe(pair.key, listener);\n      if (this.options.externalControl)\n        this.options.externalControl.observe(pair.key, listener);\n    }.bind(this));\n  },\n  removeForm: function() {\n    if (!this._form) return;\n    this._form.remove();\n    this._form = null;\n    this._controls = { };\n  },\n  showSaving: function() {\n    this._oldInnerHTML = this.element.innerHTML;\n    this.element.innerHTML = this.options.savingText;\n    this.element.addClassName(this.options.savingClassName);\n    this.element.style.backgroundColor = this._originalBackground;\n    this.element.show();\n  },\n  triggerCallback: function(cbName, arg) {\n    if ('function' == typeof this.options[cbName]) {\n      this.options[cbName](this, arg);\n    }\n  },\n  unregisterListeners: function() {\n    $H(this._listeners).each(function(pair) {\n      if (!this.options.externalControlOnly)\n        this.element.stopObserving(pair.key, pair.value);\n      if (this.options.externalControl)\n        this.options.externalControl.stopObserving(pair.key, pair.value);\n    }.bind(this));\n  },\n  wrapUp: function(transport) {\n    this.leaveEditMode();\n    // Can't use triggerCallback due to backward compatibility: requires\n    // binding + direct element\n    this._boundComplete(transport, this.element);\n  }\n});\n\nObject.extend(Ajax.InPlaceEditor.prototype, {\n  dispose: Ajax.InPlaceEditor.prototype.destroy\n});\n\nAjax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {\n  initialize: function($super, element, url, options) {\n    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;\n    $super(element, url, options);\n  },\n\n  createEditField: function() {\n    var list = document.createElement('select');\n    list.name = this.options.paramName;\n    list.size = 1;\n    this._controls.editor = list;\n    this._collection = this.options.collection || [];\n    if (this.options.loadCollectionURL)\n      this.loadCollection();\n    else\n      this.checkForExternalText();\n    this._form.appendChild(this._controls.editor);\n  },\n\n  loadCollection: function() {\n    this._form.addClassName(this.options.loadingClassName);\n    this.showLoadingText(this.options.loadingCollectionText);\n    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n    Object.extend(options, {\n      parameters: 'editorId=' + encodeURIComponent(this.element.id),\n      onComplete: Prototype.emptyFunction,\n      onSuccess: function(transport) {\n        var js = transport.responseText.strip();\n        if (!/^\\[.*\\]$/.test(js)) // TODO: improve sanity check\n          throw('Server returned an invalid collection representation.');\n        this._collection = eval(js);\n        this.checkForExternalText();\n      }.bind(this),\n      onFailure: this.onFailure\n    });\n    new Ajax.Request(this.options.loadCollectionURL, options);\n  },\n\n  showLoadingText: function(text) {\n    this._controls.editor.disabled = true;\n    var tempOption = this._controls.editor.firstChild;\n    if (!tempOption) {\n      tempOption = document.createElement('option');\n      tempOption.value = '';\n      this._controls.editor.appendChild(tempOption);\n      tempOption.selected = true;\n    }\n    tempOption.update((text || '').stripScripts().stripTags());\n  },\n\n  checkForExternalText: function() {\n    this._text = this.getText();\n    if (this.options.loadTextURL)\n      this.loadExternalText();\n    else\n      this.buildOptionList();\n  },\n\n  loadExternalText: function() {\n    this.showLoadingText(this.options.loadingText);\n    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n    Object.extend(options, {\n      parameters: 'editorId=' + encodeURIComponent(this.element.id),\n      onComplete: Prototype.emptyFunction,\n      onSuccess: function(transport) {\n        this._text = transport.responseText.strip();\n        this.buildOptionList();\n      }.bind(this),\n      onFailure: this.onFailure\n    });\n    new Ajax.Request(this.options.loadTextURL, options);\n  },\n\n  buildOptionList: function() {\n    this._form.removeClassName(this.options.loadingClassName);\n    this._collection = this._collection.map(function(entry) {\n      return 2 === entry.length ? entry : [entry, entry].flatten();\n    });\n    var marker = ('value' in this.options) ? this.options.value : this._text;\n    var textFound = this._collection.any(function(entry) {\n      return entry[0] == marker;\n    }.bind(this));\n    this._controls.editor.update('');\n    var option;\n    this._collection.each(function(entry, index) {\n      option = document.createElement('option');\n      option.value = entry[0];\n      option.selected = textFound ? entry[0] == marker : 0 == index;\n      option.appendChild(document.createTextNode(entry[1]));\n      this._controls.editor.appendChild(option);\n    }.bind(this));\n    this._controls.editor.disabled = false;\n    Field.scrollFreeActivate(this._controls.editor);\n  }\n});\n\n//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****\n//**** This only  exists for a while,  in order to  let ****\n//**** users adapt to  the new API.  Read up on the new ****\n//**** API and convert your code to it ASAP!            ****\n\nAjax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {\n  if (!options) return;\n  function fallback(name, expr) {\n    if (name in options || expr === undefined) return;\n    options[name] = expr;\n  };\n  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :\n    options.cancelLink == options.cancelButton == false ? false : undefined)));\n  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :\n    options.okLink == options.okButton == false ? false : undefined)));\n  fallback('highlightColor', options.highlightcolor);\n  fallback('highlightEndColor', options.highlightendcolor);\n};\n\nObject.extend(Ajax.InPlaceEditor, {\n  DefaultOptions: {\n    ajaxOptions: { },\n    autoRows: 3,                                // Use when multi-line w/ rows == 1\n    cancelControl: 'link',                      // 'link'|'button'|false\n    cancelText: 'cancel',\n    clickToEditText: 'Click to edit',\n    externalControl: null,                      // id|elt\n    externalControlOnly: false,\n    fieldPostCreation: 'activate',              // 'activate'|'focus'|false\n    formClassName: 'inplaceeditor-form',\n    formId: null,                               // id|elt\n    highlightColor: '#ffff99',\n    highlightEndColor: '#ffffff',\n    hoverClassName: '',\n    htmlResponse: true,\n    loadingClassName: 'inplaceeditor-loading',\n    loadingText: 'Loading...',\n    okControl: 'button',                        // 'link'|'button'|false\n    okText: 'ok',\n    paramName: 'value',\n    rows: 1,                                    // If 1 and multi-line, uses autoRows\n    savingClassName: 'inplaceeditor-saving',\n    savingText: 'Saving...',\n    size: 0,\n    stripLoadedTextTags: false,\n    submitOnBlur: false,\n    textAfterControls: '',\n    textBeforeControls: '',\n    textBetweenControls: ''\n  },\n  DefaultCallbacks: {\n    callback: function(form) {\n      return Form.serialize(form);\n    },\n    onComplete: function(transport, element) {\n      // For backward compatibility, this one is bound to the IPE, and passes\n      // the element directly.  It was too often customized, so we don't break it.\n      new Effect.Highlight(element, {\n        startcolor: this.options.highlightColor, keepBackgroundImage: true });\n    },\n    onEnterEditMode: null,\n    onEnterHover: function(ipe) {\n      ipe.element.style.backgroundColor = ipe.options.highlightColor;\n      if (ipe._effect)\n        ipe._effect.cancel();\n    },\n    onFailure: function(transport, ipe) {\n      alert('Error communication with the server: ' + transport.responseText.stripTags());\n    },\n    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.\n    onLeaveEditMode: null,\n    onLeaveHover: function(ipe) {\n      ipe._effect = new Effect.Highlight(ipe.element, {\n        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,\n        restorecolor: ipe._originalBackground, keepBackgroundImage: true\n      });\n    }\n  },\n  Listeners: {\n    click: 'enterEditMode',\n    keydown: 'checkForEscapeOrReturn',\n    mouseover: 'enterHover',\n    mouseout: 'leaveHover'\n  }\n});\n\nAjax.InPlaceCollectionEditor.DefaultOptions = {\n  loadingCollectionText: 'Loading options...'\n};\n\n// Delayed observer, like Form.Element.Observer,\n// but waits for delay after last key input\n// Ideal for live-search fields\n\nForm.Element.DelayedObserver = Class.create({\n  initialize: function(element, delay, callback) {\n    this.delay     = delay || 0.5;\n    this.element   = $(element);\n    this.callback  = callback;\n    this.timer     = null;\n    this.lastValue = $F(this.element);\n    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));\n  },\n  delayedListener: function(event) {\n    if(this.lastValue == $F(this.element)) return;\n    if(this.timer) clearTimeout(this.timer);\n    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);\n    this.lastValue = $F(this.element);\n  },\n  onTimerEvent: function() {\n    this.timer = null;\n    this.callback(this.element, $F(this.element));\n  }\n});"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/public/javascripts/dragdrop.js",
    "content": "// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n//           (c) 2005-2008 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\nif(Object.isUndefined(Effect))\n  throw(\"dragdrop.js requires including script.aculo.us' effects.js library\");\n\nvar Droppables = {\n  drops: [],\n\n  remove: function(element) {\n    this.drops = this.drops.reject(function(d) { return d.element==$(element) });\n  },\n\n  add: function(element) {\n    element = $(element);\n    var options = Object.extend({\n      greedy:     true,\n      hoverclass: null,\n      tree:       false\n    }, arguments[1] || { });\n\n    // cache containers\n    if(options.containment) {\n      options._containers = [];\n      var containment = options.containment;\n      if(Object.isArray(containment)) {\n        containment.each( function(c) { options._containers.push($(c)) });\n      } else {\n        options._containers.push($(containment));\n      }\n    }\n\n    if(options.accept) options.accept = [options.accept].flatten();\n\n    Element.makePositioned(element); // fix IE\n    options.element = element;\n\n    this.drops.push(options);\n  },\n\n  findDeepestChild: function(drops) {\n    deepest = drops[0];\n\n    for (i = 1; i < drops.length; ++i)\n      if (Element.isParent(drops[i].element, deepest.element))\n        deepest = drops[i];\n\n    return deepest;\n  },\n\n  isContained: function(element, drop) {\n    var containmentNode;\n    if(drop.tree) {\n      containmentNode = element.treeNode;\n    } else {\n      containmentNode = element.parentNode;\n    }\n    return drop._containers.detect(function(c) { return containmentNode == c });\n  },\n\n  isAffected: function(point, element, drop) {\n    return (\n      (drop.element!=element) &&\n      ((!drop._containers) ||\n        this.isContained(element, drop)) &&\n      ((!drop.accept) ||\n        (Element.classNames(element).detect(\n          function(v) { return drop.accept.include(v) } ) )) &&\n      Position.within(drop.element, point[0], point[1]) );\n  },\n\n  deactivate: function(drop) {\n    if(drop.hoverclass)\n      Element.removeClassName(drop.element, drop.hoverclass);\n    this.last_active = null;\n  },\n\n  activate: function(drop) {\n    if(drop.hoverclass)\n      Element.addClassName(drop.element, drop.hoverclass);\n    this.last_active = drop;\n  },\n\n  show: function(point, element) {\n    if(!this.drops.length) return;\n    var drop, affected = [];\n\n    this.drops.each( function(drop) {\n      if(Droppables.isAffected(point, element, drop))\n        affected.push(drop);\n    });\n\n    if(affected.length>0)\n      drop = Droppables.findDeepestChild(affected);\n\n    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);\n    if (drop) {\n      Position.within(drop.element, point[0], point[1]);\n      if(drop.onHover)\n        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));\n\n      if (drop != this.last_active) Droppables.activate(drop);\n    }\n  },\n\n  fire: function(event, element) {\n    if(!this.last_active) return;\n    Position.prepare();\n\n    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))\n      if (this.last_active.onDrop) {\n        this.last_active.onDrop(element, this.last_active.element, event);\n        return true;\n      }\n  },\n\n  reset: function() {\n    if(this.last_active)\n      this.deactivate(this.last_active);\n  }\n};\n\nvar Draggables = {\n  drags: [],\n  observers: [],\n\n  register: function(draggable) {\n    if(this.drags.length == 0) {\n      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);\n      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);\n      this.eventKeypress  = this.keyPress.bindAsEventListener(this);\n\n      Event.observe(document, \"mouseup\", this.eventMouseUp);\n      Event.observe(document, \"mousemove\", this.eventMouseMove);\n      Event.observe(document, \"keypress\", this.eventKeypress);\n    }\n    this.drags.push(draggable);\n  },\n\n  unregister: function(draggable) {\n    this.drags = this.drags.reject(function(d) { return d==draggable });\n    if(this.drags.length == 0) {\n      Event.stopObserving(document, \"mouseup\", this.eventMouseUp);\n      Event.stopObserving(document, \"mousemove\", this.eventMouseMove);\n      Event.stopObserving(document, \"keypress\", this.eventKeypress);\n    }\n  },\n\n  activate: function(draggable) {\n    if(draggable.options.delay) {\n      this._timeout = setTimeout(function() {\n        Draggables._timeout = null;\n        window.focus();\n        Draggables.activeDraggable = draggable;\n      }.bind(this), draggable.options.delay);\n    } else {\n      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari\n      this.activeDraggable = draggable;\n    }\n  },\n\n  deactivate: function() {\n    this.activeDraggable = null;\n  },\n\n  updateDrag: function(event) {\n    if(!this.activeDraggable) return;\n    var pointer = [Event.pointerX(event), Event.pointerY(event)];\n    // Mozilla-based browsers fire successive mousemove events with\n    // the same coordinates, prevent needless redrawing (moz bug?)\n    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;\n    this._lastPointer = pointer;\n\n    this.activeDraggable.updateDrag(event, pointer);\n  },\n\n  endDrag: function(event) {\n    if(this._timeout) {\n      clearTimeout(this._timeout);\n      this._timeout = null;\n    }\n    if(!this.activeDraggable) return;\n    this._lastPointer = null;\n    this.activeDraggable.endDrag(event);\n    this.activeDraggable = null;\n  },\n\n  keyPress: function(event) {\n    if(this.activeDraggable)\n      this.activeDraggable.keyPress(event);\n  },\n\n  addObserver: function(observer) {\n    this.observers.push(observer);\n    this._cacheObserverCallbacks();\n  },\n\n  removeObserver: function(element) {  // element instead of observer fixes mem leaks\n    this.observers = this.observers.reject( function(o) { return o.element==element });\n    this._cacheObserverCallbacks();\n  },\n\n  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'\n    if(this[eventName+'Count'] > 0)\n      this.observers.each( function(o) {\n        if(o[eventName]) o[eventName](eventName, draggable, event);\n      });\n    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);\n  },\n\n  _cacheObserverCallbacks: function() {\n    ['onStart','onEnd','onDrag'].each( function(eventName) {\n      Draggables[eventName+'Count'] = Draggables.observers.select(\n        function(o) { return o[eventName]; }\n      ).length;\n    });\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Draggable = Class.create({\n  initialize: function(element) {\n    var defaults = {\n      handle: false,\n      reverteffect: function(element, top_offset, left_offset) {\n        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;\n        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,\n          queue: {scope:'_draggable', position:'end'}\n        });\n      },\n      endeffect: function(element) {\n        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;\n        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,\n          queue: {scope:'_draggable', position:'end'},\n          afterFinish: function(){\n            Draggable._dragging[element] = false\n          }\n        });\n      },\n      zindex: 1000,\n      revert: false,\n      quiet: false,\n      scroll: false,\n      scrollSensitivity: 20,\n      scrollSpeed: 15,\n      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }\n      delay: 0\n    };\n\n    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))\n      Object.extend(defaults, {\n        starteffect: function(element) {\n          element._opacity = Element.getOpacity(element);\n          Draggable._dragging[element] = true;\n          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});\n        }\n      });\n\n    var options = Object.extend(defaults, arguments[1] || { });\n\n    this.element = $(element);\n\n    if(options.handle && Object.isString(options.handle))\n      this.handle = this.element.down('.'+options.handle, 0);\n\n    if(!this.handle) this.handle = $(options.handle);\n    if(!this.handle) this.handle = this.element;\n\n    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {\n      options.scroll = $(options.scroll);\n      this._isScrollChild = Element.childOf(this.element, options.scroll);\n    }\n\n    Element.makePositioned(this.element); // fix IE\n\n    this.options  = options;\n    this.dragging = false;\n\n    this.eventMouseDown = this.initDrag.bindAsEventListener(this);\n    Event.observe(this.handle, \"mousedown\", this.eventMouseDown);\n\n    Draggables.register(this);\n  },\n\n  destroy: function() {\n    Event.stopObserving(this.handle, \"mousedown\", this.eventMouseDown);\n    Draggables.unregister(this);\n  },\n\n  currentDelta: function() {\n    return([\n      parseInt(Element.getStyle(this.element,'left') || '0'),\n      parseInt(Element.getStyle(this.element,'top') || '0')]);\n  },\n\n  initDrag: function(event) {\n    if(!Object.isUndefined(Draggable._dragging[this.element]) &&\n      Draggable._dragging[this.element]) return;\n    if(Event.isLeftClick(event)) {\n      // abort on form elements, fixes a Firefox issue\n      var src = Event.element(event);\n      if((tag_name = src.tagName.toUpperCase()) && (\n        tag_name=='INPUT' ||\n        tag_name=='SELECT' ||\n        tag_name=='OPTION' ||\n        tag_name=='BUTTON' ||\n        tag_name=='TEXTAREA')) return;\n\n      var pointer = [Event.pointerX(event), Event.pointerY(event)];\n      var pos     = Position.cumulativeOffset(this.element);\n      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });\n\n      Draggables.activate(this);\n      Event.stop(event);\n    }\n  },\n\n  startDrag: function(event) {\n    this.dragging = true;\n    if(!this.delta)\n      this.delta = this.currentDelta();\n\n    if(this.options.zindex) {\n      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);\n      this.element.style.zIndex = this.options.zindex;\n    }\n\n    if(this.options.ghosting) {\n      this._clone = this.element.cloneNode(true);\n      this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');\n      if (!this._originallyAbsolute)\n        Position.absolutize(this.element);\n      this.element.parentNode.insertBefore(this._clone, this.element);\n    }\n\n    if(this.options.scroll) {\n      if (this.options.scroll == window) {\n        var where = this._getWindowScroll(this.options.scroll);\n        this.originalScrollLeft = where.left;\n        this.originalScrollTop = where.top;\n      } else {\n        this.originalScrollLeft = this.options.scroll.scrollLeft;\n        this.originalScrollTop = this.options.scroll.scrollTop;\n      }\n    }\n\n    Draggables.notify('onStart', this, event);\n\n    if(this.options.starteffect) this.options.starteffect(this.element);\n  },\n\n  updateDrag: function(event, pointer) {\n    if(!this.dragging) this.startDrag(event);\n\n    if(!this.options.quiet){\n      Position.prepare();\n      Droppables.show(pointer, this.element);\n    }\n\n    Draggables.notify('onDrag', this, event);\n\n    this.draw(pointer);\n    if(this.options.change) this.options.change(this);\n\n    if(this.options.scroll) {\n      this.stopScrolling();\n\n      var p;\n      if (this.options.scroll == window) {\n        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }\n      } else {\n        p = Position.page(this.options.scroll);\n        p[0] += this.options.scroll.scrollLeft + Position.deltaX;\n        p[1] += this.options.scroll.scrollTop + Position.deltaY;\n        p.push(p[0]+this.options.scroll.offsetWidth);\n        p.push(p[1]+this.options.scroll.offsetHeight);\n      }\n      var speed = [0,0];\n      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);\n      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);\n      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);\n      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);\n      this.startScrolling(speed);\n    }\n\n    // fix AppleWebKit rendering\n    if(Prototype.Browser.WebKit) window.scrollBy(0,0);\n\n    Event.stop(event);\n  },\n\n  finishDrag: function(event, success) {\n    this.dragging = false;\n\n    if(this.options.quiet){\n      Position.prepare();\n      var pointer = [Event.pointerX(event), Event.pointerY(event)];\n      Droppables.show(pointer, this.element);\n    }\n\n    if(this.options.ghosting) {\n      if (!this._originallyAbsolute)\n        Position.relativize(this.element);\n      delete this._originallyAbsolute;\n      Element.remove(this._clone);\n      this._clone = null;\n    }\n\n    var dropped = false;\n    if(success) {\n      dropped = Droppables.fire(event, this.element);\n      if (!dropped) dropped = false;\n    }\n    if(dropped && this.options.onDropped) this.options.onDropped(this.element);\n    Draggables.notify('onEnd', this, event);\n\n    var revert = this.options.revert;\n    if(revert && Object.isFunction(revert)) revert = revert(this.element);\n\n    var d = this.currentDelta();\n    if(revert && this.options.reverteffect) {\n      if (dropped == 0 || revert != 'failure')\n        this.options.reverteffect(this.element,\n          d[1]-this.delta[1], d[0]-this.delta[0]);\n    } else {\n      this.delta = d;\n    }\n\n    if(this.options.zindex)\n      this.element.style.zIndex = this.originalZ;\n\n    if(this.options.endeffect)\n      this.options.endeffect(this.element);\n\n    Draggables.deactivate(this);\n    Droppables.reset();\n  },\n\n  keyPress: function(event) {\n    if(event.keyCode!=Event.KEY_ESC) return;\n    this.finishDrag(event, false);\n    Event.stop(event);\n  },\n\n  endDrag: function(event) {\n    if(!this.dragging) return;\n    this.stopScrolling();\n    this.finishDrag(event, true);\n    Event.stop(event);\n  },\n\n  draw: function(point) {\n    var pos = Position.cumulativeOffset(this.element);\n    if(this.options.ghosting) {\n      var r   = Position.realOffset(this.element);\n      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;\n    }\n\n    var d = this.currentDelta();\n    pos[0] -= d[0]; pos[1] -= d[1];\n\n    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {\n      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;\n      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;\n    }\n\n    var p = [0,1].map(function(i){\n      return (point[i]-pos[i]-this.offset[i])\n    }.bind(this));\n\n    if(this.options.snap) {\n      if(Object.isFunction(this.options.snap)) {\n        p = this.options.snap(p[0],p[1],this);\n      } else {\n      if(Object.isArray(this.options.snap)) {\n        p = p.map( function(v, i) {\n          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));\n      } else {\n        p = p.map( function(v) {\n          return (v/this.options.snap).round()*this.options.snap }.bind(this));\n      }\n    }}\n\n    var style = this.element.style;\n    if((!this.options.constraint) || (this.options.constraint=='horizontal'))\n      style.left = p[0] + \"px\";\n    if((!this.options.constraint) || (this.options.constraint=='vertical'))\n      style.top  = p[1] + \"px\";\n\n    if(style.visibility==\"hidden\") style.visibility = \"\"; // fix gecko rendering\n  },\n\n  stopScrolling: function() {\n    if(this.scrollInterval) {\n      clearInterval(this.scrollInterval);\n      this.scrollInterval = null;\n      Draggables._lastScrollPointer = null;\n    }\n  },\n\n  startScrolling: function(speed) {\n    if(!(speed[0] || speed[1])) return;\n    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];\n    this.lastScrolled = new Date();\n    this.scrollInterval = setInterval(this.scroll.bind(this), 10);\n  },\n\n  scroll: function() {\n    var current = new Date();\n    var delta = current - this.lastScrolled;\n    this.lastScrolled = current;\n    if(this.options.scroll == window) {\n      with (this._getWindowScroll(this.options.scroll)) {\n        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {\n          var d = delta / 1000;\n          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );\n        }\n      }\n    } else {\n      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;\n      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;\n    }\n\n    Position.prepare();\n    Droppables.show(Draggables._lastPointer, this.element);\n    Draggables.notify('onDrag', this);\n    if (this._isScrollChild) {\n      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);\n      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;\n      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;\n      if (Draggables._lastScrollPointer[0] < 0)\n        Draggables._lastScrollPointer[0] = 0;\n      if (Draggables._lastScrollPointer[1] < 0)\n        Draggables._lastScrollPointer[1] = 0;\n      this.draw(Draggables._lastScrollPointer);\n    }\n\n    if(this.options.change) this.options.change(this);\n  },\n\n  _getWindowScroll: function(w) {\n    var T, L, W, H;\n    with (w.document) {\n      if (w.document.documentElement && documentElement.scrollTop) {\n        T = documentElement.scrollTop;\n        L = documentElement.scrollLeft;\n      } else if (w.document.body) {\n        T = body.scrollTop;\n        L = body.scrollLeft;\n      }\n      if (w.innerWidth) {\n        W = w.innerWidth;\n        H = w.innerHeight;\n      } else if (w.document.documentElement && documentElement.clientWidth) {\n        W = documentElement.clientWidth;\n        H = documentElement.clientHeight;\n      } else {\n        W = body.offsetWidth;\n        H = body.offsetHeight;\n      }\n    }\n    return { top: T, left: L, width: W, height: H };\n  }\n});\n\nDraggable._dragging = { };\n\n/*--------------------------------------------------------------------------*/\n\nvar SortableObserver = Class.create({\n  initialize: function(element, observer) {\n    this.element   = $(element);\n    this.observer  = observer;\n    this.lastValue = Sortable.serialize(this.element);\n  },\n\n  onStart: function() {\n    this.lastValue = Sortable.serialize(this.element);\n  },\n\n  onEnd: function() {\n    Sortable.unmark();\n    if(this.lastValue != Sortable.serialize(this.element))\n      this.observer(this.element)\n  }\n});\n\nvar Sortable = {\n  SERIALIZE_RULE: /^[^_\\-](?:[A-Za-z0-9\\-\\_]*)[_](.*)$/,\n\n  sortables: { },\n\n  _findRootElement: function(element) {\n    while (element.tagName.toUpperCase() != \"BODY\") {\n      if(element.id && Sortable.sortables[element.id]) return element;\n      element = element.parentNode;\n    }\n  },\n\n  options: function(element) {\n    element = Sortable._findRootElement($(element));\n    if(!element) return;\n    return Sortable.sortables[element.id];\n  },\n\n  destroy: function(element){\n    element = $(element);\n    var s = Sortable.sortables[element.id];\n\n    if(s) {\n      Draggables.removeObserver(s.element);\n      s.droppables.each(function(d){ Droppables.remove(d) });\n      s.draggables.invoke('destroy');\n\n      delete Sortable.sortables[s.element.id];\n    }\n  },\n\n  create: function(element) {\n    element = $(element);\n    var options = Object.extend({\n      element:     element,\n      tag:         'li',       // assumes li children, override with tag: 'tagname'\n      dropOnEmpty: false,\n      tree:        false,\n      treeTag:     'ul',\n      overlap:     'vertical', // one of 'vertical', 'horizontal'\n      constraint:  'vertical', // one of 'vertical', 'horizontal', false\n      containment: element,    // also takes array of elements (or id's); or false\n      handle:      false,      // or a CSS class\n      only:        false,\n      delay:       0,\n      hoverclass:  null,\n      ghosting:    false,\n      quiet:       false,\n      scroll:      false,\n      scrollSensitivity: 20,\n      scrollSpeed: 15,\n      format:      this.SERIALIZE_RULE,\n\n      // these take arrays of elements or ids and can be\n      // used for better initialization performance\n      elements:    false,\n      handles:     false,\n\n      onChange:    Prototype.emptyFunction,\n      onUpdate:    Prototype.emptyFunction\n    }, arguments[1] || { });\n\n    // clear any old sortable with same element\n    this.destroy(element);\n\n    // build options for the draggables\n    var options_for_draggable = {\n      revert:      true,\n      quiet:       options.quiet,\n      scroll:      options.scroll,\n      scrollSpeed: options.scrollSpeed,\n      scrollSensitivity: options.scrollSensitivity,\n      delay:       options.delay,\n      ghosting:    options.ghosting,\n      constraint:  options.constraint,\n      handle:      options.handle };\n\n    if(options.starteffect)\n      options_for_draggable.starteffect = options.starteffect;\n\n    if(options.reverteffect)\n      options_for_draggable.reverteffect = options.reverteffect;\n    else\n      if(options.ghosting) options_for_draggable.reverteffect = function(element) {\n        element.style.top  = 0;\n        element.style.left = 0;\n      };\n\n    if(options.endeffect)\n      options_for_draggable.endeffect = options.endeffect;\n\n    if(options.zindex)\n      options_for_draggable.zindex = options.zindex;\n\n    // build options for the droppables\n    var options_for_droppable = {\n      overlap:     options.overlap,\n      containment: options.containment,\n      tree:        options.tree,\n      hoverclass:  options.hoverclass,\n      onHover:     Sortable.onHover\n    };\n\n    var options_for_tree = {\n      onHover:      Sortable.onEmptyHover,\n      overlap:      options.overlap,\n      containment:  options.containment,\n      hoverclass:   options.hoverclass\n    };\n\n    // fix for gecko engine\n    Element.cleanWhitespace(element);\n\n    options.draggables = [];\n    options.droppables = [];\n\n    // drop on empty handling\n    if(options.dropOnEmpty || options.tree) {\n      Droppables.add(element, options_for_tree);\n      options.droppables.push(element);\n    }\n\n    (options.elements || this.findElements(element, options) || []).each( function(e,i) {\n      var handle = options.handles ? $(options.handles[i]) :\n        (options.handle ? $(e).select('.' + options.handle)[0] : e);\n      options.draggables.push(\n        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));\n      Droppables.add(e, options_for_droppable);\n      if(options.tree) e.treeNode = element;\n      options.droppables.push(e);\n    });\n\n    if(options.tree) {\n      (Sortable.findTreeElements(element, options) || []).each( function(e) {\n        Droppables.add(e, options_for_tree);\n        e.treeNode = element;\n        options.droppables.push(e);\n      });\n    }\n\n    // keep reference\n    this.sortables[element.id] = options;\n\n    // for onupdate\n    Draggables.addObserver(new SortableObserver(element, options.onUpdate));\n\n  },\n\n  // return all suitable-for-sortable elements in a guaranteed order\n  findElements: function(element, options) {\n    return Element.findChildren(\n      element, options.only, options.tree ? true : false, options.tag);\n  },\n\n  findTreeElements: function(element, options) {\n    return Element.findChildren(\n      element, options.only, options.tree ? true : false, options.treeTag);\n  },\n\n  onHover: function(element, dropon, overlap) {\n    if(Element.isParent(dropon, element)) return;\n\n    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {\n      return;\n    } else if(overlap>0.5) {\n      Sortable.mark(dropon, 'before');\n      if(dropon.previousSibling != element) {\n        var oldParentNode = element.parentNode;\n        element.style.visibility = \"hidden\"; // fix gecko rendering\n        dropon.parentNode.insertBefore(element, dropon);\n        if(dropon.parentNode!=oldParentNode)\n          Sortable.options(oldParentNode).onChange(element);\n        Sortable.options(dropon.parentNode).onChange(element);\n      }\n    } else {\n      Sortable.mark(dropon, 'after');\n      var nextElement = dropon.nextSibling || null;\n      if(nextElement != element) {\n        var oldParentNode = element.parentNode;\n        element.style.visibility = \"hidden\"; // fix gecko rendering\n        dropon.parentNode.insertBefore(element, nextElement);\n        if(dropon.parentNode!=oldParentNode)\n          Sortable.options(oldParentNode).onChange(element);\n        Sortable.options(dropon.parentNode).onChange(element);\n      }\n    }\n  },\n\n  onEmptyHover: function(element, dropon, overlap) {\n    var oldParentNode = element.parentNode;\n    var droponOptions = Sortable.options(dropon);\n\n    if(!Element.isParent(dropon, element)) {\n      var index;\n\n      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});\n      var child = null;\n\n      if(children) {\n        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);\n\n        for (index = 0; index < children.length; index += 1) {\n          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {\n            offset -= Element.offsetSize (children[index], droponOptions.overlap);\n          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {\n            child = index + 1 < children.length ? children[index + 1] : null;\n            break;\n          } else {\n            child = children[index];\n            break;\n          }\n        }\n      }\n\n      dropon.insertBefore(element, child);\n\n      Sortable.options(oldParentNode).onChange(element);\n      droponOptions.onChange(element);\n    }\n  },\n\n  unmark: function() {\n    if(Sortable._marker) Sortable._marker.hide();\n  },\n\n  mark: function(dropon, position) {\n    // mark on ghosting only\n    var sortable = Sortable.options(dropon.parentNode);\n    if(sortable && !sortable.ghosting) return;\n\n    if(!Sortable._marker) {\n      Sortable._marker =\n        ($('dropmarker') || Element.extend(document.createElement('DIV'))).\n          hide().addClassName('dropmarker').setStyle({position:'absolute'});\n      document.getElementsByTagName(\"body\").item(0).appendChild(Sortable._marker);\n    }\n    var offsets = Position.cumulativeOffset(dropon);\n    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});\n\n    if(position=='after')\n      if(sortable.overlap == 'horizontal')\n        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});\n      else\n        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});\n\n    Sortable._marker.show();\n  },\n\n  _tree: function(element, options, parent) {\n    var children = Sortable.findElements(element, options) || [];\n\n    for (var i = 0; i < children.length; ++i) {\n      var match = children[i].id.match(options.format);\n\n      if (!match) continue;\n\n      var child = {\n        id: encodeURIComponent(match ? match[1] : null),\n        element: element,\n        parent: parent,\n        children: [],\n        position: parent.children.length,\n        container: $(children[i]).down(options.treeTag)\n      };\n\n      /* Get the element containing the children and recurse over it */\n      if (child.container)\n        this._tree(child.container, options, child);\n\n      parent.children.push (child);\n    }\n\n    return parent;\n  },\n\n  tree: function(element) {\n    element = $(element);\n    var sortableOptions = this.options(element);\n    var options = Object.extend({\n      tag: sortableOptions.tag,\n      treeTag: sortableOptions.treeTag,\n      only: sortableOptions.only,\n      name: element.id,\n      format: sortableOptions.format\n    }, arguments[1] || { });\n\n    var root = {\n      id: null,\n      parent: null,\n      children: [],\n      container: element,\n      position: 0\n    };\n\n    return Sortable._tree(element, options, root);\n  },\n\n  /* Construct a [i] index for a particular node */\n  _constructIndex: function(node) {\n    var index = '';\n    do {\n      if (node.id) index = '[' + node.position + ']' + index;\n    } while ((node = node.parent) != null);\n    return index;\n  },\n\n  sequence: function(element) {\n    element = $(element);\n    var options = Object.extend(this.options(element), arguments[1] || { });\n\n    return $(this.findElements(element, options) || []).map( function(item) {\n      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';\n    });\n  },\n\n  setSequence: function(element, new_sequence) {\n    element = $(element);\n    var options = Object.extend(this.options(element), arguments[2] || { });\n\n    var nodeMap = { };\n    this.findElements(element, options).each( function(n) {\n        if (n.id.match(options.format))\n            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];\n        n.parentNode.removeChild(n);\n    });\n\n    new_sequence.each(function(ident) {\n      var n = nodeMap[ident];\n      if (n) {\n        n[1].appendChild(n[0]);\n        delete nodeMap[ident];\n      }\n    });\n  },\n\n  serialize: function(element) {\n    element = $(element);\n    var options = Object.extend(Sortable.options(element), arguments[1] || { });\n    var name = encodeURIComponent(\n      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);\n\n    if (options.tree) {\n      return Sortable.tree(element, arguments[1]).children.map( function (item) {\n        return [name + Sortable._constructIndex(item) + \"[id]=\" +\n                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));\n      }).flatten().join('&');\n    } else {\n      return Sortable.sequence(element, arguments[1]).map( function(item) {\n        return name + \"[]=\" + encodeURIComponent(item);\n      }).join('&');\n    }\n  }\n};\n\n// Returns true if child is contained within element\nElement.isParent = function(child, element) {\n  if (!child.parentNode || child == element) return false;\n  if (child.parentNode == element) return true;\n  return Element.isParent(child.parentNode, element);\n};\n\nElement.findChildren = function(element, only, recursive, tagName) {\n  if(!element.hasChildNodes()) return null;\n  tagName = tagName.toUpperCase();\n  if(only) only = [only].flatten();\n  var elements = [];\n  $A(element.childNodes).each( function(e) {\n    if(e.tagName && e.tagName.toUpperCase()==tagName &&\n      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))\n        elements.push(e);\n    if(recursive) {\n      var grandchildren = Element.findChildren(e, only, recursive, tagName);\n      if(grandchildren) elements.push(grandchildren);\n    }\n  });\n\n  return (elements.length>0 ? elements.flatten() : []);\n};\n\nElement.offsetSize = function (element, type) {\n  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];\n};"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/public/javascripts/effects.js",
    "content": "// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n// Contributors:\n//  Justin Palmer (http://encytemedia.com/)\n//  Mark Pilgrim (http://diveintomark.org/)\n//  Martin Bialasinki\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\n// converts rgb() and #xxx to #xxxxxx format,\n// returns self (or first argument) if not convertable\nString.prototype.parseColor = function() {\n  var color = '#';\n  if (this.slice(0,4) == 'rgb(') {\n    var cols = this.slice(4,this.length-1).split(',');\n    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);\n  } else {\n    if (this.slice(0,1) == '#') {\n      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();\n      if (this.length==7) color = this.toLowerCase();\n    }\n  }\n  return (color.length==7 ? color : (arguments[0] || this));\n};\n\n/*--------------------------------------------------------------------------*/\n\nElement.collectTextNodes = function(element) {\n  return $A($(element).childNodes).collect( function(node) {\n    return (node.nodeType==3 ? node.nodeValue :\n      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));\n  }).flatten().join('');\n};\n\nElement.collectTextNodesIgnoreClass = function(element, className) {\n  return $A($(element).childNodes).collect( function(node) {\n    return (node.nodeType==3 ? node.nodeValue :\n      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?\n        Element.collectTextNodesIgnoreClass(node, className) : ''));\n  }).flatten().join('');\n};\n\nElement.setContentZoom = function(element, percent) {\n  element = $(element);\n  element.setStyle({fontSize: (percent/100) + 'em'});\n  if (Prototype.Browser.WebKit) window.scrollBy(0,0);\n  return element;\n};\n\nElement.getInlineOpacity = function(element){\n  return $(element).style.opacity || '';\n};\n\nElement.forceRerendering = function(element) {\n  try {\n    element = $(element);\n    var n = document.createTextNode(' ');\n    element.appendChild(n);\n    element.removeChild(n);\n  } catch(e) { }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Effect = {\n  _elementDoesNotExistError: {\n    name: 'ElementDoesNotExistError',\n    message: 'The specified DOM element does not exist, but is required for this effect to operate'\n  },\n  Transitions: {\n    linear: Prototype.K,\n    sinoidal: function(pos) {\n      return (-Math.cos(pos*Math.PI)/2) + .5;\n    },\n    reverse: function(pos) {\n      return 1-pos;\n    },\n    flicker: function(pos) {\n      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;\n      return pos > 1 ? 1 : pos;\n    },\n    wobble: function(pos) {\n      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;\n    },\n    pulse: function(pos, pulses) {\n      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;\n    },\n    spring: function(pos) {\n      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));\n    },\n    none: function(pos) {\n      return 0;\n    },\n    full: function(pos) {\n      return 1;\n    }\n  },\n  DefaultOptions: {\n    duration:   1.0,   // seconds\n    fps:        100,   // 100= assume 66fps max.\n    sync:       false, // true for combining\n    from:       0.0,\n    to:         1.0,\n    delay:      0.0,\n    queue:      'parallel'\n  },\n  tagifyText: function(element) {\n    var tagifyStyle = 'position:relative';\n    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';\n\n    element = $(element);\n    $A(element.childNodes).each( function(child) {\n      if (child.nodeType==3) {\n        child.nodeValue.toArray().each( function(character) {\n          element.insertBefore(\n            new Element('span', {style: tagifyStyle}).update(\n              character == ' ' ? String.fromCharCode(160) : character),\n              child);\n        });\n        Element.remove(child);\n      }\n    });\n  },\n  multiple: function(element, effect) {\n    var elements;\n    if (((typeof element == 'object') ||\n        Object.isFunction(element)) &&\n       (element.length))\n      elements = element;\n    else\n      elements = $(element).childNodes;\n\n    var options = Object.extend({\n      speed: 0.1,\n      delay: 0.0\n    }, arguments[2] || { });\n    var masterDelay = options.delay;\n\n    $A(elements).each( function(element, index) {\n      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));\n    });\n  },\n  PAIRS: {\n    'slide':  ['SlideDown','SlideUp'],\n    'blind':  ['BlindDown','BlindUp'],\n    'appear': ['Appear','Fade']\n  },\n  toggle: function(element, effect) {\n    element = $(element);\n    effect = (effect || 'appear').toLowerCase();\n    var options = Object.extend({\n      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }\n    }, arguments[2] || { });\n    Effect[element.visible() ?\n      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);\n  }\n};\n\nEffect.DefaultOptions.transition = Effect.Transitions.sinoidal;\n\n/* ------------- core effects ------------- */\n\nEffect.ScopedQueue = Class.create(Enumerable, {\n  initialize: function() {\n    this.effects  = [];\n    this.interval = null;\n  },\n  _each: function(iterator) {\n    this.effects._each(iterator);\n  },\n  add: function(effect) {\n    var timestamp = new Date().getTime();\n\n    var position = Object.isString(effect.options.queue) ?\n      effect.options.queue : effect.options.queue.position;\n\n    switch(position) {\n      case 'front':\n        // move unstarted effects after this effect\n        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {\n            e.startOn  += effect.finishOn;\n            e.finishOn += effect.finishOn;\n          });\n        break;\n      case 'with-last':\n        timestamp = this.effects.pluck('startOn').max() || timestamp;\n        break;\n      case 'end':\n        // start effect after last queued effect has finished\n        timestamp = this.effects.pluck('finishOn').max() || timestamp;\n        break;\n    }\n\n    effect.startOn  += timestamp;\n    effect.finishOn += timestamp;\n\n    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))\n      this.effects.push(effect);\n\n    if (!this.interval)\n      this.interval = setInterval(this.loop.bind(this), 15);\n  },\n  remove: function(effect) {\n    this.effects = this.effects.reject(function(e) { return e==effect });\n    if (this.effects.length == 0) {\n      clearInterval(this.interval);\n      this.interval = null;\n    }\n  },\n  loop: function() {\n    var timePos = new Date().getTime();\n    for(var i=0, len=this.effects.length;i<len;i++)\n      this.effects[i] && this.effects[i].loop(timePos);\n  }\n});\n\nEffect.Queues = {\n  instances: $H(),\n  get: function(queueName) {\n    if (!Object.isString(queueName)) return queueName;\n\n    return this.instances.get(queueName) ||\n      this.instances.set(queueName, new Effect.ScopedQueue());\n  }\n};\nEffect.Queue = Effect.Queues.get('global');\n\nEffect.Base = Class.create({\n  position: null,\n  start: function(options) {\n    function codeForEvent(options,eventName){\n      return (\n        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +\n        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')\n      );\n    }\n    if (options && options.transition === false) options.transition = Effect.Transitions.linear;\n    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });\n    this.currentFrame = 0;\n    this.state        = 'idle';\n    this.startOn      = this.options.delay*1000;\n    this.finishOn     = this.startOn+(this.options.duration*1000);\n    this.fromToDelta  = this.options.to-this.options.from;\n    this.totalTime    = this.finishOn-this.startOn;\n    this.totalFrames  = this.options.fps*this.options.duration;\n\n    this.render = (function() {\n      function dispatch(effect, eventName) {\n        if (effect.options[eventName + 'Internal'])\n          effect.options[eventName + 'Internal'](effect);\n        if (effect.options[eventName])\n          effect.options[eventName](effect);\n      }\n\n      return function(pos) {\n        if (this.state === \"idle\") {\n          this.state = \"running\";\n          dispatch(this, 'beforeSetup');\n          if (this.setup) this.setup();\n          dispatch(this, 'afterSetup');\n        }\n        if (this.state === \"running\") {\n          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;\n          this.position = pos;\n          dispatch(this, 'beforeUpdate');\n          if (this.update) this.update(pos);\n          dispatch(this, 'afterUpdate');\n        }\n      };\n    })();\n\n    this.event('beforeStart');\n    if (!this.options.sync)\n      Effect.Queues.get(Object.isString(this.options.queue) ?\n        'global' : this.options.queue.scope).add(this);\n  },\n  loop: function(timePos) {\n    if (timePos >= this.startOn) {\n      if (timePos >= this.finishOn) {\n        this.render(1.0);\n        this.cancel();\n        this.event('beforeFinish');\n        if (this.finish) this.finish();\n        this.event('afterFinish');\n        return;\n      }\n      var pos   = (timePos - this.startOn) / this.totalTime,\n          frame = (pos * this.totalFrames).round();\n      if (frame > this.currentFrame) {\n        this.render(pos);\n        this.currentFrame = frame;\n      }\n    }\n  },\n  cancel: function() {\n    if (!this.options.sync)\n      Effect.Queues.get(Object.isString(this.options.queue) ?\n        'global' : this.options.queue.scope).remove(this);\n    this.state = 'finished';\n  },\n  event: function(eventName) {\n    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);\n    if (this.options[eventName]) this.options[eventName](this);\n  },\n  inspect: function() {\n    var data = $H();\n    for(property in this)\n      if (!Object.isFunction(this[property])) data.set(property, this[property]);\n    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';\n  }\n});\n\nEffect.Parallel = Class.create(Effect.Base, {\n  initialize: function(effects) {\n    this.effects = effects || [];\n    this.start(arguments[1]);\n  },\n  update: function(position) {\n    this.effects.invoke('render', position);\n  },\n  finish: function(position) {\n    this.effects.each( function(effect) {\n      effect.render(1.0);\n      effect.cancel();\n      effect.event('beforeFinish');\n      if (effect.finish) effect.finish(position);\n      effect.event('afterFinish');\n    });\n  }\n});\n\nEffect.Tween = Class.create(Effect.Base, {\n  initialize: function(object, from, to) {\n    object = Object.isString(object) ? $(object) : object;\n    var args = $A(arguments), method = args.last(),\n      options = args.length == 5 ? args[3] : null;\n    this.method = Object.isFunction(method) ? method.bind(object) :\n      Object.isFunction(object[method]) ? object[method].bind(object) :\n      function(value) { object[method] = value };\n    this.start(Object.extend({ from: from, to: to }, options || { }));\n  },\n  update: function(position) {\n    this.method(position);\n  }\n});\n\nEffect.Event = Class.create(Effect.Base, {\n  initialize: function() {\n    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));\n  },\n  update: Prototype.emptyFunction\n});\n\nEffect.Opacity = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    // make this work on IE on elements without 'layout'\n    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))\n      this.element.setStyle({zoom: 1});\n    var options = Object.extend({\n      from: this.element.getOpacity() || 0.0,\n      to:   1.0\n    }, arguments[1] || { });\n    this.start(options);\n  },\n  update: function(position) {\n    this.element.setOpacity(position);\n  }\n});\n\nEffect.Move = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({\n      x:    0,\n      y:    0,\n      mode: 'relative'\n    }, arguments[1] || { });\n    this.start(options);\n  },\n  setup: function() {\n    this.element.makePositioned();\n    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');\n    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');\n    if (this.options.mode == 'absolute') {\n      this.options.x = this.options.x - this.originalLeft;\n      this.options.y = this.options.y - this.originalTop;\n    }\n  },\n  update: function(position) {\n    this.element.setStyle({\n      left: (this.options.x  * position + this.originalLeft).round() + 'px',\n      top:  (this.options.y  * position + this.originalTop).round()  + 'px'\n    });\n  }\n});\n\n// for backwards compatibility\nEffect.MoveBy = function(element, toTop, toLeft) {\n  return new Effect.Move(element,\n    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));\n};\n\nEffect.Scale = Class.create(Effect.Base, {\n  initialize: function(element, percent) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({\n      scaleX: true,\n      scaleY: true,\n      scaleContent: true,\n      scaleFromCenter: false,\n      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values\n      scaleFrom: 100.0,\n      scaleTo:   percent\n    }, arguments[2] || { });\n    this.start(options);\n  },\n  setup: function() {\n    this.restoreAfterFinish = this.options.restoreAfterFinish || false;\n    this.elementPositioning = this.element.getStyle('position');\n\n    this.originalStyle = { };\n    ['top','left','width','height','fontSize'].each( function(k) {\n      this.originalStyle[k] = this.element.style[k];\n    }.bind(this));\n\n    this.originalTop  = this.element.offsetTop;\n    this.originalLeft = this.element.offsetLeft;\n\n    var fontSize = this.element.getStyle('font-size') || '100%';\n    ['em','px','%','pt'].each( function(fontSizeType) {\n      if (fontSize.indexOf(fontSizeType)>0) {\n        this.fontSize     = parseFloat(fontSize);\n        this.fontSizeType = fontSizeType;\n      }\n    }.bind(this));\n\n    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;\n\n    this.dims = null;\n    if (this.options.scaleMode=='box')\n      this.dims = [this.element.offsetHeight, this.element.offsetWidth];\n    if (/^content/.test(this.options.scaleMode))\n      this.dims = [this.element.scrollHeight, this.element.scrollWidth];\n    if (!this.dims)\n      this.dims = [this.options.scaleMode.originalHeight,\n                   this.options.scaleMode.originalWidth];\n  },\n  update: function(position) {\n    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);\n    if (this.options.scaleContent && this.fontSize)\n      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });\n    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);\n  },\n  finish: function(position) {\n    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);\n  },\n  setDimensions: function(height, width) {\n    var d = { };\n    if (this.options.scaleX) d.width = width.round() + 'px';\n    if (this.options.scaleY) d.height = height.round() + 'px';\n    if (this.options.scaleFromCenter) {\n      var topd  = (height - this.dims[0])/2;\n      var leftd = (width  - this.dims[1])/2;\n      if (this.elementPositioning == 'absolute') {\n        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';\n        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';\n      } else {\n        if (this.options.scaleY) d.top = -topd + 'px';\n        if (this.options.scaleX) d.left = -leftd + 'px';\n      }\n    }\n    this.element.setStyle(d);\n  }\n});\n\nEffect.Highlight = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });\n    this.start(options);\n  },\n  setup: function() {\n    // Prevent executing on elements not in the layout flow\n    if (this.element.getStyle('display')=='none') { this.cancel(); return; }\n    // Disable background image during the effect\n    this.oldStyle = { };\n    if (!this.options.keepBackgroundImage) {\n      this.oldStyle.backgroundImage = this.element.getStyle('background-image');\n      this.element.setStyle({backgroundImage: 'none'});\n    }\n    if (!this.options.endcolor)\n      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');\n    if (!this.options.restorecolor)\n      this.options.restorecolor = this.element.getStyle('background-color');\n    // init color calculations\n    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));\n    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));\n  },\n  update: function(position) {\n    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){\n      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });\n  },\n  finish: function() {\n    this.element.setStyle(Object.extend(this.oldStyle, {\n      backgroundColor: this.options.restorecolor\n    }));\n  }\n});\n\nEffect.ScrollTo = function(element) {\n  var options = arguments[1] || { },\n  scrollOffsets = document.viewport.getScrollOffsets(),\n  elementOffsets = $(element).cumulativeOffset();\n\n  if (options.offset) elementOffsets[1] += options.offset;\n\n  return new Effect.Tween(null,\n    scrollOffsets.top,\n    elementOffsets[1],\n    options,\n    function(p){ scrollTo(scrollOffsets.left, p.round()); }\n  );\n};\n\n/* ------------- combination effects ------------- */\n\nEffect.Fade = function(element) {\n  element = $(element);\n  var oldOpacity = element.getInlineOpacity();\n  var options = Object.extend({\n    from: element.getOpacity() || 1.0,\n    to:   0.0,\n    afterFinishInternal: function(effect) {\n      if (effect.options.to!=0) return;\n      effect.element.hide().setStyle({opacity: oldOpacity});\n    }\n  }, arguments[1] || { });\n  return new Effect.Opacity(element,options);\n};\n\nEffect.Appear = function(element) {\n  element = $(element);\n  var options = Object.extend({\n  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),\n  to:   1.0,\n  // force Safari to render floated elements properly\n  afterFinishInternal: function(effect) {\n    effect.element.forceRerendering();\n  },\n  beforeSetup: function(effect) {\n    effect.element.setOpacity(effect.options.from).show();\n  }}, arguments[1] || { });\n  return new Effect.Opacity(element,options);\n};\n\nEffect.Puff = function(element) {\n  element = $(element);\n  var oldStyle = {\n    opacity: element.getInlineOpacity(),\n    position: element.getStyle('position'),\n    top:  element.style.top,\n    left: element.style.left,\n    width: element.style.width,\n    height: element.style.height\n  };\n  return new Effect.Parallel(\n   [ new Effect.Scale(element, 200,\n      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),\n     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],\n     Object.extend({ duration: 1.0,\n      beforeSetupInternal: function(effect) {\n        Position.absolutize(effect.effects[0].element);\n      },\n      afterFinishInternal: function(effect) {\n         effect.effects[0].element.hide().setStyle(oldStyle); }\n     }, arguments[1] || { })\n   );\n};\n\nEffect.BlindUp = function(element) {\n  element = $(element);\n  element.makeClipping();\n  return new Effect.Scale(element, 0,\n    Object.extend({ scaleContent: false,\n      scaleX: false,\n      restoreAfterFinish: true,\n      afterFinishInternal: function(effect) {\n        effect.element.hide().undoClipping();\n      }\n    }, arguments[1] || { })\n  );\n};\n\nEffect.BlindDown = function(element) {\n  element = $(element);\n  var elementDimensions = element.getDimensions();\n  return new Effect.Scale(element, 100, Object.extend({\n    scaleContent: false,\n    scaleX: false,\n    scaleFrom: 0,\n    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},\n    restoreAfterFinish: true,\n    afterSetup: function(effect) {\n      effect.element.makeClipping().setStyle({height: '0px'}).show();\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.undoClipping();\n    }\n  }, arguments[1] || { }));\n};\n\nEffect.SwitchOff = function(element) {\n  element = $(element);\n  var oldOpacity = element.getInlineOpacity();\n  return new Effect.Appear(element, Object.extend({\n    duration: 0.4,\n    from: 0,\n    transition: Effect.Transitions.flicker,\n    afterFinishInternal: function(effect) {\n      new Effect.Scale(effect.element, 1, {\n        duration: 0.3, scaleFromCenter: true,\n        scaleX: false, scaleContent: false, restoreAfterFinish: true,\n        beforeSetup: function(effect) {\n          effect.element.makePositioned().makeClipping();\n        },\n        afterFinishInternal: function(effect) {\n          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});\n        }\n      });\n    }\n  }, arguments[1] || { }));\n};\n\nEffect.DropOut = function(element) {\n  element = $(element);\n  var oldStyle = {\n    top: element.getStyle('top'),\n    left: element.getStyle('left'),\n    opacity: element.getInlineOpacity() };\n  return new Effect.Parallel(\n    [ new Effect.Move(element, {x: 0, y: 100, sync: true }),\n      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],\n    Object.extend(\n      { duration: 0.5,\n        beforeSetup: function(effect) {\n          effect.effects[0].element.makePositioned();\n        },\n        afterFinishInternal: function(effect) {\n          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);\n        }\n      }, arguments[1] || { }));\n};\n\nEffect.Shake = function(element) {\n  element = $(element);\n  var options = Object.extend({\n    distance: 20,\n    duration: 0.5\n  }, arguments[1] || {});\n  var distance = parseFloat(options.distance);\n  var split = parseFloat(options.duration) / 10.0;\n  var oldStyle = {\n    top: element.getStyle('top'),\n    left: element.getStyle('left') };\n    return new Effect.Move(element,\n      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {\n        effect.element.undoPositioned().setStyle(oldStyle);\n  }}); }}); }}); }}); }}); }});\n};\n\nEffect.SlideDown = function(element) {\n  element = $(element).cleanWhitespace();\n  // SlideDown need to have the content of the element wrapped in a container element with fixed height!\n  var oldInnerBottom = element.down().getStyle('bottom');\n  var elementDimensions = element.getDimensions();\n  return new Effect.Scale(element, 100, Object.extend({\n    scaleContent: false,\n    scaleX: false,\n    scaleFrom: window.opera ? 0 : 1,\n    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},\n    restoreAfterFinish: true,\n    afterSetup: function(effect) {\n      effect.element.makePositioned();\n      effect.element.down().makePositioned();\n      if (window.opera) effect.element.setStyle({top: ''});\n      effect.element.makeClipping().setStyle({height: '0px'}).show();\n    },\n    afterUpdateInternal: function(effect) {\n      effect.element.down().setStyle({bottom:\n        (effect.dims[0] - effect.element.clientHeight) + 'px' });\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.undoClipping().undoPositioned();\n      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }\n    }, arguments[1] || { })\n  );\n};\n\nEffect.SlideUp = function(element) {\n  element = $(element).cleanWhitespace();\n  var oldInnerBottom = element.down().getStyle('bottom');\n  var elementDimensions = element.getDimensions();\n  return new Effect.Scale(element, window.opera ? 0 : 1,\n   Object.extend({ scaleContent: false,\n    scaleX: false,\n    scaleMode: 'box',\n    scaleFrom: 100,\n    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},\n    restoreAfterFinish: true,\n    afterSetup: function(effect) {\n      effect.element.makePositioned();\n      effect.element.down().makePositioned();\n      if (window.opera) effect.element.setStyle({top: ''});\n      effect.element.makeClipping().show();\n    },\n    afterUpdateInternal: function(effect) {\n      effect.element.down().setStyle({bottom:\n        (effect.dims[0] - effect.element.clientHeight) + 'px' });\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.hide().undoClipping().undoPositioned();\n      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});\n    }\n   }, arguments[1] || { })\n  );\n};\n\n// Bug in opera makes the TD containing this element expand for a instance after finish\nEffect.Squish = function(element) {\n  return new Effect.Scale(element, window.opera ? 1 : 0, {\n    restoreAfterFinish: true,\n    beforeSetup: function(effect) {\n      effect.element.makeClipping();\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.hide().undoClipping();\n    }\n  });\n};\n\nEffect.Grow = function(element) {\n  element = $(element);\n  var options = Object.extend({\n    direction: 'center',\n    moveTransition: Effect.Transitions.sinoidal,\n    scaleTransition: Effect.Transitions.sinoidal,\n    opacityTransition: Effect.Transitions.full\n  }, arguments[1] || { });\n  var oldStyle = {\n    top: element.style.top,\n    left: element.style.left,\n    height: element.style.height,\n    width: element.style.width,\n    opacity: element.getInlineOpacity() };\n\n  var dims = element.getDimensions();\n  var initialMoveX, initialMoveY;\n  var moveX, moveY;\n\n  switch (options.direction) {\n    case 'top-left':\n      initialMoveX = initialMoveY = moveX = moveY = 0;\n      break;\n    case 'top-right':\n      initialMoveX = dims.width;\n      initialMoveY = moveY = 0;\n      moveX = -dims.width;\n      break;\n    case 'bottom-left':\n      initialMoveX = moveX = 0;\n      initialMoveY = dims.height;\n      moveY = -dims.height;\n      break;\n    case 'bottom-right':\n      initialMoveX = dims.width;\n      initialMoveY = dims.height;\n      moveX = -dims.width;\n      moveY = -dims.height;\n      break;\n    case 'center':\n      initialMoveX = dims.width / 2;\n      initialMoveY = dims.height / 2;\n      moveX = -dims.width / 2;\n      moveY = -dims.height / 2;\n      break;\n  }\n\n  return new Effect.Move(element, {\n    x: initialMoveX,\n    y: initialMoveY,\n    duration: 0.01,\n    beforeSetup: function(effect) {\n      effect.element.hide().makeClipping().makePositioned();\n    },\n    afterFinishInternal: function(effect) {\n      new Effect.Parallel(\n        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),\n          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),\n          new Effect.Scale(effect.element, 100, {\n            scaleMode: { originalHeight: dims.height, originalWidth: dims.width },\n            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})\n        ], Object.extend({\n             beforeSetup: function(effect) {\n               effect.effects[0].element.setStyle({height: '0px'}).show();\n             },\n             afterFinishInternal: function(effect) {\n               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);\n             }\n           }, options)\n      );\n    }\n  });\n};\n\nEffect.Shrink = function(element) {\n  element = $(element);\n  var options = Object.extend({\n    direction: 'center',\n    moveTransition: Effect.Transitions.sinoidal,\n    scaleTransition: Effect.Transitions.sinoidal,\n    opacityTransition: Effect.Transitions.none\n  }, arguments[1] || { });\n  var oldStyle = {\n    top: element.style.top,\n    left: element.style.left,\n    height: element.style.height,\n    width: element.style.width,\n    opacity: element.getInlineOpacity() };\n\n  var dims = element.getDimensions();\n  var moveX, moveY;\n\n  switch (options.direction) {\n    case 'top-left':\n      moveX = moveY = 0;\n      break;\n    case 'top-right':\n      moveX = dims.width;\n      moveY = 0;\n      break;\n    case 'bottom-left':\n      moveX = 0;\n      moveY = dims.height;\n      break;\n    case 'bottom-right':\n      moveX = dims.width;\n      moveY = dims.height;\n      break;\n    case 'center':\n      moveX = dims.width / 2;\n      moveY = dims.height / 2;\n      break;\n  }\n\n  return new Effect.Parallel(\n    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),\n      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),\n      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })\n    ], Object.extend({\n         beforeStartInternal: function(effect) {\n           effect.effects[0].element.makePositioned().makeClipping();\n         },\n         afterFinishInternal: function(effect) {\n           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }\n       }, options)\n  );\n};\n\nEffect.Pulsate = function(element) {\n  element = $(element);\n  var options    = arguments[1] || { },\n    oldOpacity = element.getInlineOpacity(),\n    transition = options.transition || Effect.Transitions.linear,\n    reverser   = function(pos){\n      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);\n    };\n\n  return new Effect.Opacity(element,\n    Object.extend(Object.extend({  duration: 2.0, from: 0,\n      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }\n    }, options), {transition: reverser}));\n};\n\nEffect.Fold = function(element) {\n  element = $(element);\n  var oldStyle = {\n    top: element.style.top,\n    left: element.style.left,\n    width: element.style.width,\n    height: element.style.height };\n  element.makeClipping();\n  return new Effect.Scale(element, 5, Object.extend({\n    scaleContent: false,\n    scaleX: false,\n    afterFinishInternal: function(effect) {\n    new Effect.Scale(element, 1, {\n      scaleContent: false,\n      scaleY: false,\n      afterFinishInternal: function(effect) {\n        effect.element.hide().undoClipping().setStyle(oldStyle);\n      } });\n  }}, arguments[1] || { }));\n};\n\nEffect.Morph = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({\n      style: { }\n    }, arguments[1] || { });\n\n    if (!Object.isString(options.style)) this.style = $H(options.style);\n    else {\n      if (options.style.include(':'))\n        this.style = options.style.parseStyle();\n      else {\n        this.element.addClassName(options.style);\n        this.style = $H(this.element.getStyles());\n        this.element.removeClassName(options.style);\n        var css = this.element.getStyles();\n        this.style = this.style.reject(function(style) {\n          return style.value == css[style.key];\n        });\n        options.afterFinishInternal = function(effect) {\n          effect.element.addClassName(effect.options.style);\n          effect.transforms.each(function(transform) {\n            effect.element.style[transform.style] = '';\n          });\n        };\n      }\n    }\n    this.start(options);\n  },\n\n  setup: function(){\n    function parseColor(color){\n      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';\n      color = color.parseColor();\n      return $R(0,2).map(function(i){\n        return parseInt( color.slice(i*2+1,i*2+3), 16 );\n      });\n    }\n    this.transforms = this.style.map(function(pair){\n      var property = pair[0], value = pair[1], unit = null;\n\n      if (value.parseColor('#zzzzzz') != '#zzzzzz') {\n        value = value.parseColor();\n        unit  = 'color';\n      } else if (property == 'opacity') {\n        value = parseFloat(value);\n        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))\n          this.element.setStyle({zoom: 1});\n      } else if (Element.CSS_LENGTH.test(value)) {\n          var components = value.match(/^([\\+\\-]?[0-9\\.]+)(.*)$/);\n          value = parseFloat(components[1]);\n          unit = (components.length == 3) ? components[2] : null;\n      }\n\n      var originalValue = this.element.getStyle(property);\n      return {\n        style: property.camelize(),\n        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),\n        targetValue: unit=='color' ? parseColor(value) : value,\n        unit: unit\n      };\n    }.bind(this)).reject(function(transform){\n      return (\n        (transform.originalValue == transform.targetValue) ||\n        (\n          transform.unit != 'color' &&\n          (isNaN(transform.originalValue) || isNaN(transform.targetValue))\n        )\n      );\n    });\n  },\n  update: function(position) {\n    var style = { }, transform, i = this.transforms.length;\n    while(i--)\n      style[(transform = this.transforms[i]).style] =\n        transform.unit=='color' ? '#'+\n          (Math.round(transform.originalValue[0]+\n            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +\n          (Math.round(transform.originalValue[1]+\n            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +\n          (Math.round(transform.originalValue[2]+\n            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :\n        (transform.originalValue +\n          (transform.targetValue - transform.originalValue) * position).toFixed(3) +\n            (transform.unit === null ? '' : transform.unit);\n    this.element.setStyle(style, true);\n  }\n});\n\nEffect.Transform = Class.create({\n  initialize: function(tracks){\n    this.tracks  = [];\n    this.options = arguments[1] || { };\n    this.addTracks(tracks);\n  },\n  addTracks: function(tracks){\n    tracks.each(function(track){\n      track = $H(track);\n      var data = track.values().first();\n      this.tracks.push($H({\n        ids:     track.keys().first(),\n        effect:  Effect.Morph,\n        options: { style: data }\n      }));\n    }.bind(this));\n    return this;\n  },\n  play: function(){\n    return new Effect.Parallel(\n      this.tracks.map(function(track){\n        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');\n        var elements = [$(ids) || $$(ids)].flatten();\n        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });\n      }).flatten(),\n      this.options\n    );\n  }\n});\n\nElement.CSS_PROPERTIES = $w(\n  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +\n  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +\n  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +\n  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +\n  'fontSize fontWeight height left letterSpacing lineHeight ' +\n  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+\n  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +\n  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +\n  'right textIndent top width wordSpacing zIndex');\n\nElement.CSS_LENGTH = /^(([\\+\\-]?[0-9\\.]+)(em|ex|px|in|cm|mm|pt|pc|\\%))|0$/;\n\nString.__parseStyleElement = document.createElement('div');\nString.prototype.parseStyle = function(){\n  var style, styleRules = $H();\n  if (Prototype.Browser.WebKit)\n    style = new Element('div',{style:this}).style;\n  else {\n    String.__parseStyleElement.innerHTML = '<div style=\"' + this + '\"></div>';\n    style = String.__parseStyleElement.childNodes[0].style;\n  }\n\n  Element.CSS_PROPERTIES.each(function(property){\n    if (style[property]) styleRules.set(property, style[property]);\n  });\n\n  if (Prototype.Browser.IE && this.include('opacity'))\n    styleRules.set('opacity', this.match(/opacity:\\s*((?:0|1)?(?:\\.\\d*)?)/)[1]);\n\n  return styleRules;\n};\n\nif (document.defaultView && document.defaultView.getComputedStyle) {\n  Element.getStyles = function(element) {\n    var css = document.defaultView.getComputedStyle($(element), null);\n    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {\n      styles[property] = css[property];\n      return styles;\n    });\n  };\n} else {\n  Element.getStyles = function(element) {\n    element = $(element);\n    var css = element.currentStyle, styles;\n    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {\n      results[property] = css[property];\n      return results;\n    });\n    if (!styles.opacity) styles.opacity = element.getOpacity();\n    return styles;\n  };\n}\n\nEffect.Methods = {\n  morph: function(element, style) {\n    element = $(element);\n    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));\n    return element;\n  },\n  visualEffect: function(element, effect, options) {\n    element = $(element);\n    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);\n    new Effect[klass](element, options);\n    return element;\n  },\n  highlight: function(element, options) {\n    element = $(element);\n    new Effect.Highlight(element, options);\n    return element;\n  }\n};\n\n$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+\n  'pulsate shake puff squish switchOff dropOut').each(\n  function(effect) {\n    Effect.Methods[effect] = function(element, options){\n      element = $(element);\n      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);\n      return element;\n    };\n  }\n);\n\n$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(\n  function(f) { Effect.Methods[f] = Element[f]; }\n);\n\nElement.addMethods(Effect.Methods);"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/public/javascripts/prototype.js",
    "content": "/*  Prototype JavaScript framework, version 1.6.0.3\n *  (c) 2005-2008 Sam Stephenson\n *\n *  Prototype is freely distributable under the terms of an MIT-style license.\n *  For details, see the Prototype web site: http://www.prototypejs.org/\n *\n *--------------------------------------------------------------------------*/\n\nvar Prototype = {\n  Version: '1.6.0.3',\n\n  Browser: {\n    IE:     !!(window.attachEvent &&\n      navigator.userAgent.indexOf('Opera') === -1),\n    Opera:  navigator.userAgent.indexOf('Opera') > -1,\n    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,\n    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 &&\n      navigator.userAgent.indexOf('KHTML') === -1,\n    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)\n  },\n\n  BrowserFeatures: {\n    XPath: !!document.evaluate,\n    SelectorsAPI: !!document.querySelector,\n    ElementExtensions: !!window.HTMLElement,\n    SpecificElementExtensions:\n      document.createElement('div')['__proto__'] &&\n      document.createElement('div')['__proto__'] !==\n        document.createElement('form')['__proto__']\n  },\n\n  ScriptFragment: '<script[^>]*>([\\\\S\\\\s]*?)<\\/script>',\n  JSONFilter: /^\\/\\*-secure-([\\s\\S]*)\\*\\/\\s*$/,\n\n  emptyFunction: function() { },\n  K: function(x) { return x }\n};\n\nif (Prototype.Browser.MobileSafari)\n  Prototype.BrowserFeatures.SpecificElementExtensions = false;\n\n\n/* Based on Alex Arnell's inheritance implementation. */\nvar Class = {\n  create: function() {\n    var parent = null, properties = $A(arguments);\n    if (Object.isFunction(properties[0]))\n      parent = properties.shift();\n\n    function klass() {\n      this.initialize.apply(this, arguments);\n    }\n\n    Object.extend(klass, Class.Methods);\n    klass.superclass = parent;\n    klass.subclasses = [];\n\n    if (parent) {\n      var subclass = function() { };\n      subclass.prototype = parent.prototype;\n      klass.prototype = new subclass;\n      parent.subclasses.push(klass);\n    }\n\n    for (var i = 0; i < properties.length; i++)\n      klass.addMethods(properties[i]);\n\n    if (!klass.prototype.initialize)\n      klass.prototype.initialize = Prototype.emptyFunction;\n\n    klass.prototype.constructor = klass;\n\n    return klass;\n  }\n};\n\nClass.Methods = {\n  addMethods: function(source) {\n    var ancestor   = this.superclass && this.superclass.prototype;\n    var properties = Object.keys(source);\n\n    if (!Object.keys({ toString: true }).length)\n      properties.push(\"toString\", \"valueOf\");\n\n    for (var i = 0, length = properties.length; i < length; i++) {\n      var property = properties[i], value = source[property];\n      if (ancestor && Object.isFunction(value) &&\n          value.argumentNames().first() == \"$super\") {\n        var method = value;\n        value = (function(m) {\n          return function() { return ancestor[m].apply(this, arguments) };\n        })(property).wrap(method);\n\n        value.valueOf = method.valueOf.bind(method);\n        value.toString = method.toString.bind(method);\n      }\n      this.prototype[property] = value;\n    }\n\n    return this;\n  }\n};\n\nvar Abstract = { };\n\nObject.extend = function(destination, source) {\n  for (var property in source)\n    destination[property] = source[property];\n  return destination;\n};\n\nObject.extend(Object, {\n  inspect: function(object) {\n    try {\n      if (Object.isUndefined(object)) return 'undefined';\n      if (object === null) return 'null';\n      return object.inspect ? object.inspect() : String(object);\n    } catch (e) {\n      if (e instanceof RangeError) return '...';\n      throw e;\n    }\n  },\n\n  toJSON: function(object) {\n    var type = typeof object;\n    switch (type) {\n      case 'undefined':\n      case 'function':\n      case 'unknown': return;\n      case 'boolean': return object.toString();\n    }\n\n    if (object === null) return 'null';\n    if (object.toJSON) return object.toJSON();\n    if (Object.isElement(object)) return;\n\n    var results = [];\n    for (var property in object) {\n      var value = Object.toJSON(object[property]);\n      if (!Object.isUndefined(value))\n        results.push(property.toJSON() + ': ' + value);\n    }\n\n    return '{' + results.join(', ') + '}';\n  },\n\n  toQueryString: function(object) {\n    return $H(object).toQueryString();\n  },\n\n  toHTML: function(object) {\n    return object && object.toHTML ? object.toHTML() : String.interpret(object);\n  },\n\n  keys: function(object) {\n    var keys = [];\n    for (var property in object)\n      keys.push(property);\n    return keys;\n  },\n\n  values: function(object) {\n    var values = [];\n    for (var property in object)\n      values.push(object[property]);\n    return values;\n  },\n\n  clone: function(object) {\n    return Object.extend({ }, object);\n  },\n\n  isElement: function(object) {\n    return !!(object && object.nodeType == 1);\n  },\n\n  isArray: function(object) {\n    return object != null && typeof object == \"object\" &&\n      'splice' in object && 'join' in object;\n  },\n\n  isHash: function(object) {\n    return object instanceof Hash;\n  },\n\n  isFunction: function(object) {\n    return typeof object == \"function\";\n  },\n\n  isString: function(object) {\n    return typeof object == \"string\";\n  },\n\n  isNumber: function(object) {\n    return typeof object == \"number\";\n  },\n\n  isUndefined: function(object) {\n    return typeof object == \"undefined\";\n  }\n});\n\nObject.extend(Function.prototype, {\n  argumentNames: function() {\n    var names = this.toString().match(/^[\\s\\(]*function[^(]*\\(([^\\)]*)\\)/)[1]\n      .replace(/\\s+/g, '').split(',');\n    return names.length == 1 && !names[0] ? [] : names;\n  },\n\n  bind: function() {\n    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;\n    var __method = this, args = $A(arguments), object = args.shift();\n    return function() {\n      return __method.apply(object, args.concat($A(arguments)));\n    }\n  },\n\n  bindAsEventListener: function() {\n    var __method = this, args = $A(arguments), object = args.shift();\n    return function(event) {\n      return __method.apply(object, [event || window.event].concat(args));\n    }\n  },\n\n  curry: function() {\n    if (!arguments.length) return this;\n    var __method = this, args = $A(arguments);\n    return function() {\n      return __method.apply(this, args.concat($A(arguments)));\n    }\n  },\n\n  delay: function() {\n    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;\n    return window.setTimeout(function() {\n      return __method.apply(__method, args);\n    }, timeout);\n  },\n\n  defer: function() {\n    var args = [0.01].concat($A(arguments));\n    return this.delay.apply(this, args);\n  },\n\n  wrap: function(wrapper) {\n    var __method = this;\n    return function() {\n      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));\n    }\n  },\n\n  methodize: function() {\n    if (this._methodized) return this._methodized;\n    var __method = this;\n    return this._methodized = function() {\n      return __method.apply(null, [this].concat($A(arguments)));\n    };\n  }\n});\n\nDate.prototype.toJSON = function() {\n  return '\"' + this.getUTCFullYear() + '-' +\n    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +\n    this.getUTCDate().toPaddedString(2) + 'T' +\n    this.getUTCHours().toPaddedString(2) + ':' +\n    this.getUTCMinutes().toPaddedString(2) + ':' +\n    this.getUTCSeconds().toPaddedString(2) + 'Z\"';\n};\n\nvar Try = {\n  these: function() {\n    var returnValue;\n\n    for (var i = 0, length = arguments.length; i < length; i++) {\n      var lambda = arguments[i];\n      try {\n        returnValue = lambda();\n        break;\n      } catch (e) { }\n    }\n\n    return returnValue;\n  }\n};\n\nRegExp.prototype.match = RegExp.prototype.test;\n\nRegExp.escape = function(str) {\n  return String(str).replace(/([.*+?^=!:${}()|[\\]\\/\\\\])/g, '\\\\$1');\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar PeriodicalExecuter = Class.create({\n  initialize: function(callback, frequency) {\n    this.callback = callback;\n    this.frequency = frequency;\n    this.currentlyExecuting = false;\n\n    this.registerCallback();\n  },\n\n  registerCallback: function() {\n    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);\n  },\n\n  execute: function() {\n    this.callback(this);\n  },\n\n  stop: function() {\n    if (!this.timer) return;\n    clearInterval(this.timer);\n    this.timer = null;\n  },\n\n  onTimerEvent: function() {\n    if (!this.currentlyExecuting) {\n      try {\n        this.currentlyExecuting = true;\n        this.execute();\n      } finally {\n        this.currentlyExecuting = false;\n      }\n    }\n  }\n});\nObject.extend(String, {\n  interpret: function(value) {\n    return value == null ? '' : String(value);\n  },\n  specialChar: {\n    '\\b': '\\\\b',\n    '\\t': '\\\\t',\n    '\\n': '\\\\n',\n    '\\f': '\\\\f',\n    '\\r': '\\\\r',\n    '\\\\': '\\\\\\\\'\n  }\n});\n\nObject.extend(String.prototype, {\n  gsub: function(pattern, replacement) {\n    var result = '', source = this, match;\n    replacement = arguments.callee.prepareReplacement(replacement);\n\n    while (source.length > 0) {\n      if (match = source.match(pattern)) {\n        result += source.slice(0, match.index);\n        result += String.interpret(replacement(match));\n        source  = source.slice(match.index + match[0].length);\n      } else {\n        result += source, source = '';\n      }\n    }\n    return result;\n  },\n\n  sub: function(pattern, replacement, count) {\n    replacement = this.gsub.prepareReplacement(replacement);\n    count = Object.isUndefined(count) ? 1 : count;\n\n    return this.gsub(pattern, function(match) {\n      if (--count < 0) return match[0];\n      return replacement(match);\n    });\n  },\n\n  scan: function(pattern, iterator) {\n    this.gsub(pattern, iterator);\n    return String(this);\n  },\n\n  truncate: function(length, truncation) {\n    length = length || 30;\n    truncation = Object.isUndefined(truncation) ? '...' : truncation;\n    return this.length > length ?\n      this.slice(0, length - truncation.length) + truncation : String(this);\n  },\n\n  strip: function() {\n    return this.replace(/^\\s+/, '').replace(/\\s+$/, '');\n  },\n\n  stripTags: function() {\n    return this.replace(/<\\/?[^>]+>/gi, '');\n  },\n\n  stripScripts: function() {\n    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');\n  },\n\n  extractScripts: function() {\n    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');\n    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');\n    return (this.match(matchAll) || []).map(function(scriptTag) {\n      return (scriptTag.match(matchOne) || ['', ''])[1];\n    });\n  },\n\n  evalScripts: function() {\n    return this.extractScripts().map(function(script) { return eval(script) });\n  },\n\n  escapeHTML: function() {\n    var self = arguments.callee;\n    self.text.data = this;\n    return self.div.innerHTML;\n  },\n\n  unescapeHTML: function() {\n    var div = new Element('div');\n    div.innerHTML = this.stripTags();\n    return div.childNodes[0] ? (div.childNodes.length > 1 ?\n      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :\n      div.childNodes[0].nodeValue) : '';\n  },\n\n  toQueryParams: function(separator) {\n    var match = this.strip().match(/([^?#]*)(#.*)?$/);\n    if (!match) return { };\n\n    return match[1].split(separator || '&').inject({ }, function(hash, pair) {\n      if ((pair = pair.split('='))[0]) {\n        var key = decodeURIComponent(pair.shift());\n        var value = pair.length > 1 ? pair.join('=') : pair[0];\n        if (value != undefined) value = decodeURIComponent(value);\n\n        if (key in hash) {\n          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];\n          hash[key].push(value);\n        }\n        else hash[key] = value;\n      }\n      return hash;\n    });\n  },\n\n  toArray: function() {\n    return this.split('');\n  },\n\n  succ: function() {\n    return this.slice(0, this.length - 1) +\n      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);\n  },\n\n  times: function(count) {\n    return count < 1 ? '' : new Array(count + 1).join(this);\n  },\n\n  camelize: function() {\n    var parts = this.split('-'), len = parts.length;\n    if (len == 1) return parts[0];\n\n    var camelized = this.charAt(0) == '-'\n      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)\n      : parts[0];\n\n    for (var i = 1; i < len; i++)\n      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);\n\n    return camelized;\n  },\n\n  capitalize: function() {\n    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();\n  },\n\n  underscore: function() {\n    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();\n  },\n\n  dasherize: function() {\n    return this.gsub(/_/,'-');\n  },\n\n  inspect: function(useDoubleQuotes) {\n    var escapedString = this.gsub(/[\\x00-\\x1f\\\\]/, function(match) {\n      var character = String.specialChar[match[0]];\n      return character ? character : '\\\\u00' + match[0].charCodeAt().toPaddedString(2, 16);\n    });\n    if (useDoubleQuotes) return '\"' + escapedString.replace(/\"/g, '\\\\\"') + '\"';\n    return \"'\" + escapedString.replace(/'/g, '\\\\\\'') + \"'\";\n  },\n\n  toJSON: function() {\n    return this.inspect(true);\n  },\n\n  unfilterJSON: function(filter) {\n    return this.sub(filter || Prototype.JSONFilter, '#{1}');\n  },\n\n  isJSON: function() {\n    var str = this;\n    if (str.blank()) return false;\n    str = this.replace(/\\\\./g, '@').replace(/\"[^\"\\\\\\n\\r]*\"/g, '');\n    return (/^[,:{}\\[\\]0-9.\\-+Eaeflnr-u \\n\\r\\t]*$/).test(str);\n  },\n\n  evalJSON: function(sanitize) {\n    var json = this.unfilterJSON();\n    try {\n      if (!sanitize || json.isJSON()) return eval('(' + json + ')');\n    } catch (e) { }\n    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());\n  },\n\n  include: function(pattern) {\n    return this.indexOf(pattern) > -1;\n  },\n\n  startsWith: function(pattern) {\n    return this.indexOf(pattern) === 0;\n  },\n\n  endsWith: function(pattern) {\n    var d = this.length - pattern.length;\n    return d >= 0 && this.lastIndexOf(pattern) === d;\n  },\n\n  empty: function() {\n    return this == '';\n  },\n\n  blank: function() {\n    return /^\\s*$/.test(this);\n  },\n\n  interpolate: function(object, pattern) {\n    return new Template(this, pattern).evaluate(object);\n  }\n});\n\nif (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {\n  escapeHTML: function() {\n    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');\n  },\n  unescapeHTML: function() {\n    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');\n  }\n});\n\nString.prototype.gsub.prepareReplacement = function(replacement) {\n  if (Object.isFunction(replacement)) return replacement;\n  var template = new Template(replacement);\n  return function(match) { return template.evaluate(match) };\n};\n\nString.prototype.parseQuery = String.prototype.toQueryParams;\n\nObject.extend(String.prototype.escapeHTML, {\n  div:  document.createElement('div'),\n  text: document.createTextNode('')\n});\n\nString.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);\n\nvar Template = Class.create({\n  initialize: function(template, pattern) {\n    this.template = template.toString();\n    this.pattern = pattern || Template.Pattern;\n  },\n\n  evaluate: function(object) {\n    if (Object.isFunction(object.toTemplateReplacements))\n      object = object.toTemplateReplacements();\n\n    return this.template.gsub(this.pattern, function(match) {\n      if (object == null) return '';\n\n      var before = match[1] || '';\n      if (before == '\\\\') return match[2];\n\n      var ctx = object, expr = match[3];\n      var pattern = /^([^.[]+|\\[((?:.*?[^\\\\])?)\\])(\\.|\\[|$)/;\n      match = pattern.exec(expr);\n      if (match == null) return before;\n\n      while (match != null) {\n        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\\\\\]', ']') : match[1];\n        ctx = ctx[comp];\n        if (null == ctx || '' == match[3]) break;\n        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);\n        match = pattern.exec(expr);\n      }\n\n      return before + String.interpret(ctx);\n    });\n  }\n});\nTemplate.Pattern = /(^|.|\\r|\\n)(#\\{(.*?)\\})/;\n\nvar $break = { };\n\nvar Enumerable = {\n  each: function(iterator, context) {\n    var index = 0;\n    try {\n      this._each(function(value) {\n        iterator.call(context, value, index++);\n      });\n    } catch (e) {\n      if (e != $break) throw e;\n    }\n    return this;\n  },\n\n  eachSlice: function(number, iterator, context) {\n    var index = -number, slices = [], array = this.toArray();\n    if (number < 1) return array;\n    while ((index += number) < array.length)\n      slices.push(array.slice(index, index+number));\n    return slices.collect(iterator, context);\n  },\n\n  all: function(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result = true;\n    this.each(function(value, index) {\n      result = result && !!iterator.call(context, value, index);\n      if (!result) throw $break;\n    });\n    return result;\n  },\n\n  any: function(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result = false;\n    this.each(function(value, index) {\n      if (result = !!iterator.call(context, value, index))\n        throw $break;\n    });\n    return result;\n  },\n\n  collect: function(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var results = [];\n    this.each(function(value, index) {\n      results.push(iterator.call(context, value, index));\n    });\n    return results;\n  },\n\n  detect: function(iterator, context) {\n    var result;\n    this.each(function(value, index) {\n      if (iterator.call(context, value, index)) {\n        result = value;\n        throw $break;\n      }\n    });\n    return result;\n  },\n\n  findAll: function(iterator, context) {\n    var results = [];\n    this.each(function(value, index) {\n      if (iterator.call(context, value, index))\n        results.push(value);\n    });\n    return results;\n  },\n\n  grep: function(filter, iterator, context) {\n    iterator = iterator || Prototype.K;\n    var results = [];\n\n    if (Object.isString(filter))\n      filter = new RegExp(filter);\n\n    this.each(function(value, index) {\n      if (filter.match(value))\n        results.push(iterator.call(context, value, index));\n    });\n    return results;\n  },\n\n  include: function(object) {\n    if (Object.isFunction(this.indexOf))\n      if (this.indexOf(object) != -1) return true;\n\n    var found = false;\n    this.each(function(value) {\n      if (value == object) {\n        found = true;\n        throw $break;\n      }\n    });\n    return found;\n  },\n\n  inGroupsOf: function(number, fillWith) {\n    fillWith = Object.isUndefined(fillWith) ? null : fillWith;\n    return this.eachSlice(number, function(slice) {\n      while(slice.length < number) slice.push(fillWith);\n      return slice;\n    });\n  },\n\n  inject: function(memo, iterator, context) {\n    this.each(function(value, index) {\n      memo = iterator.call(context, memo, value, index);\n    });\n    return memo;\n  },\n\n  invoke: function(method) {\n    var args = $A(arguments).slice(1);\n    return this.map(function(value) {\n      return value[method].apply(value, args);\n    });\n  },\n\n  max: function(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result;\n    this.each(function(value, index) {\n      value = iterator.call(context, value, index);\n      if (result == null || value >= result)\n        result = value;\n    });\n    return result;\n  },\n\n  min: function(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result;\n    this.each(function(value, index) {\n      value = iterator.call(context, value, index);\n      if (result == null || value < result)\n        result = value;\n    });\n    return result;\n  },\n\n  partition: function(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var trues = [], falses = [];\n    this.each(function(value, index) {\n      (iterator.call(context, value, index) ?\n        trues : falses).push(value);\n    });\n    return [trues, falses];\n  },\n\n  pluck: function(property) {\n    var results = [];\n    this.each(function(value) {\n      results.push(value[property]);\n    });\n    return results;\n  },\n\n  reject: function(iterator, context) {\n    var results = [];\n    this.each(function(value, index) {\n      if (!iterator.call(context, value, index))\n        results.push(value);\n    });\n    return results;\n  },\n\n  sortBy: function(iterator, context) {\n    return this.map(function(value, index) {\n      return {\n        value: value,\n        criteria: iterator.call(context, value, index)\n      };\n    }).sort(function(left, right) {\n      var a = left.criteria, b = right.criteria;\n      return a < b ? -1 : a > b ? 1 : 0;\n    }).pluck('value');\n  },\n\n  toArray: function() {\n    return this.map();\n  },\n\n  zip: function() {\n    var iterator = Prototype.K, args = $A(arguments);\n    if (Object.isFunction(args.last()))\n      iterator = args.pop();\n\n    var collections = [this].concat(args).map($A);\n    return this.map(function(value, index) {\n      return iterator(collections.pluck(index));\n    });\n  },\n\n  size: function() {\n    return this.toArray().length;\n  },\n\n  inspect: function() {\n    return '#<Enumerable:' + this.toArray().inspect() + '>';\n  }\n};\n\nObject.extend(Enumerable, {\n  map:     Enumerable.collect,\n  find:    Enumerable.detect,\n  select:  Enumerable.findAll,\n  filter:  Enumerable.findAll,\n  member:  Enumerable.include,\n  entries: Enumerable.toArray,\n  every:   Enumerable.all,\n  some:    Enumerable.any\n});\nfunction $A(iterable) {\n  if (!iterable) return [];\n  if (iterable.toArray) return iterable.toArray();\n  var length = iterable.length || 0, results = new Array(length);\n  while (length--) results[length] = iterable[length];\n  return results;\n}\n\nif (Prototype.Browser.WebKit) {\n  $A = function(iterable) {\n    if (!iterable) return [];\n    // In Safari, only use the `toArray` method if it's not a NodeList.\n    // A NodeList is a function, has an function `item` property, and a numeric\n    // `length` property. Adapted from Google Doctype.\n    if (!(typeof iterable === 'function' && typeof iterable.length ===\n        'number' && typeof iterable.item === 'function') && iterable.toArray)\n      return iterable.toArray();\n    var length = iterable.length || 0, results = new Array(length);\n    while (length--) results[length] = iterable[length];\n    return results;\n  };\n}\n\nArray.from = $A;\n\nObject.extend(Array.prototype, Enumerable);\n\nif (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;\n\nObject.extend(Array.prototype, {\n  _each: function(iterator) {\n    for (var i = 0, length = this.length; i < length; i++)\n      iterator(this[i]);\n  },\n\n  clear: function() {\n    this.length = 0;\n    return this;\n  },\n\n  first: function() {\n    return this[0];\n  },\n\n  last: function() {\n    return this[this.length - 1];\n  },\n\n  compact: function() {\n    return this.select(function(value) {\n      return value != null;\n    });\n  },\n\n  flatten: function() {\n    return this.inject([], function(array, value) {\n      return array.concat(Object.isArray(value) ?\n        value.flatten() : [value]);\n    });\n  },\n\n  without: function() {\n    var values = $A(arguments);\n    return this.select(function(value) {\n      return !values.include(value);\n    });\n  },\n\n  reverse: function(inline) {\n    return (inline !== false ? this : this.toArray())._reverse();\n  },\n\n  reduce: function() {\n    return this.length > 1 ? this : this[0];\n  },\n\n  uniq: function(sorted) {\n    return this.inject([], function(array, value, index) {\n      if (0 == index || (sorted ? array.last() != value : !array.include(value)))\n        array.push(value);\n      return array;\n    });\n  },\n\n  intersect: function(array) {\n    return this.uniq().findAll(function(item) {\n      return array.detect(function(value) { return item === value });\n    });\n  },\n\n  clone: function() {\n    return [].concat(this);\n  },\n\n  size: function() {\n    return this.length;\n  },\n\n  inspect: function() {\n    return '[' + this.map(Object.inspect).join(', ') + ']';\n  },\n\n  toJSON: function() {\n    var results = [];\n    this.each(function(object) {\n      var value = Object.toJSON(object);\n      if (!Object.isUndefined(value)) results.push(value);\n    });\n    return '[' + results.join(', ') + ']';\n  }\n});\n\n// use native browser JS 1.6 implementation if available\nif (Object.isFunction(Array.prototype.forEach))\n  Array.prototype._each = Array.prototype.forEach;\n\nif (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {\n  i || (i = 0);\n  var length = this.length;\n  if (i < 0) i = length + i;\n  for (; i < length; i++)\n    if (this[i] === item) return i;\n  return -1;\n};\n\nif (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {\n  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;\n  var n = this.slice(0, i).reverse().indexOf(item);\n  return (n < 0) ? n : i - n - 1;\n};\n\nArray.prototype.toArray = Array.prototype.clone;\n\nfunction $w(string) {\n  if (!Object.isString(string)) return [];\n  string = string.strip();\n  return string ? string.split(/\\s+/) : [];\n}\n\nif (Prototype.Browser.Opera){\n  Array.prototype.concat = function() {\n    var array = [];\n    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);\n    for (var i = 0, length = arguments.length; i < length; i++) {\n      if (Object.isArray(arguments[i])) {\n        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)\n          array.push(arguments[i][j]);\n      } else {\n        array.push(arguments[i]);\n      }\n    }\n    return array;\n  };\n}\nObject.extend(Number.prototype, {\n  toColorPart: function() {\n    return this.toPaddedString(2, 16);\n  },\n\n  succ: function() {\n    return this + 1;\n  },\n\n  times: function(iterator, context) {\n    $R(0, this, true).each(iterator, context);\n    return this;\n  },\n\n  toPaddedString: function(length, radix) {\n    var string = this.toString(radix || 10);\n    return '0'.times(length - string.length) + string;\n  },\n\n  toJSON: function() {\n    return isFinite(this) ? this.toString() : 'null';\n  }\n});\n\n$w('abs round ceil floor').each(function(method){\n  Number.prototype[method] = Math[method].methodize();\n});\nfunction $H(object) {\n  return new Hash(object);\n};\n\nvar Hash = Class.create(Enumerable, (function() {\n\n  function toQueryPair(key, value) {\n    if (Object.isUndefined(value)) return key;\n    return key + '=' + encodeURIComponent(String.interpret(value));\n  }\n\n  return {\n    initialize: function(object) {\n      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);\n    },\n\n    _each: function(iterator) {\n      for (var key in this._object) {\n        var value = this._object[key], pair = [key, value];\n        pair.key = key;\n        pair.value = value;\n        iterator(pair);\n      }\n    },\n\n    set: function(key, value) {\n      return this._object[key] = value;\n    },\n\n    get: function(key) {\n      // simulating poorly supported hasOwnProperty\n      if (this._object[key] !== Object.prototype[key])\n        return this._object[key];\n    },\n\n    unset: function(key) {\n      var value = this._object[key];\n      delete this._object[key];\n      return value;\n    },\n\n    toObject: function() {\n      return Object.clone(this._object);\n    },\n\n    keys: function() {\n      return this.pluck('key');\n    },\n\n    values: function() {\n      return this.pluck('value');\n    },\n\n    index: function(value) {\n      var match = this.detect(function(pair) {\n        return pair.value === value;\n      });\n      return match && match.key;\n    },\n\n    merge: function(object) {\n      return this.clone().update(object);\n    },\n\n    update: function(object) {\n      return new Hash(object).inject(this, function(result, pair) {\n        result.set(pair.key, pair.value);\n        return result;\n      });\n    },\n\n    toQueryString: function() {\n      return this.inject([], function(results, pair) {\n        var key = encodeURIComponent(pair.key), values = pair.value;\n\n        if (values && typeof values == 'object') {\n          if (Object.isArray(values))\n            return results.concat(values.map(toQueryPair.curry(key)));\n        } else results.push(toQueryPair(key, values));\n        return results;\n      }).join('&');\n    },\n\n    inspect: function() {\n      return '#<Hash:{' + this.map(function(pair) {\n        return pair.map(Object.inspect).join(': ');\n      }).join(', ') + '}>';\n    },\n\n    toJSON: function() {\n      return Object.toJSON(this.toObject());\n    },\n\n    clone: function() {\n      return new Hash(this);\n    }\n  }\n})());\n\nHash.prototype.toTemplateReplacements = Hash.prototype.toObject;\nHash.from = $H;\nvar ObjectRange = Class.create(Enumerable, {\n  initialize: function(start, end, exclusive) {\n    this.start = start;\n    this.end = end;\n    this.exclusive = exclusive;\n  },\n\n  _each: function(iterator) {\n    var value = this.start;\n    while (this.include(value)) {\n      iterator(value);\n      value = value.succ();\n    }\n  },\n\n  include: function(value) {\n    if (value < this.start)\n      return false;\n    if (this.exclusive)\n      return value < this.end;\n    return value <= this.end;\n  }\n});\n\nvar $R = function(start, end, exclusive) {\n  return new ObjectRange(start, end, exclusive);\n};\n\nvar Ajax = {\n  getTransport: function() {\n    return Try.these(\n      function() {return new XMLHttpRequest()},\n      function() {return new ActiveXObject('Msxml2.XMLHTTP')},\n      function() {return new ActiveXObject('Microsoft.XMLHTTP')}\n    ) || false;\n  },\n\n  activeRequestCount: 0\n};\n\nAjax.Responders = {\n  responders: [],\n\n  _each: function(iterator) {\n    this.responders._each(iterator);\n  },\n\n  register: function(responder) {\n    if (!this.include(responder))\n      this.responders.push(responder);\n  },\n\n  unregister: function(responder) {\n    this.responders = this.responders.without(responder);\n  },\n\n  dispatch: function(callback, request, transport, json) {\n    this.each(function(responder) {\n      if (Object.isFunction(responder[callback])) {\n        try {\n          responder[callback].apply(responder, [request, transport, json]);\n        } catch (e) { }\n      }\n    });\n  }\n};\n\nObject.extend(Ajax.Responders, Enumerable);\n\nAjax.Responders.register({\n  onCreate:   function() { Ajax.activeRequestCount++ },\n  onComplete: function() { Ajax.activeRequestCount-- }\n});\n\nAjax.Base = Class.create({\n  initialize: function(options) {\n    this.options = {\n      method:       'post',\n      asynchronous: true,\n      contentType:  'application/x-www-form-urlencoded',\n      encoding:     'UTF-8',\n      parameters:   '',\n      evalJSON:     true,\n      evalJS:       true\n    };\n    Object.extend(this.options, options || { });\n\n    this.options.method = this.options.method.toLowerCase();\n\n    if (Object.isString(this.options.parameters))\n      this.options.parameters = this.options.parameters.toQueryParams();\n    else if (Object.isHash(this.options.parameters))\n      this.options.parameters = this.options.parameters.toObject();\n  }\n});\n\nAjax.Request = Class.create(Ajax.Base, {\n  _complete: false,\n\n  initialize: function($super, url, options) {\n    $super(options);\n    this.transport = Ajax.getTransport();\n    this.request(url);\n  },\n\n  request: function(url) {\n    this.url = url;\n    this.method = this.options.method;\n    var params = Object.clone(this.options.parameters);\n\n    if (!['get', 'post'].include(this.method)) {\n      // simulate other verbs over post\n      params['_method'] = this.method;\n      this.method = 'post';\n    }\n\n    this.parameters = params;\n\n    if (params = Object.toQueryString(params)) {\n      // when GET, append parameters to URL\n      if (this.method == 'get')\n        this.url += (this.url.include('?') ? '&' : '?') + params;\n      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))\n        params += '&_=';\n    }\n\n    try {\n      var response = new Ajax.Response(this);\n      if (this.options.onCreate) this.options.onCreate(response);\n      Ajax.Responders.dispatch('onCreate', this, response);\n\n      this.transport.open(this.method.toUpperCase(), this.url,\n        this.options.asynchronous);\n\n      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);\n\n      this.transport.onreadystatechange = this.onStateChange.bind(this);\n      this.setRequestHeaders();\n\n      this.body = this.method == 'post' ? (this.options.postBody || params) : null;\n      this.transport.send(this.body);\n\n      /* Force Firefox to handle ready state 4 for synchronous requests */\n      if (!this.options.asynchronous && this.transport.overrideMimeType)\n        this.onStateChange();\n\n    }\n    catch (e) {\n      this.dispatchException(e);\n    }\n  },\n\n  onStateChange: function() {\n    var readyState = this.transport.readyState;\n    if (readyState > 1 && !((readyState == 4) && this._complete))\n      this.respondToReadyState(this.transport.readyState);\n  },\n\n  setRequestHeaders: function() {\n    var headers = {\n      'X-Requested-With': 'XMLHttpRequest',\n      'X-Prototype-Version': Prototype.Version,\n      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'\n    };\n\n    if (this.method == 'post') {\n      headers['Content-type'] = this.options.contentType +\n        (this.options.encoding ? '; charset=' + this.options.encoding : '');\n\n      /* Force \"Connection: close\" for older Mozilla browsers to work\n       * around a bug where XMLHttpRequest sends an incorrect\n       * Content-length header. See Mozilla Bugzilla #246651.\n       */\n      if (this.transport.overrideMimeType &&\n          (navigator.userAgent.match(/Gecko\\/(\\d{4})/) || [0,2005])[1] < 2005)\n            headers['Connection'] = 'close';\n    }\n\n    // user-defined headers\n    if (typeof this.options.requestHeaders == 'object') {\n      var extras = this.options.requestHeaders;\n\n      if (Object.isFunction(extras.push))\n        for (var i = 0, length = extras.length; i < length; i += 2)\n          headers[extras[i]] = extras[i+1];\n      else\n        $H(extras).each(function(pair) { headers[pair.key] = pair.value });\n    }\n\n    for (var name in headers)\n      this.transport.setRequestHeader(name, headers[name]);\n  },\n\n  success: function() {\n    var status = this.getStatus();\n    return !status || (status >= 200 && status < 300);\n  },\n\n  getStatus: function() {\n    try {\n      return this.transport.status || 0;\n    } catch (e) { return 0 }\n  },\n\n  respondToReadyState: function(readyState) {\n    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);\n\n    if (state == 'Complete') {\n      try {\n        this._complete = true;\n        (this.options['on' + response.status]\n         || this.options['on' + (this.success() ? 'Success' : 'Failure')]\n         || Prototype.emptyFunction)(response, response.headerJSON);\n      } catch (e) {\n        this.dispatchException(e);\n      }\n\n      var contentType = response.getHeader('Content-type');\n      if (this.options.evalJS == 'force'\n          || (this.options.evalJS && this.isSameOrigin() && contentType\n          && contentType.match(/^\\s*(text|application)\\/(x-)?(java|ecma)script(;.*)?\\s*$/i)))\n        this.evalResponse();\n    }\n\n    try {\n      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);\n      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);\n    } catch (e) {\n      this.dispatchException(e);\n    }\n\n    if (state == 'Complete') {\n      // avoid memory leak in MSIE: clean up\n      this.transport.onreadystatechange = Prototype.emptyFunction;\n    }\n  },\n\n  isSameOrigin: function() {\n    var m = this.url.match(/^\\s*https?:\\/\\/[^\\/]*/);\n    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({\n      protocol: location.protocol,\n      domain: document.domain,\n      port: location.port ? ':' + location.port : ''\n    }));\n  },\n\n  getHeader: function(name) {\n    try {\n      return this.transport.getResponseHeader(name) || null;\n    } catch (e) { return null }\n  },\n\n  evalResponse: function() {\n    try {\n      return eval((this.transport.responseText || '').unfilterJSON());\n    } catch (e) {\n      this.dispatchException(e);\n    }\n  },\n\n  dispatchException: function(exception) {\n    (this.options.onException || Prototype.emptyFunction)(this, exception);\n    Ajax.Responders.dispatch('onException', this, exception);\n  }\n});\n\nAjax.Request.Events =\n  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];\n\nAjax.Response = Class.create({\n  initialize: function(request){\n    this.request = request;\n    var transport  = this.transport  = request.transport,\n        readyState = this.readyState = transport.readyState;\n\n    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {\n      this.status       = this.getStatus();\n      this.statusText   = this.getStatusText();\n      this.responseText = String.interpret(transport.responseText);\n      this.headerJSON   = this._getHeaderJSON();\n    }\n\n    if(readyState == 4) {\n      var xml = transport.responseXML;\n      this.responseXML  = Object.isUndefined(xml) ? null : xml;\n      this.responseJSON = this._getResponseJSON();\n    }\n  },\n\n  status:      0,\n  statusText: '',\n\n  getStatus: Ajax.Request.prototype.getStatus,\n\n  getStatusText: function() {\n    try {\n      return this.transport.statusText || '';\n    } catch (e) { return '' }\n  },\n\n  getHeader: Ajax.Request.prototype.getHeader,\n\n  getAllHeaders: function() {\n    try {\n      return this.getAllResponseHeaders();\n    } catch (e) { return null }\n  },\n\n  getResponseHeader: function(name) {\n    return this.transport.getResponseHeader(name);\n  },\n\n  getAllResponseHeaders: function() {\n    return this.transport.getAllResponseHeaders();\n  },\n\n  _getHeaderJSON: function() {\n    var json = this.getHeader('X-JSON');\n    if (!json) return null;\n    json = decodeURIComponent(escape(json));\n    try {\n      return json.evalJSON(this.request.options.sanitizeJSON ||\n        !this.request.isSameOrigin());\n    } catch (e) {\n      this.request.dispatchException(e);\n    }\n  },\n\n  _getResponseJSON: function() {\n    var options = this.request.options;\n    if (!options.evalJSON || (options.evalJSON != 'force' &&\n      !(this.getHeader('Content-type') || '').include('application/json')) ||\n        this.responseText.blank())\n          return null;\n    try {\n      return this.responseText.evalJSON(options.sanitizeJSON ||\n        !this.request.isSameOrigin());\n    } catch (e) {\n      this.request.dispatchException(e);\n    }\n  }\n});\n\nAjax.Updater = Class.create(Ajax.Request, {\n  initialize: function($super, container, url, options) {\n    this.container = {\n      success: (container.success || container),\n      failure: (container.failure || (container.success ? null : container))\n    };\n\n    options = Object.clone(options);\n    var onComplete = options.onComplete;\n    options.onComplete = (function(response, json) {\n      this.updateContent(response.responseText);\n      if (Object.isFunction(onComplete)) onComplete(response, json);\n    }).bind(this);\n\n    $super(url, options);\n  },\n\n  updateContent: function(responseText) {\n    var receiver = this.container[this.success() ? 'success' : 'failure'],\n        options = this.options;\n\n    if (!options.evalScripts) responseText = responseText.stripScripts();\n\n    if (receiver = $(receiver)) {\n      if (options.insertion) {\n        if (Object.isString(options.insertion)) {\n          var insertion = { }; insertion[options.insertion] = responseText;\n          receiver.insert(insertion);\n        }\n        else options.insertion(receiver, responseText);\n      }\n      else receiver.update(responseText);\n    }\n  }\n});\n\nAjax.PeriodicalUpdater = Class.create(Ajax.Base, {\n  initialize: function($super, container, url, options) {\n    $super(options);\n    this.onComplete = this.options.onComplete;\n\n    this.frequency = (this.options.frequency || 2);\n    this.decay = (this.options.decay || 1);\n\n    this.updater = { };\n    this.container = container;\n    this.url = url;\n\n    this.start();\n  },\n\n  start: function() {\n    this.options.onComplete = this.updateComplete.bind(this);\n    this.onTimerEvent();\n  },\n\n  stop: function() {\n    this.updater.options.onComplete = undefined;\n    clearTimeout(this.timer);\n    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);\n  },\n\n  updateComplete: function(response) {\n    if (this.options.decay) {\n      this.decay = (response.responseText == this.lastText ?\n        this.decay * this.options.decay : 1);\n\n      this.lastText = response.responseText;\n    }\n    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);\n  },\n\n  onTimerEvent: function() {\n    this.updater = new Ajax.Updater(this.container, this.url, this.options);\n  }\n});\nfunction $(element) {\n  if (arguments.length > 1) {\n    for (var i = 0, elements = [], length = arguments.length; i < length; i++)\n      elements.push($(arguments[i]));\n    return elements;\n  }\n  if (Object.isString(element))\n    element = document.getElementById(element);\n  return Element.extend(element);\n}\n\nif (Prototype.BrowserFeatures.XPath) {\n  document._getElementsByXPath = function(expression, parentElement) {\n    var results = [];\n    var query = document.evaluate(expression, $(parentElement) || document,\n      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);\n    for (var i = 0, length = query.snapshotLength; i < length; i++)\n      results.push(Element.extend(query.snapshotItem(i)));\n    return results;\n  };\n}\n\n/*--------------------------------------------------------------------------*/\n\nif (!window.Node) var Node = { };\n\nif (!Node.ELEMENT_NODE) {\n  // DOM level 2 ECMAScript Language Binding\n  Object.extend(Node, {\n    ELEMENT_NODE: 1,\n    ATTRIBUTE_NODE: 2,\n    TEXT_NODE: 3,\n    CDATA_SECTION_NODE: 4,\n    ENTITY_REFERENCE_NODE: 5,\n    ENTITY_NODE: 6,\n    PROCESSING_INSTRUCTION_NODE: 7,\n    COMMENT_NODE: 8,\n    DOCUMENT_NODE: 9,\n    DOCUMENT_TYPE_NODE: 10,\n    DOCUMENT_FRAGMENT_NODE: 11,\n    NOTATION_NODE: 12\n  });\n}\n\n(function() {\n  var element = this.Element;\n  this.Element = function(tagName, attributes) {\n    attributes = attributes || { };\n    tagName = tagName.toLowerCase();\n    var cache = Element.cache;\n    if (Prototype.Browser.IE && attributes.name) {\n      tagName = '<' + tagName + ' name=\"' + attributes.name + '\">';\n      delete attributes.name;\n      return Element.writeAttribute(document.createElement(tagName), attributes);\n    }\n    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));\n    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);\n  };\n  Object.extend(this.Element, element || { });\n  if (element) this.Element.prototype = element.prototype;\n}).call(window);\n\nElement.cache = { };\n\nElement.Methods = {\n  visible: function(element) {\n    return $(element).style.display != 'none';\n  },\n\n  toggle: function(element) {\n    element = $(element);\n    Element[Element.visible(element) ? 'hide' : 'show'](element);\n    return element;\n  },\n\n  hide: function(element) {\n    element = $(element);\n    element.style.display = 'none';\n    return element;\n  },\n\n  show: function(element) {\n    element = $(element);\n    element.style.display = '';\n    return element;\n  },\n\n  remove: function(element) {\n    element = $(element);\n    element.parentNode.removeChild(element);\n    return element;\n  },\n\n  update: function(element, content) {\n    element = $(element);\n    if (content && content.toElement) content = content.toElement();\n    if (Object.isElement(content)) return element.update().insert(content);\n    content = Object.toHTML(content);\n    element.innerHTML = content.stripScripts();\n    content.evalScripts.bind(content).defer();\n    return element;\n  },\n\n  replace: function(element, content) {\n    element = $(element);\n    if (content && content.toElement) content = content.toElement();\n    else if (!Object.isElement(content)) {\n      content = Object.toHTML(content);\n      var range = element.ownerDocument.createRange();\n      range.selectNode(element);\n      content.evalScripts.bind(content).defer();\n      content = range.createContextualFragment(content.stripScripts());\n    }\n    element.parentNode.replaceChild(content, element);\n    return element;\n  },\n\n  insert: function(element, insertions) {\n    element = $(element);\n\n    if (Object.isString(insertions) || Object.isNumber(insertions) ||\n        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))\n          insertions = {bottom:insertions};\n\n    var content, insert, tagName, childNodes;\n\n    for (var position in insertions) {\n      content  = insertions[position];\n      position = position.toLowerCase();\n      insert = Element._insertionTranslations[position];\n\n      if (content && content.toElement) content = content.toElement();\n      if (Object.isElement(content)) {\n        insert(element, content);\n        continue;\n      }\n\n      content = Object.toHTML(content);\n\n      tagName = ((position == 'before' || position == 'after')\n        ? element.parentNode : element).tagName.toUpperCase();\n\n      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());\n\n      if (position == 'top' || position == 'after') childNodes.reverse();\n      childNodes.each(insert.curry(element));\n\n      content.evalScripts.bind(content).defer();\n    }\n\n    return element;\n  },\n\n  wrap: function(element, wrapper, attributes) {\n    element = $(element);\n    if (Object.isElement(wrapper))\n      $(wrapper).writeAttribute(attributes || { });\n    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);\n    else wrapper = new Element('div', wrapper);\n    if (element.parentNode)\n      element.parentNode.replaceChild(wrapper, element);\n    wrapper.appendChild(element);\n    return wrapper;\n  },\n\n  inspect: function(element) {\n    element = $(element);\n    var result = '<' + element.tagName.toLowerCase();\n    $H({'id': 'id', 'className': 'class'}).each(function(pair) {\n      var property = pair.first(), attribute = pair.last();\n      var value = (element[property] || '').toString();\n      if (value) result += ' ' + attribute + '=' + value.inspect(true);\n    });\n    return result + '>';\n  },\n\n  recursivelyCollect: function(element, property) {\n    element = $(element);\n    var elements = [];\n    while (element = element[property])\n      if (element.nodeType == 1)\n        elements.push(Element.extend(element));\n    return elements;\n  },\n\n  ancestors: function(element) {\n    return $(element).recursivelyCollect('parentNode');\n  },\n\n  descendants: function(element) {\n    return $(element).select(\"*\");\n  },\n\n  firstDescendant: function(element) {\n    element = $(element).firstChild;\n    while (element && element.nodeType != 1) element = element.nextSibling;\n    return $(element);\n  },\n\n  immediateDescendants: function(element) {\n    if (!(element = $(element).firstChild)) return [];\n    while (element && element.nodeType != 1) element = element.nextSibling;\n    if (element) return [element].concat($(element).nextSiblings());\n    return [];\n  },\n\n  previousSiblings: function(element) {\n    return $(element).recursivelyCollect('previousSibling');\n  },\n\n  nextSiblings: function(element) {\n    return $(element).recursivelyCollect('nextSibling');\n  },\n\n  siblings: function(element) {\n    element = $(element);\n    return element.previousSiblings().reverse().concat(element.nextSiblings());\n  },\n\n  match: function(element, selector) {\n    if (Object.isString(selector))\n      selector = new Selector(selector);\n    return selector.match($(element));\n  },\n\n  up: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return $(element.parentNode);\n    var ancestors = element.ancestors();\n    return Object.isNumber(expression) ? ancestors[expression] :\n      Selector.findElement(ancestors, expression, index);\n  },\n\n  down: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return element.firstDescendant();\n    return Object.isNumber(expression) ? element.descendants()[expression] :\n      Element.select(element, expression)[index || 0];\n  },\n\n  previous: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));\n    var previousSiblings = element.previousSiblings();\n    return Object.isNumber(expression) ? previousSiblings[expression] :\n      Selector.findElement(previousSiblings, expression, index);\n  },\n\n  next: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));\n    var nextSiblings = element.nextSiblings();\n    return Object.isNumber(expression) ? nextSiblings[expression] :\n      Selector.findElement(nextSiblings, expression, index);\n  },\n\n  select: function() {\n    var args = $A(arguments), element = $(args.shift());\n    return Selector.findChildElements(element, args);\n  },\n\n  adjacent: function() {\n    var args = $A(arguments), element = $(args.shift());\n    return Selector.findChildElements(element.parentNode, args).without(element);\n  },\n\n  identify: function(element) {\n    element = $(element);\n    var id = element.readAttribute('id'), self = arguments.callee;\n    if (id) return id;\n    do { id = 'anonymous_element_' + self.counter++ } while ($(id));\n    element.writeAttribute('id', id);\n    return id;\n  },\n\n  readAttribute: function(element, name) {\n    element = $(element);\n    if (Prototype.Browser.IE) {\n      var t = Element._attributeTranslations.read;\n      if (t.values[name]) return t.values[name](element, name);\n      if (t.names[name]) name = t.names[name];\n      if (name.include(':')) {\n        return (!element.attributes || !element.attributes[name]) ? null :\n         element.attributes[name].value;\n      }\n    }\n    return element.getAttribute(name);\n  },\n\n  writeAttribute: function(element, name, value) {\n    element = $(element);\n    var attributes = { }, t = Element._attributeTranslations.write;\n\n    if (typeof name == 'object') attributes = name;\n    else attributes[name] = Object.isUndefined(value) ? true : value;\n\n    for (var attr in attributes) {\n      name = t.names[attr] || attr;\n      value = attributes[attr];\n      if (t.values[attr]) name = t.values[attr](element, value);\n      if (value === false || value === null)\n        element.removeAttribute(name);\n      else if (value === true)\n        element.setAttribute(name, name);\n      else element.setAttribute(name, value);\n    }\n    return element;\n  },\n\n  getHeight: function(element) {\n    return $(element).getDimensions().height;\n  },\n\n  getWidth: function(element) {\n    return $(element).getDimensions().width;\n  },\n\n  classNames: function(element) {\n    return new Element.ClassNames(element);\n  },\n\n  hasClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    var elementClassName = element.className;\n    return (elementClassName.length > 0 && (elementClassName == className ||\n      new RegExp(\"(^|\\\\s)\" + className + \"(\\\\s|$)\").test(elementClassName)));\n  },\n\n  addClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    if (!element.hasClassName(className))\n      element.className += (element.className ? ' ' : '') + className;\n    return element;\n  },\n\n  removeClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    element.className = element.className.replace(\n      new RegExp(\"(^|\\\\s+)\" + className + \"(\\\\s+|$)\"), ' ').strip();\n    return element;\n  },\n\n  toggleClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    return element[element.hasClassName(className) ?\n      'removeClassName' : 'addClassName'](className);\n  },\n\n  // removes whitespace-only text node children\n  cleanWhitespace: function(element) {\n    element = $(element);\n    var node = element.firstChild;\n    while (node) {\n      var nextNode = node.nextSibling;\n      if (node.nodeType == 3 && !/\\S/.test(node.nodeValue))\n        element.removeChild(node);\n      node = nextNode;\n    }\n    return element;\n  },\n\n  empty: function(element) {\n    return $(element).innerHTML.blank();\n  },\n\n  descendantOf: function(element, ancestor) {\n    element = $(element), ancestor = $(ancestor);\n\n    if (element.compareDocumentPosition)\n      return (element.compareDocumentPosition(ancestor) & 8) === 8;\n\n    if (ancestor.contains)\n      return ancestor.contains(element) && ancestor !== element;\n\n    while (element = element.parentNode)\n      if (element == ancestor) return true;\n\n    return false;\n  },\n\n  scrollTo: function(element) {\n    element = $(element);\n    var pos = element.cumulativeOffset();\n    window.scrollTo(pos[0], pos[1]);\n    return element;\n  },\n\n  getStyle: function(element, style) {\n    element = $(element);\n    style = style == 'float' ? 'cssFloat' : style.camelize();\n    var value = element.style[style];\n    if (!value || value == 'auto') {\n      var css = document.defaultView.getComputedStyle(element, null);\n      value = css ? css[style] : null;\n    }\n    if (style == 'opacity') return value ? parseFloat(value) : 1.0;\n    return value == 'auto' ? null : value;\n  },\n\n  getOpacity: function(element) {\n    return $(element).getStyle('opacity');\n  },\n\n  setStyle: function(element, styles) {\n    element = $(element);\n    var elementStyle = element.style, match;\n    if (Object.isString(styles)) {\n      element.style.cssText += ';' + styles;\n      return styles.include('opacity') ?\n        element.setOpacity(styles.match(/opacity:\\s*(\\d?\\.?\\d*)/)[1]) : element;\n    }\n    for (var property in styles)\n      if (property == 'opacity') element.setOpacity(styles[property]);\n      else\n        elementStyle[(property == 'float' || property == 'cssFloat') ?\n          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :\n            property] = styles[property];\n\n    return element;\n  },\n\n  setOpacity: function(element, value) {\n    element = $(element);\n    element.style.opacity = (value == 1 || value === '') ? '' :\n      (value < 0.00001) ? 0 : value;\n    return element;\n  },\n\n  getDimensions: function(element) {\n    element = $(element);\n    var display = element.getStyle('display');\n    if (display != 'none' && display != null) // Safari bug\n      return {width: element.offsetWidth, height: element.offsetHeight};\n\n    // All *Width and *Height properties give 0 on elements with display none,\n    // so enable the element temporarily\n    var els = element.style;\n    var originalVisibility = els.visibility;\n    var originalPosition = els.position;\n    var originalDisplay = els.display;\n    els.visibility = 'hidden';\n    els.position = 'absolute';\n    els.display = 'block';\n    var originalWidth = element.clientWidth;\n    var originalHeight = element.clientHeight;\n    els.display = originalDisplay;\n    els.position = originalPosition;\n    els.visibility = originalVisibility;\n    return {width: originalWidth, height: originalHeight};\n  },\n\n  makePositioned: function(element) {\n    element = $(element);\n    var pos = Element.getStyle(element, 'position');\n    if (pos == 'static' || !pos) {\n      element._madePositioned = true;\n      element.style.position = 'relative';\n      // Opera returns the offset relative to the positioning context, when an\n      // element is position relative but top and left have not been defined\n      if (Prototype.Browser.Opera) {\n        element.style.top = 0;\n        element.style.left = 0;\n      }\n    }\n    return element;\n  },\n\n  undoPositioned: function(element) {\n    element = $(element);\n    if (element._madePositioned) {\n      element._madePositioned = undefined;\n      element.style.position =\n        element.style.top =\n        element.style.left =\n        element.style.bottom =\n        element.style.right = '';\n    }\n    return element;\n  },\n\n  makeClipping: function(element) {\n    element = $(element);\n    if (element._overflow) return element;\n    element._overflow = Element.getStyle(element, 'overflow') || 'auto';\n    if (element._overflow !== 'hidden')\n      element.style.overflow = 'hidden';\n    return element;\n  },\n\n  undoClipping: function(element) {\n    element = $(element);\n    if (!element._overflow) return element;\n    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;\n    element._overflow = null;\n    return element;\n  },\n\n  cumulativeOffset: function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      element = element.offsetParent;\n    } while (element);\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  positionedOffset: function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      element = element.offsetParent;\n      if (element) {\n        if (element.tagName.toUpperCase() == 'BODY') break;\n        var p = Element.getStyle(element, 'position');\n        if (p !== 'static') break;\n      }\n    } while (element);\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  absolutize: function(element) {\n    element = $(element);\n    if (element.getStyle('position') == 'absolute') return element;\n    // Position.prepare(); // To be done manually by Scripty when it needs it.\n\n    var offsets = element.positionedOffset();\n    var top     = offsets[1];\n    var left    = offsets[0];\n    var width   = element.clientWidth;\n    var height  = element.clientHeight;\n\n    element._originalLeft   = left - parseFloat(element.style.left  || 0);\n    element._originalTop    = top  - parseFloat(element.style.top || 0);\n    element._originalWidth  = element.style.width;\n    element._originalHeight = element.style.height;\n\n    element.style.position = 'absolute';\n    element.style.top    = top + 'px';\n    element.style.left   = left + 'px';\n    element.style.width  = width + 'px';\n    element.style.height = height + 'px';\n    return element;\n  },\n\n  relativize: function(element) {\n    element = $(element);\n    if (element.getStyle('position') == 'relative') return element;\n    // Position.prepare(); // To be done manually by Scripty when it needs it.\n\n    element.style.position = 'relative';\n    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);\n    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);\n\n    element.style.top    = top + 'px';\n    element.style.left   = left + 'px';\n    element.style.height = element._originalHeight;\n    element.style.width  = element._originalWidth;\n    return element;\n  },\n\n  cumulativeScrollOffset: function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.scrollTop  || 0;\n      valueL += element.scrollLeft || 0;\n      element = element.parentNode;\n    } while (element);\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  getOffsetParent: function(element) {\n    if (element.offsetParent) return $(element.offsetParent);\n    if (element == document.body) return $(element);\n\n    while ((element = element.parentNode) && element != document.body)\n      if (Element.getStyle(element, 'position') != 'static')\n        return $(element);\n\n    return $(document.body);\n  },\n\n  viewportOffset: function(forElement) {\n    var valueT = 0, valueL = 0;\n\n    var element = forElement;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n\n      // Safari fix\n      if (element.offsetParent == document.body &&\n        Element.getStyle(element, 'position') == 'absolute') break;\n\n    } while (element = element.offsetParent);\n\n    element = forElement;\n    do {\n      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {\n        valueT -= element.scrollTop  || 0;\n        valueL -= element.scrollLeft || 0;\n      }\n    } while (element = element.parentNode);\n\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  clonePosition: function(element, source) {\n    var options = Object.extend({\n      setLeft:    true,\n      setTop:     true,\n      setWidth:   true,\n      setHeight:  true,\n      offsetTop:  0,\n      offsetLeft: 0\n    }, arguments[2] || { });\n\n    // find page position of source\n    source = $(source);\n    var p = source.viewportOffset();\n\n    // find coordinate system to use\n    element = $(element);\n    var delta = [0, 0];\n    var parent = null;\n    // delta [0,0] will do fine with position: fixed elements,\n    // position:absolute needs offsetParent deltas\n    if (Element.getStyle(element, 'position') == 'absolute') {\n      parent = element.getOffsetParent();\n      delta = parent.viewportOffset();\n    }\n\n    // correct by body offsets (fixes Safari)\n    if (parent == document.body) {\n      delta[0] -= document.body.offsetLeft;\n      delta[1] -= document.body.offsetTop;\n    }\n\n    // set position\n    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';\n    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';\n    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';\n    if (options.setHeight) element.style.height = source.offsetHeight + 'px';\n    return element;\n  }\n};\n\nElement.Methods.identify.counter = 1;\n\nObject.extend(Element.Methods, {\n  getElementsBySelector: Element.Methods.select,\n  childElements: Element.Methods.immediateDescendants\n});\n\nElement._attributeTranslations = {\n  write: {\n    names: {\n      className: 'class',\n      htmlFor:   'for'\n    },\n    values: { }\n  }\n};\n\nif (Prototype.Browser.Opera) {\n  Element.Methods.getStyle = Element.Methods.getStyle.wrap(\n    function(proceed, element, style) {\n      switch (style) {\n        case 'left': case 'top': case 'right': case 'bottom':\n          if (proceed(element, 'position') === 'static') return null;\n        case 'height': case 'width':\n          // returns '0px' for hidden elements; we want it to return null\n          if (!Element.visible(element)) return null;\n\n          // returns the border-box dimensions rather than the content-box\n          // dimensions, so we subtract padding and borders from the value\n          var dim = parseInt(proceed(element, style), 10);\n\n          if (dim !== element['offset' + style.capitalize()])\n            return dim + 'px';\n\n          var properties;\n          if (style === 'height') {\n            properties = ['border-top-width', 'padding-top',\n             'padding-bottom', 'border-bottom-width'];\n          }\n          else {\n            properties = ['border-left-width', 'padding-left',\n             'padding-right', 'border-right-width'];\n          }\n          return properties.inject(dim, function(memo, property) {\n            var val = proceed(element, property);\n            return val === null ? memo : memo - parseInt(val, 10);\n          }) + 'px';\n        default: return proceed(element, style);\n      }\n    }\n  );\n\n  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(\n    function(proceed, element, attribute) {\n      if (attribute === 'title') return element.title;\n      return proceed(element, attribute);\n    }\n  );\n}\n\nelse if (Prototype.Browser.IE) {\n  // IE doesn't report offsets correctly for static elements, so we change them\n  // to \"relative\" to get the values, then change them back.\n  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(\n    function(proceed, element) {\n      element = $(element);\n      // IE throws an error if element is not in document\n      try { element.offsetParent }\n      catch(e) { return $(document.body) }\n      var position = element.getStyle('position');\n      if (position !== 'static') return proceed(element);\n      element.setStyle({ position: 'relative' });\n      var value = proceed(element);\n      element.setStyle({ position: position });\n      return value;\n    }\n  );\n\n  $w('positionedOffset viewportOffset').each(function(method) {\n    Element.Methods[method] = Element.Methods[method].wrap(\n      function(proceed, element) {\n        element = $(element);\n        try { element.offsetParent }\n        catch(e) { return Element._returnOffset(0,0) }\n        var position = element.getStyle('position');\n        if (position !== 'static') return proceed(element);\n        // Trigger hasLayout on the offset parent so that IE6 reports\n        // accurate offsetTop and offsetLeft values for position: fixed.\n        var offsetParent = element.getOffsetParent();\n        if (offsetParent && offsetParent.getStyle('position') === 'fixed')\n          offsetParent.setStyle({ zoom: 1 });\n        element.setStyle({ position: 'relative' });\n        var value = proceed(element);\n        element.setStyle({ position: position });\n        return value;\n      }\n    );\n  });\n\n  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(\n    function(proceed, element) {\n      try { element.offsetParent }\n      catch(e) { return Element._returnOffset(0,0) }\n      return proceed(element);\n    }\n  );\n\n  Element.Methods.getStyle = function(element, style) {\n    element = $(element);\n    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();\n    var value = element.style[style];\n    if (!value && element.currentStyle) value = element.currentStyle[style];\n\n    if (style == 'opacity') {\n      if (value = (element.getStyle('filter') || '').match(/alpha\\(opacity=(.*)\\)/))\n        if (value[1]) return parseFloat(value[1]) / 100;\n      return 1.0;\n    }\n\n    if (value == 'auto') {\n      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))\n        return element['offset' + style.capitalize()] + 'px';\n      return null;\n    }\n    return value;\n  };\n\n  Element.Methods.setOpacity = function(element, value) {\n    function stripAlpha(filter){\n      return filter.replace(/alpha\\([^\\)]*\\)/gi,'');\n    }\n    element = $(element);\n    var currentStyle = element.currentStyle;\n    if ((currentStyle && !currentStyle.hasLayout) ||\n      (!currentStyle && element.style.zoom == 'normal'))\n        element.style.zoom = 1;\n\n    var filter = element.getStyle('filter'), style = element.style;\n    if (value == 1 || value === '') {\n      (filter = stripAlpha(filter)) ?\n        style.filter = filter : style.removeAttribute('filter');\n      return element;\n    } else if (value < 0.00001) value = 0;\n    style.filter = stripAlpha(filter) +\n      'alpha(opacity=' + (value * 100) + ')';\n    return element;\n  };\n\n  Element._attributeTranslations = {\n    read: {\n      names: {\n        'class': 'className',\n        'for':   'htmlFor'\n      },\n      values: {\n        _getAttr: function(element, attribute) {\n          return element.getAttribute(attribute, 2);\n        },\n        _getAttrNode: function(element, attribute) {\n          var node = element.getAttributeNode(attribute);\n          return node ? node.value : \"\";\n        },\n        _getEv: function(element, attribute) {\n          attribute = element.getAttribute(attribute);\n          return attribute ? attribute.toString().slice(23, -2) : null;\n        },\n        _flag: function(element, attribute) {\n          return $(element).hasAttribute(attribute) ? attribute : null;\n        },\n        style: function(element) {\n          return element.style.cssText.toLowerCase();\n        },\n        title: function(element) {\n          return element.title;\n        }\n      }\n    }\n  };\n\n  Element._attributeTranslations.write = {\n    names: Object.extend({\n      cellpadding: 'cellPadding',\n      cellspacing: 'cellSpacing'\n    }, Element._attributeTranslations.read.names),\n    values: {\n      checked: function(element, value) {\n        element.checked = !!value;\n      },\n\n      style: function(element, value) {\n        element.style.cssText = value ? value : '';\n      }\n    }\n  };\n\n  Element._attributeTranslations.has = {};\n\n  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +\n      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {\n    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;\n    Element._attributeTranslations.has[attr.toLowerCase()] = attr;\n  });\n\n  (function(v) {\n    Object.extend(v, {\n      href:        v._getAttr,\n      src:         v._getAttr,\n      type:        v._getAttr,\n      action:      v._getAttrNode,\n      disabled:    v._flag,\n      checked:     v._flag,\n      readonly:    v._flag,\n      multiple:    v._flag,\n      onload:      v._getEv,\n      onunload:    v._getEv,\n      onclick:     v._getEv,\n      ondblclick:  v._getEv,\n      onmousedown: v._getEv,\n      onmouseup:   v._getEv,\n      onmouseover: v._getEv,\n      onmousemove: v._getEv,\n      onmouseout:  v._getEv,\n      onfocus:     v._getEv,\n      onblur:      v._getEv,\n      onkeypress:  v._getEv,\n      onkeydown:   v._getEv,\n      onkeyup:     v._getEv,\n      onsubmit:    v._getEv,\n      onreset:     v._getEv,\n      onselect:    v._getEv,\n      onchange:    v._getEv\n    });\n  })(Element._attributeTranslations.read.values);\n}\n\nelse if (Prototype.Browser.Gecko && /rv:1\\.8\\.0/.test(navigator.userAgent)) {\n  Element.Methods.setOpacity = function(element, value) {\n    element = $(element);\n    element.style.opacity = (value == 1) ? 0.999999 :\n      (value === '') ? '' : (value < 0.00001) ? 0 : value;\n    return element;\n  };\n}\n\nelse if (Prototype.Browser.WebKit) {\n  Element.Methods.setOpacity = function(element, value) {\n    element = $(element);\n    element.style.opacity = (value == 1 || value === '') ? '' :\n      (value < 0.00001) ? 0 : value;\n\n    if (value == 1)\n      if(element.tagName.toUpperCase() == 'IMG' && element.width) {\n        element.width++; element.width--;\n      } else try {\n        var n = document.createTextNode(' ');\n        element.appendChild(n);\n        element.removeChild(n);\n      } catch (e) { }\n\n    return element;\n  };\n\n  // Safari returns margins on body which is incorrect if the child is absolutely\n  // positioned.  For performance reasons, redefine Element#cumulativeOffset for\n  // KHTML/WebKit only.\n  Element.Methods.cumulativeOffset = function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      if (element.offsetParent == document.body)\n        if (Element.getStyle(element, 'position') == 'absolute') break;\n\n      element = element.offsetParent;\n    } while (element);\n\n    return Element._returnOffset(valueL, valueT);\n  };\n}\n\nif (Prototype.Browser.IE || Prototype.Browser.Opera) {\n  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements\n  Element.Methods.update = function(element, content) {\n    element = $(element);\n\n    if (content && content.toElement) content = content.toElement();\n    if (Object.isElement(content)) return element.update().insert(content);\n\n    content = Object.toHTML(content);\n    var tagName = element.tagName.toUpperCase();\n\n    if (tagName in Element._insertionTranslations.tags) {\n      $A(element.childNodes).each(function(node) { element.removeChild(node) });\n      Element._getContentFromAnonymousElement(tagName, content.stripScripts())\n        .each(function(node) { element.appendChild(node) });\n    }\n    else element.innerHTML = content.stripScripts();\n\n    content.evalScripts.bind(content).defer();\n    return element;\n  };\n}\n\nif ('outerHTML' in document.createElement('div')) {\n  Element.Methods.replace = function(element, content) {\n    element = $(element);\n\n    if (content && content.toElement) content = content.toElement();\n    if (Object.isElement(content)) {\n      element.parentNode.replaceChild(content, element);\n      return element;\n    }\n\n    content = Object.toHTML(content);\n    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();\n\n    if (Element._insertionTranslations.tags[tagName]) {\n      var nextSibling = element.next();\n      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());\n      parent.removeChild(element);\n      if (nextSibling)\n        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });\n      else\n        fragments.each(function(node) { parent.appendChild(node) });\n    }\n    else element.outerHTML = content.stripScripts();\n\n    content.evalScripts.bind(content).defer();\n    return element;\n  };\n}\n\nElement._returnOffset = function(l, t) {\n  var result = [l, t];\n  result.left = l;\n  result.top = t;\n  return result;\n};\n\nElement._getContentFromAnonymousElement = function(tagName, html) {\n  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];\n  if (t) {\n    div.innerHTML = t[0] + html + t[1];\n    t[2].times(function() { div = div.firstChild });\n  } else div.innerHTML = html;\n  return $A(div.childNodes);\n};\n\nElement._insertionTranslations = {\n  before: function(element, node) {\n    element.parentNode.insertBefore(node, element);\n  },\n  top: function(element, node) {\n    element.insertBefore(node, element.firstChild);\n  },\n  bottom: function(element, node) {\n    element.appendChild(node);\n  },\n  after: function(element, node) {\n    element.parentNode.insertBefore(node, element.nextSibling);\n  },\n  tags: {\n    TABLE:  ['<table>',                '</table>',                   1],\n    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],\n    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],\n    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],\n    SELECT: ['<select>',               '</select>',                  1]\n  }\n};\n\n(function() {\n  Object.extend(this.tags, {\n    THEAD: this.tags.TBODY,\n    TFOOT: this.tags.TBODY,\n    TH:    this.tags.TD\n  });\n}).call(Element._insertionTranslations);\n\nElement.Methods.Simulated = {\n  hasAttribute: function(element, attribute) {\n    attribute = Element._attributeTranslations.has[attribute] || attribute;\n    var node = $(element).getAttributeNode(attribute);\n    return !!(node && node.specified);\n  }\n};\n\nElement.Methods.ByTag = { };\n\nObject.extend(Element, Element.Methods);\n\nif (!Prototype.BrowserFeatures.ElementExtensions &&\n    document.createElement('div')['__proto__']) {\n  window.HTMLElement = { };\n  window.HTMLElement.prototype = document.createElement('div')['__proto__'];\n  Prototype.BrowserFeatures.ElementExtensions = true;\n}\n\nElement.extend = (function() {\n  if (Prototype.BrowserFeatures.SpecificElementExtensions)\n    return Prototype.K;\n\n  var Methods = { }, ByTag = Element.Methods.ByTag;\n\n  var extend = Object.extend(function(element) {\n    if (!element || element._extendedByPrototype ||\n        element.nodeType != 1 || element == window) return element;\n\n    var methods = Object.clone(Methods),\n      tagName = element.tagName.toUpperCase(), property, value;\n\n    // extend methods for specific tags\n    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);\n\n    for (property in methods) {\n      value = methods[property];\n      if (Object.isFunction(value) && !(property in element))\n        element[property] = value.methodize();\n    }\n\n    element._extendedByPrototype = Prototype.emptyFunction;\n    return element;\n\n  }, {\n    refresh: function() {\n      // extend methods for all tags (Safari doesn't need this)\n      if (!Prototype.BrowserFeatures.ElementExtensions) {\n        Object.extend(Methods, Element.Methods);\n        Object.extend(Methods, Element.Methods.Simulated);\n      }\n    }\n  });\n\n  extend.refresh();\n  return extend;\n})();\n\nElement.hasAttribute = function(element, attribute) {\n  if (element.hasAttribute) return element.hasAttribute(attribute);\n  return Element.Methods.Simulated.hasAttribute(element, attribute);\n};\n\nElement.addMethods = function(methods) {\n  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;\n\n  if (!methods) {\n    Object.extend(Form, Form.Methods);\n    Object.extend(Form.Element, Form.Element.Methods);\n    Object.extend(Element.Methods.ByTag, {\n      \"FORM\":     Object.clone(Form.Methods),\n      \"INPUT\":    Object.clone(Form.Element.Methods),\n      \"SELECT\":   Object.clone(Form.Element.Methods),\n      \"TEXTAREA\": Object.clone(Form.Element.Methods)\n    });\n  }\n\n  if (arguments.length == 2) {\n    var tagName = methods;\n    methods = arguments[1];\n  }\n\n  if (!tagName) Object.extend(Element.Methods, methods || { });\n  else {\n    if (Object.isArray(tagName)) tagName.each(extend);\n    else extend(tagName);\n  }\n\n  function extend(tagName) {\n    tagName = tagName.toUpperCase();\n    if (!Element.Methods.ByTag[tagName])\n      Element.Methods.ByTag[tagName] = { };\n    Object.extend(Element.Methods.ByTag[tagName], methods);\n  }\n\n  function copy(methods, destination, onlyIfAbsent) {\n    onlyIfAbsent = onlyIfAbsent || false;\n    for (var property in methods) {\n      var value = methods[property];\n      if (!Object.isFunction(value)) continue;\n      if (!onlyIfAbsent || !(property in destination))\n        destination[property] = value.methodize();\n    }\n  }\n\n  function findDOMClass(tagName) {\n    var klass;\n    var trans = {\n      \"OPTGROUP\": \"OptGroup\", \"TEXTAREA\": \"TextArea\", \"P\": \"Paragraph\",\n      \"FIELDSET\": \"FieldSet\", \"UL\": \"UList\", \"OL\": \"OList\", \"DL\": \"DList\",\n      \"DIR\": \"Directory\", \"H1\": \"Heading\", \"H2\": \"Heading\", \"H3\": \"Heading\",\n      \"H4\": \"Heading\", \"H5\": \"Heading\", \"H6\": \"Heading\", \"Q\": \"Quote\",\n      \"INS\": \"Mod\", \"DEL\": \"Mod\", \"A\": \"Anchor\", \"IMG\": \"Image\", \"CAPTION\":\n      \"TableCaption\", \"COL\": \"TableCol\", \"COLGROUP\": \"TableCol\", \"THEAD\":\n      \"TableSection\", \"TFOOT\": \"TableSection\", \"TBODY\": \"TableSection\", \"TR\":\n      \"TableRow\", \"TH\": \"TableCell\", \"TD\": \"TableCell\", \"FRAMESET\":\n      \"FrameSet\", \"IFRAME\": \"IFrame\"\n    };\n    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';\n    if (window[klass]) return window[klass];\n    klass = 'HTML' + tagName + 'Element';\n    if (window[klass]) return window[klass];\n    klass = 'HTML' + tagName.capitalize() + 'Element';\n    if (window[klass]) return window[klass];\n\n    window[klass] = { };\n    window[klass].prototype = document.createElement(tagName)['__proto__'];\n    return window[klass];\n  }\n\n  if (F.ElementExtensions) {\n    copy(Element.Methods, HTMLElement.prototype);\n    copy(Element.Methods.Simulated, HTMLElement.prototype, true);\n  }\n\n  if (F.SpecificElementExtensions) {\n    for (var tag in Element.Methods.ByTag) {\n      var klass = findDOMClass(tag);\n      if (Object.isUndefined(klass)) continue;\n      copy(T[tag], klass.prototype);\n    }\n  }\n\n  Object.extend(Element, Element.Methods);\n  delete Element.ByTag;\n\n  if (Element.extend.refresh) Element.extend.refresh();\n  Element.cache = { };\n};\n\ndocument.viewport = {\n  getDimensions: function() {\n    var dimensions = { }, B = Prototype.Browser;\n    $w('width height').each(function(d) {\n      var D = d.capitalize();\n      if (B.WebKit && !document.evaluate) {\n        // Safari <3.0 needs self.innerWidth/Height\n        dimensions[d] = self['inner' + D];\n      } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {\n        // Opera <9.5 needs document.body.clientWidth/Height\n        dimensions[d] = document.body['client' + D]\n      } else {\n        dimensions[d] = document.documentElement['client' + D];\n      }\n    });\n    return dimensions;\n  },\n\n  getWidth: function() {\n    return this.getDimensions().width;\n  },\n\n  getHeight: function() {\n    return this.getDimensions().height;\n  },\n\n  getScrollOffsets: function() {\n    return Element._returnOffset(\n      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,\n      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);\n  }\n};\n/* Portions of the Selector class are derived from Jack Slocum's DomQuery,\n * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style\n * license.  Please see http://www.yui-ext.com/ for more information. */\n\nvar Selector = Class.create({\n  initialize: function(expression) {\n    this.expression = expression.strip();\n\n    if (this.shouldUseSelectorsAPI()) {\n      this.mode = 'selectorsAPI';\n    } else if (this.shouldUseXPath()) {\n      this.mode = 'xpath';\n      this.compileXPathMatcher();\n    } else {\n      this.mode = \"normal\";\n      this.compileMatcher();\n    }\n\n  },\n\n  shouldUseXPath: function() {\n    if (!Prototype.BrowserFeatures.XPath) return false;\n\n    var e = this.expression;\n\n    // Safari 3 chokes on :*-of-type and :empty\n    if (Prototype.Browser.WebKit &&\n     (e.include(\"-of-type\") || e.include(\":empty\")))\n      return false;\n\n    // XPath can't do namespaced attributes, nor can it read\n    // the \"checked\" property from DOM nodes\n    if ((/(\\[[\\w-]*?:|:checked)/).test(e))\n      return false;\n\n    return true;\n  },\n\n  shouldUseSelectorsAPI: function() {\n    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;\n\n    if (!Selector._div) Selector._div = new Element('div');\n\n    // Make sure the browser treats the selector as valid. Test on an\n    // isolated element to minimize cost of this check.\n    try {\n      Selector._div.querySelector(this.expression);\n    } catch(e) {\n      return false;\n    }\n\n    return true;\n  },\n\n  compileMatcher: function() {\n    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,\n        c = Selector.criteria, le, p, m;\n\n    if (Selector._cache[e]) {\n      this.matcher = Selector._cache[e];\n      return;\n    }\n\n    this.matcher = [\"this.matcher = function(root) {\",\n                    \"var r = root, h = Selector.handlers, c = false, n;\"];\n\n    while (e && le != e && (/\\S/).test(e)) {\n      le = e;\n      for (var i in ps) {\n        p = ps[i];\n        if (m = e.match(p)) {\n          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :\n            new Template(c[i]).evaluate(m));\n          e = e.replace(m[0], '');\n          break;\n        }\n      }\n    }\n\n    this.matcher.push(\"return h.unique(n);\\n}\");\n    eval(this.matcher.join('\\n'));\n    Selector._cache[this.expression] = this.matcher;\n  },\n\n  compileXPathMatcher: function() {\n    var e = this.expression, ps = Selector.patterns,\n        x = Selector.xpath, le, m;\n\n    if (Selector._cache[e]) {\n      this.xpath = Selector._cache[e]; return;\n    }\n\n    this.matcher = ['.//*'];\n    while (e && le != e && (/\\S/).test(e)) {\n      le = e;\n      for (var i in ps) {\n        if (m = e.match(ps[i])) {\n          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :\n            new Template(x[i]).evaluate(m));\n          e = e.replace(m[0], '');\n          break;\n        }\n      }\n    }\n\n    this.xpath = this.matcher.join('');\n    Selector._cache[this.expression] = this.xpath;\n  },\n\n  findElements: function(root) {\n    root = root || document;\n    var e = this.expression, results;\n\n    switch (this.mode) {\n      case 'selectorsAPI':\n        // querySelectorAll queries document-wide, then filters to descendants\n        // of the context element. That's not what we want.\n        // Add an explicit context to the selector if necessary.\n        if (root !== document) {\n          var oldId = root.id, id = $(root).identify();\n          e = \"#\" + id + \" \" + e;\n        }\n\n        results = $A(root.querySelectorAll(e)).map(Element.extend);\n        root.id = oldId;\n\n        return results;\n      case 'xpath':\n        return document._getElementsByXPath(this.xpath, root);\n      default:\n       return this.matcher(root);\n    }\n  },\n\n  match: function(element) {\n    this.tokens = [];\n\n    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;\n    var le, p, m;\n\n    while (e && le !== e && (/\\S/).test(e)) {\n      le = e;\n      for (var i in ps) {\n        p = ps[i];\n        if (m = e.match(p)) {\n          // use the Selector.assertions methods unless the selector\n          // is too complex.\n          if (as[i]) {\n            this.tokens.push([i, Object.clone(m)]);\n            e = e.replace(m[0], '');\n          } else {\n            // reluctantly do a document-wide search\n            // and look for a match in the array\n            return this.findElements(document).include(element);\n          }\n        }\n      }\n    }\n\n    var match = true, name, matches;\n    for (var i = 0, token; token = this.tokens[i]; i++) {\n      name = token[0], matches = token[1];\n      if (!Selector.assertions[name](element, matches)) {\n        match = false; break;\n      }\n    }\n\n    return match;\n  },\n\n  toString: function() {\n    return this.expression;\n  },\n\n  inspect: function() {\n    return \"#<Selector:\" + this.expression.inspect() + \">\";\n  }\n});\n\nObject.extend(Selector, {\n  _cache: { },\n\n  xpath: {\n    descendant:   \"//*\",\n    child:        \"/*\",\n    adjacent:     \"/following-sibling::*[1]\",\n    laterSibling: '/following-sibling::*',\n    tagName:      function(m) {\n      if (m[1] == '*') return '';\n      return \"[local-name()='\" + m[1].toLowerCase() +\n             \"' or local-name()='\" + m[1].toUpperCase() + \"']\";\n    },\n    className:    \"[contains(concat(' ', @class, ' '), ' #{1} ')]\",\n    id:           \"[@id='#{1}']\",\n    attrPresence: function(m) {\n      m[1] = m[1].toLowerCase();\n      return new Template(\"[@#{1}]\").evaluate(m);\n    },\n    attr: function(m) {\n      m[1] = m[1].toLowerCase();\n      m[3] = m[5] || m[6];\n      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);\n    },\n    pseudo: function(m) {\n      var h = Selector.xpath.pseudos[m[1]];\n      if (!h) return '';\n      if (Object.isFunction(h)) return h(m);\n      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);\n    },\n    operators: {\n      '=':  \"[@#{1}='#{3}']\",\n      '!=': \"[@#{1}!='#{3}']\",\n      '^=': \"[starts-with(@#{1}, '#{3}')]\",\n      '$=': \"[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']\",\n      '*=': \"[contains(@#{1}, '#{3}')]\",\n      '~=': \"[contains(concat(' ', @#{1}, ' '), ' #{3} ')]\",\n      '|=': \"[contains(concat('-', @#{1}, '-'), '-#{3}-')]\"\n    },\n    pseudos: {\n      'first-child': '[not(preceding-sibling::*)]',\n      'last-child':  '[not(following-sibling::*)]',\n      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',\n      'empty':       \"[count(*) = 0 and (count(text()) = 0)]\",\n      'checked':     \"[@checked]\",\n      'disabled':    \"[(@disabled) and (@type!='hidden')]\",\n      'enabled':     \"[not(@disabled) and (@type!='hidden')]\",\n      'not': function(m) {\n        var e = m[6], p = Selector.patterns,\n            x = Selector.xpath, le, v;\n\n        var exclusion = [];\n        while (e && le != e && (/\\S/).test(e)) {\n          le = e;\n          for (var i in p) {\n            if (m = e.match(p[i])) {\n              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);\n              exclusion.push(\"(\" + v.substring(1, v.length - 1) + \")\");\n              e = e.replace(m[0], '');\n              break;\n            }\n          }\n        }\n        return \"[not(\" + exclusion.join(\" and \") + \")]\";\n      },\n      'nth-child':      function(m) {\n        return Selector.xpath.pseudos.nth(\"(count(./preceding-sibling::*) + 1) \", m);\n      },\n      'nth-last-child': function(m) {\n        return Selector.xpath.pseudos.nth(\"(count(./following-sibling::*) + 1) \", m);\n      },\n      'nth-of-type':    function(m) {\n        return Selector.xpath.pseudos.nth(\"position() \", m);\n      },\n      'nth-last-of-type': function(m) {\n        return Selector.xpath.pseudos.nth(\"(last() + 1 - position()) \", m);\n      },\n      'first-of-type':  function(m) {\n        m[6] = \"1\"; return Selector.xpath.pseudos['nth-of-type'](m);\n      },\n      'last-of-type':   function(m) {\n        m[6] = \"1\"; return Selector.xpath.pseudos['nth-last-of-type'](m);\n      },\n      'only-of-type':   function(m) {\n        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);\n      },\n      nth: function(fragment, m) {\n        var mm, formula = m[6], predicate;\n        if (formula == 'even') formula = '2n+0';\n        if (formula == 'odd')  formula = '2n+1';\n        if (mm = formula.match(/^(\\d+)$/)) // digit only\n          return '[' + fragment + \"= \" + mm[1] + ']';\n        if (mm = formula.match(/^(-?\\d*)?n(([+-])(\\d+))?/)) { // an+b\n          if (mm[1] == \"-\") mm[1] = -1;\n          var a = mm[1] ? Number(mm[1]) : 1;\n          var b = mm[2] ? Number(mm[2]) : 0;\n          predicate = \"[((#{fragment} - #{b}) mod #{a} = 0) and \" +\n          \"((#{fragment} - #{b}) div #{a} >= 0)]\";\n          return new Template(predicate).evaluate({\n            fragment: fragment, a: a, b: b });\n        }\n      }\n    }\n  },\n\n  criteria: {\n    tagName:      'n = h.tagName(n, r, \"#{1}\", c);      c = false;',\n    className:    'n = h.className(n, r, \"#{1}\", c);    c = false;',\n    id:           'n = h.id(n, r, \"#{1}\", c);           c = false;',\n    attrPresence: 'n = h.attrPresence(n, r, \"#{1}\", c); c = false;',\n    attr: function(m) {\n      m[3] = (m[5] || m[6]);\n      return new Template('n = h.attr(n, r, \"#{1}\", \"#{3}\", \"#{2}\", c); c = false;').evaluate(m);\n    },\n    pseudo: function(m) {\n      if (m[6]) m[6] = m[6].replace(/\"/g, '\\\\\"');\n      return new Template('n = h.pseudo(n, \"#{1}\", \"#{6}\", r, c); c = false;').evaluate(m);\n    },\n    descendant:   'c = \"descendant\";',\n    child:        'c = \"child\";',\n    adjacent:     'c = \"adjacent\";',\n    laterSibling: 'c = \"laterSibling\";'\n  },\n\n  patterns: {\n    // combinators must be listed first\n    // (and descendant needs to be last combinator)\n    laterSibling: /^\\s*~\\s*/,\n    child:        /^\\s*>\\s*/,\n    adjacent:     /^\\s*\\+\\s*/,\n    descendant:   /^\\s/,\n\n    // selectors follow\n    tagName:      /^\\s*(\\*|[\\w\\-]+)(\\b|$)?/,\n    id:           /^#([\\w\\-\\*]+)(\\b|$)/,\n    className:    /^\\.([\\w\\-\\*]+)(\\b|$)/,\n    pseudo:\n/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\\((.*?)\\))?(\\b|$|(?=\\s|[:+~>]))/,\n    attrPresence: /^\\[((?:[\\w]+:)?[\\w]+)\\]/,\n    attr:         /\\[((?:[\\w-]*:)?[\\w-]+)\\s*(?:([!^$*~|]?=)\\s*((['\"])([^\\4]*?)\\4|([^'\"][^\\]]*?)))?\\]/\n  },\n\n  // for Selector.match and Element#match\n  assertions: {\n    tagName: function(element, matches) {\n      return matches[1].toUpperCase() == element.tagName.toUpperCase();\n    },\n\n    className: function(element, matches) {\n      return Element.hasClassName(element, matches[1]);\n    },\n\n    id: function(element, matches) {\n      return element.id === matches[1];\n    },\n\n    attrPresence: function(element, matches) {\n      return Element.hasAttribute(element, matches[1]);\n    },\n\n    attr: function(element, matches) {\n      var nodeValue = Element.readAttribute(element, matches[1]);\n      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);\n    }\n  },\n\n  handlers: {\n    // UTILITY FUNCTIONS\n    // joins two collections\n    concat: function(a, b) {\n      for (var i = 0, node; node = b[i]; i++)\n        a.push(node);\n      return a;\n    },\n\n    // marks an array of nodes for counting\n    mark: function(nodes) {\n      var _true = Prototype.emptyFunction;\n      for (var i = 0, node; node = nodes[i]; i++)\n        node._countedByPrototype = _true;\n      return nodes;\n    },\n\n    unmark: function(nodes) {\n      for (var i = 0, node; node = nodes[i]; i++)\n        node._countedByPrototype = undefined;\n      return nodes;\n    },\n\n    // mark each child node with its position (for nth calls)\n    // \"ofType\" flag indicates whether we're indexing for nth-of-type\n    // rather than nth-child\n    index: function(parentNode, reverse, ofType) {\n      parentNode._countedByPrototype = Prototype.emptyFunction;\n      if (reverse) {\n        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {\n          var node = nodes[i];\n          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;\n        }\n      } else {\n        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)\n          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;\n      }\n    },\n\n    // filters out duplicates and extends all nodes\n    unique: function(nodes) {\n      if (nodes.length == 0) return nodes;\n      var results = [], n;\n      for (var i = 0, l = nodes.length; i < l; i++)\n        if (!(n = nodes[i])._countedByPrototype) {\n          n._countedByPrototype = Prototype.emptyFunction;\n          results.push(Element.extend(n));\n        }\n      return Selector.handlers.unmark(results);\n    },\n\n    // COMBINATOR FUNCTIONS\n    descendant: function(nodes) {\n      var h = Selector.handlers;\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        h.concat(results, node.getElementsByTagName('*'));\n      return results;\n    },\n\n    child: function(nodes) {\n      var h = Selector.handlers;\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        for (var j = 0, child; child = node.childNodes[j]; j++)\n          if (child.nodeType == 1 && child.tagName != '!') results.push(child);\n      }\n      return results;\n    },\n\n    adjacent: function(nodes) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        var next = this.nextElementSibling(node);\n        if (next) results.push(next);\n      }\n      return results;\n    },\n\n    laterSibling: function(nodes) {\n      var h = Selector.handlers;\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        h.concat(results, Element.nextSiblings(node));\n      return results;\n    },\n\n    nextElementSibling: function(node) {\n      while (node = node.nextSibling)\n        if (node.nodeType == 1) return node;\n      return null;\n    },\n\n    previousElementSibling: function(node) {\n      while (node = node.previousSibling)\n        if (node.nodeType == 1) return node;\n      return null;\n    },\n\n    // TOKEN FUNCTIONS\n    tagName: function(nodes, root, tagName, combinator) {\n      var uTagName = tagName.toUpperCase();\n      var results = [], h = Selector.handlers;\n      if (nodes) {\n        if (combinator) {\n          // fastlane for ordinary descendant combinators\n          if (combinator == \"descendant\") {\n            for (var i = 0, node; node = nodes[i]; i++)\n              h.concat(results, node.getElementsByTagName(tagName));\n            return results;\n          } else nodes = this[combinator](nodes);\n          if (tagName == \"*\") return nodes;\n        }\n        for (var i = 0, node; node = nodes[i]; i++)\n          if (node.tagName.toUpperCase() === uTagName) results.push(node);\n        return results;\n      } else return root.getElementsByTagName(tagName);\n    },\n\n    id: function(nodes, root, id, combinator) {\n      var targetNode = $(id), h = Selector.handlers;\n      if (!targetNode) return [];\n      if (!nodes && root == document) return [targetNode];\n      if (nodes) {\n        if (combinator) {\n          if (combinator == 'child') {\n            for (var i = 0, node; node = nodes[i]; i++)\n              if (targetNode.parentNode == node) return [targetNode];\n          } else if (combinator == 'descendant') {\n            for (var i = 0, node; node = nodes[i]; i++)\n              if (Element.descendantOf(targetNode, node)) return [targetNode];\n          } else if (combinator == 'adjacent') {\n            for (var i = 0, node; node = nodes[i]; i++)\n              if (Selector.handlers.previousElementSibling(targetNode) == node)\n                return [targetNode];\n          } else nodes = h[combinator](nodes);\n        }\n        for (var i = 0, node; node = nodes[i]; i++)\n          if (node == targetNode) return [targetNode];\n        return [];\n      }\n      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];\n    },\n\n    className: function(nodes, root, className, combinator) {\n      if (nodes && combinator) nodes = this[combinator](nodes);\n      return Selector.handlers.byClassName(nodes, root, className);\n    },\n\n    byClassName: function(nodes, root, className) {\n      if (!nodes) nodes = Selector.handlers.descendant([root]);\n      var needle = ' ' + className + ' ';\n      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {\n        nodeClassName = node.className;\n        if (nodeClassName.length == 0) continue;\n        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))\n          results.push(node);\n      }\n      return results;\n    },\n\n    attrPresence: function(nodes, root, attr, combinator) {\n      if (!nodes) nodes = root.getElementsByTagName(\"*\");\n      if (nodes && combinator) nodes = this[combinator](nodes);\n      var results = [];\n      for (var i = 0, node; node = nodes[i]; i++)\n        if (Element.hasAttribute(node, attr)) results.push(node);\n      return results;\n    },\n\n    attr: function(nodes, root, attr, value, operator, combinator) {\n      if (!nodes) nodes = root.getElementsByTagName(\"*\");\n      if (nodes && combinator) nodes = this[combinator](nodes);\n      var handler = Selector.operators[operator], results = [];\n      for (var i = 0, node; node = nodes[i]; i++) {\n        var nodeValue = Element.readAttribute(node, attr);\n        if (nodeValue === null) continue;\n        if (handler(nodeValue, value)) results.push(node);\n      }\n      return results;\n    },\n\n    pseudo: function(nodes, name, value, root, combinator) {\n      if (nodes && combinator) nodes = this[combinator](nodes);\n      if (!nodes) nodes = root.getElementsByTagName(\"*\");\n      return Selector.pseudos[name](nodes, value, root);\n    }\n  },\n\n  pseudos: {\n    'first-child': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        if (Selector.handlers.previousElementSibling(node)) continue;\n          results.push(node);\n      }\n      return results;\n    },\n    'last-child': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        if (Selector.handlers.nextElementSibling(node)) continue;\n          results.push(node);\n      }\n      return results;\n    },\n    'only-child': function(nodes, value, root) {\n      var h = Selector.handlers;\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))\n          results.push(node);\n      return results;\n    },\n    'nth-child':        function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, formula, root);\n    },\n    'nth-last-child':   function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, formula, root, true);\n    },\n    'nth-of-type':      function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, formula, root, false, true);\n    },\n    'nth-last-of-type': function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, formula, root, true, true);\n    },\n    'first-of-type':    function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, \"1\", root, false, true);\n    },\n    'last-of-type':     function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, \"1\", root, true, true);\n    },\n    'only-of-type':     function(nodes, formula, root) {\n      var p = Selector.pseudos;\n      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);\n    },\n\n    // handles the an+b logic\n    getIndices: function(a, b, total) {\n      if (a == 0) return b > 0 ? [b] : [];\n      return $R(1, total).inject([], function(memo, i) {\n        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);\n        return memo;\n      });\n    },\n\n    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type\n    nth: function(nodes, formula, root, reverse, ofType) {\n      if (nodes.length == 0) return [];\n      if (formula == 'even') formula = '2n+0';\n      if (formula == 'odd')  formula = '2n+1';\n      var h = Selector.handlers, results = [], indexed = [], m;\n      h.mark(nodes);\n      for (var i = 0, node; node = nodes[i]; i++) {\n        if (!node.parentNode._countedByPrototype) {\n          h.index(node.parentNode, reverse, ofType);\n          indexed.push(node.parentNode);\n        }\n      }\n      if (formula.match(/^\\d+$/)) { // just a number\n        formula = Number(formula);\n        for (var i = 0, node; node = nodes[i]; i++)\n          if (node.nodeIndex == formula) results.push(node);\n      } else if (m = formula.match(/^(-?\\d*)?n(([+-])(\\d+))?/)) { // an+b\n        if (m[1] == \"-\") m[1] = -1;\n        var a = m[1] ? Number(m[1]) : 1;\n        var b = m[2] ? Number(m[2]) : 0;\n        var indices = Selector.pseudos.getIndices(a, b, nodes.length);\n        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {\n          for (var j = 0; j < l; j++)\n            if (node.nodeIndex == indices[j]) results.push(node);\n        }\n      }\n      h.unmark(nodes);\n      h.unmark(indexed);\n      return results;\n    },\n\n    'empty': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        // IE treats comments as element nodes\n        if (node.tagName == '!' || node.firstChild) continue;\n        results.push(node);\n      }\n      return results;\n    },\n\n    'not': function(nodes, selector, root) {\n      var h = Selector.handlers, selectorType, m;\n      var exclusions = new Selector(selector).findElements(root);\n      h.mark(exclusions);\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (!node._countedByPrototype) results.push(node);\n      h.unmark(exclusions);\n      return results;\n    },\n\n    'enabled': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (!node.disabled && (!node.type || node.type !== 'hidden'))\n          results.push(node);\n      return results;\n    },\n\n    'disabled': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (node.disabled) results.push(node);\n      return results;\n    },\n\n    'checked': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (node.checked) results.push(node);\n      return results;\n    }\n  },\n\n  operators: {\n    '=':  function(nv, v) { return nv == v; },\n    '!=': function(nv, v) { return nv != v; },\n    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },\n    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },\n    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },\n    '$=': function(nv, v) { return nv.endsWith(v); },\n    '*=': function(nv, v) { return nv.include(v); },\n    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },\n    '|=': function(nv, v) { return ('-' + (nv || \"\").toUpperCase() +\n     '-').include('-' + (v || \"\").toUpperCase() + '-'); }\n  },\n\n  split: function(expression) {\n    var expressions = [];\n    expression.scan(/(([\\w#:.~>+()\\s-]+|\\*|\\[.*?\\])+)\\s*(,|$)/, function(m) {\n      expressions.push(m[1].strip());\n    });\n    return expressions;\n  },\n\n  matchElements: function(elements, expression) {\n    var matches = $$(expression), h = Selector.handlers;\n    h.mark(matches);\n    for (var i = 0, results = [], element; element = elements[i]; i++)\n      if (element._countedByPrototype) results.push(element);\n    h.unmark(matches);\n    return results;\n  },\n\n  findElement: function(elements, expression, index) {\n    if (Object.isNumber(expression)) {\n      index = expression; expression = false;\n    }\n    return Selector.matchElements(elements, expression || '*')[index || 0];\n  },\n\n  findChildElements: function(element, expressions) {\n    expressions = Selector.split(expressions.join(','));\n    var results = [], h = Selector.handlers;\n    for (var i = 0, l = expressions.length, selector; i < l; i++) {\n      selector = new Selector(expressions[i].strip());\n      h.concat(results, selector.findElements(element));\n    }\n    return (l > 1) ? h.unique(results) : results;\n  }\n});\n\nif (Prototype.Browser.IE) {\n  Object.extend(Selector.handlers, {\n    // IE returns comment nodes on getElementsByTagName(\"*\").\n    // Filter them out.\n    concat: function(a, b) {\n      for (var i = 0, node; node = b[i]; i++)\n        if (node.tagName !== \"!\") a.push(node);\n      return a;\n    },\n\n    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.\n    unmark: function(nodes) {\n      for (var i = 0, node; node = nodes[i]; i++)\n        node.removeAttribute('_countedByPrototype');\n      return nodes;\n    }\n  });\n}\n\nfunction $$() {\n  return Selector.findChildElements(document, $A(arguments));\n}\nvar Form = {\n  reset: function(form) {\n    $(form).reset();\n    return form;\n  },\n\n  serializeElements: function(elements, options) {\n    if (typeof options != 'object') options = { hash: !!options };\n    else if (Object.isUndefined(options.hash)) options.hash = true;\n    var key, value, submitted = false, submit = options.submit;\n\n    var data = elements.inject({ }, function(result, element) {\n      if (!element.disabled && element.name) {\n        key = element.name; value = $(element).getValue();\n        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&\n            submit !== false && (!submit || key == submit) && (submitted = true)))) {\n          if (key in result) {\n            // a key is already present; construct an array of values\n            if (!Object.isArray(result[key])) result[key] = [result[key]];\n            result[key].push(value);\n          }\n          else result[key] = value;\n        }\n      }\n      return result;\n    });\n\n    return options.hash ? data : Object.toQueryString(data);\n  }\n};\n\nForm.Methods = {\n  serialize: function(form, options) {\n    return Form.serializeElements(Form.getElements(form), options);\n  },\n\n  getElements: function(form) {\n    return $A($(form).getElementsByTagName('*')).inject([],\n      function(elements, child) {\n        if (Form.Element.Serializers[child.tagName.toLowerCase()])\n          elements.push(Element.extend(child));\n        return elements;\n      }\n    );\n  },\n\n  getInputs: function(form, typeName, name) {\n    form = $(form);\n    var inputs = form.getElementsByTagName('input');\n\n    if (!typeName && !name) return $A(inputs).map(Element.extend);\n\n    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {\n      var input = inputs[i];\n      if ((typeName && input.type != typeName) || (name && input.name != name))\n        continue;\n      matchingInputs.push(Element.extend(input));\n    }\n\n    return matchingInputs;\n  },\n\n  disable: function(form) {\n    form = $(form);\n    Form.getElements(form).invoke('disable');\n    return form;\n  },\n\n  enable: function(form) {\n    form = $(form);\n    Form.getElements(form).invoke('enable');\n    return form;\n  },\n\n  findFirstElement: function(form) {\n    var elements = $(form).getElements().findAll(function(element) {\n      return 'hidden' != element.type && !element.disabled;\n    });\n    var firstByIndex = elements.findAll(function(element) {\n      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;\n    }).sortBy(function(element) { return element.tabIndex }).first();\n\n    return firstByIndex ? firstByIndex : elements.find(function(element) {\n      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());\n    });\n  },\n\n  focusFirstElement: function(form) {\n    form = $(form);\n    form.findFirstElement().activate();\n    return form;\n  },\n\n  request: function(form, options) {\n    form = $(form), options = Object.clone(options || { });\n\n    var params = options.parameters, action = form.readAttribute('action') || '';\n    if (action.blank()) action = window.location.href;\n    options.parameters = form.serialize(true);\n\n    if (params) {\n      if (Object.isString(params)) params = params.toQueryParams();\n      Object.extend(options.parameters, params);\n    }\n\n    if (form.hasAttribute('method') && !options.method)\n      options.method = form.method;\n\n    return new Ajax.Request(action, options);\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nForm.Element = {\n  focus: function(element) {\n    $(element).focus();\n    return element;\n  },\n\n  select: function(element) {\n    $(element).select();\n    return element;\n  }\n};\n\nForm.Element.Methods = {\n  serialize: function(element) {\n    element = $(element);\n    if (!element.disabled && element.name) {\n      var value = element.getValue();\n      if (value != undefined) {\n        var pair = { };\n        pair[element.name] = value;\n        return Object.toQueryString(pair);\n      }\n    }\n    return '';\n  },\n\n  getValue: function(element) {\n    element = $(element);\n    var method = element.tagName.toLowerCase();\n    return Form.Element.Serializers[method](element);\n  },\n\n  setValue: function(element, value) {\n    element = $(element);\n    var method = element.tagName.toLowerCase();\n    Form.Element.Serializers[method](element, value);\n    return element;\n  },\n\n  clear: function(element) {\n    $(element).value = '';\n    return element;\n  },\n\n  present: function(element) {\n    return $(element).value != '';\n  },\n\n  activate: function(element) {\n    element = $(element);\n    try {\n      element.focus();\n      if (element.select && (element.tagName.toLowerCase() != 'input' ||\n          !['button', 'reset', 'submit'].include(element.type)))\n        element.select();\n    } catch (e) { }\n    return element;\n  },\n\n  disable: function(element) {\n    element = $(element);\n    element.disabled = true;\n    return element;\n  },\n\n  enable: function(element) {\n    element = $(element);\n    element.disabled = false;\n    return element;\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Field = Form.Element;\nvar $F = Form.Element.Methods.getValue;\n\n/*--------------------------------------------------------------------------*/\n\nForm.Element.Serializers = {\n  input: function(element, value) {\n    switch (element.type.toLowerCase()) {\n      case 'checkbox':\n      case 'radio':\n        return Form.Element.Serializers.inputSelector(element, value);\n      default:\n        return Form.Element.Serializers.textarea(element, value);\n    }\n  },\n\n  inputSelector: function(element, value) {\n    if (Object.isUndefined(value)) return element.checked ? element.value : null;\n    else element.checked = !!value;\n  },\n\n  textarea: function(element, value) {\n    if (Object.isUndefined(value)) return element.value;\n    else element.value = value;\n  },\n\n  select: function(element, value) {\n    if (Object.isUndefined(value))\n      return this[element.type == 'select-one' ?\n        'selectOne' : 'selectMany'](element);\n    else {\n      var opt, currentValue, single = !Object.isArray(value);\n      for (var i = 0, length = element.length; i < length; i++) {\n        opt = element.options[i];\n        currentValue = this.optionValue(opt);\n        if (single) {\n          if (currentValue == value) {\n            opt.selected = true;\n            return;\n          }\n        }\n        else opt.selected = value.include(currentValue);\n      }\n    }\n  },\n\n  selectOne: function(element) {\n    var index = element.selectedIndex;\n    return index >= 0 ? this.optionValue(element.options[index]) : null;\n  },\n\n  selectMany: function(element) {\n    var values, length = element.length;\n    if (!length) return null;\n\n    for (var i = 0, values = []; i < length; i++) {\n      var opt = element.options[i];\n      if (opt.selected) values.push(this.optionValue(opt));\n    }\n    return values;\n  },\n\n  optionValue: function(opt) {\n    // extend element because hasAttribute may not be native\n    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nAbstract.TimedObserver = Class.create(PeriodicalExecuter, {\n  initialize: function($super, element, frequency, callback) {\n    $super(callback, frequency);\n    this.element   = $(element);\n    this.lastValue = this.getValue();\n  },\n\n  execute: function() {\n    var value = this.getValue();\n    if (Object.isString(this.lastValue) && Object.isString(value) ?\n        this.lastValue != value : String(this.lastValue) != String(value)) {\n      this.callback(this.element, value);\n      this.lastValue = value;\n    }\n  }\n});\n\nForm.Element.Observer = Class.create(Abstract.TimedObserver, {\n  getValue: function() {\n    return Form.Element.getValue(this.element);\n  }\n});\n\nForm.Observer = Class.create(Abstract.TimedObserver, {\n  getValue: function() {\n    return Form.serialize(this.element);\n  }\n});\n\n/*--------------------------------------------------------------------------*/\n\nAbstract.EventObserver = Class.create({\n  initialize: function(element, callback) {\n    this.element  = $(element);\n    this.callback = callback;\n\n    this.lastValue = this.getValue();\n    if (this.element.tagName.toLowerCase() == 'form')\n      this.registerFormCallbacks();\n    else\n      this.registerCallback(this.element);\n  },\n\n  onElementEvent: function() {\n    var value = this.getValue();\n    if (this.lastValue != value) {\n      this.callback(this.element, value);\n      this.lastValue = value;\n    }\n  },\n\n  registerFormCallbacks: function() {\n    Form.getElements(this.element).each(this.registerCallback, this);\n  },\n\n  registerCallback: function(element) {\n    if (element.type) {\n      switch (element.type.toLowerCase()) {\n        case 'checkbox':\n        case 'radio':\n          Event.observe(element, 'click', this.onElementEvent.bind(this));\n          break;\n        default:\n          Event.observe(element, 'change', this.onElementEvent.bind(this));\n          break;\n      }\n    }\n  }\n});\n\nForm.Element.EventObserver = Class.create(Abstract.EventObserver, {\n  getValue: function() {\n    return Form.Element.getValue(this.element);\n  }\n});\n\nForm.EventObserver = Class.create(Abstract.EventObserver, {\n  getValue: function() {\n    return Form.serialize(this.element);\n  }\n});\nif (!window.Event) var Event = { };\n\nObject.extend(Event, {\n  KEY_BACKSPACE: 8,\n  KEY_TAB:       9,\n  KEY_RETURN:   13,\n  KEY_ESC:      27,\n  KEY_LEFT:     37,\n  KEY_UP:       38,\n  KEY_RIGHT:    39,\n  KEY_DOWN:     40,\n  KEY_DELETE:   46,\n  KEY_HOME:     36,\n  KEY_END:      35,\n  KEY_PAGEUP:   33,\n  KEY_PAGEDOWN: 34,\n  KEY_INSERT:   45,\n\n  cache: { },\n\n  relatedTarget: function(event) {\n    var element;\n    switch(event.type) {\n      case 'mouseover': element = event.fromElement; break;\n      case 'mouseout':  element = event.toElement;   break;\n      default: return null;\n    }\n    return Element.extend(element);\n  }\n});\n\nEvent.Methods = (function() {\n  var isButton;\n\n  if (Prototype.Browser.IE) {\n    var buttonMap = { 0: 1, 1: 4, 2: 2 };\n    isButton = function(event, code) {\n      return event.button == buttonMap[code];\n    };\n\n  } else if (Prototype.Browser.WebKit) {\n    isButton = function(event, code) {\n      switch (code) {\n        case 0: return event.which == 1 && !event.metaKey;\n        case 1: return event.which == 1 && event.metaKey;\n        default: return false;\n      }\n    };\n\n  } else {\n    isButton = function(event, code) {\n      return event.which ? (event.which === code + 1) : (event.button === code);\n    };\n  }\n\n  return {\n    isLeftClick:   function(event) { return isButton(event, 0) },\n    isMiddleClick: function(event) { return isButton(event, 1) },\n    isRightClick:  function(event) { return isButton(event, 2) },\n\n    element: function(event) {\n      event = Event.extend(event);\n\n      var node          = event.target,\n          type          = event.type,\n          currentTarget = event.currentTarget;\n\n      if (currentTarget && currentTarget.tagName) {\n        // Firefox screws up the \"click\" event when moving between radio buttons\n        // via arrow keys. It also screws up the \"load\" and \"error\" events on images,\n        // reporting the document as the target instead of the original image.\n        if (type === 'load' || type === 'error' ||\n          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'\n            && currentTarget.type === 'radio'))\n              node = currentTarget;\n      }\n      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;\n      return Element.extend(node);\n    },\n\n    findElement: function(event, expression) {\n      var element = Event.element(event);\n      if (!expression) return element;\n      var elements = [element].concat(element.ancestors());\n      return Selector.findElement(elements, expression, 0);\n    },\n\n    pointer: function(event) {\n      var docElement = document.documentElement,\n      body = document.body || { scrollLeft: 0, scrollTop: 0 };\n      return {\n        x: event.pageX || (event.clientX +\n          (docElement.scrollLeft || body.scrollLeft) -\n          (docElement.clientLeft || 0)),\n        y: event.pageY || (event.clientY +\n          (docElement.scrollTop || body.scrollTop) -\n          (docElement.clientTop || 0))\n      };\n    },\n\n    pointerX: function(event) { return Event.pointer(event).x },\n    pointerY: function(event) { return Event.pointer(event).y },\n\n    stop: function(event) {\n      Event.extend(event);\n      event.preventDefault();\n      event.stopPropagation();\n      event.stopped = true;\n    }\n  };\n})();\n\nEvent.extend = (function() {\n  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {\n    m[name] = Event.Methods[name].methodize();\n    return m;\n  });\n\n  if (Prototype.Browser.IE) {\n    Object.extend(methods, {\n      stopPropagation: function() { this.cancelBubble = true },\n      preventDefault:  function() { this.returnValue = false },\n      inspect: function() { return \"[object Event]\" }\n    });\n\n    return function(event) {\n      if (!event) return false;\n      if (event._extendedByPrototype) return event;\n\n      event._extendedByPrototype = Prototype.emptyFunction;\n      var pointer = Event.pointer(event);\n      Object.extend(event, {\n        target: event.srcElement,\n        relatedTarget: Event.relatedTarget(event),\n        pageX:  pointer.x,\n        pageY:  pointer.y\n      });\n      return Object.extend(event, methods);\n    };\n\n  } else {\n    Event.prototype = Event.prototype || document.createEvent(\"HTMLEvents\")['__proto__'];\n    Object.extend(Event.prototype, methods);\n    return Prototype.K;\n  }\n})();\n\nObject.extend(Event, (function() {\n  var cache = Event.cache;\n\n  function getEventID(element) {\n    if (element._prototypeEventID) return element._prototypeEventID[0];\n    arguments.callee.id = arguments.callee.id || 1;\n    return element._prototypeEventID = [++arguments.callee.id];\n  }\n\n  function getDOMEventName(eventName) {\n    if (eventName && eventName.include(':')) return \"dataavailable\";\n    return eventName;\n  }\n\n  function getCacheForID(id) {\n    return cache[id] = cache[id] || { };\n  }\n\n  function getWrappersForEventName(id, eventName) {\n    var c = getCacheForID(id);\n    return c[eventName] = c[eventName] || [];\n  }\n\n  function createWrapper(element, eventName, handler) {\n    var id = getEventID(element);\n    var c = getWrappersForEventName(id, eventName);\n    if (c.pluck(\"handler\").include(handler)) return false;\n\n    var wrapper = function(event) {\n      if (!Event || !Event.extend ||\n        (event.eventName && event.eventName != eventName))\n          return false;\n\n      Event.extend(event);\n      handler.call(element, event);\n    };\n\n    wrapper.handler = handler;\n    c.push(wrapper);\n    return wrapper;\n  }\n\n  function findWrapper(id, eventName, handler) {\n    var c = getWrappersForEventName(id, eventName);\n    return c.find(function(wrapper) { return wrapper.handler == handler });\n  }\n\n  function destroyWrapper(id, eventName, handler) {\n    var c = getCacheForID(id);\n    if (!c[eventName]) return false;\n    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));\n  }\n\n  function destroyCache() {\n    for (var id in cache)\n      for (var eventName in cache[id])\n        cache[id][eventName] = null;\n  }\n\n\n  // Internet Explorer needs to remove event handlers on page unload\n  // in order to avoid memory leaks.\n  if (window.attachEvent) {\n    window.attachEvent(\"onunload\", destroyCache);\n  }\n\n  // Safari has a dummy event handler on page unload so that it won't\n  // use its bfcache. Safari <= 3.1 has an issue with restoring the \"document\"\n  // object when page is returned to via the back button using its bfcache.\n  if (Prototype.Browser.WebKit) {\n    window.addEventListener('unload', Prototype.emptyFunction, false);\n  }\n\n  return {\n    observe: function(element, eventName, handler) {\n      element = $(element);\n      var name = getDOMEventName(eventName);\n\n      var wrapper = createWrapper(element, eventName, handler);\n      if (!wrapper) return element;\n\n      if (element.addEventListener) {\n        element.addEventListener(name, wrapper, false);\n      } else {\n        element.attachEvent(\"on\" + name, wrapper);\n      }\n\n      return element;\n    },\n\n    stopObserving: function(element, eventName, handler) {\n      element = $(element);\n      var id = getEventID(element), name = getDOMEventName(eventName);\n\n      if (!handler && eventName) {\n        getWrappersForEventName(id, eventName).each(function(wrapper) {\n          element.stopObserving(eventName, wrapper.handler);\n        });\n        return element;\n\n      } else if (!eventName) {\n        Object.keys(getCacheForID(id)).each(function(eventName) {\n          element.stopObserving(eventName);\n        });\n        return element;\n      }\n\n      var wrapper = findWrapper(id, eventName, handler);\n      if (!wrapper) return element;\n\n      if (element.removeEventListener) {\n        element.removeEventListener(name, wrapper, false);\n      } else {\n        element.detachEvent(\"on\" + name, wrapper);\n      }\n\n      destroyWrapper(id, eventName, handler);\n\n      return element;\n    },\n\n    fire: function(element, eventName, memo) {\n      element = $(element);\n      if (element == document && document.createEvent && !element.dispatchEvent)\n        element = document.documentElement;\n\n      var event;\n      if (document.createEvent) {\n        event = document.createEvent(\"HTMLEvents\");\n        event.initEvent(\"dataavailable\", true, true);\n      } else {\n        event = document.createEventObject();\n        event.eventType = \"ondataavailable\";\n      }\n\n      event.eventName = eventName;\n      event.memo = memo || { };\n\n      if (document.createEvent) {\n        element.dispatchEvent(event);\n      } else {\n        element.fireEvent(event.eventType, event);\n      }\n\n      return Event.extend(event);\n    }\n  };\n})());\n\nObject.extend(Event, Event.Methods);\n\nElement.addMethods({\n  fire:          Event.fire,\n  observe:       Event.observe,\n  stopObserving: Event.stopObserving\n});\n\nObject.extend(document, {\n  fire:          Element.Methods.fire.methodize(),\n  observe:       Element.Methods.observe.methodize(),\n  stopObserving: Element.Methods.stopObserving.methodize(),\n  loaded:        false\n});\n\n(function() {\n  /* Support for the DOMContentLoaded event is based on work by Dan Webb,\n     Matthias Miller, Dean Edwards and John Resig. */\n\n  var timer;\n\n  function fireContentLoadedEvent() {\n    if (document.loaded) return;\n    if (timer) window.clearInterval(timer);\n    document.fire(\"dom:loaded\");\n    document.loaded = true;\n  }\n\n  if (document.addEventListener) {\n    if (Prototype.Browser.WebKit) {\n      timer = window.setInterval(function() {\n        if (/loaded|complete/.test(document.readyState))\n          fireContentLoadedEvent();\n      }, 0);\n\n      Event.observe(window, \"load\", fireContentLoadedEvent);\n\n    } else {\n      document.addEventListener(\"DOMContentLoaded\",\n        fireContentLoadedEvent, false);\n    }\n\n  } else {\n    document.write(\"<script id=__onDOMContentLoaded defer src=//:><\\/script>\");\n    $(\"__onDOMContentLoaded\").onreadystatechange = function() {\n      if (this.readyState == \"complete\") {\n        this.onreadystatechange = null;\n        fireContentLoadedEvent();\n      }\n    };\n  }\n})();\n/*------------------------------- DEPRECATED -------------------------------*/\n\nHash.toQueryString = Object.toQueryString;\n\nvar Toggle = { display: Element.toggle };\n\nElement.Methods.childOf = Element.Methods.descendantOf;\n\nvar Insertion = {\n  Before: function(element, content) {\n    return Element.insert(element, {before:content});\n  },\n\n  Top: function(element, content) {\n    return Element.insert(element, {top:content});\n  },\n\n  Bottom: function(element, content) {\n    return Element.insert(element, {bottom:content});\n  },\n\n  After: function(element, content) {\n    return Element.insert(element, {after:content});\n  }\n};\n\nvar $continue = new Error('\"throw $continue\" is deprecated, use \"return\" instead');\n\n// This should be moved to script.aculo.us; notice the deprecated methods\n// further below, that map to the newer Element methods.\nvar Position = {\n  // set to true if needed, warning: firefox performance problems\n  // NOT needed for page scrolling, only if draggable contained in\n  // scrollable elements\n  includeScrollOffsets: false,\n\n  // must be called before calling withinIncludingScrolloffset, every time the\n  // page is scrolled\n  prepare: function() {\n    this.deltaX =  window.pageXOffset\n                || document.documentElement.scrollLeft\n                || document.body.scrollLeft\n                || 0;\n    this.deltaY =  window.pageYOffset\n                || document.documentElement.scrollTop\n                || document.body.scrollTop\n                || 0;\n  },\n\n  // caches x/y coordinate pair to use with overlap\n  within: function(element, x, y) {\n    if (this.includeScrollOffsets)\n      return this.withinIncludingScrolloffsets(element, x, y);\n    this.xcomp = x;\n    this.ycomp = y;\n    this.offset = Element.cumulativeOffset(element);\n\n    return (y >= this.offset[1] &&\n            y <  this.offset[1] + element.offsetHeight &&\n            x >= this.offset[0] &&\n            x <  this.offset[0] + element.offsetWidth);\n  },\n\n  withinIncludingScrolloffsets: function(element, x, y) {\n    var offsetcache = Element.cumulativeScrollOffset(element);\n\n    this.xcomp = x + offsetcache[0] - this.deltaX;\n    this.ycomp = y + offsetcache[1] - this.deltaY;\n    this.offset = Element.cumulativeOffset(element);\n\n    return (this.ycomp >= this.offset[1] &&\n            this.ycomp <  this.offset[1] + element.offsetHeight &&\n            this.xcomp >= this.offset[0] &&\n            this.xcomp <  this.offset[0] + element.offsetWidth);\n  },\n\n  // within must be called directly before\n  overlap: function(mode, element) {\n    if (!mode) return 0;\n    if (mode == 'vertical')\n      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /\n        element.offsetHeight;\n    if (mode == 'horizontal')\n      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /\n        element.offsetWidth;\n  },\n\n  // Deprecation layer -- use newer Element methods now (1.5.2).\n\n  cumulativeOffset: Element.Methods.cumulativeOffset,\n\n  positionedOffset: Element.Methods.positionedOffset,\n\n  absolutize: function(element) {\n    Position.prepare();\n    return Element.absolutize(element);\n  },\n\n  relativize: function(element) {\n    Position.prepare();\n    return Element.relativize(element);\n  },\n\n  realOffset: Element.Methods.cumulativeScrollOffset,\n\n  offsetParent: Element.Methods.getOffsetParent,\n\n  page: Element.Methods.viewportOffset,\n\n  clone: function(source, target, options) {\n    options = options || { };\n    return Element.clonePosition(target, source, options);\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nif (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){\n  function iter(name) {\n    return name.blank() ? null : \"[contains(concat(' ', @class, ' '), ' \" + name + \" ')]\";\n  }\n\n  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?\n  function(element, className) {\n    className = className.toString().strip();\n    var cond = /\\s/.test(className) ? $w(className).map(iter).join('') : iter(className);\n    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];\n  } : function(element, className) {\n    className = className.toString().strip();\n    var elements = [], classNames = (/\\s/.test(className) ? $w(className) : null);\n    if (!classNames && !className) return elements;\n\n    var nodes = $(element).getElementsByTagName('*');\n    className = ' ' + className + ' ';\n\n    for (var i = 0, child, cn; child = nodes[i]; i++) {\n      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||\n          (classNames && classNames.all(function(name) {\n            return !name.toString().blank() && cn.include(' ' + name + ' ');\n          }))))\n        elements.push(Element.extend(child));\n    }\n    return elements;\n  };\n\n  return function(className, parentElement) {\n    return $(parentElement || document.body).getElementsByClassName(className);\n  };\n}(Element.Methods);\n\n/*--------------------------------------------------------------------------*/\n\nElement.ClassNames = Class.create();\nElement.ClassNames.prototype = {\n  initialize: function(element) {\n    this.element = $(element);\n  },\n\n  _each: function(iterator) {\n    this.element.className.split(/\\s+/).select(function(name) {\n      return name.length > 0;\n    })._each(iterator);\n  },\n\n  set: function(className) {\n    this.element.className = className;\n  },\n\n  add: function(classNameToAdd) {\n    if (this.include(classNameToAdd)) return;\n    this.set($A(this).concat(classNameToAdd).join(' '));\n  },\n\n  remove: function(classNameToRemove) {\n    if (!this.include(classNameToRemove)) return;\n    this.set($A(this).without(classNameToRemove).join(' '));\n  },\n\n  toString: function() {\n    return $A(this).join(' ');\n  }\n};\n\nObject.extend(Element.ClassNames.prototype, Enumerable);\n\n/*--------------------------------------------------------------------------*/\n\nElement.addMethods();"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/public/robots.txt",
    "content": "# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file\n#\n# To ban all spiders from the entire site uncomment the next two lines:\n# User-Agent: *\n# Disallow: /\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/public/stylesheets/scaffold.css",
    "content": "body { background-color: #fff; color: #333; }\n\nbody, p, ol, ul, td {\n  font-family: verdana, arial, helvetica, sans-serif;\n  font-size:   13px;\n  line-height: 18px;\n}\n\npre {\n  background-color: #eee;\n  padding: 10px;\n  font-size: 11px;\n}\n\na { color: #000; }\na:visited { color: #666; }\na:hover { color: #fff; background-color:#000; }\n\n.fieldWithErrors {\n  padding: 2px;\n  background-color: red;\n  display: table;\n}\n\n#errorExplanation {\n  width: 400px;\n  border: 2px solid red;\n  padding: 7px;\n  padding-bottom: 12px;\n  margin-bottom: 20px;\n  background-color: #f0f0f0;\n}\n\n#errorExplanation h2 {\n  text-align: left;\n  font-weight: bold;\n  padding: 5px 5px 5px 15px;\n  font-size: 12px;\n  margin: -7px;\n  background-color: #c00;\n  color: #fff;\n}\n\n#errorExplanation p {\n  color: #333;\n  margin-bottom: 0;\n  padding: 5px;\n}\n\n#errorExplanation ul li {\n  font-size: 12px;\n  list-style: square;\n}\n\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/script/about",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\n$LOAD_PATH.unshift \"#{RAILTIES_PATH}/builtin/rails_info\"\nrequire 'commands/about'\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/script/console",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/console'\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/script/dbconsole",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/dbconsole'\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/script/destroy",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/destroy'\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/script/generate",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/generate'\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/script/performance/benchmarker",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../../config/boot',  __FILE__)\nrequire 'commands/performance/benchmarker'\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/script/performance/profiler",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../../config/boot',  __FILE__)\nrequire 'commands/performance/profiler'\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/script/plugin",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/plugin'\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/script/runner",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/runner'\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/script/server",
    "content": "#!/usr/bin/env ruby\nrequire File.expand_path('../../config/boot',  __FILE__)\nrequire 'commands/server'\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/test/fixtures/posts.yml",
    "content": "# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html\n\none:\n  user_id: 1\n  title: MyString\n  body: MyString\n  in_reply_to: 1\n\ntwo:\n  user_id: 1\n  title: MyString\n  body: MyString\n  in_reply_to: 1\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/test/fixtures/users.yml",
    "content": "# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html\n\none:\n  display_name: MyString\n  user_name: MyString\n  signature: MyString\n  profile: MyString\n  password: MyString\n  admin: false\n\ntwo:\n  display_name: MyString\n  user_name: MyString\n  signature: MyString\n  profile: MyString\n  password: MyString\n  admin: false\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/test/functional/posts_controller_test.rb",
    "content": "require 'test_helper'\n\nclass PostsControllerTest < ActionController::TestCase\n  test \"should get index\" do\n    get :index\n    assert_response :success\n    assert_not_nil assigns(:posts)\n  end\n\n  test \"should get new\" do\n    get :new\n    assert_response :success\n  end\n\n  test \"should create post\" do\n    assert_difference('Post.count') do\n      post :create, :post => { }\n    end\n\n    assert_redirected_to post_path(assigns(:post))\n  end\n\n  test \"should show post\" do\n    get :show, :id => posts(:one).to_param\n    assert_response :success\n  end\n\n  test \"should get edit\" do\n    get :edit, :id => posts(:one).to_param\n    assert_response :success\n  end\n\n  test \"should update post\" do\n    put :update, :id => posts(:one).to_param, :post => { }\n    assert_redirected_to post_path(assigns(:post))\n  end\n\n  test \"should destroy post\" do\n    assert_difference('Post.count', -1) do\n      delete :destroy, :id => posts(:one).to_param\n    end\n\n    assert_redirected_to posts_path\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/test/functional/users_controller_test.rb",
    "content": "require 'test_helper'\n\nclass UsersControllerTest < ActionController::TestCase\n  test \"should get index\" do\n    get :index\n    assert_response :success\n    assert_not_nil assigns(:users)\n  end\n\n  test \"should get new\" do\n    get :new\n    assert_response :success\n  end\n\n  test \"should create user\" do\n    assert_difference('User.count') do\n      post :create, :user => { }\n    end\n\n    assert_redirected_to user_path(assigns(:user))\n  end\n\n  test \"should show user\" do\n    get :show, :id => users(:one).to_param\n    assert_response :success\n  end\n\n  test \"should get edit\" do\n    get :edit, :id => users(:one).to_param\n    assert_response :success\n  end\n\n  test \"should update user\" do\n    put :update, :id => users(:one).to_param, :user => { }\n    assert_redirected_to user_path(assigns(:user))\n  end\n\n  test \"should destroy user\" do\n    assert_difference('User.count', -1) do\n      delete :destroy, :id => users(:one).to_param\n    end\n\n    assert_redirected_to users_path\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/test/performance/browsing_test.rb",
    "content": "require 'test_helper'\nrequire 'performance_test_help'\n\n# Profiling results for each test method are written to tmp/performance.\nclass BrowsingTest < ActionController::PerformanceTest\n  def test_homepage\n    get '/'\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/test/test_helper.rb",
    "content": "ENV[\"RAILS_ENV\"] = \"test\"\nrequire File.expand_path(File.dirname(__FILE__) + \"/../config/environment\")\nrequire 'test_help'\n\nclass ActiveSupport::TestCase\n  # Transactional fixtures accelerate your tests by wrapping each test method\n  # in a transaction that's rolled back on completion.  This ensures that the\n  # test database remains unchanged so your fixtures don't have to be reloaded\n  # between every test method.  Fewer database queries means faster tests.\n  #\n  # Read Mike Clark's excellent walkthrough at\n  #   http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting\n  #\n  # Every Active Record database supports transactions except MyISAM tables\n  # in MySQL.  Turn off transactional fixtures in this case; however, if you\n  # don't care one way or the other, switching from MyISAM to InnoDB tables\n  # is recommended.\n  #\n  # The only drawback to using transactional fixtures is when you actually \n  # need to test transactions.  Since your test is bracketed by a transaction,\n  # any transactions started in your code will be automatically rolled back.\n  self.use_transactional_fixtures = true\n\n  # Instantiated fixtures are slow, but give you @david where otherwise you\n  # would need people(:david).  If you don't want to migrate your existing\n  # test cases which use the @david style and don't mind the speed hit (each\n  # instantiated fixtures translates to a database query per test method),\n  # then set this back to true.\n  self.use_instantiated_fixtures  = false\n\n  # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.\n  #\n  # Note: You'll currently still have to declare fixtures explicitly in integration tests\n  # -- they do not yet inherit this setting\n  fixtures :all\n\n  # Add more helper methods to be used by all tests here...\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/test/unit/helpers/posts_helper_test.rb",
    "content": "require 'test_helper'\n\nclass PostsHelperTest < ActionView::TestCase\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/test/unit/helpers/users_helper_test.rb",
    "content": "require 'test_helper'\n\nclass UsersHelperTest < ActionView::TestCase\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/test/unit/post_test.rb",
    "content": "require 'test_helper'\n\nclass PostTest < ActiveSupport::TestCase\n  # Replace this with your real tests.\n  test \"the truth\" do\n    assert true\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/test/unit/user_test.rb",
    "content": "require 'test_helper'\n\nclass UserTest < ActiveSupport::TestCase\n  # Replace this with your real tests.\n  test \"the truth\" do\n    assert true\n  end\nend\n"
  },
  {
    "path": "test/apps/rails_with_xss_plugin/vendor/plugins/rails_xss/README",
    "content": "Don't need to include whole plugin, just the directory\n"
  },
  {
    "path": "test/test.rb",
    "content": "#Set paths\nunless defined? TEST_PATH\n  TEST_PATH = File.expand_path(File.dirname(__FILE__))\n  $LOAD_PATH.unshift \"#{TEST_PATH}/../lib\"\nend\n\nbegin\n  require 'simplecov'\n\n  SimpleCov.start\nrescue LoadError\n  $stderr.puts \"Install simplecov for test coverage report\"\nend\n\nrequire 'brakeman'\nrequire 'brakeman/scanner'\nrequire 'minitest/autorun'\nrequire 'minitest/pride'\nrequire 'minitest/mock'\n\nif ENV[\"CIRCLECI\"]\n  require 'minitest/ci'\n  Minitest::Ci.report_dir = File.join(\"test-results\", \"minitest\")\nend\n\nif ENV['TEST_PRISM']\n  gem 'prism'\n  require 'prism'\nend\n\nclass Minitest::Test\n  def assert_nothing_raised *args\n    yield\n  end\nend\n\nBrakeman.set_default_logger({})\n\n#Helper methods for running scans\nmodule BrakemanTester\n  class << self\n    #Run scan on app at the given path\n    def run_scan path, name = nil, opts = {}\n      opts.merge! :app_path => \"#{TEST_PATH}/apps/#{path}\",\n        :url_safe_methods => [:ensure_valid_proto!],\n        :parallel_checks => false # Something broken with tests+parallel\n\n      if ENV['TEST_PRISM']\n        opts[:use_prism] = true\n      end\n\n      Brakeman.run(opts).report.to_hash\n    end\n\n    def new_tracker options = {}\n      Brakeman.logger ||= Brakeman::Logger.get_logger(options)\n      Brakeman::Tracker.new(Brakeman::AppTree.new(\"/tmp/FAKE_BRAKEMAN_PATH#{rand(10000)}\"), nil, options)\n    end\n  end\nend\n\n#Helpers for finding warnings in the report\nmodule BrakemanTester::FindWarning\n  def assert_warning opts\n    warnings = find opts\n    refute_equal 0, warnings.length, \"No warning found\"\n    assert_equal 1, warnings.length, \"Matched more than one warning\"\n  end\n\n  def assert_no_warning opts\n    warnings = find opts\n    assert_equal 0, warnings.length, \"Found warning when no warning was expected\"\n  end\n\n  def warning_table type\n    case type\n    when :warning, :generic, nil\n      :generic_warnings\n    when :template\n      :template_warnings\n    when :controller\n      :controller_warnings\n    when :model\n      :model_warnings\n    else\n      raise \"Unknown warning type: #{type.inspect}\"\n    end\n  end\n\n  def find opts = {}\n    warnings = report[warning_table(opts[:type])]\n\n    opts.delete :type\n\n    warnings.select do |w|\n      opts.all? do |k,v|\n        if k == :relative_path\n          v === w.file.relative\n        else\n          v === w.send(k)\n        end\n      end\n    end\n  end\nend\n\n#Check that the number of warnings reported are as expected.\n#This is mainly to look for new warnings that are not being tested.\nmodule BrakemanTester::CheckExpected\n  def test_number_of_warnings\n    require 'pp'\n\n    expected.each do |type, number|\n      warnings = report[warning_table(type)]\n\n      assert_equal number, warnings.length, lambda { \"Expected #{number} #{type} warnings, but found #{warnings.length}:\\n#{warnings.map { |w| w.message }.join(\"\\n\")}\" }\n    end\n  end\n\n  def test_zero_errors\n    assert_equal 0, report[:errors].length, \"Unexpected warning found: #{report[:errors].inspect}\"\n  end\n\n  def test_every_warning_has_file\n    [:generic_warnings, :template_warnings, :controller_warnings, :model_warnings].each do |type|\n      report[type].each do |w|\n        refute_nil w.file, lambda { \"Warning did not have a file: #{w.message}\" }\n      end\n    end\n  end\n\n  def test_every_warning_has_cwe_id\n    [:generic_warnings, :template_warnings, :controller_warnings, :model_warnings].each do |type|\n      report[type].each do |w|\n        refute_nil w.cwe_id, lambda { \"Warning did not have a CWE ID: #{w.message}\" }\n        assert_kind_of Array, w.cwe_id, lambda { 'Warnings must have a CWE that is an Array'}\n        w.cwe_id.each do |cwe|\n          assert_kind_of Integer, cwe, lambda { 'Warnings must have a CWE IDs that are Integers'}\n        end\n      end\n    end\n  end\nend\n\nmodule BrakemanTester::RescanTestHelper\n  attr_reader :original, :rescan, :rescanner\n\n  @@temp_dirs = {}\n  @@scans = {}\n\n  Minitest.after_run do\n    @@temp_dirs.each do |_, dir|\n      FileUtils.remove_dir(dir, true)\n    end\n\n    # Make sure to reset console\n    require 'brakeman/logger'\n    Brakeman::Logger::Console.new({}).cleanup\n  end\n\n  def self.included _\n    unless Brakeman::Rescanner.instance_methods.include? :reindex\n      Brakeman::Rescanner.class_eval do\n        #For access to internals\n        attr_reader :changes, :reindex\n      end\n    end\n  end\n\n  #Takes care of copying files to a temporary directory, scanning the files,\n  #performing operations in the block (if provided), then rescanning the files\n  #given in `changed`.\n  #\n  #Provide an array of changed files for rescanning.\n  def before_rescan_of changed, app = \"rails3.2\", options = {}\n    changed = [changed] unless changed.is_a? Array\n\n    if @@temp_dirs[app]\n      dir = @dir = @@temp_dirs[app]\n    else\n      dir = @dir = @@temp_dirs[app] = Dir.mktmpdir('brakeman-test')\n      FileUtils.cp_r(File.join(TEST_PATH, 'apps', app, '.'), dir)\n    end\n\n    options = {app_path: dir, debug: false, support_rescanning: true}.merge(options)\n\n    if @@scans[[app, options]]\n      @original = @@scans[[app, options]]\n    else\n      @@scans[[app, options]] = @original = Brakeman.run(options)\n    end\n\n    begin\n      yield dir if block_given?\n\n      # Not really sure why we do this..?\n      t = Marshal.load(Marshal.dump(@original.marshallable))\n\n      @rescanner = Brakeman::Rescanner.new(t.options, t.processor, changed)\n      @rescan = @rescanner.recheck\n    ensure\n      changed.each do |file|\n        original = File.join(TEST_PATH, 'apps', app, file)\n        if File.exist? original\n          FileUtils.cp original, full_path(file) \n        else\n          FileUtils.rm full_path(file)\n        end\n      end\n    end\n\n    assert_existing\n  end\n\n  def fixed\n    rescan.fixed_warnings\n  end\n\n  def new\n    rescan.new_warnings\n  end\n\n  def existing\n    rescan.existing_warnings\n  end\n\n  #Check how many fixed warnings were reported\n  def assert_fixed expected\n    assert_equal expected, fixed.length, lambda { \"Expected #{expected} fixed warnings, but found #{fixed.length}:\\n#{fixed.map {|w| \"\\t#{w.message}\" }.join(\"\\n\")}\" }\n  end\n\n  #Check how many new warnings were reported\n  def assert_new expected\n    assert_equal expected, new.length, lambda {\n      \"Expected #{expected} new warnings, but found #{new.length}:\\n#{new.map {|w| w.to_json}.join(\"\\n\")}\\n\" \\\n      \"Also these are the old ones:\\n#{existing.map {|w| w.to_json }.join(\"\\n\")}\"\n    }\n  end\n\n  #Check how many existing warnings were reported\n  def assert_existing\n    expected = (@rescan.old_results.length - fixed.length)\n\n    assert_equal expected, existing.length, \"Expected #{expected} existing warnings, but found #{existing.length}\"\n  end\n\n  def full_path file\n    File.expand_path file, @dir\n  end\n\n  def remove file\n    path = full_path file\n\n    assert File.exist?(path), \"Could not find #{path} to delete\"\n    File.delete path\n    assert_equal false, File.exist?(path)\n  end\n\n  def rename from_file, to_file\n    require 'fileutils'\n    old_path = full_path from_file\n    new_path = full_path to_file\n\n    assert File.exist?(old_path), \"Could not find #{old_path} to delete\"\n\n    FileUtils.mv old_path, new_path\n  end\n\n  def append file, code\n    File.open full_path(file), \"a\" do |f|\n      f.puts code\n    end\n  end\n\n  def replace_with_sexp file\n    path = full_path file\n    parsed = parse File.read path\n\n    output = yield parsed\n\n    File.open path, \"w\" do |f|\n      f.puts Brakeman::OutputProcessor.new.process output\n    end\n  end\n\n  def replace file, pattern, replacement\n    path = full_path file\n    input = File.read path\n    input.sub! pattern, replacement\n\n    File.open path, \"w\" do |f|\n      f.puts input\n    end\n  end\n\n  def write_file file, content\n    require 'fileutils'\n    path = full_path(file)\n    FileUtils.mkdir_p(File.dirname(path))\n    File.open path, \"w\" do |f|\n      f.puts content\n    end\n  end\n\n  def remove_method file, method_name\n    replace_with_sexp file do |parsed|\n      class_body = parsed.body\n\n      class_body.reject! do |node|\n        node.is_a? Sexp and\n        node.node_type == :defn and\n        node.method_name == method_name\n      end\n\n      parsed.body = class_body\n\n      parsed\n    end\n  end\n\n  def add_method file, code\n    parsed_method = parse code\n\n    replace_with_sexp file do |parsed|\n      parsed.body = parsed.body << parsed_method\n\n      parsed\n    end\n  end\n\n  def parse code\n    RubyParser.new.parse code\n  end\nend\n\nmodule BrakemanTester::DiffHelper\n  def assert_fixed expected, diff = @diff\n    assert_equal expected, diff[:fixed].length, \"Expected #{expected} fixed warnings, but found #{diff[:fixed].length}\"\n  end\n\n  def assert_new expected, diff = @diff\n    assert_equal expected, diff[:new].length, \"Expected #{expected} new warnings, but found #{diff[:new].length}\"\n  end\nend\n\nif __FILE__ == $0\n  Dir.glob \"#{TEST_PATH}/tests/*.rb\" do |file|\n    require file\n  end\nend\n"
  },
  {
    "path": "test/tests/active_record_only.rb",
    "content": "require_relative '../test'\n\nclass ActiveRecordOnlyTests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def expected\n    @expected ||= {\n      :controller => 0,\n      :model => 0,\n      :template => 0,\n      :warning => 1 }\n  end\n\n  def report\n    @@report ||=\n      Date.stub :today, Date.parse('2022-04-05') do\n        BrakemanTester.run_scan \"active_record_only\", \"ActiveRecordOnly\"\n      end\n  end\n\n  def test_no_attribute_restriction\n    assert_no_warning :type => :model,\n      :warning_code => 19,\n      :fingerprint => \"b660c00ebcf323130f61f3f402bd8ea067b472c785f9b069e1317216aa94360f\",\n      :warning_type => \"Attribute Restriction\",\n      :line => 5,\n      :message => /^Mass\\ assignment\\ is\\ not\\ restricted\\ using\\ /,\n      :confidence => 0,\n      :relative_path => \"app/models/book.rb\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_unmaintained_dependency_1\n    assert_warning check_name: \"EOLRails\",\n      type: :warning,\n      warning_code: 122,\n      fingerprint: \"ae8b91c42bce1bcab89b00b4d4f44479bd4376726f36207abc94d896eddd2320\",\n      warning_type: \"Unmaintained Dependency\",\n      line: nil,\n      message: /^Support\\ for\\ Rails\\ 5\\.2\\.4\\.3\\ ends\\ on\\ 2022\\-0/,\n      confidence: 2,\n      relative_path: \"Gemfile\",\n      code: nil,\n      user_input: nil\n  end\nend\n"
  },
  {
    "path": "test/tests/alias_processor.rb",
    "content": "require_relative '../test'\n\nclass AliasProcessorTests < Minitest::Test\n  def assert_alias expected, original, full = false\n    original_sexp = RubyParser.new.parse original\n    expected_sexp = RubyParser.new.parse expected\n    processed_sexp = Brakeman::AliasProcessor.new.process_safely original_sexp\n\n    if full\n      assert_equal expected_sexp, processed_sexp\n    else\n      assert_equal expected_sexp, processed_sexp.last\n    end\n  end\n\n  def assert_output input, output\n    assert_alias output, input, true\n  end\n\n  def test_addition\n    assert_alias '10', <<-RUBY\n      x = 1 + 2 + 3\n      x += 4\n      x\n    RUBY\n  end\n\n  def test_simple_math\n    assert_alias '42', <<-RUBY\n      x = 8 * 5\n      y = 32 / 8\n      y -= 2\n      x += y\n      x\n    RUBY\n  end\n\n  def test_divide_by_zero\n    assert_alias '1 / 0', <<-RUBY\n    x = 1 / 0\n    x\n    RUBY\n  end\n\n  def test_infinity\n    e = RubyParser.new.parse \"x = 1.0 / 0; x\"\n    a = Brakeman::AliasProcessor.new.process_safely e\n\n    assert_equal Sexp.new(:lit, 1.0 / 0), a.last\n  end\n\n  def test_concatentation\n    assert_alias \"'Hello world!'\", <<-RUBY\n      x = \"Hello\"\n      y = x + \" \"\n      z = y + \"world!\"\n      z\n    RUBY\n  end\n\n  def test_string_append\n    assert_alias \"'hello world'\", <<-RUBY\n      x = \"\"\n      x << \"hello\" << \" \" << \"world\"\n      x\n    RUBY\n  end\n\n  def test_string_new_append\n    assert_alias \"'hello world'\", <<-RUBY\n      x = String.new\n      x << \"hello\" << \" \" << \"world\"\n      x\n    RUBY\n  end\n\n  def test_string_append_call\n    assert_alias \"'hello ' << params[:x]\", <<-RUBY\n    x = \"\"\n    x << 'hello ' << params[:x]\n    x\n    RUBY\n  end\n\n  def test_string_interp_concat\n    assert_alias '\"#{y}\\nthing\"', <<-'RUBY'\n    x = \"#{y}\\n\"\n    x << \"thing\"\n    x\n    RUBY\n  end\n\n  def test_string_concat_interp\n    assert_alias '\"hello\\n#{world}\"', <<-'RUBY'\n    x = \"\"\n    x << \"hello\"\n    x << \"\\n#{world}\"\n    x\n    RUBY\n  end\n\n  def test_array_index\n    assert_alias \"'cookie'\", <<-RUBY\n      dessert = [\"fruit\", \"pie\", \"ice cream\"]\n      dessert << \"cookie\"\n      dessert[1] = \"cake\"\n      dessert[1]\n      index = 2\n      index = index + 1\n      dessert[index]\n    RUBY\n  end\n\n  def test_array_negative_index\n    assert_alias \"'ice cream'\", <<-RUBY\n      dessert = [\"fruit\", \"pie\", \"ice cream\"]\n      dessert << \"cookie\"\n      dessert[1] = \"cake\"\n      dessert[1]\n      index = -3\n      index = 1 + index\n      dessert[index]\n    RUBY\n  end\n\n  def test_array_fetch\n    assert_alias '3', <<-RUBY\n    x = [1, 2, 3]\n    x.fetch(2)\n    RUBY\n  end\n\n  def test_array_fetch_unknown_literal\n    assert_alias ':BRAKEMAN_SAFE_LITERAL', <<-RUBY\n    x = [1, 2, 3]\n    y = x.fetch(z)\n    y\n    RUBY\n  end\n\n  def test_array_index_unknown_literal\n    assert_alias ':BRAKEMAN_SAFE_LITERAL', <<-RUBY\n    x = [1, 2, 3][y]\n    x\n    RUBY\n  end\n\n  def test_array_append\n    assert_alias '[1, 2, 3]', <<-RUBY\n      x = [1]\n      x << 2 << 3\n      x\n    RUBY\n  end\n\n  def test_array_new_append\n    assert_alias '[1, 2, 3]', <<-RUBY\n      x = Array.new\n      x << 1 << 2 << 3\n      x\n    RUBY\n  end\n\n  def test_array_new_init_append\n    assert_alias '[1, 2, 3]', <<-RUBY\n      x = Array.new(1)\n      x << 2 << 3\n      x\n    RUBY\n  end\n\n  def test_array_push\n    assert_alias '[1, 2, 3]', <<-RUBY\n      x = [1]\n      x.push(2)\n      x.push(3)\n      x\n    RUBY\n  end\n\n  def test_array_detect\n    assert_alias ':BRAKEMAN_SAFE_LITERAL', <<-RUBY\n      x = [1,2,3].detect { |x| x.odd? }\n      x\n    RUBY\n  end\n\n  def test_array_first\n    assert_alias '1', <<-RUBY\n      x = [1, 2, 3]\n      y = x.first\n      y\n    RUBY\n  end\n\n  def test_array_plus_equals\n    assert_alias '[1, 2, 3]', <<-RUBY\n    x = [1]\n    x += [2, 3]\n    x\n    RUBY\n  end\n\n  def test_array_plus\n    assert_alias '[1, 2, 3]', <<-RUBY\n    x = [1]\n    y = x + [2, 3]\n    y\n    RUBY\n  end\n\n  def test_array_plus_no_lines\n    a1 = s(:array, s(:lit, 1))\n    a2 = s(:array, s(:lit, 2))\n    joined = Brakeman::AliasProcessor.new.join_arrays(a1, a2)\n    expected = s(:array,\n                 s(:lit, 1),\n                 s(:lit, 2))\n\n    assert_equal expected, joined\n  end\n\n  def test_hash_index\n    assert_alias \"'You say goodbye, I say :hello'\", <<-RUBY\n      x = {:goodbye => \"goodbye cruel world\" }\n      x[:hello] = \"hello world\"\n      x.merge! :goodbye => \"You say goodbye, I say :hello\"\n      x[:goodbye]\n    RUBY\n  end\n\n  def test_hash_new_index\n    assert_alias \"'You say goodbye, I say :hello'\", <<-RUBY\n      x = Hash.new\n      x[:hello] = \"hello world\"\n      x.merge! :goodbye => \"You say goodbye, I say :hello\"\n      x[:goodbye]\n    RUBY\n  end\n\n  def test_hash_fetch\n    assert_alias '1', <<-RUBY\n      x = { a: 0, b: 1, c: 3 }\n      x.fetch(:b)\n    RUBY\n  end\n\n  def test_hash_fetch_unknown_literal\n    assert_alias ':BRAKEMAN_SAFE_LITERAL', <<-RUBY\n      x = { a: 0, b: 1, c: 3 }\n      x.fetch(:z)\n    RUBY\n  end\n\n  def test_hash_update\n    assert_alias \"2\", <<-RUBY\n      @foo = {\n        :denominator => 0\n      }\n\n      @foo[:denominator] += 2\n\n      x = @foo[:denominator]\n      x\n    RUBY\n  end\n\n  def test_hash_values\n    assert_alias '[1, 2, 3]', <<-RUBY\n      h = { a: 1, b: 2, c: 3 }\n      h.values\n    RUBY\n  end\n\n  def test_hash_values_at\n    assert_alias '[1, 2]', <<-RUBY\n      h = { a: 1, b: 2, c: 3 }\n      h.values_at(:a, :b)\n    RUBY\n  end\n\n  def test_hash_values_at_missing\n    assert_alias '{ a: 1, b: 2, c: x }.values_at(:a, :b, :z)', <<-RUBY\n      h = { a: 1, b: 2, c: x }\n      h.values_at(:a, :b, :z)\n    RUBY\n  end\n\n  def test_hash_values_at_missing_safe\n    assert_alias '[1, 2, :BRAKEMAN_SAFE_LITERAL]', <<-RUBY\n      h = { a: 1, b: 2, c: 3 }\n      h.values_at(:a, :b, :z)\n    RUBY\n  end\n\n  def test_hash_double_splat\n    assert_alias \"1\", <<-RUBY\n      a = { a: 1, b: \"two\", c: :three }\n      b = { **a, d: 4 }\n      b[:a]\n    RUBY\n  end\n\n  def test_hash_shorthand_syntax\n    assert_alias '2', <<-RUBY\n      a = 1\n      b = 2\n      h = { a:, b: }\n      h[:b]\n    RUBY\n  end\n\n  def test_hash_shorthand_syntax_unknown_value\n    assert_alias 'b', <<-RUBY\n      h = { a:, b:, c: 1 }\n      h[:b]\n    RUBY\n  end\n\n  def test_hash_shorthand_syntax_mix\n    assert_alias '3', <<-RUBY\n      a = 1\n      h = { a:, b:, c: 3 }\n      h[:c]\n    RUBY\n  end\n\n  def test_splat_array_args\n    assert_alias 'x(1, b, :c)', <<-RUBY\n      a = b\n      args = [1, a, :c]\n      x(*args)\n    RUBY\n  end\n\n  def test_obvious_if\n    assert_alias \"'Yes!'\", <<-RUBY\n      condition = true\n\n      if condition\n        x = \"Yes!\"\n      else\n        x = \"No!\"\n      end\n\n      x\n    RUBY\n  end\n\n  def test_skip_obvious_if\n    assert_output <<-INPUT, <<-OUTPUT\n      condition = false\n\n      if condition\n        x = true\n      else\n        x = false\n      end\n      INPUT\n      condition = false\n\n      if false\n      else\n        x = false\n      end\n      OUTPUT\n  end\n\n  def test_skip_rails_env_test\n    assert_alias \"'No!'\", <<-RUBY\n      if Rails.env.test?\n        x = \"Yes!\"\n      else\n        x = \"No!\"\n      end\n\n      x\n    RUBY\n  end\n\n  def test_if\n    assert_alias \"'Awesome!' or 'Else awesome!'\", <<-RUBY\n      if something\n        x = \"Awesome!\"\n      elsif something_else\n        x = \"Else awesome!\"\n      end\n\n      x\n    RUBY\n  end\n\n  def test_if_in_when\n    # Just testing that `if` inside a `when` doesn't cause an error\n    # https://github.com/presidentbeef/brakeman/issues/1743\n    assert_alias '1', <<-RUBY\n    case something\n    when (if a then b else c end) then 1\n    end\n\n    1\n    RUBY\n  end\n\n  def test_or_equal\n    assert_alias '10', <<-RUBY\n      x.y = 10\n      x.y ||= \"not this!\"\n      x.y\n    RUBY\n  end\n\n  def test_safe_or_equal\n    assert_alias '10', <<-RUBY\n      x&.y ||= 10\n      x&.y ||= \"not this!\"\n      x&.y\n    RUBY\n  end\n\n  def test_unknown_hash\n    assert_alias '1', <<-RUBY\n      some_hash[:x] = 1\n      some_hash[:x]\n    RUBY\n  end\n\n  def test_global\n    assert_alias '1', <<-RUBY\n      $x = 1\n      $x\n    RUBY\n  end\n\n  def test_class_var\n    assert_alias '1', <<-RUBY\n      @@x = 1\n      @@x\n    RUBY\n  end\n\n  def test_constant\n    assert_alias '1', <<-RUBY\n      X = 1\n      X\n    RUBY\n  end\n\n  def test_addition_chained\n    assert_alias 'y + 5', <<-RUBY\n      x = y + 2 + 3\n      x\n    RUBY\n  end\n\n  def test_send_collapse\n    assert_alias 'x.y(1)', <<-RUBY\n      z = x.send(:y, 1)\n      z\n    RUBY\n  end\n\n  def test_send_collapse_with_no_target\n    assert_alias 'y(1)', <<-RUBY\n      x = send(:y, 1)\n      x\n    RUBY\n  end\n\n  def test_safe_send_collapse\n    assert_alias 'x.y(1)', <<-RUBY\n      z = x&.send(:y, 1)\n      z\n    RUBY\n  end\n\n  def test_safe___send__\n    assert_alias 'X.y(:z)', <<-RUBY\n    a = X.__send__(:y, :z)\n    a\n    RUBY\n  end\n\n  def test_try_collapse\n    assert_alias 'x.y', <<-RUBY\n      z = x.try(:y)\n      z\n    RUBY\n  end\n\n  def test_try_symbol_to_proc_collapse\n    assert_alias 'x.y', <<-RUBY\n      z = x.try(&:y)\n      z\n    RUBY\n  end\n\n  def test_multiple_assignments_in_if\n    assert_alias \"1 or 4\", <<-RUBY\n    x = 1\n\n    if y\n      x = 2\n      x = 3\n      x = 4\n    end\n\n    x\n    RUBY\n  end\n\n  def test_assignments_both_branches\n    assert_alias \"'1234' or 6\", <<-RUBY\n    if y\n      x = '1'\n      x += '2'\n      x += '3'\n      x += '4'\n    else\n      x = 5\n      x = 6\n    end\n\n    x\n    RUBY\n  end\n\n  def test_assignments_in_forced_branch\n    assert_alias \"4\", <<-RUBY\n    x = 1\n\n    if true\n      x = 2\n      x = 3\n      x = 4\n    else\n      x = 5\n      x = 6\n    end\n\n    x\n    RUBY\n  end\n\n  def test_assignments_inside_branch_are_isolated\n    assert_alias \"5\", <<-RUBY\n    x = 1\n    if something\n      x = 2\n      x = 3\n    else\n      x = 4\n      x = 5\n      y = x\n    end\n\n    y\n    RUBY\n  end\n\n  def test_simple_if_branch_replacement\n    assert_alias \"1\", <<-RUBY\n    x = 1\n\n    y = true ? x : z\n    y\n    RUBY\n  end\n\n  def test_assignment_in_equality_forced_branch\n    assert_alias ':yes', <<-RUBY\n      x = 1\n      y = nil\n\n      if x == 1\n        y = :yes\n      else\n        y = :no\n      end\n\n      y\n    RUBY\n  end\n\n  def test_simple_or_operation_compaction\n    assert_alias \"[0, 4, (4 || 8)]\", <<-RUBY\n    x = 1\n\n    if z\n      x += 1\n      y = 2\n      w = 10\n    else\n      x += 2\n      y = 4\n      w = 5\n    end\n\n    w = w * 0\n    y = y * 2\n\n    [w, x, y]\n    RUBY\n  end\n\n  def test_assignment_of_simple_if_expression\n    assert_alias \"1 or 2\", <<-RUBY\n    x = (test ? 1 : 2)\n    x\n    RUBY\n  end\n\n  def test_assignment_of_forced_if_expression\n    assert_alias \"1\", <<-RUBY\n    x = (true ? 1 : 2)\n    x\n    RUBY\n  end\n\n  def test_default_branch_limit_5\n    assert_alias \"(6 or 7) or 8\", <<-RUBY\n    x = 0\n\n    if something\n      x = 1\n    else\n      x = 2\n    end\n\n    x = 3 unless this\n    x = 4 if that\n\n    if another_thing\n      x = 5\n    else\n      x = 6\n    end\n\n    x = 7 if getting_crazy\n    x = 8 if so_crazy\n\n    x\n    RUBY\n  end\n\n  def test_default_branch_limit_not_reached\n    assert_alias \"(((1 or 2) or 3) or 4)\", <<-RUBY\n    x = 1\n\n    if something\n      x = 2\n    else\n      x = 3\n    end\n\n    x = 4 unless this\n\n    x\n    RUBY\n  end\n\n  def test_default_branch_limit_before_reset_with_option\n    expected_y = RubyParser.new.parse \"((((0 or 1) or 2) or 3) or 4)\"\n    expected_x = RubyParser.new.parse \"5 or 6\"\n    original_sexp = RubyParser.new.parse <<-RUBY\n    x = 0\n\n    if something\n      x = 1\n    else\n      x = 2\n    end\n\n    if another_thing\n      x = 3\n    else\n      x = 4\n    end\n\n    y = x\n\n    x = 5 if getting_crazy\n    x = 6 if so_crazy\n\n    [x, y]\n    RUBY\n\n    tracker = BrakemanTester.new_tracker(:branch_limit => 4)\n\n    assert_equal 4, tracker.options[:branch_limit]\n\n    processed_sexp = Brakeman::AliasProcessor.new(tracker).process_safely original_sexp\n    result = processed_sexp.last\n\n    assert_equal expected_x, result[1]\n    assert_equal expected_y, result[2]\n  end\n\n  def test_simple_block_args\n    assert_alias '1', <<-RUBY\n    y = 1\n\n    x do |y|\n      y = 2\n    end\n\n    y\n    RUBY\n  end\n\n  def test_block_arg_assignment\n    assert_alias '1 + z', <<-RUBY\n    y = 1\n\n    blah do |y = 3, x = 2|\n      y = 2\n      z = x\n    end\n\n    y + z\n    RUBY\n  end\n\n  def test_block_arg_destructing\n    assert_alias '1', <<-RUBY\n    y = 1\n\n    blah do |(x, y)|\n      y = 2\n    end\n\n    y\n    RUBY\n  end\n\n  def test_block_args_trailing_comma\n    # Just testing it doesn't blow up\n    assert_alias '1', <<-RUBY\n    blah do |x,|\n    end\n\n    1\n    RUBY\n  end\n\n  def test_block_with_local\n    assert_output <<-INPUT, <<-OUTPUT\n      def a\n        if b\n          c = nil\n          ds.each do |d|\n            e = T.new\n            c = e.map\n          end\n\n          r(\"f\" + c.name)\n        else\n          g\n        end\n      end\n    INPUT\n      def a\n        if b\n          c = nil\n          ds.each do |d|\n            e = T.new\n            c = T.new.map\n          end\n\n          r(\"f\" + T.new.map.name)\n        else\n          g\n        end\n      end\n    OUTPUT\n  end\n\n  def test_shadowed_block_arg\n    assert_output <<-INPUT, <<-OUTPUT\n      def a\n        y = 1\n        x do |w; y, z|\n          y = 2\n        end\n        puts y\n      end\n    INPUT\n      def a\n        y = 1\n        x do |w; y, z|\n          y = 2\n        end\n        puts 1\n      end\n    OUTPUT\n  end\n\n  def test_block_in_class_scope\n    # Make sure blocks in class do not mess up instance variable scope\n    # for subsequent methods\n    assert_output <<-INPUT, <<-OUTPUT\n      class A\n        x do\n          @a = 1\n        end\n\n        def b\n          @a\n        end\n      end\n    INPUT\n      class A\n        x do\n          @a = 1\n        end\n\n        def b\n          @a\n        end\n      end\n    OUTPUT\n  end\n\n  def test_instance_method_scope_in_block\n    # Make sure instance variables set inside blocks are set at the method\n    # scope\n    assert_output <<-INPUT, <<-OUTPUT\n      class A\n        def b\n          x do\n            @a = 1\n          end\n\n          @a\n        end\n      end\n    INPUT\n      class A\n        def b\n          x do\n            @a = 1\n          end\n\n          1\n        end\n      end\n    OUTPUT\n  end\n\n  def test_instance_method_scope_in_if_with_blocks\n    # Make sure instance variables set inside if expressions are set at the\n    # method scope after being combined\n    assert_output <<-INPUT, <<-OUTPUT\n      class A\n        def b\n          if something\n            x do\n              @a = 1\n            end\n          else\n            y do\n              @a = 2\n            end\n          end\n\n          @a\n        end\n      end\n    INPUT\n      class A\n        def b\n          if something\n            x do\n              @a = 1\n            end\n          else\n            y do\n              @a = 2\n            end\n          end\n\n          (1 or 2)\n        end\n      end\n    OUTPUT\n  end\n\n  def test_branch_env_is_closed_after_if_statement\n    assert_output <<-'INPUT', <<-'OUTPUT'\n      def a\n        if b\n          return unless c # this was causing problems\n          @d = D.find(1)\n          @d\n        end\n      end\n    INPUT\n      def a\n        if b\n          return unless c\n          @d = D.find(1)\n          D.find(1)\n       end\n      end\n    OUTPUT\n  end\n\n  def test_no_branch_for_plus_equals_with_string\n    assert_alias '\"abc\"', <<-INPUT\n      x = \"a\"\n      x += \"b\" if something\n      x += \"c\" if something_else\n      x\n    INPUT\n  end\n\n  def test_no_branch_for_plus_equals_with_string_in_ivar\n    assert_alias '\"abc\"', <<-INPUT\n      @x = \"a\"\n      @x += \"b\" if something\n      @x += \"c\" if something_else\n      @x\n    INPUT\n  end\n\n  #We could do better, but this prevents some Sexp explosions and retains\n  #information about the values\n  def test_no_branch_for_plus_equals_with_interpolated_string\n    assert_alias '\"a\" + \"#{b}\" + \"c\"', <<-'INPUT'\n      x = \"a\"\n      x += \"#{b}\" if something\n      x += \"c\" if something_else\n      x\n    INPUT\n  end\n\n  #Unfortunate to go to this behavior which loses information\n  #but I can't think of a scenario in which the several integers\n  #in ORs would be handled right anyway\n  def test_no_branch_for_plus_equals_with_number\n    assert_alias '6', <<-INPUT\n      x = 1\n      x += 2 if something\n      x += 3 if something_else\n      x\n    INPUT\n  end\n\n  def test_keywords_in_blocks\n    assert_output <<-'INPUT', <<-'OUTPUT'\n    x do |y: 1, z: \"2\"|\n      puts y, z\n    end\n    INPUT\n    x do |y: 1, z: \"2\"|\n      puts 1, \"2\"\n    end\n    OUTPUT\n  end\n\n  def test_multiple_assignment\n    assert_output <<-INPUT, <<-OUTPUT\n    x, $y = 1, 2\n    x\n    $y\n    INPUT\n    x, $y = 1, 2\n    1\n    2\n    OUTPUT\n  end\n\n  def test_chained_assignment\n    assert_alias '1', <<-INPUT\n    x = y = 1\n    x\n    INPUT\n\n    assert_alias '1', <<-INPUT\n    @x = @y = 1\n    @x\n    INPUT\n\n    assert_alias '1', <<-INPUT\n    $x = $y = 1\n    $x\n    INPUT\n\n    assert_alias '1', <<-INPUT\n    X = Y = 1\n    X\n    INPUT\n\n    assert_alias '1', <<-INPUT\n    @@x = @@y = 1\n    @@x\n    INPUT\n\n    assert_alias '1', <<-INPUT\n    w.x = x.y = 1\n    w.x\n    INPUT\n\n    assert_alias '5', <<-INPUT\n    A = @b = @@c = $D = e.f = 1\n    z = A + @b + @@c + $D + e.f\n    z\n    INPUT\n  end\n\n  def test_branch_with_self_assign_target\n    assert_alias 'a.w.y', <<-INPUT\n    x = a\n    x = x.w if thing\n    x = x.y if other_thing\n    x\n    INPUT\n  end\n\n  def test_splat_assign\n    assert_alias '[[3, 4]]', <<-INPUT\n    x, y, *z = [1, 2, [3, 4]]\n    z\n    INPUT\n\n    assert_alias '[3, 4]', <<-INPUT\n    x, y, *z = 1, 2, 3, 4\n    z\n    INPUT\n\n    assert_alias '[2, 3]', <<-INPUT\n    a, *b, c = 1, 2, 3, 4\n    b\n    INPUT\n\n    assert_alias '[2]', <<-INPUT\n    a, *b, c, d = 1, 2, 3, 4\n    b\n    INPUT\n\n    assert_alias '[]', <<-INPUT\n    a, *b, c, d = 1, 2, 3\n    b\n    INPUT\n\n    assert_alias '[[]]', <<-INPUT\n    *a = [[]]\n    a\n    INPUT\n  end\n\n  def test_presence_in_all_literals\n    assert_alias \"'1'\", <<-INPUT\n    x = '1'.presence_in ['1', '2', '3']\n    x\n    INPUT\n  end\n\n  def test_presence_in_unknown\n    assert_alias ':BRAKEMAN_SAFE_LITERAL', <<-INPUT\n    x = y.presence_in ['1', '2', '3']\n    x\n    INPUT\n  end\n\n  def test_branch_in_array\n    assert_alias 'x', <<-INPUT\n    if x.in? [1,2,3]\n      stuff\n    end\n\n    x\n    INPUT\n\n    assert_output <<-INPUT, <<-OUTPUT\n    if x.in? [1,2,3]\n      y = x + 2\n      p y\n    end\n\n    x\n    INPUT\n    if x.in? [1,2,3]\n      y = :BRAKEMAN_SAFE_LITERAL\n      p :BRAKEMAN_SAFE_LITERAL\n    end\n\n    x\n    OUTPUT\n\n    assert_output <<-INPUT, <<-OUTPUT\n    x = params[:x]\n    if x.in? ['a','b']\n      User.send x\n    end\n\n    x\n    INPUT\n    x = params[:x]\n    if params[:x].in? ['a','b']\n      User.BRAKEMAN_SAFE_LITERAL\n    end\n\n    params[:x]\n    OUTPUT\n  end\n\n  def test_branch_array_include\n    assert_alias 'x', <<-INPUT\n    if [1,2,3].include? x\n      stuff\n    end\n\n    x\n    INPUT\n\n    assert_output <<-INPUT, <<-OUTPUT\n    if [1,2,3].include? x\n      y = x + 2\n      p y\n    end\n\n    x\n    INPUT\n    if [1,2,3].include? x\n      y = :BRAKEMAN_SAFE_LITERAL\n      p :BRAKEMAN_SAFE_LITERAL\n    end\n\n    x\n    OUTPUT\n\n    assert_output <<-INPUT, <<-OUTPUT\n    x = params[:x].presence\n    if ['a','b'].include? x\n      User.send x\n    end\n\n    x\n    INPUT\n    x = params[:x]\n    if ['a','b'].include? params[:x]\n      User.BRAKEMAN_SAFE_LITERAL\n    end\n\n    params[:x]\n    OUTPUT\n  end\n\n  def test_branch_array_include_return\n    assert_output <<-INPUT, <<-OUTPUT\n    return unless ['a', 'b'].include? x\n    x\n    INPUT\n    return unless ['a', 'b'].include? x\n    :BRAKEMAN_SAFE_LITERAL\n    OUTPUT\n  end\n\n  def test_branch_array_include_return_more\n    assert_output <<-INPUT, <<-OUTPUT\n    x = params[:thing].first\n    y = ['a', 'b']\n    unless y.include? x\n      do_stuff\n      return\n    end\n    x\n    INPUT\n    x = params[:thing].first\n    y = ['a', 'b']\n    unless ['a', 'b'].include? params[:thing].first\n      do_stuff\n      return\n    end\n    :BRAKEMAN_SAFE_LITERAL\n    OUTPUT\n  end\n\n  def test_branch_array_include_fail\n    assert_output <<-INPUT, <<-OUTPUT\n    fail unless ['a', 'b'].include? x\n    x\n    INPUT\n    fail unless ['a', 'b'].include? x\n    :BRAKEMAN_SAFE_LITERAL\n    OUTPUT\n  end\n\n  def test_branch_array_include_raise\n    assert_output <<-INPUT, <<-OUTPUT\n    raise Z unless ['a', 'b'].include? x\n    x\n    INPUT\n    raise Z unless ['a', 'b'].include? x\n    :BRAKEMAN_SAFE_LITERAL\n    OUTPUT\n  end\n\n  def test_equality_condition_in_branch\n    # Expect `x` inside true branch to be `1`\n    # but elsewhere unknown\n    assert_output <<-INPUT, <<-OUTPUT\n      if x == 1\n        a(x)\n      else\n        b(x)\n      end\n\n      c(x)\n    INPUT\n      if x == 1\n        a(1)\n      else\n        b(x)\n      end\n\n      c(x)\n    OUTPUT\n  end\n\n  def test_case_basic\n    assert_output <<-INPUT, <<-OUTPUT\n      z = 3\n\n      case x\n      when 1\n        y = 1\n      when 2\n        y = 2\n      else\n        z = z + 1\n        y = z\n      end\n\n      p y\n    INPUT\n      z = 3\n\n      case x\n      when 1\n        y = 1\n      when 2\n        y = 2\n      else\n        z = 4\n        y = 4\n      end\n\n      p(((1 or 2) or 4))\n    OUTPUT\n  end\n\n  def test_case_assignment\n    assert_output <<-INPUT, <<-OUTPUT\n      y = case x\n      when 1\n        1\n      when 2\n        2\n      else\n        3\n      end\n\n      p y\n    INPUT\n      y = case x\n      when 1\n        1\n      when 2\n        2\n      else\n        3\n      end\n\n      p(((1 or 2) or 3))\n    OUTPUT\n  end\n\n  def test_case_value\n    assert_output <<-INPUT, <<-OUTPUT\n      y = case x\n      when 1\n        x + 1\n      when 2\n        x + 2\n      else\n        x + 3\n      end\n\n      p y\n    INPUT\n      y = case x\n      when 1\n        2\n      when 2\n        4\n      else\n        x + 3\n      end\n\n      p(((2 or 4) or (x + 3)))\n    OUTPUT\n  end\n\n  def test_case_value_params\n    assert_output <<-INPUT, <<-OUTPUT\n      case params[:x]\n      when 1\n        y(params[:x])\n      when 2\n        z(params[:x])\n      end\n    INPUT\n      case params[:x]\n      when 1\n        y(1)\n      when 2\n        z(2)\n      end\n    OUTPUT\n  end\n\n  def test_case_list\n    assert_output <<-INPUT, <<-OUTPUT\n      case y\n      when :a, :b, :c\n        something(y)\n      end\n    INPUT\n      case y\n      when :a, :b, :c\n        something(:BRAKEMAN_SAFE_LITERAL)\n      end\n    OUTPUT\n  end\n\n  def test_case_splat\n    assert_output <<-INPUT, <<-OUTPUT\n      x = [1, 2, 3]\n\n      case y\n      when *x\n        something(y)\n      end\n    INPUT\n      x = [1, 2, 3]\n\n      case y\n      when *[1, 2, 3]\n        something(:BRAKEMAN_SAFE_LITERAL)\n      end\n    OUTPUT\n  end\n\n  def test_less_copying_of_arrays_and_hashes\n    assert_output <<-'INPUT', <<-'OUTPUT'\n      x = {}\n      x[y] = x[z]\n      x[z] = x[q] + x[y]\n      x[g] = x[z] + x[y]\n    INPUT\n      x = {}\n      x[y] = x[z]\n      x[z] = x[q] + x[z]\n      x[g] = x[q] + x[z] + x[z]\n    OUTPUT\n  end\n\n  def test_less_copying_of_arrays_and_hashes_and_params\n    assert_output <<-INPUT, <<-OUTPUT\n      x = params\n      x.symbolize_keys[:x]\n    INPUT\n      x = params\n      params.symbolize_keys[:x]\n    OUTPUT\n  end\n\n  def test_array_destructuring_asgn\n    assert_alias \"1\", <<-INPUT\n    x = [:a, [0, 1], :b, :c]\n    a, (x, y), b, c = x\n    y\n    INPUT\n  end\n\n  def test_array_join_to_interpolation\n    assert_alias '\"blah 1 #{thing} else\"', <<-'INPUT'\n      x = [\"blah\", 1, thing, \"else\"].join(' ')\n      x\n    INPUT\n  end\n\n  def test_array_join_no_separater\n    assert_alias '\"blah1#{thing}else\"', <<-'INPUT'\n      x = [\"blah\", 1, thing, \"else\"].join\n      x\n    INPUT\n\n    assert_alias '\"#{this}#{that}#{annd}#{uh}\"', <<-'INPUT'\n      x = [this, that, annd, uh].join\n      x\n    INPUT\n  end\n\n  def test_array_join_lots_of_interp\n    assert_alias '\"blah:#{this}:#{that}:end\"', <<-'INPUT'\n      x = [\"blah\", this, that, \"end\"].join(':')\n      x\n    INPUT\n\n    assert_alias '\"#{this}!#{that}!#{annd}!#{uh}\"', <<-'INPUT'\n      x = [this, that, annd, uh].join('!')\n      x\n    INPUT\n  end\n\n  def test_array_star_join\n    assert_alias '\"a b 1\"', <<-'INPUT'\n      a = :a\n      b = 'b'\n      c = 1\n      [a, b, c] * ' '\n    INPUT\n  end\n\n  def test_array_join_line_numbers\n    # Test that line numbers are set for all parts of a joined string\n    original_sexp = RubyParser.new.parse \"z = [x, 1].join(' ')\"\n    processed_sexp = Brakeman::AliasProcessor.new.process_safely(original_sexp)\n\n    assert_equal s(:lasgn, :z, s(:dstr, \"\", s(:evstr, s(:call, nil, :x)), s(:str, \" 1\"))), processed_sexp\n    assert_equal 1, processed_sexp[2].line\n    assert_equal 1, processed_sexp[2][2].line\n    assert_equal 1, processed_sexp[2][2][1].line\n    assert_equal 1, processed_sexp[2][3].line\n  end\n\n  def test_array_join_single_value\n    assert_alias \"'hello'\", <<-INPUT\n      x = [\"hello\"].join(' ')\n      x\n    INPUT\n  end\n\n  def test_array_join_empty_array\n    assert_alias \"''\", <<-INPUT\n      x = [].join(' ')\n      x\n    INPUT\n  end\n\n  def test_ignore_freeze\n    assert_alias \"blah\", <<-INPUT\n    x = blah.freeze\n    x\n    INPUT\n  end\n\n  def test_ignore_dup\n    assert_alias \"blah\", <<-INPUT\n    x = blah.dup\n    x\n    INPUT\n  end\n\n  def test_ignore_presence\n    assert_alias \"blah\", <<-INPUT\n    x = blah.presence\n    x\n    INPUT\n  end\n\n  def test_join_incompatible_strings\n    # Fails to completely join strings\n    # because of encoding issues, but that's better\n    # than raising an exception\n    assert_alias '\"P1\\xC0f\\xB88\\x01\" + \"\\xcd\\x80\"', <<-'INPUT'\n    # -*- coding: binary -*-\n    a = \"\\x50\" + \"\\x31\\xc0\" + \"\\x66\\xb8\\x38\\x01\" + \"\\xcd\\x80\"\n    a\n    INPUT\n  end\n\n  def test_join_very_long_string\n    long_string = \"*\" * 50\n\n    assert_alias \"#{long_string.inspect}\", <<-INPUT\n    a = #{long_string.inspect} + '1'\n    a\n    INPUT\n  end\n\n  def test_bang_bang\n    assert_alias \"true or false\", <<-INPUT\n    x = !!a\n    x\n    INPUT\n  end\n\n  def test_join_item_works_with_nil_item\n    assert_nothing_raised do\n      Brakeman::AliasProcessor.new.join_item(nil, \"something\")\n    end\n  end\n\n  def test_alias_processor_failure\n    assert_raises do\n      Brakeman::AliasProcessor.new.process s(:block, s(:attrasgn, s(:call, nil, :x), :\"[]!\", s(:lit, 1), s(:lit, 2)))\n    end\n\n    # Does not raise because the error is caught and handled by the Tracker\n    assert Brakeman::AliasProcessor.new(BrakemanTester.new_tracker).process s(:block, s(:attrasgn, s(:call, nil, :x), :\"[]!\", s(:lit, 1), s(:lit, 2)))\n  end\nend\n"
  },
  {
    "path": "test/tests/app_tree.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/app_tree'\nrequire 'fileutils'\n\nclass AppTreeTests < Minitest::Test\n  def temp_dir_and_file_from_path(relative_path)\n    Dir.mktmpdir do |dir|\n      file = File.join(dir, relative_path)\n      FileUtils.mkdir_p(File.dirname(file))\n      FileUtils.touch(file)\n      yield dir, file\n    end\n  end\n\n  def temp_dir_absolute_symlink_and_file_from_path(relative_path)\n    Dir.mktmpdir do |dir|\n      sibling_dir = File.join(dir, \"sibling\")\n      FileUtils.mkdir_p(sibling_dir)\n\n      target_dir = File.join(dir, \"target\")\n      FileUtils.mkdir_p(target_dir)\n\n      file = File.join(sibling_dir, relative_path)\n      FileUtils.mkdir_p(File.dirname(file))\n      FileUtils.touch(file)\n\n      symlink = File.join(target_dir, \"symlink\")\n      FileUtils.ln_s(sibling_dir, symlink, force: true)\n      yield target_dir, file\n    end\n  end\n\n  def temp_dir_relative_symlink_and_file_from_path(relative_path)\n    Dir.mktmpdir do |dir|\n      sibling_dir = File.join(dir, \"sibling\")\n      FileUtils.mkdir_p(sibling_dir)\n\n      target_dir = File.join(dir, \"target\")\n      FileUtils.mkdir_p(target_dir)\n\n      file = File.join(sibling_dir, relative_path)\n      FileUtils.mkdir_p(File.dirname(file))\n      FileUtils.touch(file)\n\n      symlink = File.join(target_dir, \"symlink\")\n      FileUtils.ln_s(\"../sibling\", symlink, force: true)\n      yield target_dir, file\n    end\n  end\n\n  def test_directory_absolute_symlink_support\n    temp_dir_absolute_symlink_and_file_from_path(\"test.rb\") do |dir, file|\n      at = Brakeman::AppTree.new(dir, follow_symlinks: true)\n      assert_equal [file], at.ruby_file_paths.collect(&:absolute).to_a\n    end\n  end\n\n  def test_directory_relative_symlink_support\n    temp_dir_relative_symlink_and_file_from_path(\"test.rb\") do |dir, file|\n      at = Brakeman::AppTree.new(dir, follow_symlinks: true)\n      assert_equal [file], at.ruby_file_paths.collect(&:absolute).to_a\n    end\n  end\n\n  def test_directory_relative_disabled_symlink_support\n    temp_dir_relative_symlink_and_file_from_path(\"test.rb\") do |dir, file|\n      at = Brakeman::AppTree.new(dir, follow_symlinks: false)\n      assert_empty at.ruby_file_paths.collect(&:absolute).to_a\n    end\n  end\n\n  def test_ruby_file_paths\n    temp_dir_and_file_from_path(\"test.rb\") do |dir, file|\n      at = Brakeman::AppTree.new(dir)\n      assert_equal [file], at.ruby_file_paths.collect(&:absolute).to_a\n    end\n  end\n\n  def test_ruby_file_paths_skip_vendor_false\n    temp_dir_and_file_from_path(\"vendor/test.rb\") do |dir, file|\n      at = Brakeman::AppTree.new(dir, :skip_vendor => false)\n      assert_equal [file], at.ruby_file_paths.collect(&:absolute).to_a\n    end\n  end\n\n  def test_ruby_file_paths_skip_vendor_true\n    temp_dir_and_file_from_path(\"vendor/test.rb\") do |dir, file|\n      at = Brakeman::AppTree.new(dir, :skip_vendor => true)\n      assert_equal [], at.ruby_file_paths.collect(&:absolute).to_a\n    end\n  end\n\n  def test_ruby_file_paths_skip_vendor_true_add_libs_path\n    temp_dir_and_file_from_path(\"vendor/bundle/gems/gem123/lib/test.rb\") do |dir, file|\n      at = Brakeman::AppTree.new(dir, :skip_vendor => true, :additional_libs_path => [\"vendor/bundle/gems/gem123/lib\"])\n      assert_equal [file], at.ruby_file_paths.collect(&:absolute).to_a\n    end\n  end\n\n  def test_ruby_file_paths_skip_vendor_true_add_engine_path\n    temp_dir_and_file_from_path(\"vendor/bundle/gems/gem123/test.rb\") do |dir, file|\n      at = Brakeman::AppTree.new(dir, :skip_vendor => true, :engine_paths => [\"vendor/bundle/gems\"])\n      assert_equal [file], at.ruby_file_paths.collect(&:absolute).to_a\n    end\n  end\n\n  def test_ruby_file_paths_skip_vendor_true_tests_in_engine_path_still_excluded\n    temp_dir_and_file_from_path(\"vendor/bundle/gems/gem123/test/test.rb\") do |dir, file|\n      at = Brakeman::AppTree.new(dir, :skip_vendor => true, :engine_paths => [\"vendor/bundler/gems\"])\n      assert_equal [], at.ruby_file_paths.collect(&:absolute).to_a\n    end\n  end\n\n  def test_ruby_file_paths_add_engine_path\n    temp_dir_and_file_from_path(\"lib/gems/gem123/test.rb\") do |dir, file|\n      at = Brakeman::AppTree.new(dir, :engine_paths => [\"lib/gems\"])\n      assert_equal [file], at.ruby_file_paths.collect(&:absolute).to_a\n    end\n  end\n\n  def test_ruby_file_paths_add_libs_path\n    temp_dir_and_file_from_path(\"gem123/lib/test.rb\") do |dir, file|\n      at = Brakeman::AppTree.new(dir, :additional_libs_path => [\"gems/gem123/lib\"])\n      assert_equal [file], at.ruby_file_paths.collect(&:absolute).to_a\n    end\n  end\n\n  def test_ruby_file_paths_directory_with_rb_extension\n    Dir.mktmpdir do |dir|\n      FileUtils.mkdir_p(File.join(dir, \"test.rb\"))\n\n      at = Brakeman::AppTree.new(dir)\n      assert_equal [], at.ruby_file_paths.collect(&:absolute).to_a\n    end\n  end\n\n  def test_match_path\n    temp_dir_and_file_from_path('match/this/file.rb') do |dir, file|\n      at = Brakeman::AppTree.new(dir)\n\n      # '/path1/ - Matches any path that is rooted at 'path1' in the project directory.\n      paths = Brakeman::AppTree.__send__(:regex_for_paths, ['/match/'])\n\n      assert app_tree_match?(at, paths, File.join(dir, 'match'))\n      assert app_tree_match?(at, paths, File.join(dir, 'match', 'this'))\n      assert app_tree_match?(at, paths, File.join(dir, 'match', 'this', 'file.rb'))\n      assert app_tree_match?(at, paths, File.join(dir, 'match', '/'))\n      refute app_tree_match?(at, paths, File.join(dir, 'nested', 'match'))\n      refute app_tree_match?(at, paths, File.join(dir, 'not_exact_match'))\n\n      # 'path1/' - Matches any path that contains 'path1' in the project directory.\n      FileUtils.mkdir_p(File.join(dir, 'this'))\n      paths = Brakeman::AppTree.__send__(:regex_for_paths, ['this/'])\n\n      assert app_tree_match?(at, paths, File.join(dir, 'this', 'one.rb'))\n      assert app_tree_match?(at, paths, File.join(dir, 'this', File::SEPARATOR))\n      assert app_tree_match?(at, paths, File.join(dir, 'match', 'this', File::SEPARATOR))\n      assert app_tree_match?(at, paths, File.join(dir, 'match', 'this', 'file.rb'))\n      assert app_tree_match?(at, paths, File.join(dir, 'also', 'this', 'other.rb'))\n      refute app_tree_match?(at, paths, File.join(dir, 'not_this_match'))\n\n      # 'path1/file1.rb' - Matches a specific filename in the project directory.\n      paths = Brakeman::AppTree.__send__(:regex_for_paths, ['file.rb', 'this'])\n\n      assert app_tree_match?(at, paths, File.join(dir, 'file.rb'))\n      assert app_tree_match?(at, paths, File.join(dir, 'this', 'file.rb'))\n      assert app_tree_match?(at, paths, File.join(dir, 'match', 'this', 'file.rb'))\n      refute app_tree_match?(at, paths, File.join(dir, 'no', 'file.rb', 'match'))\n      refute app_tree_match?(at, paths, File.join(dir, 'match', 'this', File::SEPARATOR))\n    end\n  end\n\n  private\n\n  def app_tree_match?(app_tree, paths, path)\n    app_tree.__send__(:match_path, paths, path)\n  end\nend\n"
  },
  {
    "path": "test/tests/brakeman.rb",
    "content": "require 'tempfile'\nrequire_relative '../test'\n\nclass BrakemanTests < Minitest::Test\n  def test_exception_on_no_application\n    assert_raises Brakeman::NoApplication do\n      Brakeman.run \"/tmp#{rand}\" #better not exist\n    end\n  end\n\n  def test_exception_no_on_no_application_if_forced\n    assert_nothing_raised Brakeman::NoApplication do\n      Brakeman.run :app_path => \"/tmp#{rand}\", :force_scan => true #better not exist\n    end\n  end\n\n  def test_app_tree_root_is_absolute\n    require 'brakeman/options'\n    relative_path = Pathname.new(File.dirname(__FILE__)).relative_path_from(Pathname.getwd)\n    absolute_path = relative_path.realpath.to_s\n    input = [\"-p\", relative_path.to_s]\n    options, _ = Brakeman::Options.parse input\n    at = Brakeman::AppTree.from_options options\n\n    assert !options[:app_path].start_with?(\"/\")\n    assert_equal absolute_path, at.root\n    assert_equal File.join(absolute_path, \"Gemfile\"), at.expand_path(\"Gemfile\")\n  end\n\n  def test_app_tree_flexible_file_paths\n    require 'brakeman/options'\n    relative_path = File.expand_path(File.join(TEST_PATH, \"/apps/rails4_non_standard_structure/\"))\n    input = [\"-p\", relative_path.to_s]\n    options, _ = Brakeman::Options.parse input\n    at = Brakeman::AppTree.from_options options\n\n    contains_foo_controller = false\n    at.controller_paths.each do |path|\n      if File.basename(path) == 'foo_controller.rb'\n        contains_foo_controller = true\n        break\n      end\n    end\n\n    contains_foo_model = false\n    at.model_paths.each do |path|\n      if File.basename(path) == 'foo.rb'\n        contains_foo_model = true\n        break\n      end\n    end\n\n    contains_foo_view = false\n    at.template_paths.each do |path|\n      if File.basename(path) == 'foo.html.erb'\n        contains_foo_view = true\n        break\n      end\n    end\n\n    assert contains_foo_controller\n    assert contains_foo_model\n    assert contains_foo_view\n  end\n\n  def test_engines_path\n    require 'brakeman/options'\n    relative_path = File.expand_path(File.join(TEST_PATH, \"/apps/rails4_with_engines\"))\n    input = [\"-p\", relative_path.to_s,\n             \"--add-engines-path\", \"engine/user_removal\"]\n    options, _ = Brakeman::Options.parse input\n    at = Brakeman::AppTree.from_options options\n\n    expected_controllers = %w{application_controller.rb removal_controller.rb users_controller.rb}\n    basename = Proc.new { |path| File.basename path }\n    assert (at.controller_paths.map(&basename) - expected_controllers).empty?\n  end\nend\n\nclass UtilTests < Minitest::Test\n  def setup\n    @ruby_parser = RubyParser\n  end\n\n  def util\n    @@util ||= Class.new.extend Brakeman::Util\n  end\n\n  def test_cookies?\n    assert util.cookies?(@ruby_parser.new.parse 'cookies[:x][:y][:z]')\n  end\n\n  def test_params?\n    assert util.params?(@ruby_parser.new.parse 'params[:x][:y][:z]')\n  end\n\n  def test_request_headers?\n    assert util.request_headers?(@ruby_parser.new.parse 'request.env[\"HTTP_SOMETHING\"]')\n    assert util.request_headers?(@ruby_parser.new.parse 'request.env[x]')\n    refute util.request_headers?(@ruby_parser.new.parse 'request.env[\"omniauth.something\"]')\n    refute util.request_headers?(@ruby_parser.new.parse 'request.env')\n    refute util.request_headers?(@ruby_parser.new.parse 'request.env.x')\n  end\n\n  def test_template_path_to_name_with_views\n    path = Brakeman::FilePath.new('app/views/a/b/c/d.html.haml', 'app/views/a/b/c/d.html.haml')\n    name = util.template_path_to_name(path)\n    assert_equal :'a/b/c/d', name\n  end\n\n  def test_template_path_to_name_without_views\n    path = Brakeman::FilePath.new('lib/templates/a/b/c/d.js.erb', 'lib/templates/a/b/c/d.js.erb')\n    name = util.template_path_to_name(path)\n    assert_equal :'lib/templates/a/b/c/d', name\n  end\nend\n\nclass BaseCheckTests < Minitest::Test\n  def setup\n    @tracker = BrakemanTester.new_tracker\n    @check = Brakeman::BaseCheck.new(@tracker)\n  end\n\n  def version_between? version, low, high\n    @tracker.config.set_rails_version version\n    @check.send(:version_between?, low, high)\n  end\n\n  def lts_version? version, low\n    if version\n      @tracker.config.add_gem :\"railslts-version\", version, nil, nil\n    end\n    @check.send(:lts_version?, low)\n  end\n\n  def test_version_between\n    assert version_between?(\"2.3.8\", \"2.3.0\", \"2.3.8\")\n    assert version_between?(\"2.3.8\", \"2.3.0\", \"2.3.14\")\n    assert version_between?(\"2.3.8\", \"1.0.0\", \"5.0.0\")\n  end\n\n  def test_version_not_between\n    assert_equal false, version_between?(\"3.2.1\", \"2.0.0\", \"3.0.0\")\n    assert_equal false, version_between?(\"3.2.1\", \"3.0.0\", \"3.2.0\")\n    assert_equal false, version_between?(\"0.0.0\", \"3.0.0\", \"3.2.0\")\n    assert_equal false, version_between?(\"3.2.1\", \"3.2.1.beta1\", \"3.2.1.beta7\")\n  end\n\n  def test_version_between_longer\n    assert_equal false, version_between?(\"1.0.1.2\", \"1.0.0\", \"1.0.1\")\n  end\n\n  def test_version_between_pre_release\n    assert version_between?(\"3.2.9.rc2\", \"3.2.5\", \"4.0.0\")\n  end\n\n  def test_lts_version\n    @tracker.config.set_rails_version \"2.3.18\"\n    assert lts_version? '2.3.18.6', '2.3.18.6'\n    assert !lts_version?('2.3.18.1', '2.3.18.6')\n    assert !lts_version?(nil, '2.3.18.6')\n  end\n\n  def test_major_minor_version\n    assert version_between?(\"4.0\", \"4.0.0\", \"4.0.1\")\n  end\n\n  def test_beta_versions\n    assert version_between?(\"4.0.0.beta2\", \"4.0.0.beta1\", \"4.0.0.beta5\")\n    refute version_between?(\"4.0.0.beta8\", \"4.0.0.beta1\", \"4.0.0.beta5\")\n    refute version_between?(\"4.0.1\", \"4.0.0.beta1\", \"4.0.0.beta5\")\n  end\nend\n\nclass ConfigTests < Minitest::Test\n\n  def setup\n    Brakeman.instance_variable_set(:@quiet, false)\n  end\n\n  def test_quiet_option_from_file\n    config = Tempfile.new(\"config\")\n\n    config.write <<-YAML.strip\n    ---\n    :quiet: true\n    YAML\n\n    config.close\n\n    options = {\n      :config_file => config.path,\n      :app_path => \"/tmp\" #doesn't need to be real\n    }\n\n    assert_output \"\" do\n      final_options = Brakeman.set_options(options)\n\n      config.unlink\n\n      assert final_options[:quiet], \"Expected quiet option to be true, but was #{final_options[:quiet]}\"\n    end\n  end\n\n  def test_quiet_option_from_commandline\n    config = Tempfile.new(\"config\")\n\n    config.write <<-YAML.strip\n    ---\n    app_path: \"/tmp\"\n    YAML\n\n    config.close\n\n    options = {\n      :config_file => config.path,\n      :quiet => true,\n      :app_path => \"/tmp\" #doesn't need to be real\n    }\n\n    assert_output \"\" do\n      Brakeman.set_options(options)\n    end\n  end\n\n  def test_quiet_option_default\n    options = {\n      :app_path => \"/tmp\" #doesn't need to be real\n    }\n\n    final_options = Brakeman.set_options(options)\n\n    assert final_options[:quiet], \"Expected quiet option to be true, but was #{final_options[:quiet]}\"\n  end\n\n  def test_quiet_command_line_default\n    options = {\n      :quiet => :command_line,\n      :app_path => \"/tmp\" #doesn't need to be real\n    }\n\n    final_options = Brakeman.set_options(options)\n\n    assert_nil final_options[:quiet]\n  end\n\n  def test_quiet_inconfig_with_command_line\n    config = Tempfile.new(\"config\")\n\n    config.write <<-YAML.strip\n    ---\n    :quiet: true\n    YAML\n\n    config.close\n\n    options = {\n      :quiet => :command_line,\n      :config_file => config.path,\n      :app_path => \"#{TEST_PATH}/apps/rails4\",\n      :run_checks => []\n    }\n\n    assert_output \"\" do\n      Brakeman.run options\n      config.unlink\n    end\n  end\n\n  def test_timing_output\n    options = {\n      :app_path => \"#{TEST_PATH}/apps/rails4\",\n      :show_timing => true,\n      :quiet => false,\n      :run_checks => []\n    }\n\n    assert_output nil, / in \\d+\\.\\d+s/ do\n      Brakeman.run options\n    end\n\n  end\n\n  def output_format_tester options, expected_options\n    output_formats = Brakeman.get_output_formats(options)\n\n    assert_equal expected_options, output_formats\n  end\n\n  def test_output_format\n    output_format_tester({}, [:to_s])\n    output_format_tester({:output_format => :html}, [:to_html])\n    output_format_tester({:output_format => :to_html}, [:to_html])\n    output_format_tester({:output_format => :cc}, [:to_codeclimate])\n    output_format_tester({:output_format => :to_codeclimate}, [:to_codeclimate])\n    output_format_tester({:output_format => :csv}, [:to_csv])\n    output_format_tester({:output_format => :to_csv}, [:to_csv])\n    output_format_tester({:output_format => :pdf}, [:to_pdf])\n    output_format_tester({:output_format => :to_pdf}, [:to_pdf])\n    output_format_tester({:output_format => :plain}, [:to_text])\n    output_format_tester({:output_format => :to_plain}, [:to_text])\n    output_format_tester({:output_format => :json}, [:to_json])\n    output_format_tester({:output_format => :to_json}, [:to_json])\n    output_format_tester({:output_format => :table}, [:to_table])\n    output_format_tester({:output_format => :to_table}, [:to_table])\n    output_format_tester({:output_format => :tabs}, [:to_tabs])\n    output_format_tester({:output_format => :to_tabs}, [:to_tabs])\n    output_format_tester({:output_format => :markdown}, [:to_markdown])\n    output_format_tester({:output_format => :to_markdown}, [:to_markdown])\n    output_format_tester({:output_format => :text}, [:to_text])\n    output_format_tester({:output_format => :to_text}, [:to_text])\n    output_format_tester({:output_format => :github}, [:to_github])\n    output_format_tester({:output_format => :to_github}, [:to_github])\n    output_format_tester({:output_format => :others}, [:to_text])\n\n    output_format_tester({:output_files => ['xx.html', 'xx.pdf']}, [:to_html, :to_pdf])\n    output_format_tester({:output_files => ['xx.pdf', 'xx.json']}, [:to_pdf, :to_json])\n    output_format_tester({:output_files => ['xx.json', 'xx.tabs']}, [:to_json, :to_tabs])\n    output_format_tester({:output_files => ['xx.tabs', 'xx.csv']}, [:to_tabs, :to_csv])\n    output_format_tester({:output_files => ['xx.csv', 'xx.xxx']}, [:to_csv, :to_text])\n    output_format_tester({:output_files => ['xx.md', 'xx.xxx']}, [:to_markdown, :to_text])\n    output_format_tester({:output_files => ['xx.xx', 'xx.xx']}, [:to_text, :to_text])\n    output_format_tester({:output_files => ['xx.cc', 'xx.table']}, [:to_codeclimate, :to_table])\n    output_format_tester({:output_files => ['xx.plain', 'xx.text']}, [:to_text, :to_text])\n    output_format_tester({:output_files => ['xx.html', 'xx.pdf', 'xx.csv', 'xx.tabs', 'xx.json', 'xx.md', 'xx.github']}, [:to_html, :to_pdf, :to_csv, :to_tabs, :to_json, :to_markdown, :to_github])\n  end\n\n  def test_output_format_errors_raised\n    options = {:output_format => :to_json, :output_files => ['xx.csv', 'xx.xxx']}\n    assert_raises(ArgumentError) { Brakeman.get_output_formats(options) }\n  end\n\n  def test_github_options_raises_error\n    options = {:github_repo => 'www.test.com', :app_path => \"/tmp\"}\n    assert_raises ArgumentError do\n      Brakeman.set_options(options)\n    end\n  end\n\n  def test_github_options_returns_url\n    options = {:github_repo => 'presidentbeef/brakeman', :app_path => \"/tmp\"}\n\n    final_options = Brakeman.set_options(options)\n    assert final_options[:github_url], \"https://www.github.com/presidentbeef/brakeman\"\n  end\n\n  def test_rails_version_options\n    versions = [:rails3, :rails4, :rails5, :rails6]\n\n    versions.each_with_index do |v, i|\n      options = { v => true, :app_path => \"/tmp\" }\n\n      final_options = Brakeman.set_options(options)\n\n      versions[0..i].each do |opt|\n        assert final_options[opt]\n      end\n    end\n  end\n\n  def test_optional_check_options\n    options = {:list_optional_checks => true}\n    check_list = capture_io {\n      Brakeman.list_checks(options)\n    }[1]\n    Brakeman::Checks.optional_checks.each do |check|\n      assert check_list.include? check.to_s.split(\"::\").last\n      assert check_list.include? check.description\n    end\n  end\n\n  def test_default_check_options\n    options = {}\n    check_list = capture_io {\n      Brakeman.list_checks(options)\n    }[1]\n    Brakeman::Checks.checks.each do |check|\n      assert check_list.include? check.to_s.split(\"::\").last\n      assert check_list.include? check.description\n    end\n  end\n\n  def test_dump_config_no_file\n    options = {:create_config => true, :test_option => \"test\"}\n\n    assert_output \"---\\n:test_option: test\\n\" do\n      Brakeman.dump_config(options)\n    end\n  end\n\n  def test_dump_config_with_set\n    require 'set'\n    test_set = Set.new [\"test\", \"test2\"]\n    options = {:create_config => true, :test_option => test_set}\n\n    assert_output \"---\\n:test_option:\\n- test\\n- test2\\n\" do\n      Brakeman.dump_config(options)\n    end\n  end\n\n  def test_ensure_latest_is_current\n    test_spec = Gem::Specification.new do |s|\n      s.version = Brakeman::Version\n    end\n\n    Gem.stub :latest_spec_for, test_spec do\n      refute Brakeman.ensure_latest\n    end\n  end\n\n  def test_ensure_latest_is_newer\n    test_spec = Gem::Specification.new do |s|\n      s.version = '99.99.99'\n    end\n\n    Gem.stub :latest_spec_for, test_spec do\n      assert_equal \"Brakeman #{Brakeman::Version} is not the latest version #{test_spec.version.to_s}\", Brakeman.ensure_latest\n    end\n  end\n\n  def test_ensure_latest_too_new\n    test_spec = Gem::Specification.new do |s|\n      s.version = Brakeman::Version\n      s.date = Date.today\n    end\n\n    Gem.stub :latest_spec_for, test_spec do\n      # Latest release must be 10 days old\n      refute Brakeman.ensure_latest(days_old: 10)\n    end\n  end\n\n  def test_ensure_latest_old_enough\n    test_spec = Gem::Specification.new do |s|\n      s.version = Brakeman::Version\n      s.date = Time.parse((Date.today - 3).to_s)\n    end\n\n    Gem.stub :latest_spec_for, test_spec do\n      # Latest release must be 2 days old\n      refute Brakeman.ensure_latest(days_old: 2)\n    end\n  end\n\n  def test_ignore_file_entries_with_empty_notes\n    assert Brakeman.ignore_file_entries_with_empty_notes(nil).empty?\n\n    ignore_file_missing_notes = Tempfile.new('brakeman.ignore')\n    ignore_file_missing_notes.write IGNORE_WITH_MISSING_NOTES_JSON\n    ignore_file_missing_notes.close\n    assert_equal(\n      Brakeman.ignore_file_entries_with_empty_notes(ignore_file_missing_notes.path).to_set,\n      [\n        '006ac5fe3834bf2e73ee51b67eb111066f618be46e391d493c541ea2a906a82f',\n      ].to_set\n    )\n    ignore_file_missing_notes.unlink\n\n    ignore_file_with_notes = Tempfile.new('brakeman.ignore')\n    ignore_file_with_notes.write IGNORE_WITH_NOTES_JSON\n    ignore_file_with_notes.close\n    assert Brakeman.ignore_file_entries_with_empty_notes(ignore_file_with_notes.path).empty?\n    ignore_file_with_notes.unlink\n  end\n\n  def test_dump_config_with_file\n    test_file = \"test.cfg\"\n    options = {:create_config => test_file, :test_option => \"test\"}\n\n    assert_output nil, /Output configuration to test.cfg/ do\n      Brakeman.dump_config(options)\n    end\n\n    file_text = File.read(test_file)\n    assert_equal file_text, \"---\\n:test_option: test\\n\"\n  ensure\n    assert File.delete test_file\n  end\n\n  IGNORE_WITH_MISSING_NOTES_JSON = <<~JSON.freeze\n    {\n      \"ignored_warnings\": [\n        {\n          \"warning_type\": \"Remote Code Execution\",\n          \"warning_code\": 25,\n          \"fingerprint\": \"006ac5fe3834bf2e73ee51b67eb111066f618be46e391d493c541ea2a906a82f\",\n          \"check_name\": \"Deserialize\",\n          \"message\": \"`Oj.load` called with parameter value\",\n          \"file\": \"app/controllers/users_controller.rb\",\n          \"line\": 52,\n          \"link\": \"https://brakemanscanner.org/docs/warning_types/unsafe_deserialization\",\n          \"code\": \"Oj.load(params[:json], :mode => :object)\",\n          \"render_path\": null,\n          \"location\": {\n            \"type\": \"method\",\n            \"class\": \"UsersController\",\n            \"method\": \"some_api\"\n          },\n          \"user_input\": \"params[:json]\",\n          \"confidence\": \"High\",\n          \"note\": \"\"\n        },\n        {\n          \"warning_type\": \"Remote Code Execution\",\n          \"warning_code\": 25,\n          \"fingerprint\": \"97ecaa5677c8eadaed09217a704e59092921fab24cc751e05dfb7b167beda2cf\",\n          \"check_name\": \"Deserialize\",\n          \"message\": \"`Oj.load` called with parameter value\",\n          \"file\": \"app/controllers/users_controller.rb\",\n          \"line\": 51,\n          \"link\": \"https://brakemanscanner.org/docs/warning_types/unsafe_deserialization\",\n          \"code\": \"Oj.load(params[:json])\",\n          \"render_path\": null,\n          \"location\": {\n            \"type\": \"method\",\n            \"class\": \"UsersController\",\n            \"method\": \"some_api\"\n          },\n          \"user_input\": \"params[:json]\",\n          \"confidence\": \"High\",\n          \"note\": \"Here's a note!\"\n        }\n      ],\n      \"brakeman_version\": \"4.5.0\"\n    }\n  JSON\n\n  IGNORE_WITH_NOTES_JSON = <<~JSON.freeze\n    {\n      \"ignored_warnings\": [\n        {\n          \"warning_type\": \"Remote Code Execution\",\n          \"warning_code\": 25,\n          \"fingerprint\": \"006ac5fe3834bf2e73ee51b67eb111066f618be46e391d493c541ea2a906a82f\",\n          \"check_name\": \"Deserialize\",\n          \"message\": \"`Oj.load` called with parameter value\",\n          \"file\": \"app/controllers/users_controller.rb\",\n          \"line\": 52,\n          \"link\": \"https://brakemanscanner.org/docs/warning_types/unsafe_deserialization\",\n          \"code\": \"Oj.load(params[:json], :mode => :object)\",\n          \"render_path\": null,\n          \"location\": {\n            \"type\": \"method\",\n            \"class\": \"UsersController\",\n            \"method\": \"some_api\"\n          },\n          \"user_input\": \"params[:json]\",\n          \"confidence\": \"High\",\n          \"note\": \"Note here.\"\n        }\n      ],\n      \"brakeman_version\": \"4.5.0\"\n    }\n  JSON\nend\n\nclass GemProcessorTests < Minitest::Test\n  def assert_version version, name, msg = nil\n    assert_equal version, @tracker.config.gem_version(name), msg\n  end\n\n  def setup\n    @tracker = BrakemanTester.new_tracker\n    @gem_processor = Brakeman::GemProcessor.new @tracker\n    @eol_representations = [\"\\r\\n\", \"\\n\"]\n    @gem_locks = @eol_representations.inject({}) {|h, eol|\n      h[eol] = \"    paperclip (3.2.1)#    erubis (4.3.1)#     rails (3.2.1.rc2)#    simplecov (1.1)#\".gsub('#', eol); h\n    }\n  end\n\n  def test_gem_lock_parsing\n    empty_block = Sexp.new(:block)\n    @gem_locks.each do |eol, gem_lock|\n      @gem_processor.process_gems :gemfile => { :file => \"Gemfile\", :src => empty_block}, :gemlock => { :file => \"gems.locked\", :src => gem_lock }\n      assert_version \"4.3.1\", :erubis, \"Couldn't match gemlock with eol: #{eol}\"\n      assert_version \"3.2.1\", :paperclip, \"Couldn't match gemlock with eol: #{eol}\"\n      assert_version \"3.2.1.rc2\", :rails, \"Couldn't match gemlock with eol: #{eol}\"\n      assert_version \"1.1\", :simplecov, \"Couldn't match gemlock with eol: #{eol}\"\n    end\n  end\nend\n"
  },
  {
    "path": "test/tests/call_index.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/processors/lib/find_all_calls'\n\nclass CallIndexTests < Minitest::Test\n  def setup\n    @calls = [\n      {:method => :hello, :target => :world, :call => {}, :nested => false },\n      {:method => :goodbye, :target => :world, :call => {}, :nested => false  },\n      {:method => :foo, :target => :world, :call => {}, :nested => false  },\n      {:method => :foo, :target => :the_bar, :call => {}, :nested => false  },\n      {:method => :foo, :target => :the_baz, :call => {}, :nested => false  },\n      {:method => :do_it, :target => nil, :call => {}, :nested => false  },\n      {:method => :do_it_now, :target => nil, :call => {}, :nested => false  },\n      {:method => :with_target, :target => :blah, :call => {}, :nested => false  },\n    ]\n\n    meth_src = Brakeman::AliasProcessor.new.process RubyParser.new.parse <<-RUBY\n      def x\n        x.y.z(1)\n        params[:x].y.z(2)\n        third(second.thing(first.thing))\n      end\n    RUBY\n\n    class_src = Brakeman::AliasProcessor.new.process RubyParser.new.parse <<-RUBY\n    class A\n      do_a_thing\n\n      # Not indexed\n      def x\n        x.y.z(1)\n        params[:x].y.z(2)\n      end\n    end\n    RUBY\n    all_calls = Brakeman::FindAllCalls.new(Object.new)\n    all_calls.process_source(meth_src, method: :x)\n    all_calls.process_source(class_src, class: :A)\n    @calls += all_calls.calls\n\n    @call_index = Brakeman::CallIndex.new(@calls)\n  end\n\n  def assert_found num, opts\n    assert_equal num, @call_index.find_calls(opts).length\n  end\n\n  def test_find_by_method_regex\n    assert_found 2, :method => %r{do_it(?:_now)?}\n    assert_found 2, :target => :world, :method => %r{oo}\n  end\n\n  def test_find_by_method\n    assert_found 1, :method => :hello\n  end\n\n  def test_find_by_target\n    assert_found 3, :target => :world\n  end\n\n  def test_find_by_target_regex\n    # x.y.z\n    assert_found 1, :targets => %r{^x\\.y$}\n\n    # x.y.z\n    # params[].y.z\n    assert_found 2, :targets => %r{\\.y$}\n\n\n    # the_bar.foo\n    # the_baz.foo\n    assert_found 2, :targets => %r{^the}, methods: [:foo, :goodbye]\n  end\n\n  def test_find_by_methods\n    assert_found 5, :methods => [:foo, :hello, :goodbye]\n  end\n\n  def test_find_by_targets\n    assert_found 4, :targets => [:world, :the_bar]\n  end\n\n  def test_find_by_target_and_method\n    assert_found 1, :target => :the_bar, :method => :foo\n  end\n\n  def test_find_by_target_and_methods\n    assert_found 2, :target => :world, :methods => [:foo, :hello]\n  end\n\n  def test_find_by_targets_and_method\n    assert_found 2, :target => [:world, :the_bar], :methods => :foo\n  end\n\n  def test_find_by_more_targets\n    assert_found 2, :target => [:world, :the_bar], :methods => [:foo]\n  end\n\n  def test_find_by_more_methods\n    assert_found 2, :target => [:world], :methods => [:foo, :hello]\n  end\n\n  def test_find_by_no_target_and_method\n    assert_found 1, :target => nil, :method => :do_it\n    assert_found 0, :targets => nil, :method => :with_target\n  end\n\n  def test_find_by_no_target_and_methods\n    assert_found 2, :target => nil, :method => [:do_it, :do_it_now]\n  end\n\n  def test_find_by_target_and_method_in_chain\n    assert_found 0, :target => :x, :method => :z\n    assert_found 1, :target => :x, :method => :z, :chained => true\n  end\n\n  def test_find_params_and_method_in_chain\n    assert_found 0, :target => :params, :method => :z\n    assert_found 1, :target => :params, :method => :z, :chained => true\n  end\n\n  def test_filter_by_chain\n    assert_found 1, :target => [:params], :method => :z, :chained => true\n    assert_found 1, :target => /^x$/, :method => :z, :chained => true\n  end\n\n  def test_find_class_scope_call_by_method\n    assert_found 1, :method => :do_a_thing\n  end\n\n  def test_parent_call\n    first = @call_index.find_calls(method: :first, nested: true).first\n    first_thing = @call_index.find_calls(target: :first, method: :thing).first\n    second = @call_index.find_calls(method: :second, nested: true).first\n    second_thing = @call_index.find_calls(target: :second, method: :thing).first\n    third = @call_index.find_calls(target: nil, method: :third).first\n\n    assert_equal second_thing, first_thing[:parent]\n    assert_equal third, second_thing[:parent]\n\n    assert_equal second_thing, first[:parent]\n    assert_equal third, second[:parent]\n    assert_nil third[:parent]\n  end\n\n  def test_full_call\n    xyz = @call_index.find_calls(target: :x, method: :z, chained: true).first\n    xy = @call_index.find_calls(target: :x, method: :y, nested: true).first\n    x = @call_index.find_calls(method: :x, nested: true).first\n    thing = @call_index.find_calls(method: :do_a_thing).first\n\n    # Full call for the call should be itself\n    assert_equal thing, thing[:full_call]\n    assert_equal xyz, xyz[:full_call]\n\n    assert_equal xyz, xy[:full_call]\n    assert_equal xyz, x[:full_call]\n  end\n\n  def test_find_error\n    assert_raises do\n      @call_index.find_calls :target => nil, :methods => nil\n    end\n  end\nend\n"
  },
  {
    "path": "test/tests/checks.rb",
    "content": "require_relative '../test'\n\nclass ChecksTests < Minitest::Test\n  def setup_tracker options = {}\n    options[:app_path] = \"/tmp/NOT_REAL\"\n    options = Brakeman.set_options(options)\n    app_tree = Brakeman::AppTree.from_options(options)\n    @tracker = Brakeman::Tracker.new(app_tree, nil, options)\n  end\n\n  def test_default_checks\n    t = setup_tracker\n\n    all_checks = Brakeman::Checks.checks.length\n    optional_checks = Brakeman::Checks.optional_checks.length\n    default_checks = Brakeman::Checks.checks_to_run(t).length\n\n    assert_operator all_checks, :>, default_checks\n    assert_operator all_checks, :>, optional_checks\n    assert_equal all_checks, default_checks + optional_checks\n  end\n\n  def test_run_all_checks\n    t = setup_tracker run_all_checks: true\n\n    assert_equal Brakeman::Checks.checks.length, Brakeman::Checks.checks_to_run(t).length\n  end\n\n  def test_run_single_check\n    t = setup_tracker run_checks: [\"CheckCrossSiteScripting\"]\n\n    assert_equal [Brakeman::CheckCrossSiteScripting], Brakeman::Checks.checks_to_run(t)\n  end\n\n  def test_enable_single_optional_check\n    t = setup_tracker enable_checks: [\"CheckSymbolDoS\"]\n\n    assert_includes Brakeman::Checks.checks_to_run(t), Brakeman::CheckSymbolDoS\n\n    expected = Brakeman::Checks.checks.length - Brakeman::Checks.optional_checks.length + 1\n\n    assert_equal expected, Brakeman::Checks.checks_to_run(t).length\n  end\n\n  def test_enable_optional_checks\n    t = setup_tracker enable_checks: [\"CheckSymbolDoS\", \"CheckUnscopedFind\"]\n\n    assert_includes Brakeman::Checks.checks_to_run(t), Brakeman::CheckSymbolDoS\n    assert_includes Brakeman::Checks.checks_to_run(t), Brakeman::CheckUnscopedFind\n\n    expected = Brakeman::Checks.checks.length - Brakeman::Checks.optional_checks.length + 2\n\n    assert_equal expected, Brakeman::Checks.checks_to_run(t).length\n  end\n\n  def test_enable_optional_checks_duplicate\n    t = setup_tracker enable_checks: [\"CheckUnscopedFind\"], run_checks: [\"CheckUnscopedFind\"]\n\n    assert_includes Brakeman::Checks.checks_to_run(t), Brakeman::CheckUnscopedFind\n    assert_equal 1, Brakeman::Checks.checks_to_run(t).length\n  end\n\n  def test_enable_optional_checks_with_explicit\n    t = setup_tracker enable_checks: [\"CheckUnscopedFind\"], run_checks: [\"CheckCrossSiteScripting\"]\n\n    assert_includes Brakeman::Checks.checks_to_run(t), Brakeman::CheckUnscopedFind\n    assert_includes Brakeman::Checks.checks_to_run(t), Brakeman::CheckCrossSiteScripting\n    assert_equal 2, Brakeman::Checks.checks_to_run(t).length\n  end\n\n  def test_missing_checks\n    checks = [\"Checkarghlbarghl\", \"CheckUnscopedFind\"]\n\n    assert_equal Set[\"Checkarghlbarghl\"], Brakeman::Checks.missing_checks(checks)\n  end\n\n  def test_check_for_missing_checks\n    require 'brakeman/options'\n    options, _ = Brakeman::Options.parse([\"-t\", \"blah\"])\n\n    assert_raises Brakeman::MissingChecksError do\n      Brakeman.check_for_missing_checks options[:run_checks], options[:skip_checks], options[:enable_checks]\n    end\n  end\n\n  def test_check_for_missing_skipped_checks\n    require 'brakeman/options'\n    options, _ = Brakeman::Options.parse([\"-x\", \"blah\"])\n\n    assert_raises Brakeman::MissingChecksError do\n      Brakeman.check_for_missing_checks options[:run_checks], options[:skip_checks], options[:enable_checks]\n    end\n  end\n\n  def test_check_for_missing_enabled_checks\n    require 'brakeman/options'\n    options, _ = Brakeman::Options.parse([\"-E\", \"blah\"])\n\n    assert_raises Brakeman::MissingChecksError do\n      Brakeman.check_for_missing_checks options[:run_checks], options[:skip_checks], options[:enable_checks]\n    end\n  end\nend\n"
  },
  {
    "path": "test/tests/codeclimate_engine_configuration.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/codeclimate/engine_configuration'\n\nclass EngineConfigurationTests < Minitest::Test\n  def test_for_expected_keys\n    config = {\n      \"include_paths\" => [\"/foo\"]\n    }\n\n    expected = [:app_path, :only_files, :output_files, :output_format, :pager, :quiet]\n    actual = Brakeman::Codeclimate::EngineConfiguration.new(config).options.keys.sort\n    assert_equal expected, actual\n  end\n\n  def test_debug_key\n    config = {\n      \"config\" => {\n        \"debug\" => \"true\"\n      }\n    }\n    assert Brakeman::Codeclimate::EngineConfiguration.new(config).options[:debug]\n    assert !Brakeman::Codeclimate::EngineConfiguration.new(config).options[:report_progress]\n  end\n\n  def test_include_paths\n    config = {\n      \"include_paths\" => [\"/foo/bar\", nil, \"/fizz/buzz\"]\n    }\n    assert_equal [\"/foo/bar\", \"/fizz/buzz\"],\n      Brakeman::Codeclimate::EngineConfiguration.new(config).options[:only_files]\n  end\n\n  def test_output_format\n    assert_equal :codeclimate,\n      Brakeman::Codeclimate::EngineConfiguration.new.options[:output_format]\n  end\n\n  def test_default_app_path\n    assert_equal Dir.pwd,\n      Brakeman::Codeclimate::EngineConfiguration.new.options[:app_path]\n  end\n\n  def test_custom_app_path\n    config = {\n      \"config\" => {\n        \"app_path\" => \"foo/bar\"\n      }\n    }\n    assert_equal File.join(Dir.pwd, \"foo/bar\"),\n      Brakeman::Codeclimate::EngineConfiguration.new(config).options[:app_path]\n  end\n\n  def test_custom_app_path_include_paths\n    config = {\n      \"include_paths\" => [\"foo/bar\", \"foo/42.rb\", \"foo/blub/neat\", \"README\", \"baz\"],\n      \"config\" => {\n        \"app_path\" => \"foo\"\n      }\n    }\n    assert_equal [\"/bar\", \"/42.rb\", \"/blub/neat\"],\n      Brakeman::Codeclimate::EngineConfiguration.new(config).options[:only_files]\n  end\n\n  def test_custom_app_path_include_paths_exact_match\n    config = {\n      \"include_paths\" => [\"foo/\"],\n      \"config\" => {\n        \"app_path\" => \"foo/\"\n      }\n    }\n    assert_equal [\"/\"],\n      Brakeman::Codeclimate::EngineConfiguration.new(config).options[:only_files]\n  end\n\n  def test_custom_nested_app_path_include_paths\n    config = {\n      \"include_paths\" => [\"foo/\"],\n      \"config\" => {\n        \"app_path\" => \"foo/bar/baz\"\n      }\n    }\n    assert_equal [\"/\"],\n      Brakeman::Codeclimate::EngineConfiguration.new(config).options[:only_files]\n  end\n\n  def test_custom_nested_app_path_include_paths_no_trailing_slash\n    config = {\n      \"include_paths\" => [\"foo\"],\n      \"config\" => {\n        \"app_path\" => \"foo/bar/baz\"\n      }\n    }\n    assert_equal [\"/\"],\n      Brakeman::Codeclimate::EngineConfiguration.new(config).options[:only_files]\n  end\n\n  def test_custom_nested_app_path_include_paths_not_a_parent\n    config = {\n      \"include_paths\" => [\"foo/nope\"],\n      \"config\" => {\n        \"app_path\" => \"foo/bar/baz\"\n      }\n    }\n    assert_equal [],\n      Brakeman::Codeclimate::EngineConfiguration.new(config).options[:only_files]\n  end\nend\n"
  },
  {
    "path": "test/tests/codeclimate_output.rb",
    "content": "require_relative '../test'\n\nclass TestCodeClimateOutput < Minitest::Test\n  def setup\n    @@report ||= Brakeman.run(\"#{TEST_PATH}/apps/rails2\").report.to_codeclimate\n    @@issues ||= @@report.split(\"\\0\").map { |json| JSON.parse(json) }\n  end\n\n  def test_for_expected_keys\n    expected = [\"type\", \"check_name\", \"description\", \"fingerprint\", \"categories\",\n                \"severity\", \"remediation_points\", \"location\", \"content\"]\n\n    @@issues.each do |issue|\n      assert (issue.keys - expected).empty?\n    end\n  end\n\n  def test_location_key\n    @@issues.each do |issue|\n      assert issue[\"location\"][\"path\"].length > 0\n      assert issue[\"location\"][\"lines\"][\"begin\"] >= 1\n      assert issue[\"location\"][\"lines\"][\"end\"] >= 1\n    end\n  end\n\n  def test_content_key\n    @@issues.each do |issue|\n      assert issue[\"content\"][\"body\"].length > 0\n    end\n  end\n\n  def test_file_path_with_prefix\n    report = Brakeman.run({app_path: \"#{TEST_PATH}/apps/rails2\", path_prefix: \"apps/rails2\"}).report.to_codeclimate\n    issues ||= report.split(\"\\0\").map { |json| JSON.parse(json) }\n    assert issues.first[\"location\"][\"path\"].start_with?(\"apps/rails2\")\n  end\nend\n"
  },
  {
    "path": "test/tests/commandline.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/commandline'\nrequire 'tempfile'\n\nclass CLExit < StandardError\n  attr_reader :exit_code\n\n  def initialize exit_code, message\n    super message\n\n    @exit_code = exit_code\n  end\nend\n\nclass TestCommandline < Brakeman::Commandline\n  def self.quit exit_code = 0, message = nil\n    raise CLExit.new(exit_code, message)\n  end\nend\n\nclass CommandlineTests < Minitest::Test\n\n  # Helper assertions\n\n  def assert_exit exit_code = 0, message = nil\n    begin\n      yield\n    rescue CLExit => e\n      assert_equal exit_code, e.exit_code\n      assert_equal message, e.message if message\n    else\n      assert_equal exit_code, 0\n    end\n  end\n\n  def assert_stdout message, exit_code = 0\n    assert_output message, \"\" do\n      assert_exit exit_code do\n        yield\n      end\n    end\n  end\n\n  def assert_stderr message, exit_code = 0\n    assert_output \"\", message do\n      assert_exit exit_code do\n        yield\n      end\n    end\n  end\n\n  # Helpers\n\n  def cl_with_options *opts\n    TestCommandline.start(*TestCommandline.parse_options(opts))\n  end\n\n  def scan_app *opts\n    opts << \"#{TEST_PATH}/apps/rails4\"\n    capture_io do\n      cl_with_options(*opts)\n    end\n  end\n\n  def setup\n    Brakeman.debug = false\n    Brakeman.quiet = false\n  end\n\n  # Tests\n\n  def test_nonexistent_scan_path\n    assert_exit Brakeman::No_App_Found_Exit_Code do\n      cl_with_options \"/fake_brakeman_test_path\"\n    end\n  end\n\n  def test_default_scan_path\n    options = {}\n\n    TestCommandline.set_options options\n\n    assert_equal \".\", options[:app_path]\n  end\n\n  def test_list_checks\n    assert_stderr(/\\AAvailable Checks:/) do\n      cl_with_options \"--checks\"\n    end\n  end\n\n  def test_bad_options\n    assert_stderr(/\\Ainvalid option: --not-a-real-option\\nPlease see `brakeman --help`/, -1) do\n      cl_with_options \"--not-a-real-option\"\n    end\n  end\n\n  def test_version\n    assert_stdout \"brakeman #{Brakeman::Version}\\n\" do\n      cl_with_options \"-v\"\n    end\n  end\n\n  def test_empty_config\n    empty_config = \"--- {}\\n\"\n\n    assert_stdout empty_config do\n      cl_with_options \"-C\"\n    end\n  end\n\n  def test_show_help\n    assert_stdout(/\\AUsage: brakeman \\[options\\] rails\\/root\\/path/) do\n      assert_exit do\n        cl_with_options \"--help\"\n      end\n    end\n  end\n\n  def test_exit_on_warn_default\n    assert_exit Brakeman::Warnings_Found_Exit_Code do\n      scan_app\n    end\n  end\n\n  def test_no_exit_on_warn\n    assert_exit do\n      scan_app \"--no-exit-on-warn\"\n    end\n  end\n\n  def test_exit_on_warn_no_warnings\n    assert_exit do\n      scan_app \"-t\", \"None\"\n    end\n  end\n\n  # Assert default when using `--show-ignored` flag.\n  def test_show_ignored_warnings\n    assert_exit Brakeman::Warnings_Found_Exit_Code do\n      scan_app \"--show-ignored\"\n    end\n  end\n\n  def test_compare_deactivates_ensure_ignore_notes\n    opts, = Brakeman::Commandline.parse_options [\n      '--ensure-ignore-notes',\n      '--compare', 'foo.json',\n    ]\n    assert_equal false, opts[:ensure_ignore_notes]\n  end\n\n  def test_ensure_ignore_notes\n    ignore_file_missing_notes = Tempfile.new('brakeman.ignore')\n    ignore_file_missing_notes.write IGNORE_WITH_MISSING_NOTES_JSON\n    ignore_file_missing_notes.close\n\n    assert_exit Brakeman::Empty_Ignore_Note_Exit_Code do\n      scan_app '--no-exit-on-warn',\n               '--ensure-ignore-notes',\n               '-i', ignore_file_missing_notes.path.to_s\n    end\n    ignore_file_missing_notes.unlink\n\n    ignore_file_with_notes = Tempfile.new('brakeman.ignore')\n    ignore_file_with_notes.write IGNORE_WITH_NOTES_JSON\n    ignore_file_with_notes.close\n\n    assert_exit do\n      scan_app '--no-exit-on-warn',\n               '--ensure-ignore-notes',\n               '-i', ignore_file_with_notes.path.to_s\n    end\n    ignore_file_with_notes.unlink\n  end\n\n  def test_ensure_no_obsolete_ignore_entries\n    ignore_file_obsolete_entries = Tempfile.new('brakeman.ignore')\n    ignore_file_obsolete_entries.write IGNORE_WITH_OBSOLETE_ENTRIES\n    ignore_file_obsolete_entries.close\n\n    assert_exit Brakeman::Obsolete_Ignore_Entries_Exit_Code do\n      scan_app '--ensure-no-obsolete-ignore-entries',\n               '-i', ignore_file_obsolete_entries.path.to_s,\n               '-t', 'None'\n    end\n\n    ignore_file_obsolete_entries.unlink\n  end\n\n  IGNORE_WITH_MISSING_NOTES_JSON = <<~JSON.freeze\n    {\n      \"ignored_warnings\": [\n        {\n          \"warning_type\": \"Remote Code Execution\",\n          \"warning_code\": 25,\n          \"fingerprint\": \"006ac5fe3834bf2e73ee51b67eb111066f618be46e391d493c541ea2a906a82f\",\n          \"check_name\": \"Deserialize\",\n          \"message\": \"`Oj.load` called with parameter value\",\n          \"file\": \"app/controllers/users_controller.rb\",\n          \"line\": 52,\n          \"link\": \"https://brakemanscanner.org/docs/warning_types/unsafe_deserialization\",\n          \"code\": \"Oj.load(params[:json], :mode => :object)\",\n          \"render_path\": null,\n          \"location\": {\n            \"type\": \"method\",\n            \"class\": \"UsersController\",\n            \"method\": \"some_api\"\n          },\n          \"user_input\": \"params[:json]\",\n          \"confidence\": \"High\",\n          \"note\": \"\"\n        },\n        {\n          \"warning_type\": \"Remote Code Execution\",\n          \"warning_code\": 25,\n          \"fingerprint\": \"97ecaa5677c8eadaed09217a704e59092921fab24cc751e05dfb7b167beda2cf\",\n          \"check_name\": \"Deserialize\",\n          \"message\": \"`Oj.load` called with parameter value\",\n          \"file\": \"app/controllers/users_controller.rb\",\n          \"line\": 51,\n          \"link\": \"https://brakemanscanner.org/docs/warning_types/unsafe_deserialization\",\n          \"code\": \"Oj.load(params[:json])\",\n          \"render_path\": null,\n          \"location\": {\n            \"type\": \"method\",\n            \"class\": \"UsersController\",\n            \"method\": \"some_api\"\n          },\n          \"user_input\": \"params[:json]\",\n          \"confidence\": \"High\",\n          \"note\": \"Here's a note!\"\n        }\n      ],\n      \"brakeman_version\": \"4.5.0\"\n    }\n  JSON\n\n  IGNORE_WITH_NOTES_JSON = <<~JSON.freeze\n    {\n      \"ignored_warnings\": [\n        {\n          \"warning_type\": \"Remote Code Execution\",\n          \"warning_code\": 25,\n          \"fingerprint\": \"006ac5fe3834bf2e73ee51b67eb111066f618be46e391d493c541ea2a906a82f\",\n          \"check_name\": \"Deserialize\",\n          \"message\": \"`Oj.load` called with parameter value\",\n          \"file\": \"app/controllers/users_controller.rb\",\n          \"line\": 52,\n          \"link\": \"https://brakemanscanner.org/docs/warning_types/unsafe_deserialization\",\n          \"code\": \"Oj.load(params[:json], :mode => :object)\",\n          \"render_path\": null,\n          \"location\": {\n            \"type\": \"method\",\n            \"class\": \"UsersController\",\n            \"method\": \"some_api\"\n          },\n          \"user_input\": \"params[:json]\",\n          \"confidence\": \"High\",\n          \"note\": \"Note here.\"\n        }\n      ],\n      \"brakeman_version\": \"4.5.0\"\n    }\n  JSON\n\n  IGNORE_WITH_OBSOLETE_ENTRIES = IGNORE_WITH_NOTES_JSON\nend\n"
  },
  {
    "path": "test/tests/config.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/rescanner'\n\nclass RailsConfiguration < Minitest::Test\n  include BrakemanTester::RescanTestHelper\n\n  def test_rails5_2_configuration_load_defaults\n    tracker = Brakeman.run(File.join(TEST_PATH, \"apps\", \"rails5.2\"))\n\n    refute tracker.config.rails.empty?\n\n    # Settings like `config.load_defaults(5.2)` shold be included, not\n    # just settings like `config.some.attribute = ...`\n    assert_equal Sexp.new(:lit, 5.2), tracker.config.rails[:load_defaults]\n\n    # Rails 5.0 settings should be included\n    assert_equal Sexp.new(:true), tracker.config.rails[:action_controller][:per_form_csrf_tokens]\n\n    # Rails 5.1 settings should be included\n    assert_equal Sexp.new(:false), tracker.config.rails[:assets][:unknown_asset_fallback]\n\n    # Rails 5.2 settings should be included\n    assert_equal Sexp.new(:true), tracker.config.rails[:active_record][:belongs_to_required_by_default]\n    assert_equal Sexp.new(:true), tracker.config.rails[:action_controller][:default_protect_from_forgery]\n\n    # Rails 6.0 settings should not be included\n    assert_nil tracker.config.rails[:action_dispatch][:use_cookies_with_metadata]\n  end\n\n  def test_rails5_2_configuration_load_defaults_with_config\n    before_rescan_of [\"config/application.rb\", \"config/environments/production.rb\"], \"rails5.2\" do\n      replace \"config/application.rb\", \"# config.action_controller.per_form_csrf_tokens = true\", \"config.action_controller.per_form_csrf_tokens = false\"\n      replace \"config/environments/production.rb\", \"# config.action_controller.default_protect_from_forgery = true\", \"config.action_controller.default_protect_from_forgery = false\"\n    end\n\n    tracker = @rescanner.tracker\n\n    refute tracker.config.rails.empty?\n\n    # Local Rails 5.0 overwrite should be used\n    assert_equal Sexp.new(:false), tracker.config.rails[:action_controller][:per_form_csrf_tokens]\n\n    # Local Rails 5.2 overwrite should be used\n    assert_equal Sexp.new(:false), tracker.config.rails[:action_controller][:default_protect_from_forgery]\n  end\n\n  def test_rails7_configuration_load_defaults\n    tracker = Brakeman.run(File.join(TEST_PATH, \"apps\", \"rails7\"))\n\n    assert_equal Sexp.new(:str, '7.0'), tracker.config.rails[:load_defaults]\n\n    # Check a 6.1 config\n    assert_equal Sexp.new(:true), tracker.config.rails[:action_controller][:urlsafe_csrf_tokens]\n\n    # Check a 7.0 config\n    assert_equal Sexp.new(:true), tracker.config.rails[:action_controller][:wrap_parameters_by_default]\n  end\n\n  def test_invalid_load_defaults\n    tracker = BrakemanTester.new_tracker\n    tracker.config.rails[:load_defaults] = Sexp.new(:str, 'asd2ojasdo.asodja1')\n    config = Brakeman::Config.new(tracker)\n    config.load_rails_defaults\n\n    # Defaults are not loaded\n    assert_nil config.rails.dig(:ssl_options, :hsts, :subdomains)\n  end\nend\n"
  },
  {
    "path": "test/tests/constants.rb",
    "content": "require_relative '../test'\n\nclass ConstantTests < Minitest::Test\n  def setup\n    @constants = Brakeman::Constants.new\n  end\n\n  def assert_alias expected, original, full = false\n    tracker = BrakemanTester.new_tracker\n    original_sexp = Brakeman::BaseProcessor.new(tracker).process(RubyParser.new.parse original)\n    expected_sexp = Brakeman::BaseProcessor.new(tracker).process(RubyParser.new.parse expected)\n    processed_sexp = Brakeman::AliasProcessor.new(tracker).process_safely original_sexp\n\n    if full\n      assert_equal expected_sexp, processed_sexp\n    else\n      assert_equal expected_sexp, processed_sexp.last\n    end\n  end\n\n  def test_constants_yeah\n    assert_alias <<-OUTPUT, <<-INPUT, true\n      class A\n        def x\n          puts 1\n        end\n      end\n\n      X = 1\n    OUTPUT\n      class A\n        def x\n          puts X\n        end\n      end\n\n      X = 1\n    INPUT\n  end\n\n  def test_constants_issue_453\n    assert_alias <<-OUTPUT, <<-INPUT, true\n    class User < ActiveRecord::Base\n      FOO = ['Baz']\n      BAR = 'Qux'\n\n      def variations\n        'Baz'.constantize\n        'Baz'.constantize\n        'Qux'.constantize\n        'Qux'.constantize\n      end\n    end\n    OUTPUT\n    class User < ActiveRecord::Base\n      FOO = ['Baz']\n      BAR = 'Qux'\n\n      def variations\n        User::FOO.first.constantize\n        FOO.first.constantize\n        User::BAR.constantize\n        BAR.constantize\n      end\n    end\n    INPUT\n  end\n\n  def test_constants_basic_lookup\n    @constants.add :A, s(:lit, 1)\n\n    assert_equal s(:lit, 1), @constants.get_simple_value(s(:const, :A))\n  end\n\n  def test_constants_get_simple_value\n    a_b_c = s(:colon2, s(:colon2, s(:const, :A), :B), :C)\n    @constants.add :A, s(:lit, 1) # Simple X = 1 uses just symbol for const\n    @constants.add a_b_c, s(:lit, 2)\n    @constants.add :D, s(:const, :D)\n\n    assert_equal s(:lit, 1), @constants.get_simple_value(s(:const, :A))\n    assert_equal s(:lit, 2), @constants.get_simple_value(a_b_c)\n    assert_equal s(:lit, 2), @constants.get_simple_value(s(:colon2, s(:const, :B), :C))\n    assert_equal s(:lit, 2), @constants.get_simple_value(s(:colon2, s(:colon2, s(:colon3, :A), :B), :C) )\n    assert_nil @constants.get_simple_value(s(:colon2, s(:colon3, :B), :C)) # top-level B\n    assert_nil @constants.get_simple_value(s(:colon2, s(:const, :A), :C))\n    assert_nil @constants.get_simple_value(s(:colon2, s(:const, :C), :B)) # backwards\n    assert_nil @constants.get_simple_value(s(:const, :D)) # not a literal\n  end\n\n  def test_constants_lookup\n    @constants.add :A, s(:lit, 1)\n    @constants.add s(:colon2, s(:const, :A), :B), s(:lit, 2)\n    @constants.add :D, s(:const, :D)\n    @constants.add :Y, s(:lit, 3)\n\n    assert_equal s(:lit, 1), @constants[s(:const, :A)]\n    assert_equal s(:lit, 2), @constants[s(:colon2, s(:const, :A), :B)]\n    assert_equal s(:const, :D), @constants[s(:const, :D)]\n    assert_nil @constants[s(:colon2, s(:call, s(:call, nil, :x), :constantize), :Y)]\n  end\n\n  def test_constants_find_all\n    @constants.add :A, s(:lit, 1)\n    @constants.add s(:colon2, s(:const, :B), :A), s(:lit, 2)\n\n    consts = @constants.find_all s(:const, :A)\n\n    assert_equal 2, consts.length\n  end\n\n  def test_constants_context\n    @constants.add :A, s(:lit, 1).line(10), file: \"file.rb\", class: :CoolClass\n\n    const = @constants.find_all(s(:const, :A)).first\n\n    assert_equal s(:lit, 1), const.value\n    assert_equal 10, const.value.line\n    assert_equal \"file.rb\", const.file\n    assert_equal \"file.rb\", const.context[:file]\n    assert_equal :CoolClass, const.context[:class]\n  end\n\n  def test_constant_in_module_should_not_match_different_qualified_path\n    # When MY_CONST is defined in both module A and module B,\n    # looking up B::MY_CONST should return B's value, not A's\n    assert_alias <<-OUTPUT, <<-INPUT, true\n    module A\n      MY_CONST = 1\n    end\n\n    module B\n      MY_CONST = 2\n    end\n\n    class Foo\n      def bar\n        2\n      end\n    end\n    OUTPUT\n    module A\n      MY_CONST = 1\n    end\n\n    module B\n      MY_CONST = 2\n    end\n\n    class Foo\n      def bar\n        B::MY_CONST\n      end\n    end\n    INPUT\n  end\n\n  def test_constant_in_module_should_not_match_undefined_qualified_path\n    # When MY_CONST is defined only in module A,\n    # looking up B::MY_CONST should NOT resolve to A's value\n    assert_alias <<-OUTPUT, <<-INPUT, true\n    module A\n      MY_CONST = 1\n    end\n\n    class Foo\n      def bar\n        B::MY_CONST\n      end\n    end\n    OUTPUT\n    module A\n      MY_CONST = 1\n    end\n\n    class Foo\n      def bar\n        B::MY_CONST\n      end\n    end\n    INPUT\n  end\n\n  def test_nested_constant_in_module_should_not_match_different_qualified_path\n    # When MY_CONST is defined in both A::Z and B::Z,\n    # looking up B::Z::MY_CONST should return B::Z's value, not A::Z's\n    assert_alias <<-OUTPUT, <<-INPUT, true\n    module A\n      module Z\n        MY_CONST = 1\n      end\n    end\n\n    module B\n      module Z\n        MY_CONST = 2\n      end\n    end\n\n    class Foo\n      def bar\n        2\n      end\n    end\n    OUTPUT\n    module A\n      module Z\n        MY_CONST = 1\n      end\n    end\n\n    module B\n      module Z\n        MY_CONST = 2\n      end\n    end\n\n    class Foo\n      def bar\n        B::Z::MY_CONST\n      end\n    end\n    INPUT\n  end\n\n  def test_nested_constant_in_module_should_not_match_undefined_qualified_path\n    # When MY_CONST is defined only in A::Z,\n    # looking up B::Z::MY_CONST should NOT resolve to A::Z's value\n    assert_alias <<-OUTPUT, <<-INPUT, true\n    module A\n      module Z\n        MY_CONST = 1\n      end\n    end\n\n    class Foo\n      def bar\n        B::Z::MY_CONST\n      end\n    end\n    OUTPUT\n    module A\n      module Z\n        MY_CONST = 1\n      end\n    end\n\n    class Foo\n      def bar\n        B::Z::MY_CONST\n      end\n    end\n    INPUT\n  end\n\n  def test_qualified_constant_defined_in_module_should_not_prepend_context\n    # When B::MY_CONST is explicitly defined (even from within module A),\n    # it should be stored as B::MY_CONST, not A::B::MY_CONST\n    assert_alias <<-OUTPUT, <<-INPUT, true\n    module A\n      B::MY_CONST = 1\n    end\n\n    class Foo\n      def bar\n        1\n      end\n    end\n    OUTPUT\n    module A\n      B::MY_CONST = 1\n    end\n\n    class Foo\n      def bar\n        B::MY_CONST\n      end\n    end\n    INPUT\n  end\nend\n"
  },
  {
    "path": "test/tests/cves.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/rescanner'\n\nclass CVETests < Minitest::Test\n  include BrakemanTester::RescanTestHelper\n  include BrakemanTester::FindWarning\n\n  def report\n    @rescanner.tracker.report.to_hash\n  end\n\n  def assert_version version, gem = :rails\n    if gem == :rails\n      assert_equal version, @rescanner.tracker.config.rails_version\n    else\n      assert_equal version, @rescanner.tracker.config.gem_version(gem)\n    end\n  end\n\n  def test_CVE_2015_3226_4_1_1\n    before_rescan_of \"Gemfile\", \"rails4\" do\n      replace \"Gemfile\", \"4.0.0\", \"4.1.1\"\n    end\n\n    assert_version \"4.1.1\"\n    assert_warning :type => :warning,\n      :warning_code => 87,\n      :fingerprint => \"6c2281400c467a0100bcedeb122bc2cb024d09e538e18f4c7328c3569fff6754\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.1\\.1\\ does\\ not\\ encode\\ JSON\\ keys\\ \\(C/,\n      :confidence => 0,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_CVE_2015_3226_4_2_1\n    before_rescan_of \"Gemfile\", \"rails4\" do\n      replace \"Gemfile\", \"4.0.0\", \"4.2.1\"\n    end\n\n    assert_version \"4.2.1\"\n    assert_warning :type => :warning,\n      :warning_code => 87,\n      :fingerprint => \"6c2281400c467a0100bcedeb122bc2cb024d09e538e18f4c7328c3569fff6754\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.2\\.1\\ does\\ not\\ encode\\ JSON\\ keys\\ \\(C/,\n      :confidence => 0,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_CVE_2015_3226_workaround\n    initializer = \"config/initializers/json.rb\"\n    before_rescan_of [\"Gemfile\", initializer], \"rails4\" do\n      replace \"Gemfile\", \"4.0.0\", \"4.2.1\"\n\n      write_file initializer, <<-RUBY\n      module ActiveSupport\n        module JSON\n          module Encoding\n            private\n            class EscapedString\n              def to_s\n                self\n              end\n            end\n          end\n        end\n      end\n      RUBY\n    end\n\n    assert_version \"4.2.1\"\n    assert_no_warning :type => :warning,\n      :warning_code => 87,\n      :fingerprint => \"6c2281400c467a0100bcedeb122bc2cb024d09e538e18f4c7328c3569fff6754\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.2\\.1\\ does\\ not\\ encode\\ JSON\\ keys\\ \\(C/,\n      :confidence => 0,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_CVE_2015_3227_4_2_1\n    before_rescan_of \"Gemfile\", \"rails4\" do\n      replace \"Gemfile\", \"4.0.0\", \"4.2.1\"\n    end\n\n    assert_version \"4.2.1\"\n    assert_warning :type => :warning,\n      :warning_code => 88,\n      :fingerprint => \"6ad4464dbb2a999591c7be8346dc137c3372b280f4a8b0c024fef91dfebeeb83\",\n      :warning_type => \"Denial of Service\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.2\\.1\\ is\\ vulnerable\\ to\\ denial\\ of\\ s/,\n      :confidence => 1,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_CVE_2015_3227_4_1_11\n    before_rescan_of \"Gemfile\", \"rails4\" do\n      replace \"Gemfile\", \"4.0.0\", \"4.1.11\"\n    end\n\n    assert_version \"4.1.11\"\n    assert_no_warning :type => :warning,\n      :warning_code => 88,\n      :warning_type => \"Denial of Service\",\n      :line => 4,\n      :confidence => 1,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_CVE_2015_3227_workaround\n    initializer = \"config/initializers/xml.rb\"\n    before_rescan_of [\"Gemfile\", initializer], \"rails4\" do\n      replace \"Gemfile\", \"4.0.0\", \"4.1.11\"\n      write_file initializer, \"ActiveSupport::XmlMini.backend = 'Nokogiri'\"\n    end\n\n    assert_version \"4.1.11\"\n    assert_no_warning :type => :warning,\n      :warning_code => 88,\n      :warning_type => \"Denial of Service\",\n      :line => 4,\n      :confidence => 1,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_CVE_2015_3227_3_2_22\n    before_rescan_of \"Gemfile.lock\", \"rails3.2\" do\n      replace \"Gemfile.lock\", \"rails (3.2.9.rc2)\", \"rails (3.2.22)\"\n    end\n\n    assert_version \"3.2.22\"\n    assert_no_warning :type => :warning,\n      :warning_code => 88,\n      :warning_type => \"Denial of Service\"\n  end\n\n  def test_railties_version\n    before_rescan_of \"Gemfile\", \"rails4\" do\n      replace \"Gemfile\", \"rails\", \"railties\"\n    end\n\n    assert_version \"4.0.0\"\n  end\n\n  def test_new_bundler_file_names\n    before_rescan_of [\"Gemfile\", \"Gemfile.lock\"] do\n      rename \"Gemfile\", \"gems.rb\"\n      rename \"Gemfile.lock\", \"gems.locked\"\n    end\n\n    skip \"This test was always wrong?\"\n\n    assert_version \"3.2.9.rc2\"\n    assert_new 0\n    assert_fixed 0\n  end\n\n  def test_ignored_secrets_yml\n    before_rescan_of [\".gitignore\", \"config/secrets.yml\"], \"rails4\" do\n      append \".gitignore\", \"\\nconfig/secrets.yml\"\n    end\n\n    assert_new 0\n    assert_fixed 1\n  end\n\n  def test_CVE_2015_7576\n    before_rescan_of \"Gemfile.lock\", \"rails3.1\" do\n      replace \"Gemfile.lock\", \" rails (3.1.0)\", \" rails (3.2.22.1)\"\n    end\n\n    assert_version \"3.2.22.1\"\n    assert_new 0\n    assert_no_warning type: :controller, :warning_code => 93\n  end\n\n  def test_CVE_2016_0751\n    before_rescan_of \"Gemfile.lock\", \"rails3.1\" do\n      replace \"Gemfile.lock\", \" rails (3.1.0)\", \" rails (3.2.22.1)\"\n    end\n\n    assert_new 0\n    assert_version \"3.2.22.1\"\n    assert_no_warning type: :controller, :warning_code => 94\n  end\n\n  def test_CVE_2015_7577\n    before_rescan_of \"Gemfile\", \"rails4\" do\n      replace \"Gemfile\", \"rails', '4.0.0'\", \"rails', '4.2.5.1'\"\n    end\n\n    assert_version \"4.2.5.1\"\n    assert_no_warning type: :model, :warning_code => 95\n    assert_warning :warning_code => 102 # CVE-2016-6317\n    assert_new 1\n  end\n\n  def test_sanitize_cves\n    before_rescan_of \"Gemfile.lock\", \"rails5\" do\n      replace \"Gemfile.lock\", \"rails-html-sanitizer (1.0.2)\", \"rails-html-sanitizer (1.0.3)\"\n    end\n\n    assert_version \"1.0.3\", :'rails-html-sanitizer'\n\n    assert_new 2 # XSS goes from high to weak\n    assert_fixed 5\n    assert_no_warning :warning_code => 96\n    assert_no_warning :warning_code => 97\n    assert_no_warning :warning_code => 98\n  end\n\n  def test_CVE_2015_7581\n    before_rescan_of \"Gemfile\", \"rails4\" do\n      replace \"Gemfile\", \"rails', '4.0.0'\", \"rails', '4.2.5.1'\"\n    end\n\n    assert_new 1\n    assert_version \"4.2.5.1\"\n    assert_no_warning :warning_code => 100\n    assert_warning :warning_code => 102 # CVE-2016-6317\n  end\n\n  def test_CVE_2016_6316_rails3\n    before_rescan_of [\"Gemfile.lock\", \"app/views/home/test_content_tag.html.erb\"], \"rails3\" do\n      replace \"Gemfile.lock\", \"rails (3.0.3)\", \"rails (3.2.22.4)\"\n    end\n\n    assert_version \"3.2.22.4\"\n    expected = if RUBY_PLATFORM == \"java\"\n                 31 # 1 for CVE_2013_1856\n               else\n                 30\n               end\n    assert_fixed expected # 3 for CVE-2016-6316\n  end\n\n  def test_CVE_2016_6316_rails5\n    before_rescan_of [\"Gemfile.lock\", \"app/views/widget/content_tag.html.erb\"], \"rails5\" do\n      replace \"Gemfile.lock\", \"rails (5.0.0)\", \"rails (5.0.0.1)\"\n    end\n\n    assert_version \"5.0.0.1\"\n    assert_fixed 3 # 3 for CVE-2016-6316\n  end\n\n  def test_CVE_2018_3760_sprockets\n    # Have to include `.ruby-version` otherwise it changes the EOL Ruby warning\n    # because the warning will point at Gemfile.lock instead of .ruby-version\n    before_rescan_of [\".ruby-version\", \"Gemfile.lock\", \"config/environments/production.rb\"], \"rails5.2\" do\n      replace \"Gemfile.lock\", \"sprockets (3.7.1)\", \"sprockets (4.0.0.beta2)\"\n      replace \"config/environments/production.rb\", \"config.assets.compile = false\", \"config.assets.compile = true\"\n    end\n\n    assert_version \"4.0.0.beta2\", :sprockets\n    assert_new 1 # CVE-2018-3760\n  end\n\n  def test_CVE_2018_8048_exact_fix_version\n    before_rescan_of [\".ruby-version\", \"Gemfile.lock\"], \"rails5.2\" do\n      replace \"Gemfile.lock\", \"loofah (2.1.1)\", \"loofah (2.2.1)\"\n    end\n\n    assert_version \"2.2.1\", :loofah\n    assert_fixed 1\n  end\n\n  def test_CVE_2018_8048_newer_version\n    before_rescan_of [\".ruby-version\", \"Gemfile.lock\"], \"rails5.2\" do\n      replace \"Gemfile.lock\", \"loofah (2.1.1)\", \"loofah (2.10.1)\"\n    end\n\n    assert_version \"2.10.1\", :loofah\n    assert_fixed 1\n  end\n\n  def test_CVE_2013_0276\n    before_rescan_of \"app/models/protected.rb\", \"rails2\", :collapse_mass_assignment => true do\n      replace \"app/models/protected.rb\", \"attr_accessible nil\", \"attr_protected :admin\"\n    end\n\n    warning = new.find do |w|\n      w.warning_code == 51 # CVE-2013-0276\n    end\n\n    refute_nil warning\n  end\n\n  def test_CVE_2010_3933_rails3\n    before_rescan_of [\"Gemfile.lock\", \"app/models/a.rb\"], \"rails3\", :run_checks => [\"CheckNestedAttributes\"] do\n      replace \"Gemfile.lock\", \"rails (3.0.3)\", \"rails (3.0.0)\"\n      write_file \"app/models/a.rb\", <<-RUBY\n      class A < ActiveRecord::Base\n        accepts_nested_attributes_for :b\n      end\n      RUBY\n    end\n\n    assert_version \"3.0.0\"\n\n    warning = new.find do |w|\n      w.warning_code == 31 and # CVE_2010_3933\n        w.message.to_s == \"Vulnerability in nested attributes (CVE-2010-3933). Upgrade to Rails 3.0.1\"\n    end\n\n    refute_nil warning\n  end\n\n  def test_CVE_2020_8159_rails5_upgrade\n    before_rescan_of \"Gemfile\", \"rails5\", run_checks: [\"CheckPageCachingCVE\"] do\n      replace \"Gemfile\",\n        \"gem 'actionpack-page_caching', '1.2.0'\",\n        \"gem 'actionpack-page_caching', '1.2.2'\"\n    end\n\n    assert fixed.find { |w|\n      w.warning_code == Brakeman::WarningCodes.code(:CVE_2020_8159)\n    }\n\n    assert_fixed 1 # CVE-2020-8159\n    assert_new 0\n  end\n\n  def test_CVE_2020_8159_rails5_caches_page\n    before_rescan_of \"app/controllers/test_cve_2020_8519.rb\", \"rails5\", run_checks: [\"CheckPageCachingCVE\"] do\n      write_file \"app/controllers/test_cve_2020_8519.rb\", <<-RUBY\n      class TestCVEController < ApplicationController\n        caches_page :stuff\n      end\n      RUBY\n    end\n\n    warning = new.find do |w|\n      w.warning_code == Brakeman::WarningCodes.code(:CVE_2020_8159)\n    end\n\n    refute_nil warning\n\n    # Warning should be :high now\n    assert_equal 0, warning.confidence\n    assert_fixed 1\n    assert_new 1\n  end\n\n  def test_CVE_2020_8166\n    Date.stub :today, Date.parse('2021-04-05') do\n      before_rescan_of [\".ruby-version\", \"Gemfile.lock\"], \"rails5.2\" do\n        replace \"Gemfile.lock\", \" rails (5.2.0.beta2)\", \" rails (5.2.4.3)\"\n      end\n    end\n\n    assert_new 0\n    assert_version \"5.2.4.3\"\n    assert_no_warning type: :generic, :warning_code => 116\n  end\n\n  def test_CVE_2020_8166_rails6\n    Date.stub :today, Date.parse('2022-04-06') do\n      before_rescan_of \"Gemfile\", \"rails6\" do\n        replace \"Gemfile\", \"gem 'rails', '~> 6.0.0.beta2'\", \"gem 'rails', '~> 6.0.0'\"\n      end\n    end\n\n    assert_new 1\n    assert_version \"6.0.0\"\n    assert_warning type: :generic, :warning_code => 116\n  end\n\n  def test_old_sanitize_cves\n    before_rescan_of \"app/views/users/one.html.haml\", \"rails5.2\" do\n      replace \"app/views/users/one.html.haml\", \"sanitize\", \"not_sanitize\"\n      replace \"app/views/users/one.html.haml\", \"sanitize\", \"not_sanitize\"\n    end\n\n    # Confidence is high when uses of `sanitize` are found.\n    # This tests that the confidence is lowered to medium\n    # when uses of `sanitize` are removed.\n    assert_warning warning_code: 107, confidence: 1\n    assert_warning warning_code: 106, confidence: 1\n  end\n\n  def test_CVE_2022_32209_rails6\n    before_rescan_of \"Gemfile\", \"rails6\" do\n      append \"Gemfile\", \"\\ngem 'rails-html-sanitizer', '1.4.2'\"\n    end\n\n    assert_new 1\n    assert_version '1.4.2', :'rails-html-sanitizer'\n    assert_warning type: :generic, :warning_code => 124\n  end\n\n  def test_CVE_2022_32209_fix_version\n    before_rescan_of \"Gemfile\", \"rails6\" do\n      append \"Gemfile\", \"\\ngem 'rails-html-sanitizer', '1.4.3'\"\n    end\n\n    assert_new 0\n    assert_version '1.4.3', :'rails-html-sanitizer'\n    assert_no_warning type: :generic, :warning_code => 124\n  end\nend\n"
  },
  {
    "path": "test/tests/differ.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/differ'\n\nclass DifferTests < Minitest::Test\n  include BrakemanTester::DiffHelper\n\n  def setup\n    @@diffrun ||= Brakeman.run :app_path => \"#{TEST_PATH}/apps/rails2\"\n    @warnings ||= @@diffrun.warnings\n  end\n\n  def run_diff new, old\n    @diff = Brakeman::Differ.new(new, old).diff\n  end\n\n  def assert_fixed expected, diff = @diff\n    assert_equal expected, diff[:fixed].length, \"Expected #{expected} fixed warnings, but found #{diff[:fixed].length}\"\n  end\n\n  def assert_new expected, diff = @diff\n    assert_equal expected, diff[:new].length, \"Expected #{expected} new warnings, but found #{diff[:new].length}\"\n  end\n\n  def test_sanity\n    run_diff @warnings, @warnings\n\n    assert_fixed 0\n    assert_new 0\n  end\n\n  def test_one_fixed\n    old = @warnings\n    new = @warnings.dup\n    new.shift\n\n    run_diff new, old\n\n    assert_fixed 1\n    assert_new 0\n  end\n\n  def test_one_new\n    new = @warnings\n    old = @warnings.dup\n    old.shift\n\n    run_diff new, old\n\n    assert_fixed 0\n    assert_new 1\n  end\n\n  def test_new_and_fixed\n    new = @warnings\n    old = @warnings.dup\n\n    new << old.pop\n    old << new.shift\n\n    run_diff new, old\n\n    assert_new 2\n    assert_fixed 2\n  end\n\n  def test_line_number_change_only\n    new = @warnings\n    old = @warnings.dup\n\n    changed = new.pop.dup\n    if changed.line.nil?\n      changed.instance_variable_set(:@line, 0)\n    else\n      changed.instance_variable_set(:@line, changed.line + 1)\n    end\n\n    new << changed\n\n    run_diff new, old\n\n    assert_new 0\n    assert_fixed 0\n  end\n\n  # If the new report has no warnings, then\n  # all the old warnings have been fixed.\n  def test_no_new_warnings\n    run_diff [], @warnings\n\n    assert_new 0\n    assert_fixed @warnings.length\n  end\n\n  # If the old report has no warnings, then\n  # all the warnings are new.\n  def test_no_old_warnings\n    run_diff @warnings, []\n\n    assert_new @warnings.length\n    assert_fixed 0\n  end\nend\n"
  },
  {
    "path": "test/tests/file_cache.rb",
    "content": "require_relative '../test'\n\nrequire 'securerandom'\nrequire 'brakeman/tracker/file_cache'\nrequire 'brakeman/file_path'\nrequire 'brakeman/file_parser'\n\nclass FileCacheTests < Minitest::Test\n  def test_basics\n    fc = Brakeman::FileCache.new\n    af = random_astfile\n    other = random_astfile\n\n    fc.add_file af, :controller\n\n    assert fc.controllers[af.path]\n    assert fc.cached? af.path\n    refute fc.cached? other.path\n  end\n\n  def test_valid_type\n    fc = Brakeman::FileCache.new\n\n    [:controller, :initializer, :lib, :model, :template].each do |type|\n      assert fc.valid_type? type\n    end\n\n    refute fc.valid_type? :something_else\n  end\n\n  def test_delete\n    fc = Brakeman::FileCache.new\n    af = random_astfile\n    fc.add_file af, :model\n\n    assert fc.cached? af.path\n    fc.delete af.path\n    refute fc.cached? af.path\n  end\n\n  def test_file_path_equivalence\n    fc = Brakeman::FileCache.new\n    af = random_astfile\n    fc.add_file af, :model\n    of = Brakeman::FilePath.new(af.path.absolute, af.path.relative)\n\n    assert fc.cached? of\n  end\n\n  private\n\n  def random_astfile\n    file_name = \"file_#{SecureRandom.hex}\"\n    path = Brakeman::FilePath.new(\"/tmp/file/#{file_name}\", file_name)\n\n    Brakeman::ASTFile.new(path, s(:block))\n  end\nend\n"
  },
  {
    "path": "test/tests/file_parser.rb",
    "content": "require_relative '../test'\nrequire 'tempfile'\n\nclass FileParserTests < Minitest::Test\n  def setup\n    @tracker = BrakemanTester.new_tracker\n    timeout = 10\n    @file_parser = Brakeman::FileParser.new(@tracker.app_tree, timeout)\n  end\n\n  def test_parse_error\n    tempfile = Tempfile.new\n    tempfile.write(\"x = \")\n    tempfile.close\n\n    @file_parser.parse_files([tempfile.path])\n    @tracker.add_errors(@file_parser.errors)\n\n    assert_equal 1, @tracker.errors.length\n  ensure\n    tempfile.unlink\n  end\n\n  def test_parse_error_shows_newer_failure\n    tempfile = Tempfile.new\n    tempfile.write <<-RUBY\n    blah(x: 1)\n    thing do\n    RUBY\n    tempfile.close\n\n    @file_parser.parse_files([tempfile.path])\n    @tracker.add_errors(@file_parser.errors)\n\n    assert_equal 1, @tracker.errors.length\n\n    rp_version = Gem::Version.new(RubyParser::Parser::VERSION)\n    err_re = case\n             when rp_version >= Gem::Version.new(\"3.17.0\")\n               /parse error on value \"\\$\" \\(\\$end\\)/\n             when rp_version >= Gem::Version.new(\"3.14.0\")\n               /parse error on value false \\(\\$end\\)/\n             else\n               /parse error on value \\\"\\$end\\\" \\(\\$end\\)/\n             end\n\n    assert_match(err_re, @tracker.errors.first[:error])\n  end\n\n  def test_read_files_reports_error\n    tempfile = Tempfile.new\n    tempfile.write(\"x = \")\n    tempfile.close\n\n    @file_parser.read_files([tempfile.path]) do |path, contents|\n      @file_parser.parse_ruby contents, path\n    end\n\n    @tracker.add_errors(@file_parser.errors)\n\n    assert_equal 1, @tracker.errors.length\n  end\n\n  def test_parse_ruby_accepts_file_path\n    file_path = Brakeman::FilePath.from_app_tree @tracker.app_tree, \"config/test.rb\"\n\n    parsed = @file_parser.parse_ruby <<-'RUBY', file_path\n      \"#{__FILE__}\"\n    RUBY\n\n    assert_equal s(:str, \"config/test.rb\"), parsed\n  end\nend\n"
  },
  {
    "path": "test/tests/file_path.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/app_tree'\nrequire 'brakeman/file_path'\n\nclass FilePathTests < Minitest::Test\n  def test_relative_from_app_tree\n    at = Brakeman::AppTree.new(\"/tmp/blah\")\n    fp = Brakeman::FilePath.from_app_tree at, \"thing.rb\"\n\n    assert_equal \"thing.rb\", fp.relative\n    assert_equal \"/tmp/blah/thing.rb\", fp.absolute\n  end\n\n  def test_absolute_from_app_tree\n    at = Brakeman::AppTree.new(\"/tmp/blah\")\n    fp = Brakeman::FilePath.from_app_tree at, \"/tmp/blah/thing.rb\"\n\n    assert_equal \"thing.rb\", fp.relative\n    assert_equal \"/tmp/blah/thing.rb\", fp.absolute\n  end\n\n  def test_from_app_tree_already_file_path\n    at = Brakeman::AppTree.new(\"/tmp/blah\")\n    fp1 = Brakeman::FilePath.from_app_tree at, \"/tmp/blah/thing.rb\"\n    fp2 = Brakeman::FilePath.from_app_tree at, fp1\n\n    assert_same fp1, fp2\n  end\n\n  def test_from_tracker_already_file_path\n    at = Brakeman::AppTree.new(\"/tmp/blah\")\n    fp1 = at.file_path \"/tmp/blah/thing.rb\"\n    fp2 = at.file_path fp1\n\n    assert_same fp1, fp2\n  end\n\n  def test_file_path_to_str\n    at = Brakeman::AppTree.new(\"/tmp/blah\")\n    fp = Brakeman::FilePath.from_app_tree at, \"/tmp/blah/thing.rb\"\n\n    assert_equal \"/tmp/blah/thing.rb\", fp.to_str\n    assert_equal \"/tmp/blah/thing.rb\", \"#{fp}\"\n  end\n\n  def test_file_path_equality\n    at = Brakeman::AppTree.new(\"/tmp/blah\")\n    fp1 = Brakeman::FilePath.from_app_tree at, \"/tmp/blah/thing.rb\"\n    fp2 = Brakeman::FilePath.from_app_tree at, \"thing.rb\"\n    fp3 = Brakeman::FilePath.from_app_tree at, \"thing2.rb\"\n\n    assert_equal fp1, fp2\n    assert_equal fp2, fp1\n\n    refute_equal fp1, fp3\n    refute_equal fp3, fp2\n\n    assert_includes [fp1], fp2\n    assert_includes [fp2], fp1\n\n    refute_includes [fp1], fp3\n    refute_includes [fp3], fp2\n  end\n\n\n  def test_file_path_equality_not_cached\n    fp1 = Brakeman::FilePath.new(\"/tmp/blah/thing.rb\", \"thing.rb\")\n    fp2 = Brakeman::FilePath.new(\"/tmp/blah/thing.rb\", \"thing.rb\")\n\n    assert_equal fp1, fp2\n    assert_equal fp2, fp1\n    assert_equal fp1.hash, fp2.hash\n    assert fp1.eql?(fp2)\n    assert fp2.eql?(fp1)\n\n    # Ensure FilePaths used as hash keys are equal\n    h = {fp1 => 1}\n\n    assert_equal 1, h[fp1]\n    assert_equal 1, h[fp2]\n  end\n\n  def test_file_path_cache\n    at = Brakeman::AppTree.new(\"/tmp/blah\")\n    fp1 = Brakeman::FilePath.from_app_tree at, \"/tmp/blah/thing.rb\"\n    fp2 = Brakeman::FilePath.from_app_tree at, \"thing.rb\"\n\n    assert_same fp1, fp2\n    assert_same fp2, fp1\n  end\nend\n"
  },
  {
    "path": "test/tests/find_return_value.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/processors/lib/find_return_value'\n\nclass FindReturnValueTests < Minitest::Test\n  def assert_returns expected, original, env = nil\n    expected = RubyParser.new.parse(expected) if expected.is_a? String\n    original = RubyParser.new.parse(original) if original.is_a? String\n    return_value = Brakeman::FindReturnValue.return_value original, env\n\n    assert_equal expected, return_value\n  end\n\n  def test_sanity\n    assert_returns \"1\", \"1\"\n  end\n\n  def test_implicit_return\n    assert_returns \"1\", <<-RUBY\n      def x\n        1\n      end\n    RUBY\n  end\n\n  def test_explicit_return\n    #This is kind of wrong\n    assert_returns \"'hi' or 1\", <<-RUBY\n      def x\n        return 'hi'\n        1\n      end\n    RUBY\n  end\n\n  def test_multiple_explicit_returns\n    assert_returns '1 or 2', <<-RUBY\n      def x\n        if something\n          return 1\n        else\n          return 2\n        end\n      end\n    RUBY\n  end\n\n  def test_multiple_implicit_returns\n    assert_returns '1 or 2', <<-RUBY\n      def x\n        if something\n          1\n        else\n          2\n        end\n      end\n    RUBY\n  end\n\n  def test_block_of_code\n    assert_returns '@b', <<-RUBY\n      def x\n        something\n        something\n        something_else\n        @b\n      end\n    RUBY\n  end\n\n  def test_parameters\n    env = SexpProcessor::Environment.new\n    env[s(:lvar, :y)] = s(:lit, 1)\n\n    assert_returns '2', <<-RUBY, env\n      def x y\n        y = y + 1\n        y\n      end\n    RUBY\n  end\n\n  def test_assign_as_implicit_return\n    env = SexpProcessor::Environment.new\n    env[s(:lvar, :y)] = s(:lit, 1)\n\n    assert_returns '2', <<-RUBY, env\n      def x y\n        y = y + 1\n      end\n    RUBY\n  end\n\n  def test_iassgn_as_implicit_return\n    env = SexpProcessor::Environment.new\n    env[Sexp.new(:ivar, :@y)] = Sexp.new(:lit, 2)\n\n    assert_returns '1', <<-RUBY, env\n      def x\n        @y = 1\n      end\n    RUBY\n\n    assert_equal env[Sexp.new(:ivar, :@y)], Sexp.new(:lit, 1)\n  end\n\n  def test_local_aliasing\n    assert_returns \"'a'\", <<-RUBY\n      def x\n        y = 1\n        blah_blah\n        y = 2\n        blah_blah\n        y = 'a'\n        blah_blah\n        y\n      end\n    RUBY\n  end\n\n  def test_ivar_aliasing\n    env = SexpProcessor::Environment.new\n    env[s(:ivar, :@y)] = s(:lit, 1)\n\n    assert_returns \"'a'\", <<-RUBY, env\n      def x\n        @y = 1\n        blah_blah\n        @y = 'a'\n        blah_blah\n        z = @y\n        blah_blah\n        z\n      end\n    RUBY\n  end\n\n  def test_or_asgn_value\n    assert_returns \"1\", <<-RUBY\n      def x\n        @x ||= 1\n      end\n    RUBY\n  end\n\n  def test_return_value_value\n    assert_returns \"1\", <<-RUBY\n      def x\n        return (@y = 1)\n      end\n    RUBY\n  end\n\n  def test_return_value_attrasgn\n    assert_returns \"1\", <<-RUBY\n      def x\n        return (y[:x] = 1)\n      end\n    RUBY\n  end\n\n  def test_return_begin_value\n    assert_returns '(blah1 or blah2) or blah4', <<-RUBY\n      def x\n        return nil if stuff\n        begin\n          doing_some_stuff\n          blah1\n        rescue Thing\n          blah2\n        rescue That\n          blah3\n          blah4\n        rescue Stuff\n          nil\n        rescue\n        end\n      end\n    RUBY\n  end\n\n  def test_empty_if_expression\n    assert_returns \"nil\", <<-RUBY\n    def x\n      if y\n      end\n    end\n    RUBY\n  end\nend\n"
  },
  {
    "path": "test/tests/github_output.rb",
    "content": "require_relative '../test'\n\nclass TestGithubOutput < Minitest::Test\n  def setup\n    @@report ||= github_report\n  end\n\n  def test_report_format\n    assert_equal 43, @@report.lines.count, \"Did you add or remove vulnerabilities in the Rails 6 app? Update this test please!\"\n    @@report.lines.each do |line|\n      assert line.start_with?('::'), 'Every line must start with `::`'\n      assert_equal 2, line.scan('::').count, 'Every line must have exactly 2 `::`'\n    end\n  end\n\n  def test_for_errors\n    assert_equal 2, @@report.lines.count {|line| line.start_with?('::error') }\n    assert_includes @@report, 'file=app/services/balance.rb,line=4'\n  end\n\n  private\n\n  def github_report\n    tracker = Brakeman.run(\"#{TEST_PATH}/apps/rails6\")\n    tracker.error Racc::ParseError.new('app/services/balance.rb:4 :: parse error on value \"...\" (tDOT3)')\n    tracker.error StandardError.new('Something went wrong')\n    tracker.report.to_github\n  end\nend\n"
  },
  {
    "path": "test/tests/ignore.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/report/ignore/config'\nrequire 'tempfile'\n\nclass IgnoreConfigTests < Minitest::Test\n  attr_reader :config\n\n  def setup\n    @config_json = JSON.parse(IGNORE_JSON, symbolize_names: true)\n\n    @config_file = Tempfile.new(\"brakeman.ignore\")\n    @config_file.write IGNORE_JSON\n    @config_file.close\n\n    @config = make_config\n  end\n\n  def make_config file = @config_file.path\n    c = Brakeman::IgnoreConfig.new file, report.warnings\n    c.read_from_file\n    c.filter_ignored\n    c\n  end\n\n  def teardown\n    @config_file.unlink\n  end\n\n  def report\n    @@report ||= Brakeman.run(File.join(TEST_PATH, \"apps\", \"rails5.2\"))\n  end\n\n  def test_sanity\n    assert_equal @config_json[:ignored_warnings].length, config.ignored_warnings.length\n  end\n\n  def test_ignored_warnings\n    assert_equal 3, config.ignored_warnings.length\n  end\n\n  def test_shown_warnings\n    expected = report.warnings.length - config.ignored_warnings.length\n\n    assert_equal expected, config.shown_warnings.length\n  end\n\n  def test_unignore_warning\n    original_ignored = config.ignored_warnings.dup\n    original_shown = config.shown_warnings.dup\n\n    first_ignored = config.ignored_warnings.first\n\n    config.unignore first_ignored\n    config.filter_ignored\n\n    refute config.ignored? first_ignored\n\n    assert config.ignored_warnings.length < original_ignored.length\n    assert config.shown_warnings.length > original_shown.length\n\n    refute_includes config.ignored_warnings, first_ignored\n    assert_includes config.shown_warnings, first_ignored\n  end\n\n  def test_ignore_warning\n    original_ignored = config.ignored_warnings.dup\n    original_shown = config.shown_warnings.dup\n\n    first_warning = config.shown_warnings.first\n\n    config.ignore first_warning\n    config.filter_ignored\n\n    assert config.ignored? first_warning\n\n    assert config.ignored_warnings.length > original_ignored.length\n    assert config.shown_warnings.length < original_shown.length\n\n    assert_includes config.ignored_warnings, first_warning\n    refute_includes config.shown_warnings, first_warning\n  end\n\n  def test_add_note\n    warning = config.ignored_warnings.first\n    note = \"Here is an updated note for an ignored warning\"\n\n    config.add_note warning, note\n    config.save_with_old\n\n    new_config = make_config\n\n    assert note, new_config.note_for(warning)\n  end\n\n  def test_note_for_warning\n    warning = config.ignored_warnings.find { |w| w.fingerprint == \"97ecaa5677c8eadaed09217a704e59092921fab24cc751e05dfb7b167beda2cf\" }\n\n    note = config.note_for warning\n\n    refute note.empty?\n  end\n\n  def test_note_for_hash\n    warning =  { fingerprint: \"97ecaa5677c8eadaed09217a704e59092921fab24cc751e05dfb7b167beda2cf\" }\n\n    note = config.note_for warning\n\n    refute note.empty?\n  end\n\n  def test_empty_note\n    warning =  { fingerprint: \"3bc375c9cb79d8bcd9e7f1c09a574fa3deeab17f924cf20455cbd4c15e9c66eb\" }\n\n    note = config.note_for warning\n\n    assert_equal \"\", note\n  end\n\n  def test_note_missing_for_warning\n    warning = config.shown_warnings.first\n\n    note = config.note_for warning\n\n    assert_nil note\n  end\n\n  def test_note_missing_for_hash\n    warning =  { fingerprint: \"not_real\" }\n\n    note = config.note_for warning\n\n    assert_nil note\n  end\n\n  def test_obsolete\n    first_ignored = config.ignored_warnings.first\n    known_warnings = config.instance_variable_get(:@new_warnings)\n    known_warnings.delete first_ignored\n\n    config.filter_ignored\n\n    assert_equal 1, config.obsolete_fingerprints.length\n  end\n\n  def test_prune_obsolete\n    first_ignored = config.ignored_warnings.first\n    known_warnings = config.instance_variable_get(:@new_warnings)\n    known_warnings.delete first_ignored\n\n    config.filter_ignored\n    assert_includes config.obsolete_fingerprints, first_ignored.fingerprint\n\n    config.prune_obsolete\n    refute_includes config.ignored_warnings, first_ignored\n\n    config.save_with_old\n    new_config = make_config\n\n    refute_includes new_config.ignored_warnings, first_ignored\n  end\n\n  def test_read_from_nonexistent_file\n    make_config(\"/tmp/not_a_real_file_brakeman.ignore\")\n  end\n\n  def test_save_new_ignored\n    first_ignored = config.ignored_warnings.first\n    known_warnings = config.instance_variable_get(:@new_warnings)\n    known_warnings.delete first_ignored\n\n    config.filter_ignored\n    config.save_with_old\n\n    new_config = make_config\n\n    assert new_config.ignored? first_ignored\n  end\n\n  def test_bad_ignore_json_error_message\n    file = Tempfile.new(\"brakeman.ignore2\")\n    file.write \"{[ This is bad json cuz I don't have a closing square bracket, bwahahaha...}\"\n    file.close\n    begin\n      c = Brakeman::IgnoreConfig.new file.path, report.warnings\n      c.read_from_file\n    rescue => e\n      # The message should clearly show that there was a problem parsing the json\n      assert e.message.index(\"JSON::ParserError\") > 0\n      # The message should clearly reference the file containing the bad json\n      assert e.message.index(file.path) > 0\n    end\n  end\n\n  def test_relative_paths_everywhere\n    require 'pathname'\n\n    config.shown_warnings.each do |w|\n      config.ignore w\n    end\n\n    config.filter_ignored\n    config.save_with_old\n\n    JSON.parse(File.read(config.file), symbolize_names: true)[:ignored_warnings].each do |w|\n      assert_relative w[:file]\n\n      if w[:render_path]\n        w[:render_path].each do |loc|\n          assert_relative loc[:file]\n\n          if loc[:rendered]\n            assert_relative loc[:rendered][:file]\n          end\n        end\n      end\n    end\n  end\n\n  def test_already_ignored_entries_with_empty_notes\n    require 'set'\n    assert_equal(\n      config.already_ignored_entries_with_empty_notes.map { |i| i[:fingerprint] }.to_set,\n      [\n        '3bc375c9cb79d8bcd9e7f1c09a574fa3deeab17f924cf20455cbd4c15e9c66eb',\n        '006ac5fe3834bf2e73ee51b67eb111066f618be46e391d493c541ea2a906a82f',\n      ].to_set\n    )\n  end\n\n  private\n\n  def assert_relative path\n    assert Pathname.new(path).relative?, \"#{path} is not relative\"\n  end\nend\n\nIGNORE_JSON = <<JSON\n{\n  \"ignored_warnings\": [\n    {\n      \"warning_type\": \"Remote Code Execution\",\n      \"warning_code\": 25,\n      \"fingerprint\": \"006ac5fe3834bf2e73ee51b67eb111066f618be46e391d493c541ea2a906a82f\",\n      \"check_name\": \"Deserialize\",\n      \"message\": \"`Oj.load` called with parameter value\",\n      \"file\": \"app/controllers/users_controller.rb\",\n      \"line\": 52,\n      \"link\": \"https://brakemanscanner.org/docs/warning_types/unsafe_deserialization\",\n      \"code\": \"Oj.load(params[:json], :mode => :object)\",\n      \"render_path\": null,\n      \"location\": {\n        \"type\": \"method\",\n        \"class\": \"UsersController\",\n        \"method\": \"some_api\"\n      },\n      \"user_input\": \"params[:json]\",\n      \"confidence\": \"High\",\n      \"note\": \"\"\n    },\n    {\n      \"warning_type\": \"Remote Code Execution\",\n      \"warning_code\": 25,\n      \"fingerprint\": \"3bc375c9cb79d8bcd9e7f1c09a574fa3deeab17f924cf20455cbd4c15e9c66eb\",\n      \"check_name\": \"Deserialize\",\n      \"message\": \"`Oj.object_load` called with parameter value\",\n      \"file\": \"app/controllers/users_controller.rb\",\n      \"line\": 53,\n      \"link\": \"https://brakemanscanner.org/docs/warning_types/unsafe_deserialization\",\n      \"code\": \"Oj.object_load(params[:json], :mode => :strict)\",\n      \"render_path\": null,\n      \"location\": {\n        \"type\": \"method\",\n        \"class\": \"UsersController\",\n        \"method\": \"some_api\"\n      },\n      \"user_input\": \"params[:json]\",\n      \"confidence\": \"High\",\n      \"note\": \"\"\n    },\n    {\n      \"warning_type\": \"Remote Code Execution\",\n      \"warning_code\": 25,\n      \"fingerprint\": \"97ecaa5677c8eadaed09217a704e59092921fab24cc751e05dfb7b167beda2cf\",\n      \"check_name\": \"Deserialize\",\n      \"message\": \"`Oj.load` called with parameter value\",\n      \"file\": \"app/controllers/users_controller.rb\",\n      \"line\": 51,\n      \"link\": \"https://brakemanscanner.org/docs/warning_types/unsafe_deserialization\",\n      \"code\": \"Oj.load(params[:json])\",\n      \"render_path\": null,\n      \"location\": {\n        \"type\": \"method\",\n        \"class\": \"UsersController\",\n        \"method\": \"some_api\"\n      },\n      \"user_input\": \"params[:json]\",\n      \"confidence\": \"High\",\n      \"note\": \"Here's a note!\"\n    }\n  ],\n  \"brakeman_version\": \"4.5.0\"\n}\nJSON\n"
  },
  {
    "path": "test/tests/json_compare.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/rescanner'\nrequire 'json'\n\nclass JSONCompareTests < Minitest::Test\n  include BrakemanTester::RescanTestHelper\n  include BrakemanTester::DiffHelper\n\n  def test_sanity\n    json_report = 'test-report.json'\n    ignored_warnings = [\n      'cd83ecf615b17f849ba28050e7faf1d54f218dfa9435c3f65f47cb378c18cf98',\n      'abcdef01234567890ba28050e7faf1d54f218dfa9435c3f65f47cb378c18cf98'\n    ]\n\n    # Here I go, abusing the rescan functionality again.\n    before_rescan_of ['app/models/account.rb', json_report], 'rails4' do |app_dir|\n      report_file = File.join(app_dir, json_report)\n\n      Brakeman.run(app_path: app_dir,\n                   parallel_checks: false,\n                   output_files: [report_file])\n\n      remove 'app/models/account.rb'\n\n      @diff = Brakeman.compare(app_path: app_dir,\n                               parallel_checks: false,\n                               previous_results_json: report_file)\n    end\n\n    assert_fixed 7\n    assert_new 0\n    assert_equal ignored_warnings, @diff[:obsolete]\n\n    # Man is obsolete!\n    # Our world, obsolete!\n  end\nend\n"
  },
  {
    "path": "test/tests/json_output.rb",
    "content": "require_relative '../test'\nrequire 'json'\n\nclass JSONOutputTests < Minitest::Test\n  def setup\n    @@json ||= JSON.parse(Brakeman.run(\"#{TEST_PATH}/apps/rails3.2\").report.to_json)\n  end\n\n  def test_for_render_path\n    @@json[\"warnings\"].each do |warning|\n      is_right_thing = warning.keys.include?(\"render_path\") && (warning[\"render_path\"].nil? or warning[\"render_path\"].is_a? Array)\n      assert is_right_thing, \"#{warning[\"render_path\"].class} is not right\"\n    end\n  end\n\n  def test_for_render_path_keys\n    controller_keys = %w[type class method line file rendered].sort\n    template_keys = %w[type name line file rendered].sort\n    rendered_keys = %w[name file].sort\n\n    @@json[\"warnings\"].each do |warning|\n      if warning[\"render_path\"]\n        warning[\"render_path\"].each do |rp|\n          case rp[\"type\"]\n          when \"controller\"\n            assert_equal controller_keys, rp.keys.sort\n          when \"template\"\n            assert_equal template_keys, rp.keys.sort\n          else\n            raise \"Unknown render path type: #{rp[\"type\"]}\"\n          end\n\n          if rp[\"rendered\"]\n            assert_equal rendered_keys, rp[\"rendered\"].keys.sort\n          end\n        end\n      end\n    end\n  end\n\n  def test_for_expected_keys\n    assert (@@json.keys - [\"warnings\", \"ignored_warnings\", \"scan_info\", \"errors\", \"obsolete\"]).empty?\n  end\n\n  def test_for_scan_info_keys\n    info_keys = [\"app_path\", \"rails_version\", \"security_warnings\", \"start_time\", \"end_time\", \"duration\",\n                 \"checks_performed\", \"number_of_controllers\", \"number_of_models\", \"number_of_templates\",\n                 \"ruby_version\", \"brakeman_version\"]\n\n    assert (@@json[\"scan_info\"].keys - info_keys).empty?\n  end\n\n  def test_for_expected_warning_keys\n    expected = [\"warning_type\", \"check_name\", \"message\", \"file\", \"link\", \"code\", \"location\",\n      \"render_path\", \"user_input\", \"confidence\", \"line\", \"warning_code\", \"fingerprint\", \"cwe_id\"]\n\n    @@json[\"warnings\"].each do |warning|\n      assert (warning.keys - expected).empty?, \"#{(warning.keys - expected).inspect} did not match expected keys\"\n    end\n  end\n\n  def test_for_errors\n    assert @@json[\"errors\"].is_a? Array\n  end\n\n  def test_for_obsolete\n    json = JSON.parse(Brakeman.run(\"#{TEST_PATH}/apps/rails4\").report.to_json)\n    assert_equal [\"abcdef01234567890ba28050e7faf1d54f218dfa9435c3f65f47cb378c18cf98\"], json[\"obsolete\"]\n  end\n\n  def test_paths\n    assert @@json[\"warnings\"].all? { |w| not w[\"file\"].start_with? \"/\" }\n  end\n\n  def test_template_names_dont_have_renderer\n    assert @@json[\"warnings\"].none? { |warning| warning[\"render_path\"] and warning[\"location\"][\"template\"].include? \"(\" }\n  end\n\n  def test_json_warnings_have_cwes\n    @@json[\"warnings\"].each do |warning|\n      assert warning[\"cwe_id\"]\n      assert_kind_of Array, warning[\"cwe_id\"]\n      refute warning[\"cwe_id\"].empty?\n    end\n  end\nend\n"
  },
  {
    "path": "test/tests/junit_output.rb",
    "content": "require_relative '../test'\nrequire 'rexml/document'\n\nclass JUnitOutputTests < Minitest::Test\n  NO_LINE_REPORT_CHECKS = %w[\n    CheckDefaultRoutes\n    CheckModelAttrAccessible\n  ]\n\n  def setup\n    @@document ||= REXML::Document.new(Brakeman.run(\"#{TEST_PATH}/apps/rails3.2\").report.to_junit)\n  end\n\n  def test_document_structure\n    assert_equal \"testsuites\", @@document.root.name\n    assert @@document.root.elements.count > 0, \"No test suites found\"\n\n    @@document.root.elements.each do |testsuite|\n      assert_equal \"testsuite\", testsuite.name\n      assert testsuite.elements.count > 0, \"No elements in test suite\"\n\n      # Check testcase elements\n      testsuite.elements.each(\"testcase\") do |testcase|\n        assert_equal \"testcase\", testcase.name\n        assert testcase.elements[\"failure\"], \"Missing failure element in testcase\"\n      end\n    end\n  end\n\n  def test_testsuite_attributes\n    @@document.root.elements.each do |testsuite|\n      # Required attributes for testsuite\n      assert testsuite.attributes[\"id\"], \"Missing id attribute\"\n      assert_equal \"brakeman\", testsuite.attributes[\"package\"]\n      assert testsuite.attributes[\"file\"], \"Missing file attribute\"\n      assert testsuite.attributes[\"timestamp\"], \"Missing timestamp attribute\"\n      assert testsuite.attributes[\"tests\"], \"Missing tests attribute\"\n      assert testsuite.attributes[\"failures\"], \"Missing failures attribute\"\n      assert testsuite.attributes[\"errors\"], \"Missing errors attribute\"\n      assert testsuite.attributes[\"time\"], \"Missing time attribute\"\n    end\n  end\n\n  def test_testcase_attributes\n    @@document.root.elements.each do |testsuite|\n      testsuite.elements.each(\"testcase\") do |testcase|\n        # Required attributes for testcase\n        assert testcase.attributes[\"name\"], \"Missing name attribute\"\n        assert testcase.attributes[\"file\"], \"Missing file attribute\"\n        unless NO_LINE_REPORT_CHECKS.any? { |check| testcase.attributes[\"name\"].include?(check) }\n          assert testcase.attributes[\"line\"], \"Missing line attribute: #{testcase.attributes}\"\n        end\n        assert testcase.attributes[\"time\"], \"Missing time attribute\"\n      end\n    end\n  end\n\n  def test_failure_attributes\n    @@document.root.elements.each do |testsuite|\n      testsuite.elements.each(\"testcase\") do |testcase|\n        failure = testcase.elements[\"failure\"]\n        assert failure, \"Missing failure element\"\n\n        # Required attributes for failure\n        assert failure.attributes[\"message\"], \"Missing message attribute\"\n        assert failure.attributes[\"type\"], \"Missing type attribute\"\n        assert failure.text.strip.length > 0, \"Failure text is empty\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/tests/logger.rb",
    "content": "require_relative '../test'\n\nclass LoggerTests < Minitest::Test\n  def test_logger_type\n    assert_kind_of Brakeman::Logger::Debug, Brakeman::Logger.get_logger(debug: true)\n    assert_kind_of Brakeman::Logger::Quiet, Brakeman::Logger.get_logger(quiet: true)\n    assert_kind_of Brakeman::Logger::Plain, Brakeman::Logger.get_logger(report_progress: false)\n    assert_kind_of Brakeman::Logger::Plain, Brakeman::Logger.get_logger({}, StringIO.new)\n    assert_kind_of Brakeman::Logger::Console, Brakeman::Logger.get_logger({}, $stdout)\n  end\n\n  def test_color_options\n    refute Brakeman::Logger.get_logger({}).color?\n\n    refute Brakeman::Logger.get_logger(output_color: false).color?\n\n    # No color unless TTY\n    refute Brakeman::Logger.get_logger({output_color: true}, StringIO.new).color?\n    refute Brakeman::Logger.get_logger({}, StringIO.new).color?\n\n    assert Brakeman::Logger.get_logger({output_color: true}, $stderr).color?\n\n    # :force forces color\n    assert Brakeman::Logger.get_logger({output_color: :force}, StringIO.new).color?\n  end\nend\n"
  },
  {
    "path": "test/tests/markdown_output.rb",
    "content": "require_relative '../test'\n\nclass TestMarkdownOutput < Minitest::Test\n  def setup\n    @@report ||= Brakeman.run(\n      :app_path       => \"#{TEST_PATH}/apps/rails2\",\n      :quiet          => true,\n      :run_all_checks => true\n    ).report.to_markdown\n  end\n\n  def test_reported_warnings\n    assert_equal 176, @@report.lines.to_a.count, \"Did you add vulnerabilities to the Rails 2 app? Update this test please!\"\n  end\nend\n"
  },
  {
    "path": "test/tests/mass_assign_disable.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/rescanner'\n\nclass MassAssignDisableTest < Minitest::Test\n  include BrakemanTester::RescanTestHelper\n\n  def mass_assign_disable content\n    init = \"config/initializers/mass_assign.rb\"\n\n    before_rescan_of init, \"rails2\" do\n      write_file init, content\n    end\n\n    assert_fixed 4\n    assert_new 0\n  end\n\n  def test_disable_mass_assignment_by_send\n    mass_assign_disable \"ActiveRecord::Base.send(:attr_accessible, nil)\"\n  end\n\n  def test_disable_mass_assignment_by_module\n    mass_assign_disable <<-RUBY\n      module ActiveRecord\n        class Base\n          attr_accessible\n        end\n      end\n    RUBY\n  end\n\n  def test_disable_mass_assignment_by_module_and_nil\n    mass_assign_disable <<-RUBY\n      module ActiveRecord\n        class Base\n          attr_accessible nil\n        end\n      end\n    RUBY\n  end\n\n  def test_strong_parameters_in_initializer\n    init = \"config/initializers/mass_assign.rb\"\n    gemfile = \"Gemfile\"\n    config = \"config/environments/production.rb\"\n\n    before_rescan_of [init, gemfile, config], \"rails3.2\" do\n      write_file init, <<-RUBY\n        class ActiveRecord::Base\n          include ActiveModel::ForbiddenAttributesProtection\n        end\n      RUBY\n\n      append gemfile, \"gem 'strong_parameters'\"\n\n      replace config, \"config.active_record.whitelist_attributes = true\",\n        \"config.active_record.whitelist_attributes = false\"\n    end\n\n    #We disable whitelist, but add strong_parameters globally, so\n    #there should be no change.\n    assert_fixed 0\n    assert_new 0\n  end\n\n  def test_protected_attributes_gem_without_whitelist_attributes\n    before_rescan_of \"gems.rb\", \"rails4_with_engines\" do\n      append \"gems.rb\", \"gem 'protected_attributes'\"\n    end\n\n    # I misunderstood this previously - the protected_attributes gem\n    # does not require use of attr_accessible, just allows it.\n    assert_fixed 0\n    assert_new 0\n  end\n\n  def test_protected_attributes_gem_with_whitelist_attributes\n    config = \"config/environments/production.rb\"\n\n    before_rescan_of [\"gems.rb\", config], \"rails4_with_engines\" do\n      append \"gems.rb\", \"gem 'protected_attributes'\"\n\n      replace config, \"config.active_record.whitelist_attributes = false\",\n        \"config.active_record.whitelist_attributes = true\"\n    end\n\n    assert_fixed 0\n    assert_new 0\n  end\n\n  def test_strong_parameters_with_send\n    init = \"config/initializers/mass_assign.rb\"\n    gemfile = \"Gemfile\"\n    config = \"config/environments/production.rb\"\n\n    before_rescan_of [init, gemfile, config], \"rails3.2\" do\n      write_file init, <<-RUBY\n        ActiveRecord::Base.send(:include,  ActiveModel::ForbiddenAttributesProtection)\n      RUBY\n\n      append gemfile, \"gem 'strong_parameters'\"\n\n      replace config, \"config.active_record.whitelist_attributes = true\",\n        \"config.active_record.whitelist_attributes = false\"\n    end\n\n    #We disable whitelist, but add strong_parameters globally, so\n    #there should be no change.\n    assert_fixed 0\n    assert_new 0\n  end\nend\n"
  },
  {
    "path": "test/tests/oj.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/rescanner'\n\nclass OjSettingsTests < Minitest::Test\n  include BrakemanTester::RescanTestHelper\n\n  def setup\n    @oj_config = \"config/initializers/oj.rb\"\n  end\n\n  def test_oj_mimic_json\n    before_rescan_of @oj_config, \"rails5.2\" do\n      replace @oj_config, \"# Oj.mimic_JSON\", \"Oj.mimic_JSON\"\n    end\n\n    assert_fixed 1 # Fix default Oj.load() behavior\n    assert_new 0\n  end\n\n  def test_oj_default_setting\n    before_rescan_of @oj_config, \"rails5.2\" do\n      replace @oj_config, \"# Oj.default_options\", \"Oj.default_options\"\n    end\n\n    assert_fixed 1 # Fix default Oj.load() behavior\n    assert_new 0\n  end\n\n  def test_oj_default_setting_still_unsafe\n    before_rescan_of @oj_config, \"rails5.2\" do\n      append @oj_config, \"Oj.default_options = { whatever: false }\"\n    end\n\n    assert_fixed 0 # Default is still bad, no changes \n    assert_new 0\n  end\nend\n"
  },
  {
    "path": "test/tests/only_files_option.rb",
    "content": "require_relative '../test'\n\nclass OnlyFilesOptionTests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n  parallelize_me!\n\n  def expected\n    @expected ||= {\n      :controller => 8,\n      :model => 0,\n      :template => 1,\n      :generic => 17 }\n\n    if RUBY_PLATFORM == 'java'\n      @expected[:generic] += 1\n    end\n\n    @expected\n  end\n\n  def report\n    @@report ||= BrakemanTester.run_scan \"rails3.2\", \"Rails 3.2\", { :only_files => [\"app/views/users/\", \"exec_controller.rb\", \"/app/models/user/\"], :skip_files => [\"app/views/users/sanitized.html.erb\"] }\n  end\n\n  def test_escaped_params_to_json\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 21,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /show\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_slim_partial_param\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 6,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /_slimmer\\.html\\.slim/\n  end\n\n  def test_command_injection_in_exec_controller\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :class => :\"ExecController\",\n      :line => 5,\n      :message => /^Possible command injection/,\n      :confidence => 0,\n      :file => /exec_controller\\.rb/\n  end\n\n  def test_command_injection_in_user_model_dependency\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :class => :\"User\",\n      :line => 3,\n      :message => /^Possible command injection/,\n      :confidence => 0,\n      :file => /command_dependency\\.rb/\n  end\n\n  # This is the template that is skipped, should be no warning\n  def test_xss_sanitize_css_CVE_2013_1855\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 2,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ has\\ a\\ vulnerability\\ in\\ s/,\n      :confidence => 0,\n      :file => /sanitized\\.html\\.erb/\n  end\n\n  def test_i18n_xss_CVE_2013_4491\n    assert_warning :type => :warning,\n      :warning_code => 63,\n      :fingerprint => \"7ef985c538fd302e9450be3a61b2177c26bbfc6ccad7a598006802b0f5f8d6ae\",\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ has\\ an\\ XSS\\ vulnerability/,\n      :file => /Gemfile\\.lock/,\n      :confidence => 1,\n      :relative_path => /Gemfile/\n  end\n\n  def test_denial_of_service_CVE_2013_6414\n    assert_warning :type => :warning,\n      :warning_code => 64,\n      :fingerprint => \"ee4938ce7bc4aa6f37b3d993d6fed813de6b15e5c1ada41146563207c395b0c5\",\n      :warning_type => \"Denial of Service\",\n      :line => 64,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ has\\ a\\ denial\\ of\\ service\\ /,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_number_to_currency_CVE_2014_0081\n    assert_warning :type => :warning,\n      :warning_code => 73,\n      :fingerprint => \"86f945934ed965a47c30705141157c44ee5c546d044f8de7d573bfab456e97ce\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 64,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ has\\ a\\ vulnerability\\ in\\ n/,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_sql_injection_CVE_2013_6417\n    assert_warning :type => :warning,\n      :warning_code => 69,\n      :fingerprint => \"2f63d663e9f35ba60ef81d56ffc4fbf0660fbc2067e728836176bc18f610f77f\",\n      :warning_type => \"SQL Injection\",\n      :line => 64,\n      :file => /Gemfile.lock/,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2 contains\\ a\\ SQL\\ injection\\ vul/,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_remote_code_execution_CVE_2014_0130\n    assert_warning :type => :warning,\n      :warning_code => 77,\n      :fingerprint => \"93393e44a0232d348e4db62276b18321b4cbc9051b702d43ba2fd3287175283c\",\n      :warning_type => \"Remote Code Execution\",\n      :line => nil,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ with\\ globbing\\ routes\\ is\\ /,\n      :confidence => 0,\n      :relative_path => \"config/routes.rb\",\n      :user_input => nil\n  end\n\n  def test_xml_dos_2015_3227\n    assert_warning :type => :warning,\n      :warning_code => 88,\n      :fingerprint => \"ab42647fbdea61e25c4b794e82a8b315054e2fac4328bb3fd4be6a744889a987\",\n      :warning_type => \"Denial of Service\",\n      :line => 64,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ is\\ vulnerable\\ to\\ denial\\ /,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_denial_of_service_CVE_2015_0751\n    assert_warning :type => :warning,\n      :warning_code => 94,\n      :fingerprint => \"5945a9b096557ee5771c2dd12ea6cbec933b662d169e559f524ba01c44bf2452\",\n      :warning_type => \"Denial of Service\",\n      :line => 64,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ is\\ vulnerable\\ to\\ denial\\ /,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\"\n  end\n\n  def test_cross_site_scripting_CVE_2016_6316\n    assert_warning :type => :warning,\n      :warning_code => 102,\n      :fingerprint => \"1a1b3368951a20d02976c9207e5981df37d1bfa7dbbdb925eecd9013ecfeaa0f\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 64,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ `content_tag`\\ does\\ not\\ esc/,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_path_traversal_sprockets_CVE_2018_3760\n    assert_warning :type => :warning,\n      :warning_code => 108,\n      :fingerprint => \"f22053251239417f0571439b41f7ea8ff49a7e97f4147578f021a568c2c3ba16\",\n      :warning_type => \"Path Traversal\",\n      :line => 87,\n      :message => /^sprockets\\ 2\\.1\\.3\\ has\\ a\\ path\\ traversal\\ vul/,\n      :confidence => 2,\n      :relative_path => \"Gemfile.lock\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_unmaintained_dependency_rails\n    assert_warning check_name: \"EOLRails\",\n      type: :warning,\n      warning_code: 120,\n      fingerprint: \"d84924377155b41e094acae7404ec2e521629d86f97b0ff628e3d1b263f8101c\",\n      warning_type: \"Unmaintained Dependency\",\n      line: 64,\n      message: /^Support\\ for\\ Rails\\ 3\\.2\\.9\\.rc2\\ ended\\ on\\ 201/,\n      confidence: 0,\n      relative_path: \"Gemfile.lock\",\n      code: nil,\n      user_input: nil\n  end\nend\n"
  },
  {
    "path": "test/tests/options.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/options'\n\nclass BrakemanOptionsTest < Minitest::Test\n  EASY_OPTION_INPUTS = {\n    :exit_on_warn           => \"-z\",\n    :exit_on_error          => \"--exit-on-error\",\n    :rails3                 => \"-3\",\n    :rails4                 => \"-4\",\n    :rails5                 => \"-5\",\n    :rails6                 => \"-6\",\n    :rails7                 => \"-7\",\n    :rails8                 => \"-8\",\n    :run_all_checks         => \"-A\",\n    :assume_all_routes      => \"-a\",\n    :escape_html            => \"-e\",\n    :ignore_model_output   => \"--ignore-model-output\",\n    :ignore_attr_protected  => \"--ignore-protected\",\n    :interprocedural        => \"--interprocedural\",\n    :ignore_ifs             => \"--no-branching\",\n    :debug                  => \"-d\",\n    :interactive_ignore     => \"-I\",\n    :report_routes          => \"-m\",\n    :absolute_paths         => \"--absolute-paths\",\n    :list_checks            => \"-k\",\n    :list_optional_checks   => \"--optional-checks\",\n    :show_ignored           => \"--show-ignored\",\n    :show_version           => \"-v\",\n    :show_help              => \"-h\",\n    :force_scan             => \"--force-scan\",\n    :ensure_latest          => \"--ensure-latest\",\n    :allow_check_paths_in_config => \"--allow-check-paths-in-config\",\n    :pager                  => \"--pager\",\n    :show_timing            => \"--timing\",\n  }\n\n  ALT_OPTION_INPUTS = {\n    :exit_on_warn           => \"--exit-on-warn\",\n    :rails3                 => \"--rails3\",\n    :rails4                 => \"--rails4\",\n    :rails5                 => \"--rails5\",\n    :rails6                 => \"--rails6\",\n    :rails7                 => \"--rails7\",\n    :rails8                 => \"--rails8\",\n    :run_all_checks         => \"--run-all-checks\",\n    :escape_html            => \"--escape-html\",\n    :debug                  => \"--debug\",\n    :interactive_ignore     => \"--interactive-ignore\",\n    :report_routes          => \"--routes\",\n    :show_version           => \"--version\",\n    :show_help              => \"--help\"\n  }\n\n  def test_easy_options\n    EASY_OPTION_INPUTS.each_pair do |key, value|\n      options = setup_options_from_input(value)\n      assert options[key], \"Expected #{key} to be #{!!value}.\"\n    end\n  end\n\n  def test_alt_easy_options\n    ALT_OPTION_INPUTS.each_pair do |key, value|\n      options = setup_options_from_input(value)\n      assert options[key], \"Expected #{key} to be #{!!value}.\"\n    end\n  end\n\n  def test_assume_routes_option\n    options = setup_options_from_input(\"-a\")\n    assert options[:assume_all_routes]\n\n    options = setup_options_from_input(\"--assume-routes\")\n    assert options[:assume_all_routes]\n\n    options = setup_options_from_input(\"--no-assume-routes\")\n    assert !options[:assume_all_routes]\n  end\n\n  def test_no_exit_on_warn\n    options = setup_options_from_input(\"--exit-on-warn\")\n    assert options[:exit_on_warn]\n\n    options = setup_options_from_input(\"--no-exit-on-warn\")\n    assert !options[:exit_on_warn]\n  end\n\n  def test_ensure_latest\n    options = setup_options_from_input(\"--ensure-latest\")\n    assert options[:ensure_latest]\n\n    options = setup_options_from_input(\"--ensure-latest\", \"10\")\n    assert_equal 10, options[:ensure_latest]\n\n    assert_raises OptionParser::InvalidArgument do\n      setup_options_from_input(\"--ensure-latest\", \"16\")\n    end\n\n    assert_raises OptionParser::InvalidArgument do\n      setup_options_from_input(\"--ensure-latest\", \"0\")\n    end\n\n    assert_raises OptionParser::InvalidArgument do\n      setup_options_from_input(\"--ensure-latest=-5\")\n    end\n  end\n\n  def test_faster_options\n    options = setup_options_from_input(\"--faster\")\n    assert options[:ignore_ifs]\n  end\n\n  def test_skip_vendor_option\n    options = setup_options_from_input(\"--skip-vendor\")\n    assert options[:skip_vendor]\n\n    options = setup_options_from_input(\"--no-skip-vendor\")\n    assert !options[:skip_vendor]\n  end\n\n  def test_limit_options\n    options = setup_options_from_input(\"--branch-limit\", \"17\")\n    assert_equal 17, options[:branch_limit]\n  end\n\n  def test_no_threads_option\n    options = setup_options_from_input(\"-n\").merge!({\n      :quiet => true,\n      :app_path => \"#{TEST_PATH}/apps/rails4\"})\n\n    assert !options[:parallel_checks]\n  end\n\n  def test_path_option\n    options = setup_options_from_input(\"--path\", \"#{TEST_PATH}/app/rails4\")\n    assert_equal \"#{TEST_PATH}/app/rails4\", options[:app_path]\n\n    options = setup_options_from_input(\"-p\", \"#{TEST_PATH}/app/rails4\")\n    assert_equal \"#{TEST_PATH}/app/rails4\", options[:app_path]\n  end\n\n  def test_progress_option\n    options = setup_options_from_input(\"--progress\")\n    assert options[:report_progress]\n\n    options = setup_options_from_input(\"--no-progress\")\n    assert !options[:report_progress]\n  end\n\n  def test_parser_timeout_option\n    options = setup_options_from_input(\"--parser-timeout\", \"1000\")\n    assert_equal 1000, options[:parser_timeout]\n  end\n\n  def test_quiet_option\n    options = setup_options_from_input(\"-q\")\n    assert options[:quiet]\n\n    options = setup_options_from_input(\"--quiet\")\n    assert options[:quiet]\n\n    options = setup_options_from_input(\"--no-quiet\")\n    assert !options[:quiet]\n  end\n\n  def test_rails_4_option\n    options = setup_options_from_input(\"-4\")\n    assert options[:rails4] && options[:rails3]\n\n    options = setup_options_from_input(\"--rails4\")\n    assert options[:rails4] && options[:rails3]\n  end\n\n  def test_safe_methods_option\n    options = setup_options_from_input(\"--safe-methods\", \"test_method2,test_method1,test_method2\")\n    assert_equal Set[:test_method1, :test_method2], options[:safe_methods]\n\n    options = setup_options_from_input(\"-s\", \"test_method2,test_method1,test_method2\")\n    assert_equal Set[:test_method1, :test_method2], options[:safe_methods]\n  end\n\n  def test__sql_safe_option\n    options = setup_options_from_input(\"--sql-safe-methods\", \"test_method2,test_method1,test_method2\")\n    assert_equal Set[:test_method1, :test_method2], options[:sql_safe_methods]\n  end\n\n  def test__url_safe_option\n    options = setup_options_from_input(\"--url-safe-methods\", \"test_method2,test_method1,test_method2\")\n    assert_equal Set[:test_method1, :test_method2], options[:url_safe_methods]\n  end\n\n  def test__skip_file_option\n    options = setup_options_from_input(\"--skip-files\", \"file1.rb,file2.rb,file3.js,file2.rb\")\n    assert_equal Set[\"file1.rb\", \"file2.rb\", \"file3.js\"], options[:skip_files]\n  end\n\n  def test_only_files_option\n    options = setup_options_from_input(\"--only-files\", \"file1.rb,file2.rb,file3.js,file2.rb\")\n    assert_equal Set[\"file1.rb\", \"file2.rb\", \"file3.js\"], options[:only_files]\n  end\n\n  def test_add_lib_paths_option\n    options = setup_options_from_input(\"--add-libs-path\", \"../tests/,/badStuff/hackable,/etc/junk,../tests/\")\n    assert_equal Set[\"../tests/\", \"/badStuff/hackable\", \"/etc/junk\"], options[:additional_libs_path]\n  end\n\n  def test_run_checks_option\n    options = setup_options_from_input(\"-t\", \"CheckSelectTag,CheckSelectVulnerability,CheckSend,CheckSelectTag,I18nXSS\")\n    assert_equal Set[\"CheckSelectTag\", \"CheckSelectVulnerability\", \"CheckSend\", \"CheckI18nXSS\"], options[:run_checks]\n\n    options = setup_options_from_input(\"--test\", \"CheckSelectTag,CheckSelectVulnerability,CheckSend,CheckSelectTag,I18nXSS\")\n    assert_equal Set[\"CheckSelectTag\", \"CheckSelectVulnerability\", \"CheckSend\", \"CheckI18nXSS\"], options[:run_checks]\n  end\n\n  def test_skip_checks_option\n    options = setup_options_from_input(\"-x\", \"CheckSelectTag,CheckSelectVulnerability,CheckSend,CheckSelectTag,I18nXSS\")\n    assert_equal Set[\"CheckSelectTag\", \"CheckSelectVulnerability\", \"CheckSend\", \"CheckI18nXSS\"], options[:skip_checks]\n\n    options = setup_options_from_input(\"--except\", \"CheckSelectTag,CheckSelectVulnerability,CheckSend,CheckSelectTag,I18nXSS\")\n    assert_equal Set[\"CheckSelectTag\", \"CheckSelectVulnerability\", \"CheckSend\", \"CheckI18nXSS\"], options[:skip_checks]\n  end\n\n  def test_add_checks_paths_option\n    options = setup_options_from_input(\"--add-checks-path\", \"../addl_tests/\")\n    local_path = File.expand_path('../addl_tests/')\n    assert_equal Set[\"#{local_path}\"], options[:additional_checks_path]\n  end\n\n  def test_format_options\n    format_options = {\n      pdf: :to_pdf,\n      text: :to_s,\n      html: :to_html,\n      csv: :to_csv,\n      tabs: :to_tabs,\n      json: :to_json,\n      markdown: :to_markdown,\n      codeclimate: :to_codeclimate,\n      cc: :to_cc,\n      plain: :to_plain\n    }\n\n    format_options.each_pair do |key, value|\n      options = setup_options_from_input(\"-f\", \"#{key}\")\n      assert_equal value, options[:output_format]\n    end\n\n    format_options.each_pair do |key, value|\n      options = setup_options_from_input(\"--format\", \"#{key}\")\n      assert_equal value, options[:output_format]\n    end\n  end\n\n  def test_CSS_file_option\n    options = setup_options_from_input(\"--css-file\", \"../test.css\")\n    local_path = File.expand_path('../test.css')\n    assert_equal local_path, options[:html_style]\n  end\n\n  def test_ignore_file_option\n    options = setup_options_from_input(\"-i\", \"dont_warn_for_these.rb\")\n    assert_equal \"dont_warn_for_these.rb\", options[:ignore_file]\n\n    options = setup_options_from_input(\"--ignore-config\", \"dont_warn_for_these.rb\")\n    assert_equal \"dont_warn_for_these.rb\", options[:ignore_file]\n  end\n\n  def test_show_ignored_option\n    options = setup_options_from_input(\"--show-ignored\")\n    assert options[:show_ignored]\n  end\n\n  def test_combine_warnings_option\n    options = setup_options_from_input(\"--combine-locations\")\n    assert options[:combine_locations]\n\n    options = setup_options_from_input(\"--no-combine-locations\")\n    assert !options[:combine_locations]\n  end\n\n  def test_report_direct_option\n    options = setup_options_from_input(\"-r\")\n    assert !options[:check_arguments]\n\n    options = setup_options_from_input(\"--report-direct\")\n    assert !options[:check_arguments]\n  end\n\n  def test_highlight_option\n    options = setup_options_from_input(\"--highlights\")\n    assert options[:highlight_user_input]\n\n    options = setup_options_from_input(\"--no-highlights\")\n    assert !options[:highlight_user_input]\n  end\n\n  def test_message_length_limit_option\n    options = setup_options_from_input(\"--message-limit\", \"17\")\n    assert_equal 17, options[:message_limit]\n  end\n\n  def test_table_width_option\n    options = setup_options_from_input(\"--table-width\", \"1717\")\n    assert_equal 1717, options[:table_width]\n  end\n\n  def test_output_file_options\n    options = setup_options_from_input(\"-o\", \"output.rb\")\n    assert_equal [\"output.rb\"], options[:output_files]\n\n    options = setup_options_from_input(\"--output\", \"output1.rb,output2.rb\")\n    assert_equal [\"output1.rb,output2.rb\"], options[:output_files]\n  end\n\n  def test_output_color_option\n    options = setup_options_from_input(\"--color\")\n    assert_equal :force, options[:output_color]\n\n    options = setup_options_from_input(\"--no-color\")\n    assert_equal false, options[:output_color]\n  end\n\n  def test_sperate_models_option\n    options = setup_options_from_input(\"--separate-models\")\n    assert !options[:collapse_mass_assignment]\n\n    options = setup_options_from_input(\"--no-separate-models\")\n    assert options[:collapse_mass_assignment]\n  end\n\n  def test_github_repo_option\n    options = setup_options_from_input(\"--github-repo\", \"presidentbeef/brakeman\")\n    assert_equal \"presidentbeef/brakeman\", options[:github_repo]\n  end\n\n  def test_min_confidence_option\n    options = setup_options_from_input(\"-w\", \"2\")\n    assert_equal 1, options[:min_confidence]\n\n    options = setup_options_from_input(\"--confidence\", \"1\")\n    assert_equal 2, options[:min_confidence]\n  end\n\n  def test_compare_file_options\n    options = setup_options_from_input(\"--compare\", \"past_flunks.json\")\n    compare_file = File.expand_path(\"past_flunks.json\")\n    assert_equal compare_file, options[:previous_results_json]\n  end\n\n  def test_compare_file_and_output_options\n    options = setup_options_from_input(\"-o\", \"output.json\", \"--compare\", \"output.json\")\n    assert_equal \"output.json\", options[:comparison_output_file]\n  end\n\n  def test_config_file_options\n    options = setup_options_from_input(\"--config-file\", \"config.rb\")\n    config_file = File.expand_path(\"config.rb\")\n    assert_equal config_file, options[:config_file]\n\n    options = setup_options_from_input(\"-c\", \"config.rb\")\n    assert_equal config_file, options[:config_file]\n  end\n\n  def test_create_config_file_options\n    options = setup_options_from_input(\"--create-config\", \"config.rb\")\n    assert_equal \"config.rb\", options[:create_config]\n\n    options = setup_options_from_input(\"-C\")\n    assert options[:create_config]\n  end\n\n  def test_summary_options\n    options = setup_options_from_input(\"--summary\")\n\n    assert_equal :summary_only, options[:summary_only]\n\n    options = setup_options_from_input(\"--no-summary\")\n    assert_equal :no_summary, options[:summary_only]\n  end\n\n  def test_text_report_fields\n    assert_raises OptionParser::ParseError do\n      setup_options_from_input(\"--text-fields\", \"not_a_real_field\")\n    end\n  end\n\n  def test_use_prism\n    begin\n      # If prism is installed, test that everything is fine\n\n      gem('prism', '>=1.0')\n      options = setup_options_from_input('--prism')\n      assert options[:use_prism]\n    rescue Gem::MissingSpecVersionError, Gem::MissingSpecError, Gem::LoadError\n      # Otherwise, test the error message and exception\n\n      assert_output nil, /Please install `prism`/ do\n        assert_raises Gem::MissingSpecVersionError, Gem::MissingSpecError, Gem::LoadError do\n          setup_options_from_input('--prism')\n        end\n      end\n    end\n  end\n\n  def test_follow_symlinks\n    options = setup_options_from_input(\"--follow-symlinks\")\n    assert options[:follow_symlinks]\n\n    options = setup_options_from_input(\"--no-follow-symlinks\")\n    refute options[:follow_symlinks]\n  end\n\n  def test_set_gemfile\n    options = setup_options_from_input(\"--gemfile\", \"Gemfile.mine\")\n    assert_equal \"Gemfile.mine\", options[:gemfile]\n  end\n\n  def test_gemfile_environment\n    ENV['BUNDLE_GEMFILE'] = 'test_file'\n\n    options = { app_path: '.' }\n    options = Brakeman.set_options(options)\n\n    assert_equal ENV['BUNDLE_GEMFILE'], options[:gemfile]\n  ensure\n    ENV.delete 'BUNDLE_GEMFILE'\n  end\n\n  def test_empty_gemfile_environment\n    ENV['BUNDLE_GEMFILE'] = ''\n\n    options = { app_path: '.' }\n    options = Brakeman.set_options(options)\n\n    assert_nil options[:gemfile]\n  ensure\n    ENV.delete 'BUNDLE_GEMFILE'\n  end\n\n  private\n\n  def setup_options_from_input(*args)\n    options, _ = Brakeman::Options.parse(args)\n    options\n  end\nend\n"
  },
  {
    "path": "test/tests/output_processor.rb",
    "content": "require_relative '../test'\n\nclass OutputProcessorTests < Minitest::Test\n  def assert_output expected, original\n    output = Brakeman::OutputProcessor.new.format original\n\n    assert_equal expected, output\n  end\n\n  def test_output_nil\n    assert_output \"[Format Error]\", nil\n  end\n\n  def test_output_empty_sexp\n    assert_output \"[Format Error]\", Sexp.new\n  end\n\n  def test_output_missing_node_type\n    assert_output \"[Format Error]\", Sexp.new(Sexp.new(:str, 'x'))\n  end\n\n  def test_output_bad_node_type\n    assert_output \"[Format Error]\", Sexp.new(:bad_node_type)\n  end\n\n  def test_output_local_variable\n    assert_output \"x\", Sexp.new(:lvar, :x)\n  end\n\n  def test_output_ignore\n    assert_output \"[ignored]\", Sexp.new(:ignore, :whatever)\n  end\n\n  def test_output_params\n    assert_output \"params\", Sexp.new(:params, :anything)\n  end\n\n  def test_output_session\n    assert_output \"session\", Sexp.new(:session)\n  end\n\n  def test_output_cookies\n    assert_output \"cookies[:yum]\", Sexp.new(:call,\n                                            Sexp.new(:cookies),\n                                            :[],\n                                            Sexp.new(:arglist,\n                                                     Sexp.new(:lit, :yum)))\n  end\n\n  def test_output_output\n    assert_output \"[Output] x\", Sexp.new(:output,\n                                         Sexp.new(:lvar, :x))\n  end\n\n  def test_output_output_format\n    assert_output \"\", Sexp.new(:output,\n                               Sexp.new(:format, Sexp.new(:str, 'bye')))\n  end\n\n  def test_output_escaped_output\n    assert_output '[Escaped Output] @x', Sexp.new(:escaped_output,\n                                                   Sexp.new(:ivar, :@x))\n  end\n\n  def test_output_string_output\n    assert_output '', Sexp.new(:output, Sexp.new(:str, 'x'))\n    assert_output '', Sexp.new(:escaped_output, Sexp.new(:str, 'x'))\n  end\n\n  def test_output_format_string_literal\n    assert_output \"\", Sexp.new(:output,\n                               Sexp.new(:format, Sexp.new(:str, 'hi')))\n\n  end\n\n  def test_output_escaped_format_string_literal\n    assert_output \"\", Sexp.new(:escaped_output,\n                               Sexp.new(:format, Sexp.new(:str, 'hi')))\n\n  end\n\n\n  def test_output_string_interp\n    assert_output '\"#{@x}\"', Sexp.new(:dstr,\n                                      \"\",\n                                      Sexp.new(:evstr,\n                                               Sexp.new(:ivar, :@x)))\n\n    input = '\"#{params[:plugin]}/app/views/#{params[:view]}\"'\n    s_input = RubyParser.new.parse(input)\n\n    assert_output input,\n      Brakeman::BaseProcessor.new(nil).process(s_input)\n  end\n\n  def test_output_format\n    assert_output \"[Format] @x\", Sexp.new(:format, Sexp.new(:ivar, :@x))\n  end\n\n  def test_output_format_escaped\n    assert_output \"[Escaped] @x\", Sexp.new(:format_escaped,\n                                            Sexp.new(:ivar, :@x))\n  end\n\n  def test_output_format_escaped_string_literal\n    assert_output \"\", Sexp.new(:format_escaped, Sexp.new(:str, \"hi\"))\n  end\n\n  def test_output_format_escaped_with_escaped_literal\n    assert_output \"\", Sexp.new(:format_escaped,\n                               Sexp.new(:escaped_output, Sexp.new(:str, 'hi')))\n  end\n\n\n  def test_format_string_literal\n    assert_output \"\", Sexp.new(:format, Sexp.new(:str, \"hi\"))\n  end\n\n  def test_output_format_escaped_literal\n    assert_output \"\", Sexp.new(:format,\n                               Sexp.new(:escaped_output, Sexp.new(:str, 'hi')))\n  end\n\n  def test_output_unknown_model\n    assert_output \"(Unresolved Model)\", Sexp.new(:const,\n                                                 Brakeman::Tracker::UNKNOWN_MODEL)\n  end\n\n  def test_output_render\n    assert_output 'render(partial => \"x/y\", { :locals => ({ :user => (@user) }) })',\n      Sexp.new(:render,\n               :partial,\n               Sexp.new(:str, \"x/y\"),\n               Sexp.new(:hash, \n                        Sexp.new(:lit, :locals),\n                        Sexp.new(:hash,\n                                 Sexp.new(:lit, :user),\n                                 Sexp.new(:ivar, :@user))))\n  end\n\n  def test_output_rlist\n    assert_output \"a\\nb\",\n      Sexp.new(:rlist,\n               Sexp.new(:call, nil, :a, Sexp.new(:arglist)),\n               Sexp.new(:call, nil, :b, Sexp.new(:arglist)))\n  end\n\n  def test_output_call_with_block\n    assert_output \"x do\\n y\\n end\",\n      Sexp.new(:iter,\n               Sexp.new(:call, nil, :x),\n               Sexp.new(:args),\n               Sexp.new(:call, nil, :y))\n  end\n\n  # Ruby2Ruby tries to convert some methods to attr_* calls,\n  # but it breaks some stuff because of how it accesses nodes.\n  # So we overwrite it.\n  def test_output_defn_not_attr\n    assert_output \"def x\\n  @x\\nend\",\n      Sexp.new(:defn,\n               :x,\n               Sexp.new(:args),\n               Sexp.new(:ivar, :@x))\n\n    assert_output \"def x(y)\\n  @x = y\\nend\",\n      Sexp.new(:defn,\n               :x,\n               Sexp.new(:args, :y),\n               Sexp.new(:iasgn, :@x, Sexp.new(:lvar, :y)))\n  end\n\n  def test_regexp_output_with_flags\n    assert_output '/#{x}/i',\n      s(:dregx, \"\",\n        s(:evstr,\n          s(:call, nil, :x)),\n          1)\n  end\n\n  def test_rescue_block\n    assert_output \"a rescue b\",\n      s(:rescue, s(:call, nil, :a),\n        s(:resbody, s(:array), s(:call, nil, :b)))\n  end\n\n  def test_command_interpolation\n    assert_output '`#{x}`',\n      s(:dxstr, \"\", s(:evstr, s(:call, nil, :x)))\n\n\n    input = Brakeman::BaseProcessor.new(nil).process(RubyParser.new.parse('`1#{x}2#{y}3`'))\n    assert_output '`1#{x}2#{y}3`', input\n  end\nend\n"
  },
  {
    "path": "test/tests/pager.rb",
    "content": "require_relative '../test'\nrequire \"brakeman/report/pager\"\n\nclass ReportPagerTests < Minitest::Test\n  def setup\n    @@text ||= \"Here is some text for your tests\\n\" * 100\n  end\n\n  def test_no_pager\n    out = StringIO.new\n    pager = Brakeman::Pager.new(nil, :none, out)\n\n    pager.page_output(@@text)\n\n    assert_equal @@text, out.string\n  end\n\n  def test_unknown_pager\n    out = StringIO.new\n    pager = Brakeman::Pager.new(nil, :unknown, out)\n\n    pager.page_output(@@text)\n\n    assert_equal @@text, out.string\n  end\n\n  def test_less_sort_of\n    out = StringIO.new\n    pager = Brakeman::Pager.new(nil, :less, out)\n\n    pager.page_output(\".\")\n  end\n\n  def test_highline\n    require 'highline/io_console_compatible' # For StringIO compatibility\n    out = StringIO.new\n    $stdin = StringIO.new(\"\\r\\r\\r\\r\\r\\r\\r\")\n    pager = Brakeman::Pager.new(nil, :highline, out)\n\n    pager.page_output(@@text)\n\n    assert out.string.include? \"Here is some text\"\n    assert out.string.include? \"press enter/return to continue or q to stop\"\n  ensure\n    $stdin = STDIN\n  end\n\n  def test_in_ci_test\n    pager = Brakeman::Pager.new(BrakemanTester.new_tracker)\n\n    if ENV[\"CI\"]\n      assert pager.in_ci?\n    else\n      refute pager.in_ci?\n    end\n  end\n\n  def test_set_color_force\n    t = BrakemanTester.new_tracker\n    t.options[:output_color] = :force \n    pager = Brakeman::Pager.new(t)\n    pager.set_color\n\n    assert t.options[:output_color]\n  end\n\n  def test_pager_output_report\n    $stdout = StringIO.new\n    app_path = File.expand_path \"#{TEST_PATH}/apps/rails5\"\n    tracker = Brakeman.run app_path: app_path, run_checks: [], quiet: true, summary_only: :no_summary\n    pager = Brakeman::Pager.new(tracker)\n\n    pager.page_report(tracker.report, :to_text)\n  ensure\n    $stdout = STDOUT\n  end\nend\n"
  },
  {
    "path": "test/tests/parser_timeout.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/rescanner'\n\nclass ParserTimeoutTests < Minitest::Test\n  include BrakemanTester::RescanTestHelper\n\n  def test_timeout\n    skip 'Too hard to get this to consistently pass'\n\n    before_rescan_of \"lib/large_file.rb\", \"rails5.2\", { parser_timeout: 0.5 } do\n      random_ruby = Array.new(10000) { \"def x_#{rand(1000)}\\nputs '#{\"**\" * 1000}'\\nend\" }.join(\"\\n\")\n      write_file \"lib/large_file.rb\", random_ruby\n    end\n\n    assert_equal 1, @rescanner.tracker.errors.length\n\n    timeout_error = @rescanner.tracker.errors.first\n    assert_match(/Parsing .* took too long \\(> 0.5 seconds\\)/, timeout_error[:error])\n  end\nend\n"
  },
  {
    "path": "test/tests/rails2.rb",
    "content": "# NOTE: Please do not add any further tests to the Rails 2 application unless\n# the issue being tested specifically applies to Rails 2 and not the other\n# versions.\n# If possible, please use the rails5 app.\n\nrequire_relative '../test'\n\nclass Rails2Tests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def expected\n    @expected ||= {\n      :controller => 1,\n      :model => 4,\n      :template => 47,\n      :generic => 61 }\n  end\n\n  def report\n    @@report ||= BrakemanTester.run_scan \"rails2\", \"Rails 2\", :run_all_checks => true, :collapse_mass_assignment => true\n  end\n\n  def test_no_errors\n    assert_equal 0, report[:errors].length\n  end\n\n  def test_config_sanity\n    assert_equal 'UTC', report[:config].rails[:time_zone].value\n  end\n\n  def test_eval\n    assert_warning :warning_type => \"Dangerous Eval\",\n      :line => 40,\n      :message => /^Parameter\\ value\\ evaluated\\ as\\ code/,\n      :format_code => /eval\\(params\\[:dangerous_input\\]\\)/,\n      :file => /home_controller.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_default_routes\n    assert_warning :warning_type => \"Default Routes\",\n      :line => 54,\n      :message => /All public methods in controllers are available as actions/,\n      :file => /routes\\.rb/,\n      :relative_path => \"config/routes.rb\"\n  end\n\n  def test_command_injection_interpolate\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :line => 34,\n      :message => /^Possible command injection/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_command_injection_direct\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :line => 36,\n      :message => /^Possible command injection/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\",\n      :format_code => /params\\[:user_input\\]/\n  end\n\n  def test_file_access_concatenation\n    assert_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 24,\n      :message => /^Parameter value used in file name/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_mass_assignment\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 54,\n      :message => /^Unprotected mass assignment/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_update_attribute_no_mass_assignment\n    assert_no_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 26,\n      :message => /^Unprotected mass assignment/,\n      :confidence => 0,\n      :file => /other_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_mass_assignment_with_or_equals_in_filter\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 127,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_redirect\n    assert_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 45,\n      :message => /^Possible unprotected redirect/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n\n    assert_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 182,\n      :message => /^Possible unprotected redirect/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_dynamic_render_path\n    assert_warning :type => :warning,\n      :warning_type => \"Dynamic Render Path\",\n      :line => 59,\n      :message => /^Render path contains parameter value near line 59: render/,\n      :confidence => 1,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_dynamic_render_path_high_confidence\n    assert_warning :type => :warning,\n      :warning_code => 99,\n      :fingerprint => \"d77e92530f810b945b9bd04db2e25afab968b4379d08062f7c5a822671a159a6\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 77,\n      :message => /^Passing\\ query\\ parameters\\ to\\ `render` is\\ /,\n      :confidence => 0,\n      :relative_path => \"app/controllers/home_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:params), :[], s(:lit, :my_action)), s(:hash)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :my_action))\n  end\n\n    def test_dynamic_render_path_2\n    assert_warning check_name: \"Render\",\n      type: :warning,\n      warning_code: 15,\n      fingerprint: \"9f381f7bb425b10b410d86c9be069504ca8825022823c15e0a628308fde9d604\",\n      warning_type: \"Dynamic Render Path\",\n      line: 77,\n      message: /^Render\\ path\\ contains\\ parameter\\ value/,\n      confidence: 0,\n      relative_path: \"app/controllers/home_controller.rb\",\n      code: s(:render, :action, s(:call, s(:params), :[], s(:lit, :my_action)), s(:hash)),\n      user_input: s(:call, s(:params), :[], s(:lit, :my_action))\n  end\n\n  def test_dynamic_render_path_3\n    assert_warning check_name: \"Render\",\n      type: :template,\n      warning_code: 15,\n      fingerprint: \"6b78bac3c96c2ebb1dc7d05d02d61f27e95207e566e6e83df5fefd8899afa760\",\n      warning_type: \"Dynamic Render Path\",\n      line: 8,\n      message: /^Render\\ path\\ contains\\ parameter\\ value/,\n      confidence: 1,\n      relative_path: \"app/views/home/test_render.html.erb\",\n      code: s(:render, :file, s(:dstr, \"/tmp/\", s(:evstr, s(:call, s(:call, nil, :params), :[], s(:lit, :file)))), s(:hash)),\n      user_input: s(:call, s(:call, nil, :params), :[], s(:lit, :file))\n  end\n\n  def test_file_access\n    assert_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 21,\n      :message => /^Parameter value used in file name/,\n      :confidence => 0,\n      :file => /other_controller\\.rb/,\n      :relative_path => \"app/controllers/other_controller.rb\"\n  end\n\n  def test_file_access_with_load\n    assert_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 63,\n      :message => /^Parameter value used in file name/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_file_access_load_false\n    warnings = find :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 64,\n      :message => /^Parameter value used in file name/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n\n    assert_equal 0, warnings.length, \"False positive found.\"\n  end\n\n  def test_session_secret\n    assert_warning :type => :warning,\n      :warning_type => \"Session Setting\",\n      :line => 9,\n      :message => /^Session\\ secret\\ should\\ not\\ be\\ included\\ in/,\n      :confidence => 0,\n      :file => /session_store\\.rb/,\n      :relative_path => \"config/initializers/session_store.rb\"\n  end\n\n  def test_session_cookies\n    assert_warning :type => :warning,\n      :warning_type => \"Session Setting\",\n      :line => 10,\n      :message => /^Session cookies should be set to HTTP on/,\n      :confidence => 0,\n      :file => /session_store\\.rb/,\n      :relative_path => \"config/initializers/session_store.rb\"\n  end\n\n  def test_rails_cve_2012_2660\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-2660/,\n      :confidence => 0\n  end\n\n  def test_rails_cve_2012_2695\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-2695/,\n      :confidence => 0\n  end\n\n  def test_sql_injection_find_by_sql\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 28,\n      :message => /^Possible SQL injection/,\n      :confidence => 1,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_sql_injection_conditions_local\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 29,\n      :message => /^Possible SQL injection/,\n      :confidence => 1,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_sql_injection_params\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 30,\n      :message => /^Possible SQL injection/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_sql_injection_named_scope\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 4,\n      :message => /^Possible SQL injection near line 4: named_scope\\(:phooey/,\n      :confidence => 0,\n      :file => /user\\.rb/,\n      :relative_path => \"app/models/user.rb\"\n  end\n\n  def test_sql_injection_named_scope_lambda\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 2,\n      :message => /^Possible SQL injection near line 2: named_scope\\(:dah, lambda/,\n      :confidence => 1,\n      :file => /user\\.rb/,\n      :relative_path => \"app/models/user.rb\"\n  end\n\n  def test_sql_injection_named_scope_conditional\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 6,\n      :message => /^Possible SQL injection near line 6: named_scope\\(:with_state, lambda/,\n      :confidence => 1,\n      :file => /user\\.rb/,\n      :relative_path => \"app/models/user.rb\"\n  end\n\n  def test_sql_injection_in_self_call\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 15,\n      :message => /^Possible SQL injection near line 15: self\\.find/,\n      :confidence => 1,\n      :file => /user\\.rb/,\n      :relative_path => \"app/models/user.rb\"\n  end\n\n  def test_sql_user_input_in_find_by\n    assert_no_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 116,\n      :message => /^Possible SQL injection near line 116: User.find_or_create_by_name/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  # ensure that the warning is generated for the line which contains the input, not\n  # the line of the beginning of the string\n  def test_sql_user_input_multiline\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 121,\n      :message => /^Possible SQL injection near line 121: User.find_by_sql/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_sql_injection_false_positive_quote_value\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"6ea8fe3abe8eac86e5ecb790b53fb064b1152b2574b14d9354a40d07269a952e\",\n      :warning_type => \"SQL Injection\",\n      :line => 30,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:call, s(:call, s(:str, \"DELETE FROM cool_table WHERE cool_id=\"), :+, s(:call, nil, :quote_value, s(:call, s(:self), :cool_id))), :+, s(:str, \"  AND my_id=\"))\n  end\n\n  def test_sql_injection_sanitize_sql\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"7481ff666ae949b8442400cf516615ce8b04b87f7e11e33e29d4ad1303d24dd0\",\n      :warning_type => \"SQL Injection\",\n      :line => 26,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:call, s(:str, \"select * from cool_table where stuff = \"), :+, s(:call, s(:self), :sanitize_sql, s(:lvar, :input)))\n  end\n\n  def test_csrf_protection\n    assert_warning :type => :controller,\n      :warning_type => \"Cross-Site Request Forgery\",\n      :message => /^`protect_from_forgery` should be called /,\n      :confidence => 0,\n      :file => /application_controller\\.rb/,\n      :relative_path => \"app/controllers/application_controller.rb\"\n  end\n\n  def test_attribute_restriction_1\n    assert_warning :type => :model,\n      :warning_code => 19,\n      :fingerprint => \"91d73b1b9d6920156b920729c0146292eb9f10f4ba9515740442dbe82d4dee78\",\n      :warning_type => \"Attribute Restriction\",\n      :line => 1,\n      :message => /^Mass\\ assignment\\ is\\ not\\ restricted\\ using\\ /,\n      :confidence => 0,\n      :relative_path => \"app/models/account.rb\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_attribute_restriction_2\n    assert_warning :type => :model,\n      :warning_code => 19,\n      :fingerprint => \"b325ae8a4570599cde146875ae86427506befae36a3b4a97ce2223930846fec5\",\n      :warning_type => \"Attribute Restriction\",\n      :line => 1,\n      :message => /^Mass\\ assignment\\ is\\ not\\ restricted\\ using\\ /,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_format_validation\n    assert_warning :type => :model,\n      :warning_type => \"Format Validation\",\n      :line => 2,\n      :message => /^Insufficient validation for `name` using/,\n      :confidence => 0,\n      :file => /account\\.rb/,\n      :relative_path => \"app/models/account.rb\"\n  end\n\n  def test_unescaped_parameter\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped parameter value/,\n      :confidence => 0,\n      :file => /index\\.html\\.erb/,\n      :relative_path => \"app/views/home/index.html.erb\"\n  end\n\n  def test_unescaped_request_env\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped request value/,\n      :confidence => 0,\n      :file => /test_env\\.html\\.erb/,\n      :relative_path => \"app/views/other/test_env.html.erb\"\n  end\n\n  def test_params_from_controller\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unescaped parameter value/,\n      :confidence => 0,\n      :file => /test_params\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_params.html.erb\"\n  end\n\n  def test_unrendered_sanitized_params_from_controller\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped parameter value/,\n      :confidence => 0,\n      :file => /test_sanitized_param\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_sanitized_param.html.erb\"\n  end\n\n  def test_sanitized_params_from_controller\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped parameter value/,\n      :confidence => 0,\n      :file => /test_sanitized_param\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_sanitized_param.html.erb\"\n  end\n\n  def test_indirect_xss\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 6,\n      :message => /^Unescaped parameter value/,\n      :confidence => 2,\n      :file => /test_params\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_params.html.erb\"\n  end\n\n  def test_cross_site_scripting_alias_u\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"a1f78b7e1ff25f81054b5ed38d04457e76278ba38444cb65f93cd559f9545bd9\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 20,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/home/test_params.html.erb\",\n      :code => s(:call, s(:params), :[], s(:lit, :w00t)),\n      :user_input => nil\n  end\n\n  def test_model_attribute_from_controller\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped model attribute/,\n      :confidence => 0,\n      :file => /test_model\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_model.html.erb\"\n  end\n\n  def test_model_from_controller_indirect_bad\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped model attribute/,\n      :confidence => 0,\n      :file => /test_model\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_model.html.erb\"\n  end\n\n  def test_model_in_link_to\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped model attribute in `link_to`/,\n      :confidence => 0,\n      :file => /test_model\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_model.html.erb\"\n  end\n\n  def test_indirect_model_in_link_to\n    assert_warning :type => :template,\n      :warning_code => 3,\n      :fingerprint => \"8941c902e7c71d0df4ebb1888c8ed9ac99affaf385be657838452ac3eefe563c\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 9,\n      :message => /^Unescaped\\ model\\ attribute\\ in\\ `l/,\n      :confidence => 1,\n      :relative_path => \"app/views/home/test_link_to.html.erb\"\n  end\n\n  def test_escaped_parameter_in_link_to\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 10,\n      :message => /^Unescaped parameter value in `link_to`/,\n      :confidence => 1,\n      :file => /test_params\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_params.html.erb\"\n  end\n\n  def test_cross_site_scripting_alias_u_for_link_to\n    assert_no_warning :type => :template,\n      :warning_code => 3,\n      :fingerprint => \"1803557ac730919bef3de68329461c47d5bee2a6bcdc8f467e6ee896504e6355\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 22,\n      :message => /^Unescaped\\ parameter\\ value\\ in\\ `link_to`/,\n      :confidence => 0,\n      :relative_path => \"app/views/home/test_params.html.erb\",\n      :code => s(:call, nil, :link_to, s(:call, s(:params), :[], s(:lit, :w00t)), s(:str, \"some_url\")),\n      :user_input => s(:call, s(:params), :[], s(:lit, :w00t))\n  end\n\n  def test_encoded_href_parameter_in_link_to\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 12,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 0,\n      :file => /test_params\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_params.html.erb\"\n  end\n\n  def test_href_parameter_in_link_to\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 14,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 0,\n      :file => /test_params\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_params.html.erb\"\n\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 16,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 1,\n      :file => /test_params\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_params.html.erb\"\n\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 18,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 0,\n      :file => /test_params\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_params.html.erb\"\n  end\n\n  def test_polymorphic_url_in_href\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 9,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 1,\n      :file => /test_model\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_model.html.erb\"\n\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 11,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 1,\n      :file => /test_model\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_model.html.erb\"\n  end\n\n  def test_cross_site_scripting_alias_u_for_link_to_href\n    assert_no_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"395a4782d1e015e32c62aff7b3811533d91015935bc1b4258ad17b264dcdf6fe\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 15,\n      :message => /^Unsafe\\ parameter\\ value\\ in\\ `link_to`\\ href/,\n      :confidence => 0,\n      :relative_path => \"app/views/home/test_model.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"test\"), s(:call, s(:params), :[], s(:lit, :user_id))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :user_id))\n  end\n\n  def test_unescaped_body_in_link_to\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped parameter value in `link_to`/,\n      :confidence => 0,\n      :file => /test_link_to\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_link_to.html.erb\"\n  end\n\n  def test_filter\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped parameter value/,\n      :confidence => 0,\n      :file => /test_filter\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_filter.html.erb\"\n  end\n\n  def test_unescaped_model\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unescaped model attribute/,\n      :confidence => 0,\n      :file => /test_sql\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_sql.html.erb\"\n  end\n\n  def test_param_from_filter\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped parameter value/,\n      :confidence => 0,\n      :file => /index\\.html\\.erb/,\n      :relative_path => \"app/views/home/index.html.erb\"\n  end\n\n  def test_params_from_locals_hash\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unescaped parameter value/,\n      :confidence => 0,\n      :file => /app\\/views\\/other\\/test_locals\\.html\\.erb/,\n      :relative_path => \"app/views/other/test_locals.html.erb\"\n  end\n\n  def test_model_attribute_from_collection\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped model attribute/,\n      :confidence => 0,\n      :file => /_user\\.html\\.erb/,\n      :relative_path => \"app/views/other/_user.html.erb\"\n  end\n\n  def test_model_attribute_from_iteration\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped model attribute/,\n      :confidence => 0,\n      :file => /test_iteration\\.html\\.erb/,\n      :relative_path => \"app/views/other/test_iteration.html.erb\"\n  end\n\n  def test_other_model_attribute_from_iteration\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unescaped model attribute/,\n      :confidence => 0,\n      :file => /test_iteration\\.html\\.erb/,\n      :relative_path => \"app/views/other/test_iteration.html.erb\"\n  end\n\n  def test_sql_injection_in_template\n    assert_no_warning :type => :template,\n      :warning_type => \"SQL Injection\",\n      :line => 4,\n      :message => /^Possible SQL injection/,\n      :confidence => 0,\n      :file => /test_sql\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_sql.html.erb\"\n  end\n\n  def test_sql_injection_call_chain\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 73,\n      :message => /^Possible SQL injection near line 73: User.humans.alive.find/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_sql_injection_merge_conditions\n    assert_no_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 22,\n      :message => /^Possible SQL injection near line 22: find/,\n      :confidence => 0,\n      :file => /user\\.rb/,\n      :relative_path => \"app/models/user.rb\"\n  end\n\n  def test_sql_injection_active_record_base_connection\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"4918bccd67257c7f691718b4bb10bbbf176bc4bd3ad80cce9df11032cc73515d\",\n      :warning_type => \"SQL Injection\",\n      :line => 31,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:lvar, :value)\n  end\n\n  def test_escape_once\n    results = find :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped parameter value/,\n      :confidence => 2,\n      :file => /index\\.html\\.erb/,\n      :relative_path => \"app/views/home/index.html.erb\"\n\n    assert_equal 0, results.length, \"escape_once is a safe method\"\n  end\n\n  def test_indirect_cookie\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped cookie value/,\n      :confidence => 2,\n      :file => /test_cookie\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_cookie.html.erb\"\n  end\n\n  def test_cookie_from_controller\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped cookie value/,\n      :confidence => 0,\n      :file => /test_cookie\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_cookie.html.erb\"\n  end\n\n  #Check for params that look like params[:x][:y]\n  def test_params_multidimensional\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 8,\n      :message => /^Unescaped parameter value/,\n      :confidence => 0,\n      :file => /test_params\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_params.html.erb\"\n  end\n\n  #Check for cookies that look like cookies[:blah][:blah]\n  def test_cookies_multidimensional\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped cookie value/,\n      :confidence => 0,\n      :file => /test_cookie\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_cookie.html.erb\"\n  end\n\n  def test_xss_in_unused_template\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => \"Unescaped parameter value near line 1: params[:blah]\",\n      :confidence => 0,\n      :file => /not_used\\.html\\.erb/,\n      :relative_path => \"app/views/other/not_used.html.erb\"\n  end\n\n  def test_select_vulnerability\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Upgrade\\ to\\ Rails\\ 3\\ or\\ use\\ options_for_se/,\n      :confidence => 1,\n      :file => /not_used\\.html\\.erb/,\n      :relative_path => \"app/views/other/not_used.html.erb\"\n  end\n\n  def test_explicit_render_template\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped parameter value near line 1: params\\[:ba/,\n      :confidence => 0,\n      :file => /home\\/test_render_template\\.html\\.haml/,\n      :relative_path => \"app/views/home/test_render_template.html.haml\"\n  end\n\n  def test_xss_with_or_in_view\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /test_xss_with_or\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_xss_with_or.html.erb\"\n  end\n\n  def test_xss_with_or_from_action\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /test_xss_with_or\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_xss_with_or.html.erb\"\n  end\n\n  def test_xss_with_or_from_if_branches\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /test_xss_with_or\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_xss_with_or.html.erb\"\n  end\n\n  def test_xss_with_nested_or\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /test_xss_with_or\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_xss_with_or.html.erb\"\n  end\n\n  def test_xss_with_model_in_or\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 9,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /test_xss_with_or\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_xss_with_or.html.erb\"\n  end\n\n  def test_cross_site_scripting_strip_tags\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /test_strip_tags\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_strip_tags.html.erb\"\n  end\n\n  def test_xss_content_tag_body\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped\\ model\\ attribute\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :file => /test_content_tag\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_content_tag.html.erb\"\n  end\n\n  def test_xss_content_tag_escaped\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 8,\n      :message => /^Unescaped\\ cookie\\ value\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :file => /test_content_tag\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_content_tag.html.erb\"\n  end\n\n  def test_xss_content_tag_attribute_name\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 11,\n      :message => /^Unescaped\\ cookie\\ value\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :file => /test_content_tag\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_content_tag.html.erb\"\n  end\n\n  def test_xss_content_tag_attribute_name_even_with_escape_set\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 17,\n      :message => /^Unescaped\\ model\\ attribute\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :file => /test_content_tag\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_content_tag.html.erb\"\n  end\n\n  def test_cross_site_scripting_escaped_by_default\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 20,\n      :message => /^Unescaped\\ parameter\\ value\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :file => /test_content_tag\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_content_tag.html.erb\"\n  end\n\n  def test_cross_site_scripting_u_alias_for_content_tag\n    assert_no_warning :type => :template,\n      :warning_code => 53,\n      :fingerprint => \"e0279d86dea74b0da8c9cf5fce0b38c1023c1c407e84671d03ce0ca3440f03da\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 29,\n      :message => /^Unescaped\\ parameter\\ value\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :relative_path => \"app/views/home/test_content_tag.html.erb\",\n      :code => s(:call, nil, :content_tag, s(:lit, :span), s(:call, s(:params), :[], s(:lit, :url))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :url))\n  end\n\n  #Uh...maybe this shouldn't be a warning\n  def test_cross_site_scripting_in_sanitize_method\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 2,\n      :file => /not_used\\.html\\.erb/,\n      :relative_path => \"app/views/other/not_used.html.erb\"\n  end\n\n  def test_xss_content_tag_unescaped_on_purpose\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 23,\n      :message => /^Unescaped\\ model\\ attribute\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :file => /test_content_tag\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_content_tag.html.erb\"\n  end\n\n  def test_xss_content_tag_indirect_body\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 26,\n      :message => /^Unescaped\\ parameter\\ value\\ in\\ `content_tag`/,\n      :confidence => 1,\n      :file => /test_content_tag\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_content_tag.html.erb\"\n  end\n\n  def test_cross_site_scripting_single_quotes_CVE_2012_3464\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^All\\ Rails\\ 2\\.x\\ versions\\ do\\ not\\ escape\\ sin/,\n      :confidence => 1,\n      :file => /environment\\.rb/,\n      :relative_path => \"config/environment.rb\"\n  end\n\n  def test_check_send\n    assert_warning :type => :warning,\n      :warning_type => \"Dangerous Send\",\n      :line => 83,\n      :message => /\\AUser controlled method execution/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n\n    assert_no_warning :type => :warning,\n      :warning_code => 23,\n      :warning_type => \"Dangerous Send\",\n      :line => 84,\n      :message => /^User\\ controlled\\ method\\ execution/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/home_controller.rb\"\n\n    assert_no_warning :type => :warning,\n      :warning_type => \"Dangerous Send\",\n      :line => 90,\n      :message => /\\AUser defined target of method invocation/,\n      :confidence => 1,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_strip_tags_CVE_2011_2931\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Versions\\ before\\ 2\\.3\\.13\\ have\\ a\\ vulnerabil/,\n      :confidence => 0,\n      :file => /environment\\.rb/,\n      :relative_path => \"config/environment.rb\"\n  end\n\n  def test_strip_tags_CVE_2012_3465_high\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /test_strip_tags\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_strip_tags.html.erb\"\n  end\n\n  def test_sql_injection_CVE_2012_5664\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-5664/,\n      :confidence => 0,\n      :file => /environment\\.rb/,\n      :relative_path => \"config/environment.rb\"\n  end\n\n  def test_sql_injection_CVE_2013_0155\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2013-0155/,\n      :confidence => 0,\n      :file => /environment\\.rb/,\n      :relative_path => \"config/environment.rb\"\n  end\n\n  def test_remote_code_execution_CVE_2013_0156\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Rails\\ 2\\.3\\.11\\ has\\ a\\ remote\\ code\\ execution/,\n      :confidence => 0,\n      :file => /environment\\.rb/,\n      :relative_path => \"config/environment.rb\"\n  end\n\n  def test_remote_code_execution_CVE_2013_0277\n    assert_warning :type => :model,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Serialized\\ attributes\\ are\\ vulnerable\\ in\\ /,\n      :confidence => 0,\n      :file => /unprotected\\.rb/,\n      :relative_path => \"app/models/unprotected.rb\"\n  end\n\n  def test_remote_code_execution_CVE_2013_0333\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Rails\\ 2\\.3\\.11\\ has\\ a\\ serious\\ JSON\\ parsing\\ /,\n      :confidence => 0,\n      :file => /environment\\.rb/,\n      :relative_path => \"config/environment.rb\"\n  end\n\n  def test_xss_sanitize_CVE_2013_1857\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Rails\\ 2\\.3\\.11\\ has\\ a\\ vulnerability\\ in\\ `sani/,\n      :confidence => 0,\n      :file => /not_used\\.html\\.erb/,\n      :relative_path => \"app/views/other/not_used.html.erb\"\n  end\n\n  def test_denial_of_service_CVE_2013_1854\n    assert_warning :type => :warning,\n      :warning_type => \"Denial of Service\",\n      :message => /^Rails\\ 2\\.3\\.11\\ has\\ a\\ denial\\ of\\ service\\ vul/,\n      :confidence => 1,\n      :file => /environment\\.rb/,\n      :relative_path => \"config/environment.rb\"\n  end\n\n  def test_number_to_currency_CVE_2014_0081\n    assert_warning :type => :warning,\n      :warning_code => 73,\n      :fingerprint => \"dd82650c29c3ec7b77437c32d394641744208b42b2aeb673d54e5f42c51e6c33\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => nil,\n      :message => /^Rails\\ 2\\.3\\.11\\ has\\ a\\ vulnerability\\ in\\ numb/,\n      :confidence => 1,\n      :relative_path => \"config/environment.rb\",\n      :user_input => nil\n  end\n\n  def test_sql_injection_CVE_2013_6417\n    assert_warning :type => :warning,\n      :warning_code => 69,\n      :fingerprint => \"378978cda99add8404dd38db466f6ffa0b824ea8c57270d98869241a240d12a6\",\n      :warning_type => \"SQL Injection\",\n      :line => nil,\n      :message => /^Rails\\ 2\\.3\\.11\\ contains\\ a\\ SQL\\ injection\\ vu/,\n      :confidence => 0,\n      :relative_path => \"config/environment.rb\",\n      :user_input => nil\n  end\n\n  def test_remote_code_execution_CVE_2014_0130\n    assert_warning :type => :warning,\n      :warning_code => 77,\n      :fingerprint => \"93393e44a0232d348e4db62276b18321b4cbc9051b702d43ba2fd3287175283c\",\n      :warning_type => \"Remote Code Execution\",\n      :line => nil,\n      :message => /^Rails\\ 2\\.3\\.11\\ with\\ globbing\\ routes\\ is\\ vul/,\n      :confidence => 0,\n      :relative_path => \"config/routes.rb\",\n      :user_input => nil\n  end\n\n  def test_xml_dos_CVE_2015_3227\n    assert_warning :type => :warning,\n      :warning_code => 88,\n      :fingerprint => \"73e352cd7b43b0a4045a100d43b7707bebf3caeaec223a191375cde74f7e2b52\",\n      :warning_type => \"Denial of Service\",\n      :line => nil,\n      :message => /^Rails\\ 2\\.3\\.11\\ is\\ vulnerable\\ to\\ denial\\ of\\ /,\n      :confidence => 1,\n      :relative_path => \"config/environment.rb\",\n      :user_input => nil\n  end\n\n  def test_mime_type_dos_CVE_2016_0751\n    # Used workaround\n    assert_no_warning :type => :warning,\n      :warning_code => 94,\n      :fingerprint => \"dfe71c713bd20a8e1324a38bd89b1667862ba47133fc62c5cc36372dac691a75\",\n      :warning_type => \"Denial of Service\",\n      :line => nil,\n      :message => /^Rails\\ 2\\.3\\.11\\ is\\ vulnerable\\ to\\ denial\\ of\\ /,\n      :confidence => 1,\n      :relative_path => \"config/environment.rb\",\n      :user_input => nil\n  end\n\n  def test_to_json\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped model attribute in JSON hash/,\n      :confidence => 0,\n      :file => /test_to_json\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_to_json.html.erb\"\n\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped parameter value in JSON hash/,\n      :confidence => 0,\n      :file => /test_to_json\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_to_json.html.erb\"\n\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 11,\n      :message => /^Unescaped parameter value in JSON hash/,\n      :confidence => 0,\n      :file => /test_to_json\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_to_json.html.erb\"\n\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 14,\n      :message => /^Unescaped parameter value in JSON hash/,\n      :confidence => 0,\n      :file => /test_to_json\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_to_json.html.erb\"\n  end\n\n  def test_xss_with_params_to_i\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /test_to_i\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_to_i.html.erb\"\n  end\n\n  def test_xss_with_request_env_to_i\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped\\ cookie\\ value/,\n      :confidence => 2,\n      :file => /test_to_i\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_to_i.html.erb\"\n  end\n\n  def test_xss_with_cookie_to_i\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ request\\ value/,\n      :confidence => 0,\n      :file => /test_to_i\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_to_i.html.erb\"\n  end\n\n  def test_xss_with_model_attribute_to_i\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 1,\n      :file => /test_to_i\\.html\\.erb/,\n      :relative_path => \"app/views/home/test_to_i.html.erb\"\n  end\n\n  def test_cross_site_scripting_unresolved_model_id\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /_models\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_in_layout_for_dupe\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"5d9a5790dbcd6ae68a11e8cdb791a8be9585bf0f75b18ef1f763c6965f55e431\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/layouts/thing.html.erb\"\n  end\n\n  def test_cross_site_scripting_in_layout_weak_dupe\n    assert_no_warning :type => :template,\n      :warning_code => 5,\n      :fingerprint => \"56fa0dc161d310062ae4717dd70515269b776fe532352e59f72ed2cdc4932153\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/views/layouts/thing.html.erb\"\n  end\n\n  def test_cross_site_scripting_in_haml\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"702f9bae476402bb2614794276083849342540bd8b5e8f2fc35b15b40e9f34fc\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/other/test_haml_stuff.html.haml\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_in_haml2\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"79cbc87a06ad9247362be97ba4b6cc12b9619fd0f68d468b81cbed376bfbcc5c\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/other/test_haml_stuff.html.haml\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_in_link_to_with_block\n    assert_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"a594a83998a7cbace5d65680e78dbd6e74b7b3ded069c83f8ac5452ef0ada08f\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unsafe\\ parameter\\ value\\ in\\ `link_to`\\ href/,\n      :confidence => 0,\n      :relative_path => \"app/views/home/test_link_to.html.erb\",\n      :code => s(:call, nil, :link_to, s(:call, s(:call, nil, :params), :[], s(:lit, :evil_url))),\n      :user_input => s(:call, s(:call, nil, :params), :[], s(:lit, :evil_url))\n  end\n\n  def test_cross_site_scripting_html_entities_in_json\n    assert_warning :type => :warning,\n      :warning_code => 114,\n      :fingerprint => \"c96eb07567e2a7b0ded7cda123645c4e736d3a1b124bb7c0ffaf5070f53dfcf3\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 21,\n      :message => /^HTML\\ entities\\ in\\ JSON\\ are\\ not\\ escaped\\ by/,\n      :confidence => 1,\n      :relative_path => \"config/environments/production.rb\",\n      :code => s(:attrasgn, s(:const, :ActiveSupport), :escape_html_entities_in_json=, s(:false)),\n      :user_input => nil\n  end\n\n  def test_dangerous_send_try\n    assert_warning :type => :warning,\n      :warning_type => \"Dangerous Send\",\n      :line => 155,\n      :message => /^User\\ controlled\\ method\\ execution/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_dangerous_send_underscore\n    assert_warning :type => :warning,\n      :warning_type => \"Dangerous Send\",\n      :line => 156,\n      :message => /^User\\ controlled\\ method\\ execution/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_dangerous_public_send\n    assert_warning :type => :warning,\n      :warning_type => \"Dangerous Send\",\n      :line => 157,\n      :message => /^User\\ controlled\\ method\\ execution/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_dangerous_try_on_user_input\n    assert_no_warning :type => :warning,\n      :warning_type => \"Dangerous Send\",\n      :line => 160,\n      :message => /^User\\ defined\\ target\\ of\\ method\\ invocation/,\n      :confidence => 1,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_unsafe_reflection_constantize\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 89,\n      :message => /^Unsafe\\ reflection\\ method\\ `constantize`\\ called\\ on/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n\n    # This is same call, copied to template\n    assert_no_warning :type => :template,\n      :warning_code => 24,\n      :warning_type => \"Remote Code Execution\",\n      :line => 1,\n      :message => /^Unsafe\\ reflection\\ method\\ `constantize`\\ called\\ on/,\n      :confidence => 0,\n      :relative_path => \"app/views/home/test_send_target.html.erb\"\n  end\n\n  def test_unsafe_reflection_constantize_2\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 160,\n      :message => /^Unsafe\\ reflection\\ method\\ `constantize`\\ cal/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_unsafe_symbol_creation\n    [41,42].each do |line|\n      assert_warning :type => :warning,\n        :warning_type => \"Denial of Service\",\n        :line => line,\n        :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string/,\n        :confidence => 0,\n        :file => /application_controller\\.rb/,\n        :relative_path => \"app/controllers/application_controller.rb\"\n     end\n  end\n\n  def test_unsafe_symbol_creation_2\n    assert_warning :type => :warning,\n      :warning_type => \"Denial of Service\",\n      :line => 83,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_unsafe_symbol_creation_3\n    assert_warning :type => :warning,\n      :warning_type => \"Denial of Service\",\n      :line => 29,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string/,\n      :confidence => 1,\n      :file => /application_controller\\.rb/,\n      :relative_path => \"app/controllers/application_controller.rb\"\n  end\n\n  def test_unsafe_symbol_creation_4\n    assert_warning :type => :warning,\n      :warning_type => \"Denial of Service\",\n      :line => 86,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string\\ in pa/,\n      :confidence => 0,\n      :file => /other_controller\\.rb/,\n      :relative_path => \"app/controllers/other_controller.rb\"\n  end\n\n  def test_unsafe_symbol_creation_5\n    assert_warning :type => :warning,\n      :warning_type => \"Denial of Service\",\n      :line => 88,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string\\ in pa/,\n      :confidence => 1,\n      :file => /other_controller\\.rb/,\n      :relative_path => \"app/controllers/other_controller.rb\"\n  end\n\n  def test_unsafe_symbol_creation_6\n    assert_warning :type => :warning,\n      :warning_type => \"Denial of Service\",\n      :line => 44,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string\\ in pa/,\n      :confidence => 1,\n      :file => /application_controller\\.rb/,\n      :relative_path => \"app/controllers/application_controller.rb\"\n  end\n\n  def test_regex_dos\n    assert_warning :type => :warning,\n      :warning_code => 76,\n      :fingerprint => \"de95ff1870e84933cb5a67bdd5c10cfa666b0bcd95cc78d7dd962215be9ed20c\",\n      :warning_type => \"Denial of Service\",\n      :line => 74,\n      :message => /^Parameter\\ value\\ used\\ in\\ regular\\ expression/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/other_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :regex))\n  end\n\n  def test_indirect_regex_dos\n    assert_warning :type => :warning,\n      :warning_code => 76,\n      :fingerprint => \"afdb18fa56308063ad491b76821fb76724dd6f0bd9d3e6aac83c933af0b4baac\",\n      :warning_type => \"Denial of Service\",\n      :line => 82,\n      :message => /^Parameter\\ value\\ used\\ in\\ regular\\ expression/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/other_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :regex))\n  end\n\n  def test_unsafe_symbol_creation_from_param\n    assert_warning :type => :warning,\n      :warning_code => 59,\n      :fingerprint => \"b9c29fc37080f827527feb53f29d618b91d9a5aaac9047383baf46361f08c4cc\",\n      :warning_type => \"Denial of Service\",\n      :line => 49,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string\\ in pa/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/other_controller.rb\"\n  end\n\n  def test_to_sym_duplicate_as_argument\n    assert_no_warning :type => :warning,\n      :warning_code => 59,\n      :fingerprint => \"b9c29fc37080f827527feb53f29d618b91d9a5aaac9047383baf46361f08c4cc\",\n      :warning_type => \"Denial of Service\",\n      :line => 53,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string\\ in pa/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/other_controller.rb\"\n  end\n\n  def test_to_sym_duplicate_as_target\n    assert_no_warning :type => :warning,\n      :warning_code => 59,\n      :fingerprint => \"b9c29fc37080f827527feb53f29d618b91d9a5aaac9047383baf46361f08c4cc\",\n      :warning_type => \"Denial of Service\",\n      :line => 54,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string\\ \\(pa/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/other_controller.rb\"\n  end\n\n  def test_ignored_sql_warning\n    assert_no_warning :type => :template,\n      :warning_code => 0,\n      :fingerprint => \"f2fa1da45eea252150f6920454822bda3ed5c83a2c376c1296a98037969dd45f\",\n      :warning_type => \"SQL Injection\",\n      :line => 2,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/views/other/ignore_me.html.erb\"\n  end\n\n  def test_ignored_xss_warning\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"6300805e44167e6c3446efbd06b97206928855a2bfc6e1f3e61c097795956b13\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 2,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/other/ignore_me.html.erb\"\n  end\n\n  def test_unscoped_find\n    assert_warning :type => :warning,\n      :warning_code => 82,\n      :fingerprint => \"97cfe8a3ca261dfd2dcbd9f3aae6a007bc107c5ab6045e0f9cfaa7e66333c8c8\",\n      :warning_type => \"Unscoped Find\",\n      :line => 3,\n      :message => /^Unscoped\\ call\\ to\\ `Email\\#find`/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/emails_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :email_id))\n  end\n\n  def test_unmaintained_dependency_rails\n    assert_warning check_name: \"EOLRails\",\n      type: :warning,\n      warning_code: 120,\n      fingerprint: \"b43ad7ea48b7d5f0da242e205924653198f70c50f9cfda7211fcbc1f0abec65a\",\n      warning_type: \"Unmaintained Dependency\",\n      line: nil,\n      message: /^Support\\ for\\ Rails\\ 2\\.3\\.11\\ ended\\ on\\ 2013\\-0/,\n      confidence: 0,\n      relative_path: \"config/environment.rb\"\n  end\nend\n\nclass Rails2WithOptionsTests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def expected\n    @expected ||= {\n      :controller => 1,\n      :model => 4,\n      :template => 47,\n      :generic => 61 }\n  end\n\n  def report\n    @@report ||= BrakemanTester.run_scan \"rails2\", \"Rails 2\", :run_all_checks => true\n  end\n\n  def test_no_errors\n    assert_equal 0, report[:errors].length\n  end\n\n  def test_attribute_restriction\n    assert_warning :type => :model,\n      :warning_type => \"Attribute Restriction\",\n      :warning_code => Brakeman::WarningCodes::Codes[:no_attr_accessible],\n      :message => /^Mass assignment is not restricted using /,\n      :confidence => 0,\n      :file => /account\\.rb/\n    assert_warning :type => :model,\n      :warning_type => \"Attribute Restriction\",\n      :warning_code => Brakeman::WarningCodes::Codes[:no_attr_accessible],\n      :message => /^Mass assignment is not restricted using /,\n      :confidence => 0,\n      :file => /user\\.rb/\n  end\nend\n"
  },
  {
    "path": "test/tests/rails3.rb",
    "content": "require_relative '../test'\n\nclass Rails3Tests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def report\n    @@report ||= BrakemanTester.run_scan \"rails3\", \"Rails 3\", :rails3 => true,\n      :config_file => File.join(TEST_PATH, \"apps\", \"rails3\", \"config\", \"brakeman.yml\"),\n      :enable_checks => ['CheckRender']\n  end\n\n  def expected\n    @expected ||= {\n      :controller => 1,\n      :model => 9,\n      :template => 41,\n      :generic => 79\n    }\n\n    if RUBY_PLATFORM == 'java'\n      @expected[:generic] += 1\n    end\n\n    @expected\n  end\n\n  def test_no_errors\n    assert_equal 0, report[:errors].length\n  end\n\n  def test_config_sanity\n    assert_equal 'utf-8', report[:config].rails[:encoding].value\n  end\n\n  def test_eval_params\n    assert_warning :type => :warning,\n      :warning_code => 13,\n      :fingerprint => \"4efdd73fb759135f5980b5da1d9804aa4eb5c7475eabfd0f0cf41299d1d7ec42\",\n      :warning_type => \"Dangerous Eval\",\n      :line => 40,\n      :message => /^Parameter\\ value\\ evaluated\\ as\\ code/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_class_eval_false_positive\n    assert_no_warning :type => :warning,\n      :warning_type => \"Dangerous Eval\",\n      :line => 13,\n      :message => /^User input in eval/,\n      :confidence => 0,\n      :file => /account\\.rb/\n  end\n\n  def test_command_injection_params_interpolation\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"d68453d17bca16814e8eaffdce5b1dcf3e87aeeca2d94f3dcf78e309cb1b29c6\",\n      :warning_type => \"Command Injection\",\n      :line => 34,\n      :message => /^Possible command injection near line 34:/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_command_injection_system_params\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :line => 36,\n      :message => /^Possible command injection near line 36:/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_command_injection_non_user_input_backticks\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :line => 48,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :file => /other_controller\\.rb/\n  end\n\n  def test_command_injection_non_user_input_system\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :line => 49,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :file => /other_controller\\.rb/\n  end\n\n  def test_command_injection_capture2\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"a9e14a8381114ec58551a94c281c36782ec9d6d91d93c346e6e4f7a6f32e9c25\",\n      :warning_type => \"Command Injection\",\n      :line => 146,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_command_injection_capture2e\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"99283cfdb2799fc278d5e474d11dc952ead57861e29470cf5ac16629a5b07fb2\",\n      :warning_type => \"Command Injection\",\n      :line => 147,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_command_injection_capture3\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"1c7506f2977852c07f8e41dcdad205794048b258b557e7d322acf86fab0a6877\",\n      :warning_type => \"Command Injection\",\n      :line => 148,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_command_injection_pipeline\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"a72b42173ccbc912f022e73a37afc57b8099a529a9f28ebd9e3e771ad384b81c\",\n      :warning_type => \"Command Injection\",\n      :line => 149,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_command_injection_pipeline_r\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"f28aa6e2e73662dd58169db727fc30099da36a9e0d1817375bb257faed376e52\",\n      :warning_type => \"Command Injection\",\n      :line => 150,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_command_injection_pipeline_rw\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"02485597e19623e805dfa48a797f6f453d854f87ea03e51330494bf671bf5f68\",\n      :warning_type => \"Command Injection\",\n      :line => 151,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_command_injection_pipeline_start\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"c38ddfa0340fcaaa2a626de722a7784a0448fce01b58601c9c159113d1ce6e5f\",\n      :warning_type => \"Command Injection\",\n      :line => 152,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_command_injection_pipeline_safe_ish\n    assert_no_warning check_name: \"Execute\",\n      type: :warning,\n      warning_code: 14,\n      fingerprint: \"69631817be6f91b3e9115935a0b5e23b6cd642bdb44ac5edb83ce9bf5c207528\",\n      warning_type: \"Command Injection\",\n      line: 162,\n      message: /^Possible\\ command\\ injection/,\n      confidence: 0,\n      relative_path: \"app/controllers/home_controller.rb\",\n      code: s(:call, s(:const, :Open3), :pipeline, s(:array, s(:str, \"sort\"), s(:call, s(:params), :[], s(:lit, :file)))),\n      user_input: s(:call, s(:params), :[], s(:lit, :file))\n  end\n\n  def test_command_injection_pipeline_array_cmd\n    assert_warning check_name: \"Execute\",\n      type: :warning,\n      warning_code: 14,\n      fingerprint: \"209d96d55ba1cdbce58da49efaea6c3da266411c9a4e3ba914b80969b0ebc4c8\",\n      warning_type: \"Command Injection\",\n      line: 163,\n      message: /^Possible\\ command\\ injection/,\n      confidence: 0,\n      relative_path: \"app/controllers/home_controller.rb\",\n      code: s(:call, s(:const, :Open3), :pipeline_r, s(:array, s(:str, \"ls\"), s(:str, \"*\")), s(:dstr, \"sort \", s(:evstr, s(:call, s(:params), :[], s(:lit, :order))))),\n      user_input: s(:call, s(:params), :[], s(:lit, :order))\n  end\n\n  def test_command_injection_pipeline_two_array_commands\n    assert_warning check_name: \"Execute\",\n      type: :warning,\n      warning_code: 14,\n      fingerprint: \"a64f8ffded9992faa6291a1448ea49b9121b29d00cc09d01f3608c57131f778a\",\n      warning_type: \"Command Injection\",\n      line: 164,\n      message: /^Possible\\ command\\ injection/,\n      confidence: 0,\n      relative_path: \"app/controllers/home_controller.rb\",\n      code: s(:call, s(:const, :Open3), :pipeline_rw, s(:array, s(:str, \"ls\")), s(:array, s(:call, s(:params), :[], s(:lit, :cmd)))),\n      user_input: s(:call, s(:params), :[], s(:lit, :cmd))\n  end\n\n  def test_command_injection_pipeline_bash_c\n    assert_warning check_name: \"Execute\",\n      type: :warning,\n      warning_code: 14,\n      fingerprint: \"386d96b25b2ca16ff668be680ddf4669fe4f37e12f60506f67971d99ba2f4250\",\n      warning_type: \"Command Injection\",\n      line: 165,\n      message: /^Possible\\ command\\ injection/,\n      confidence: 0,\n      relative_path: \"app/controllers/home_controller.rb\",\n      code: s(:call, s(:const, :Open3), :pipeline_start, s(:array, s(:str, \"bash\"), s(:str, \"-c\"), s(:call, s(:params), :[], s(:lit, :cmd)))),\n      user_input: s(:call, s(:params), :[], s(:lit, :cmd))\n  end\n\n  def test_command_injection_spawn\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"73d4d3114ea536247c38a4e0d5bbcde047ea3f304d2e6a22b1693003d5135409\",\n      :warning_type => \"Command Injection\",\n      :line => 153,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_command_injection_posix_spawn\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"678ea7e0c73c91df335247b2470678dd23dfe66f049add9c783e3de4fb6e5046\",\n      :warning_type => \"Command Injection\",\n      :line => 154,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/home_controller.rb\"\n  end\n\n  def test_file_access_concatenation\n    assert_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 24,\n      :message => /^Parameter value used in file name near l/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_file_access_load\n    assert_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 67,\n      :message => /^Parameter value used in file name near l/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_file_access_yaml_load\n    assert_no_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 106,\n      :message => /^Parameter\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_file_access_yaml_parse_file\n    assert_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 109,\n      :message => /^Parameter\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_mass_assignment\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 54,\n      :message => /^Unprotected mass assignment near line 54/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_protected_mass_assignment\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 43,\n      :message => /^Unprotected mass assignment near line 43: Product.new/,\n      :confidence => 1,\n      :file => /products_controller\\.rb/\n  end\n\n  def test_protected_mass_assignment_update\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 62,\n      :message => /^Unprotected mass assignment near line 62: Product.find/,\n      :confidence => 1,\n      :file => /products_controller\\.rb/\n  end\n\n  def test_update_attribute_no_mass_assignment\n    assert_no_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 26,\n      :message => /^Unprotected mass assignment near line 26/,\n      :confidence => 0,\n      :file => /other_controller\\.rb/\n  end\n\n  def test_redirect\n    assert_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 45,\n      :message => /^Possible unprotected redirect near line /,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_redirect_to_model_instance\n    assert_no_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 63,\n      :message => /^Possible unprotected redirect near line 63: redirect_to/,\n      :confidence => 2,\n      :file => /products_controller\\.rb/\n  end\n\n  def test_redirect_only_path_in_wrong_argument\n    assert_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 77,\n      :message => /^Possible unprotected redirect near line 77: redirect_to\\(params\\[/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_redirect_url_for_not_only_path\n    assert_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 83,\n      :message => /^Possible unprotected redirect near line 83: redirect_to\\(url_for/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_redirect_url_only_path\n    assert_no_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 158,\n      :message => /^Possible unprotected redirect near line 159: redirect_to\\(params\\[/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_render_path\n    assert_warning :type => :warning,\n      :warning_type => \"Dynamic Render Path\",\n      :line => 63,\n      :message => /^Render path contains parameter value near line 63: render/,\n      :confidence => 1,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_file_access_send_file\n    assert_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 21,\n      :message => /^Parameter value used in file name near l/,\n      :confidence => 0,\n      :file => /other_controller\\.rb/\n  end\n\n  def test_rails_cve_2012_2660\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-2660/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_rails_cve_2012_2661\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-2661/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_rails_cve_2012_2695\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-2695/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_sql_injection_CVE_2012_5664\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-5664/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_sql_injection_find_by_sql\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 28,\n      :message => /^Possible SQL injection near line 28: Use/,\n      :confidence => 1,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_sql_injection_unknown_variable\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 29,\n      :message => /^Possible SQL injection near line 29: Use/,\n      :confidence => 1,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_sql_injection_params\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 30,\n      :message => /^Possible SQL injection near line 30: Use/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_sql_injection_non_active_record_model\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"f804d0d9f3f0ecddf8cec14aa7bdc0020db864252cd2e7d7e3a7081c45363a7d\",\n      :warning_type => \"SQL Injection\",\n      :line => 30,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/other_controller.rb\",\n      :code => s(:call, s(:const, :Noticia), :where, s(:call, s(:params), :[], s(:lit, :bad_stuff))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :bad_stuff))\n  end\n\n  def test_csrf_protection\n    assert_warning :type => :controller,\n      :warning_code => 7,\n      :fingerprint => \"6f5239fb87c64764d0c209014deb5cf504c2c10ee424bd33590f0a4f22e01d8f\",\n      :warning_type => \"Cross-Site Request Forgery\",\n      :line => 1,\n      :message => /^`protect_from_forgery`\\ should\\ be\\ called\\ /,\n      :confidence => 0,\n      :relative_path => \"app/controllers/application_controller.rb\",\n      :user_input => nil\n  end\n\n  def test_attribute_restriction\n    assert_warning :type => :model,\n      :warning_code => 19,\n      :fingerprint => \"91d73b1b9d6920156b920729c0146292eb9f10f4ba9515740442dbe82d4dee78\",\n      :warning_type => \"Attribute Restriction\",\n      :line => 1,\n      :message => /^Mass\\ assignment\\ is\\ not\\ restricted\\ using\\ /,\n      :confidence => 0,\n      :relative_path => \"app/models/account.rb\"\n\n    assert_warning :type => :model,\n      :warning_code => 19,\n      :fingerprint => \"b325ae8a4570599cde146875ae86427506befae36a3b4a97ce2223930846fec5\",\n      :warning_type => \"Attribute Restriction\",\n      :line => 1,\n      :message => /^Mass\\ assignment\\ is\\ not\\ restricted\\ using\\ /,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\"\n  end\n\n  def test_attr_protected\n    assert_warning :type => :model,\n      :warning_type => \"Attribute Restriction\",\n      :message => /^`attr_protected` is bypassable in/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_format_validation\n    assert_warning :type => :model,\n      :warning_type => \"Format Validation\",\n      :line => 2,\n      :message => /^Insufficient validation for `name` using/,\n      :confidence => 0,\n      :file => /account\\.rb/\n  end\n\n  def test_format_validation_with_z\n    assert_warning :type => :model,\n      :warning_type => \"Format Validation\",\n      :line => 3,\n      :message => /^Insufficient validation for `blah` using/,\n      :confidence => 0,\n      :file => /account\\.rb/\n  end\n\n  def test_format_validation_with_a\n    assert_warning :type => :model,\n      :warning_type => \"Format Validation\",\n      :line => 4,\n      :message => /^Insufficient validation for `something` using/,\n      :confidence => 0,\n      :file => /account\\.rb/\n  end\n\n  def test_allowable_validation\n    results = find :type => :model,\n      :warning_type => \"Format Validation\",\n      :line => 5,\n      :message => /^Insufficient validation/,\n      :confidence => 0,\n      :file => /account\\.rb/\n\n    assert_equal 0, results.length, \"Validation was allowable, should not raise warning\"\n  end\n\n  def test_allowable_validation_with_Z\n    results = find :type => :model,\n      :warning_type => \"Format Validation\",\n      :line => 6,\n      :message => /^Insufficient validation/,\n      :confidence => 0,\n      :file => /account\\.rb/\n\n    assert_equal 0, results.length, \"Validation was allowable, should not raise warning\"\n  end\n\n  def test_xss_parameter_direct\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped parameter value near line 3: p/,\n      :confidence => 0,\n      :file => /index\\.html\\.erb/\n  end\n\n  def test_xss_parameter_variable\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped parameter value near line 5: p/,\n      :confidence => 0,\n      :file => /index\\.html\\.erb/\n  end\n\n  def test_xss_parameter_locals\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unescaped parameter value near line 4: p/,\n      :confidence => 0,\n      :file => /test_locals\\.html\\.erb/\n  end\n\n  def test_xss_model_collection\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped model attribute near line 1: User.new.first_name/,\n      :confidence => 0,\n      :file => /_user\\.html\\.erb/\n  end\n\n  def test_xss_model\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped model attribute/,\n      :confidence => 0,\n      :file => /test_model\\.html\\.erb/\n  end\n\n  def test_xss_model_known_bad\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 6,\n      :message => /^Unescaped model attribute near line 6: a/,\n      :confidence => 0,\n      :file => /test_model\\.html\\.erb/\n  end\n\n  def test_model_in_link_to\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 8,\n      :message => /^Unescaped model attribute in `link_to`/,\n      :confidence => 0,\n      :file => /test_model\\.html\\.erb/\n  end\n\n  def test_encoded_href_parameter_in_link_to\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 12,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 0,\n      :file => /test_params\\.html\\.erb/\n  end\n\n  def test_href_parameter_in_link_to\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 14,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 0,\n      :file => /test_params\\.html\\.erb/\n\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 16,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :file => /test_params\\.html\\.erb/\n\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 18,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 0,\n      :file => /test_params\\.html\\.erb/\n  end\n\n  def test_newlines_in_template\n    # Brakeman previously handled multiple newlines between nested ruby\n    # expressions incorrectly. This test verifies that multiple newlines between\n    # ruby expressions does not lead to incorrect line numbers in warnings.\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 0,\n      :file => /test_newlines\\.html\\.erb/\n\n    # Brakeman previously handled multiple newlines between HTML markup and ruby\n    # expressions incorrectly. This test verifies that multiple newlines between\n    # HTML and ruby expressions does not lead to incorrect line numbers in\n    # warnings.\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 12,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 0,\n      :file => /test_newlines\\.html\\.erb/\n  end\n\n  def test_polymorphic_url_in_href\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 10,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 1,\n      :file => /test_model\\.html\\.erb/\n\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 12,\n      :message => /^Unsafe parameter value in `link_to` href/,\n      :confidence => 1,\n      :file => /test_model\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_alias_u_for_link_to_href\n    assert_no_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"395a4782d1e015e32c62aff7b3811533d91015935bc1b4258ad17b264dcdf6fe\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 14,\n      :message => /^Unsafe\\ parameter\\ value\\ in\\ link_to\\ href/,\n      :confidence => 0,\n      :relative_path => \"app/views/home/test_model.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"test\"), s(:call, s(:params), :[], s(:lit, :user_id))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :user_id))\n  end\n\n  def test_file_access_in_template\n    assert_warning :type => :template,\n      :warning_type => \"File Access\",\n      :line => 3,\n      :message => /^Parameter value used in file name near l/,\n      :confidence => 0,\n      :file => /test_file_access\\.html\\.erb/\n  end\n\n  def test_xss_cookie_direct\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped cookie value/,\n      :confidence => 0,\n      :file => /test_cookie\\.html\\.erb/\n  end\n\n  def test_xss_filter\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped parameter value/,\n      :confidence => 0,\n      :file => /test_filter\\.html\\.erb/\n  end\n\n  def test_xss_iteration\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped model attribute/,\n      :confidence => 0,\n      :file => /test_iteration\\.html\\.erb/\n  end\n\n  def test_xss_iteration2\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unescaped model attribute/,\n      :confidence => 0,\n      :file => /test_iteration\\.html\\.erb/\n  end\n\n  def test_unescaped_model\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unescaped model attribute/,\n      :confidence => 0,\n      :file => /test_sql\\.html\\.erb/\n  end\n\n  def test_xss_params\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unescaped parameter value/,\n      :confidence => 0,\n      :file => /test_params\\.html\\.erb/\n  end\n\n  def test_indirect_xss\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 6,\n      :message => /^Unescaped parameter value/,\n      :confidence => 2,\n      :file => /test_params\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_alias_u\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"a1f78b7e1ff25f81054b5ed38d04457e76278ba38444cb65f93cd559f9545bd9\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 22,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/home/test_params.html.erb\",\n      :code => s(:call, s(:params), :[], s(:lit, :w00t)),\n      :user_input => nil\n  end\n\n  def test_sql_injection_in_template\n    #SQL injection in controllers should not warn again in views\n    assert_no_warning :type => :template,\n      :warning_type => \"SQL Injection\",\n      :line => 4,\n      :message => /^Possible SQL injection/,\n      :confidence => 0,\n      :file => /test_sql\\.html\\.erb/\n  end\n\n  def test_sql_injection_via_if\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 32,\n      :message => /^Possible SQL injection near line 32: User.where/,\n      :confidence => 0,\n      :file => /user\\.rb/\n  end\n\n  def test_sqli_in_unusual_model_name\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :warning_type => \"SQL Injection\",\n      :line => 3,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :file => /underline_model\\.rb/\n  end\n\n  def test_sql_injection_delete_all\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"4045f9ab95a70f3674f6e1ff7c1f0ac7bdd9ab39bf111f1d0c0b7a386643fbff\",\n      :warning_type => \"SQL Injection\",\n      :line => 57,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/other_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :name))\n  end\n\n  def test_sql_injection_destroy_all\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"7bbc1feebc89050e053bd3a24b9b00fe5d1879650368e82ee22b3cbc371a9ec3\",\n      :warning_type => \"SQL Injection\",\n      :line => 58,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/other_controller.rb\",\n      :user_input => s(:call, s(:call, s(:const, :User), :current), :humanity)\n  end\n\n  def test_sql_injection_to_s_value\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"0cf32bcc2320f59c97d4f5e051a764ee4fe7af987149ff118bce9900ff7a2faa\",\n      :warning_type => \"SQL Injection\",\n      :line => 64,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/other_controller.rb\",\n      :user_input => s(:call, nil, :product_action_type_key)\n\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"6066950e19a729359e867b882323ef75334791bdceac75a16f586fc53f3318a0\",\n      :warning_type => \"SQL Injection\",\n      :line => 68,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/other_controller.rb\",\n      :user_input => s(:lvar, :status)\n\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"c36f33e3b004e081622f1829be288ebdad673a7bf04922eb1d2b9a3d701362a1\"\n  end\n\n  def test_escape_once\n    results = find :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped parameter value/,\n      :confidence => 2,\n      :file => /index\\.html\\.erb/\n\n    assert_equal 0, results.length, \"escape_once is a safe method\"\n  end\n\n  def test_indirect_cookie\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped cookie value/,\n      :confidence => 2,\n      :file => /test_cookie\\.html\\.erb/\n  end\n\n  #Check for params that look like params[:x][:y]\n  def test_params_multidimensional\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 10,\n      :message => /^Unescaped parameter value/,\n      :confidence => 0,\n      :file => /test_params\\.html\\.erb/\n  end\n\n  #Check for cookies that look like cookies[:blah][:blah]\n  def test_cookies_multidimensional\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped cookie value/,\n      :confidence => 0,\n      :file => /test_cookie\\.html\\.erb/\n  end\n\n  def test_default_routes\n    assert_warning :warning_type => \"Default Routes\",\n      :line => 101,\n      :message => /All public methods in controllers are available as actions/,\n      :file => /routes\\.rb/\n  end\n\n  def test_user_input_in_mass_assignment\n    assert_no_warning :warning_type => \"Mass Assignment\",\n      :line => 58,\n      :message => /^Unprotected mass assignment/,\n      :confidence => 2,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_mass_assignment_in_chained_call\n    assert_warning :warning_type => \"Mass Assignment\",\n      :line => 9,\n      :message => /^Unprotected mass assignment near line 9: Account.new/,\n      :confidence => 0,\n      :file => /account\\.rb/\n  end\n\n  def test_mass_assign_with_strong_params\n    assert_no_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 53,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 0,\n      :file => /other_controller\\.rb/\n  end\n\n  def test_mass_assignment_first_or_create\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 114,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_mass_assignment_first_or_create!\n    assert_no_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 115,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 2,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_mass_assignment_first_or_initialize!\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 116,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_mass_assignment_update\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 118,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_mass_assignment_assign_attributes\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 119,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_mass_assignment_with_slice\n    assert_no_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 141,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_mass_assignment_with_only\n    assert_no_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 142,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_translate_bug\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Rails\\ 3\\.0\\.3\\ has\\ a\\ vulnerability\\ in\\ the\\ t/, \n      :confidence => 1,\n      :file => /Gemfile/\n  end\n\n  def test_model_build\n    assert_warning :warning_type => \"Mass Assignment\",\n      :line => 73,\n      :message => /^Unprotected mass assignment near line 73: User.new.something.something/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_string_buffer_manipulation_bug\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Rails 3\\.\\d\\.\\d has a vulnerability in `SafeBuffer`. Upgrade to Rails 3.0.12/,\n      :confidence => 1,\n      :file => /Gemfile/\n  end\n\n  def test_rails3_render_partial\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 15,\n      :message => /^Unescaped model attribute near line 15: Product/,\n      :confidence => 0,\n      :file => /_form\\.html\\.erb/\n  end\n\n  def test_xss_content_tag_raw_content\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 8,\n      :message => /^Unescaped\\ parameter\\ value\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :file => /test_content_tag\\.html\\.erb/\n  end\n\n  def test_xss_content_tag_attribute_name\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 14,\n      :message => /^Unescaped\\ cookie\\ value\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :file => /test_content_tag\\.html\\.erb/\n  end\n\n  def test_xss_content_tag_attribute_name_even_with_escape\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 20,\n      :message => /^Unescaped\\ model\\ attribute\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :file => /test_content_tag\\.html\\.erb/\n  end\n\n  def test_xss_content_tag_unescaped_attribute\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 26,\n      :message => /^Unescaped\\ model\\ attribute\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :file => /test_content_tag\\.html\\.erb/\n  end\n\n  def test_xss_content_tag_in_tag_name\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 32,\n      :message => /^Unescaped\\ parameter\\ value\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :file => /test_content_tag\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_u_alias_for_content_tag\n    assert_no_warning :type => :template,\n      :warning_code => 53,\n      :fingerprint => \"2bfdd98472f9f235b3ea683a4d911749b0c1b7ae169be697657304724d780595\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 38,\n      :message => /^Unescaped\\ parameter\\ value\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :relative_path => \"app/views/home/test_content_tag.html.erb\",\n      :code => s(:call, nil, :content_tag, s(:lit, :span), s(:str, \"test\"), s(:hash, s(:call, s(:params), :[], s(:lit, :class)), s(:str, \"display:none\"))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :class))\n  end\n\n  def test_cross_site_scripting_prepend_filter\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /use_filter12345\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_append_filter\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /use_filter12345\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_prepend_filter_overwrite\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /use_filter12345\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_prepend_filter_overwrite_2\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 8,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /use_filter12345\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_CVE_2016_6316\n    assert_warning :type => :template,\n      :warning_code => 53,\n      :fingerprint => \"0787a388cdb27d68d2e1591d02a3c84f0bc6938ede52139471082386798f7327\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 11,\n      :message => /^Unescaped\\ parameter\\ value\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :relative_path => \"app/views/home/test_content_tag.html.erb\",\n      :code => s(:call, nil, :content_tag, s(:lit, :div), s(:str, \"Blah!\"), s(:hash, s(:lit, :class), s(:call, s(:params), :[], s(:lit, :class))), s(:true)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :class))\n  end \n\n  def test_cross_site_scripting_model_in_tag_name\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 35,\n      :message => /^Unescaped\\ model\\ attribute\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :file => /test_content_tag\\.html\\.erb/\n  end\n\n  def test_content_tag_attributes_CVE_2016_6316\n    assert_warning :type => :template,\n      :warning_code => 53,\n      :fingerprint => \"e1d77d0c162fb0a1c4cc55655045755217c9e46f575d5c89848cfa2207fd1406\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 23,\n      :message => /^Unescaped\\ parameter\\ value\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :relative_path => \"app/views/home/test_content_tag.html.erb\",\n      :code => s(:call, nil, :content_tag, s(:lit, :div), s(:str, \"Blah!\"), s(:hash, s(:lit, :class), s(:call, s(:params), :[], s(:lit, :class)))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :class))\n  end\n\n  def test_cross_site_scripting_request_parameters\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 20,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /test_params\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_in_nested_controller\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /so_nested\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_from_parent\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"1e860da2c9a0cac3d898f3c4327877b3bdfa391048a19bfd6f55d6e283cc5b33\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/child/action_in_child.html.erb\"\n  end\n\n  def test_cross_site_scripting_select_tag_CVE_2012_3463\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Upgrade\\ to\\ Rails\\ 3\\.0\\.17\\. In Rails 3\\.0\\.3\\ `select_ta/,\n      :confidence => 0,\n      :file => /test_select_tag\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_single_quotes_CVE_2012_3464\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Rails\\ 3\\.0\\.3\\ does\\ not\\ escape\\ single\\ quote/,\n      :confidence => 1,\n      :file => /Gemfile/\n  end\n\n  def test_CVE_2012_3424\n    assert_warning :type => :warning,\n      :warning_type => \"Denial of Service\",\n      :message => /^Vulnerability\\ in\\ digest\\ authentication\\ \\(/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_strip_tags_CVE_2012_3465\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Versions\\ before\\ 3\\.0\\.10\\ have\\ a\\ vulnerabil/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_mail_link_CVE_2011_0446\n    assert_warning :type => :template,\n      :warning_code => 32,\n      :fingerprint => \"0102737200d60593b78d62a93bd19fbced21fe017ec20c738104cc55afb551bb\",\n      :warning_type => \"Mail Link\",\n      :line => 1,\n      :message => /^Vulnerability\\ in\\ `mail_to`\\ using\\ javascr/,\n      :confidence => 0,\n      :relative_path => \"app/views/other/test_mail_to.html.erb\"\n  end\n\n  def test_sql_injection_CVE_2013_0155\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2013-0155/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_remote_code_execution_CVE_2013_0156_fix\n    assert_no_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Rails\\ 3\\.0\\.3\\ has\\ a\\ remote\\ code\\ execution\\ /,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_remote_code_execution_CVE_2013_0277_protected\n    assert_warning :type => :model,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Serialized\\ attributes\\ are\\ vulnerable\\ in\\ /,\n      :confidence => 1,\n      :file => /product\\.rb/\n  end\n\n  def test_remote_code_execution_CVE_2013_0277_accessible\n    assert_warning :type => :model,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Serialized\\ attributes\\ are\\ vulnerable\\ in\\ /,\n      :confidence => 1,\n      :file => /purchase\\.rb/\n  end\n\n  def test_remote_code_execution_CVE_2013_0277_unprotected\n    assert_warning :type => :model,\n      :fingerprint => \"b85602475eb048cfe7941b5952c3d5a09a7d9d0607f81fbf2b7578d1055fec90\",\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Serialized\\ attributes\\ are\\ vulnerable\\ in\\ /,\n      :confidence => 0,\n      :file => /user\\.rb/\n  end\n\n  def test_remote_code_execution_CVE_2013_0333\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Rails\\ 3\\.0\\.3\\ has\\ a\\ serious\\ JSON\\ parsing\\ v/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_denial_of_service_CVE_2013_0269\n    assert_warning :type => :warning,\n      :warning_type => \"Denial of Service\",\n      :message => /^json_pure gem\\ 1\\.6\\.4\\ has\\ a\\ symbol/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_xss_CVE_2013_1857\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 40,\n      :message => /^Rails\\ 3\\.0\\.3\\ has\\ a\\ vulnerability\\ in\\ `sanit/,\n      :confidence => 0,\n      :file => /user\\.rb/\n  end\n\n  def test_xml_jruby_parsing_CVE_2013_1856\n    if RUBY_PLATFORM == 'java'\n      assert_warning :type => :warning,\n        :warning_type => \"File Access\",\n        :message => /^Rails\\ 3\\.0\\.3\\ with\\ JRuby\\ has\\ a\\ vulnerabili/,\n        :confidence => 0,\n        :file => /Gemfile/\n    end\n  end\n\n  def test_denial_of_service_CVE_2013_1854\n    assert_no_warning :type => :warning,\n      :warning_code => 55,\n      :fingerprint => \"2746b8872d4f46676a8c490a7ac906d23f6b11c9d83b6371ff5895139ec7b43b\",\n      :warning_type => \"Denial of Service\",\n      :message => /^Rails\\ 3\\.0\\.3\\ has\\ a\\ denial\\ of\\ service\\ vul/,\n      :confidence => 1,\n      :file => /Gemfile/\n  end\n\n  def test_denial_of_service_CVE_2013_6414\n    assert_warning :type => :warning,\n      :warning_code => 64,\n      :fingerprint => \"ee4938ce7bc4aa6f37b3d993d6fed813de6b15e5c1ada41146563207c395b0c5\",\n      :warning_type => \"Denial of Service\",\n      :message => /^Rails\\ 3\\.0\\.3\\ has\\ a\\ denial\\ of\\ service\\ vuln/,\n      :confidence => 1,\n      :line => 49,\n      :relative_path => \"Gemfile.lock\"\n  end\n\n  def test_number_to_currency_CVE_2014_0081\n    assert_warning :type => :warning,\n      :warning_code => 73,\n      :fingerprint => \"86f945934ed965a47c30705141157c44ee5c546d044f8de7d573bfab456e97ce\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 49,\n      :message => /^Rails\\ 3\\.0\\.3\\ has\\ a\\ vulnerability\\ in\\ numbe/,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_sql_injection_CVE_2013_6417\n    assert_warning :type => :warning,\n      :warning_code => 69,\n      :fingerprint => \"2f63d663e9f35ba60ef81d56ffc4fbf0660fbc2067e728836176bc18f610f77f\",\n      :warning_type => \"SQL Injection\",\n      :line => 49,\n      :file => /Gemfile.lock/,\n      :message => /^Rails\\ 3\\.0\\.3\\ contains\\ a\\ SQL\\ injection\\ vul/,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_denial_of_service_CVE_2014_0082\n    assert_warning :type => :warning,\n      :warning_code => 75,\n      :fingerprint => \"99b6df435353f17dff4b0d7dfeb5f21e5c0e8045dc73533e456baf78f1fc2215\",\n      :warning_type => \"Denial of Service\",\n      :line => 49,\n      :message => /^Rails\\ 3\\.0\\.3\\ has\\ a\\ denial\\ of\\ service\\ vuln/,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_remote_code_execution_CVE_2014_0130\n    assert_warning :type => :warning,\n      :warning_code => 77,\n      :fingerprint => \"93393e44a0232d348e4db62276b18321b4cbc9051b702d43ba2fd3287175283c\",\n      :warning_type => \"Remote Code Execution\",\n      :line => nil,\n      :message => /^Rails\\ 3\\.0\\.3\\ with\\ globbing\\ routes\\ is\\ vuln/,\n      :confidence => 0,\n      :relative_path => \"config/routes.rb\",\n      :user_input => nil\n  end\n\n  def test_http_only_session_setting\n    assert_warning :type => :warning,\n      :warning_type => \"Session Setting\",\n      :line => 3,\n      :message => /^Session\\ cookies\\ should\\ be\\ set\\ to\\ HTTP\\ on/,\n      :confidence => 0,\n      :file => /session_store\\.rb/\n  end\n\n  def test_secure_only_session_setting\n    assert_warning :type => :warning,\n      :warning_type => \"Session Setting\",\n      :line => 3,\n      :message => /^Session\\ cookie\\ should\\ be\\ set\\ to\\ secure\\ o/,\n      :confidence => 0,\n      :file => /session_store\\.rb/\n  end\n\n  def test_session_secret_token\n    assert_no_warning :type => :warning,\n      :warning_type => \"Session Setting\",\n      :line => 7,\n      :message => /^Session\\ secret\\ should\\ not\\ be\\ included\\ in/,\n      :confidence => 0,\n      :file => /secret_token\\.rb/\n  end\n\n  def test_remote_code_execution_yaml_load_params_interpolated\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 106,\n      :message => /^`YAML\\.load`\\ called\\ with\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_remote_code_execution_yaml_load_params\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 123,\n      :message => /^`YAML\\.load`\\ called\\ with\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_remote_code_execution_yaml_load_indirect_cookies\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 125,\n      :message => /^`YAML\\.load`\\ called\\ with\\ cookie\\ value/,\n      :confidence => 1,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_remote_code_execution_yaml_load_model_attribute\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 126,\n      :message => /^`YAML\\.load`\\ called\\ with\\ model\\ attribute/,\n      :confidence => 1,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_remote_code_execution_yaml_load_documents\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 130,\n      :message => /^`YAML\\.load_documents`\\ called\\ with\\ paramete/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n\n  def test_remote_code_execution_yaml_load_stream\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 131,\n      :message => /^`YAML\\.load_stream`\\ called\\ with\\ cookie\\ value/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n\n  def test_remote_code_execution_yaml_parse_documents\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 132,\n      :message => /^`YAML\\.parse_documents`\\ called\\ with\\ paramet/,\n      :confidence => 0,\n      :file => /home_controller\\.rb/\n  end\n\n\n  def test_remote_code_execution_yaml_parse_stream\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 133,\n      :message => /^`YAML\\.parse_stream`\\ called\\ with\\ model\\ attr/,\n      :confidence => 1,\n      :file => /home_controller\\.rb/\n  end\n\n  def test_CVE_2015_3227\n    assert_warning :type => :warning,\n      :warning_code => 88,\n      :fingerprint => \"ab42647fbdea61e25c4b794e82a8b315054e2fac4328bb3fd4be6a744889a987\",\n      :warning_type => \"Denial of Service\",\n      :line => 49,\n      :message => /^Rails\\ 3\\.0\\.3\\ is\\ vulnerable\\ to\\ denial\\ of\\ s/,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_denial_of_service_CVE_2015_7576\n    assert_warning :type => :warning,\n      :warning_code => 94,\n      :fingerprint => \"5945a9b096557ee5771c2dd12ea6cbec933b662d169e559f524ba01c44bf2452\",\n      :warning_type => \"Denial of Service\",\n      :line => 49,\n      :message => /^Rails\\ 3\\.0\\.3\\ is\\ vulnerable\\ to\\ denial\\ of\\ s/,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_CVE_2016_6316_Gemfile\n    assert_warning :type => :warning,\n      :warning_code => 102,\n      :fingerprint => \"331e69e4654f158601d9a0e124304f825da4e0156d2c94759eb02611e280feaa\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 49,\n      :message => /^Rails\\ 3\\.0\\.3\\ `content_tag`\\ does\\ not\\ escape\\ /,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_unmaintained_dependency_rails\n    assert_warning check_name: \"EOLRails\",\n      type: :warning,\n      warning_code: 120,\n      fingerprint: \"d84924377155b41e094acae7404ec2e521629d86f97b0ff628e3d1b263f8101c\",\n      warning_type: \"Unmaintained Dependency\",\n      line: 49,\n      message: /^Support\\ for\\ Rails\\ 3\\.0\\.3\\ ended\\ on\\ 2016\\-06/,\n      confidence: 0,\n      relative_path: \"Gemfile.lock\",\n      code: nil,\n      user_input: nil\n  end\nend\n"
  },
  {
    "path": "test/tests/rails31.rb",
    "content": "require_relative '../test'\n\nclass Rails31Tests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def report\n    @@report ||= BrakemanTester.run_scan \"rails3.1\", \"Rails 3.1\", :rails3 => true, :parallel_checks => false, :interprocedural => true\n  end\n\n  def expected\n    @expected ||= {\n      :model => 3,\n      :template => 23,\n      :controller => 4,\n      :generic => 90 }\n  end\n\n  def test_without_protection\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 47,\n      :message => /^Unprotected mass assignment/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_mass_assignment_user_input_is_nil\n    assert_warning :type => :warning,\n      :warning_code => 54,\n      :fingerprint => \"1ee65e0ad3785d2d56e7c854e4f6ababc3853f1dbe78ee03059d023629f3d4bd\",\n      :warning_type => \"Mass Assignment\",\n      :line => 193,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :new, s(:call, nil, :stuff), s(:hash, s(:lit, :without_protection), s(:true))),\n      :user_input => nil\n  end\n\n  def test_redirect_to_model_attribute\n    assert_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 98,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_redirect_with_model_instance\n    assert_no_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 67,\n      :message => /^Possible unprotected redirect/,\n      :confidence => 2,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_redirect_to_find_by\n    assert_no_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 102,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_redirect_to_decorated_model\n    assert_no_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 50,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 2,\n      :file => /other_controller\\.rb/\n  end\n\n  def test_link_to_decorated_model\n    assert_no_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"2eacd2da6edd4b26585956c8b36840d7631f4a5132388829d8e4e4d0b5aaae7d\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unsafe\\ model\\ attribute\\ in\\ link_to\\ href/,\n      :confidence => 1,\n      :relative_path => \"app/views/users/drape.html.erb\",\n      :user_input => s(:call, s(:const, :User), :find, s(:call, s(:params), :[], s(:lit, :id)))\n  end\n\n  def test_redirect_multiple_values\n    assert_no_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 61,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :file => /other_controller\\.rb/\n  end\n\n  def test_redirect_to_model_as_arg\n    assert_no_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 113,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 2,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_redirect_to_model_association\n    assert_no_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 117,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_redirect_to_secong_arg\n    assert_no_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 121,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 2,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_redirect_false_positive_chained_call\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"287308589926edfd6463a04b3eb5b39b67b3bf6de6da9c380f1507f377c5a333\",\n      :warning_type => \"Redirect\",\n      :line => 185,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params, s(:lit, :host), s(:str, \"http://app.webthing.com/stuff\"), s(:lit, :port), s(:str, \"80\")), :except, s(:lit, :action), s(:lit, :controller), s(:lit, :auth_token))\n  end\n\n  def test_whitelist_attributes\n    assert_no_warning :type => :model,\n      :warning_type => \"Attribute Restriction\",\n      :message => /^Mass assignment is not restricted using attr_accessible/,\n      :confidence => 0\n  end\n\n  #Such as\n  #http_basic_authenticate_with :name => \"dhh\", :password => \"secret\"\n  def test_basic_auth_with_password\n    assert_warning :type => :controller,\n      :warning_type => \"Basic Auth\",\n      :line => 4,\n      :message => /^Basic authentication password stored in source code/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_basic_auth_in_method_with_password\n    assert_warning :type => :warning,\n      :warning_code => 9,\n      :fingerprint => \"f2698a4ca148f43a8f77901a57371b6253f450d50ad388de588f32b7dbeb8937\",\n      :warning_type => \"Basic Auth\",\n      :line => 25,\n      :message => /^Basic\\ authentication\\ password\\ stored\\ in\\ /,\n      :confidence => 0,\n      :relative_path => \"app/controllers/admin_controller.rb\"\n  end\n\n  def test_translate_bug\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Rails\\ 3\\.1\\.0\\ has\\ a\\ vulnerability\\ in\\ the\\ t/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_rails_cve_2012_2660\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-2660/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_rails_cve_2012_2661\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-2661/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_rails_cve_2012_2695\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-2695/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_sql_injection_CVE_2012_5664\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-5664/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_sql_injection_scope_lambda\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 4,\n      :message => /^Possible SQL injection near line 4: where/,\n      :confidence => 0,\n      :file => /user\\.rb/\n  end\n\n  def test_sql_injection_scope\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 10,\n      :message => /^Possible SQL injection near line 10: scope\\(:phooey, :conditions =>/,\n      :confidence => 0,\n      :file => /user\\.rb/\n  end\n\n  def test_sql_injection_scope_where\n    assert_no_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 6,\n      :message => /^Possible SQL injection near line 6: where/,\n      :confidence => 1,\n      :file => /user\\.rb/\n  end\n\n  def test_sql_injection_scope_lambda_hash\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 8,\n      :message => /^Possible SQL injection/,\n      :confidence => 1,\n      :file => /user\\.rb/\n  end\n\n  def test_sql_injection_scope_multiline_lambda_where\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 22,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :file => /user\\.rb/\n  end\n\n  def test_sql_injection_in_order_param\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 4,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /user\\.rb/\n  end\n\n  def test_sql_injection_in_group_param\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 10,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_interpolated_group_param\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 11,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_in_lock_param\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 67,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_interpolated_lock_param\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 68,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_interpolated_having\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 16,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_interpolated_having_array\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 25,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_interpolated_joins\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 34,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_interpolated_joins_array\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 40,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_in_order_param_product\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 4,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_interpolated_order\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 5,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_in_select_param\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 48,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_interpolated_select\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 49,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n\n  def test_sql_injection_in_from_param\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 58,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_interpolated_from\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 59,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_local_interpolation\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 93,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_interpolated_where\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 80,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_interpolated_where_array\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 81,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_string_concat_select\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 50,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_string_concat_having\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 26,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_with_conditional\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 98,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_in_method_args\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 106,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_with_if_statements\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 130,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_in_calculate\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 139,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_in_calculate_column_name\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 140,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_in_minimum\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 141,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_in_maximum\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 142,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_in_average\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 143,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_in_sum\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 144,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_in_select\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 152,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_interpolation_in_first_arg\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 175,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /product\\.rb/\n  end\n\n  def test_select_vulnerability\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Upgrade to Rails 3.1.4. In Rails 3.1.0 `select` helper is vulnerable/,\n      :confidence => 1,\n      :file => /edit\\.html\\.erb/\n  end\n\n  def test_string_buffer_manipulation_bug\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Rails 3.1.0 has a vulnerability in `SafeBuffer`. Upgrade to Rails 3.1.4/,\n      :confidence => 1,\n      :file => /Gemfile/\n  end\n\n  def test_cross_site_request_forgery\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Request Forgery\",\n      :line => 91,\n      :message => /^List specific actions \\(`:only\\ =>\\ \\[\\.\\.\\]`\\)\\ when\\ skipp/,\n      :confidence => 1,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_authentication_skip_before_filter\n    assert_warning :type => :controller,\n      :warning_type => \"Authentication\",\n      :line => 3,\n      :message => /^List specific actions \\(`:only\\ =>\\ \\[\\.\\.\\]`\\)\\ when\\ skipp/,\n      :confidence => 1,\n      :file => /admin_controller\\.rb/\n  end\n\n  def test_authentication_skip_filter\n    assert_warning :type => :controller,\n      :warning_type => \"Authentication\",\n      :line => 5,\n      :message => /^List specific actions \\(`:only\\ =>\\ \\[\\.\\.\\]`\\)\\ when\\ skipp/,\n      :confidence => 1,\n      :file => /admin_controller\\.rb/\n  end\n\n  def test_authentication_skip_require_user\n    assert_warning :type => :controller,\n      :warning_type => \"Authentication\",\n      :line => 4,\n      :message => /^List specific actions \\(`:only\\ =>\\ \\[\\.\\.\\]`\\)\\ when\\ skipp/,\n      :confidence => 1,\n      :file => /admin_controller\\.rb/\n  end\n\n  def test_controller_mixin\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped parameter value near line 1: params\\[:bad\\]/,\n      :confidence => 0,\n      :file => /users\\/mixin_template\\.html\\.erb/\n  end\n\n  def test_controller_mixin_default_render\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped parameter value near line 1: params\\[:bad\\]/,\n      :confidence => 0,\n      :file => /users\\/mixin_default\\.html\\.erb/\n  end\n\n  def test_get_in_resources_block\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /\\/a\\.html\\.erb/\n  end\n\n  def test_get_in_controller_block\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /\\/b\\.html\\.erb/\n  end\n\n  def test_post_with_just_hash_in_controller_block\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /\\/c\\.html\\.erb/\n  end\n\n  def test_put_to_in_controller_block\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /\\/d\\.html\\.erb/\n  end\n\n  def test_match_to_route\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /\\/e\\.html\\.erb/\n  end\n\n  def test_delete_in_resources_block\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /\\/f\\.html\\.erb/\n  end\n\n  def test_route_hash_shorthand\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /\\/g\\.html\\.erb/\n  end\n\n  def test_model_name_in_collection_xss\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped model attribute near line 1: User\\.new\\.bio/,\n      :confidence => 0,\n      :file => /_bio\\.html\\.erb/\n  end\n\n  def test_xss_helper_params_return\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /test_less_simple_helpers\\.html\\.erb/\n  end\n\n  def test_xss_helper_with_args\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /test_less_simple_helpers\\.html\\.erb/\n  end\n\n  def test_xss_helper_assign_ivar\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /test_less_simple_helpers\\.html\\.erb/\n  end\n\n  def test_xss_helper_assign_ivar_twice\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /test_assign_twice\\.html\\.erb/\n  end\n\n  def test_xss_helper_model_return\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /test_simple_helper\\.html\\.erb/\n  end\n\n  def test_xss_multiple_exp_in_string_interpolation\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /test_string_interp\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_select_tag_CVE_2012_3463\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Upgrade\\ to\\ Rails\\ 3\\.1\\.8. In Rails\\ 3\\.1\\.0\\ `select_tag`/,\n      :confidence => 0,\n      :file => /test_select_tag\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_single_quotes_CVE_2012_3464\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Rails\\ 3\\.1\\.0\\ does\\ not\\ escape\\ single\\ quote/,\n      :confidence => 1,\n      :file => /Gemfile/\n  end\n\n  def test_file_access_indirect_user_input\n    assert_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 106,\n      :message => /^Parameter\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 2,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_file_access_in_string_interpolation\n    assert_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 107,\n      :message => /^Cookie\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_file_access_direct_user_input\n    assert_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 108,\n      :message => /^Parameter\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_file_access_model_attribute\n    assert_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :line => 109,\n      :message => /^Model attribute\\ used\\ in\\ file\\ name/,\n      :confidence => 1,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_CVE_2012_3424\n    assert_warning :type => :warning,\n      :warning_type => \"Denial of Service\",\n      :message => /^Vulnerability\\ in\\ digest\\ authentication\\ \\(/,\n      :confidence => 2,\n      :file => /Gemfile/\n  end\n\n  def test_strip_tags_CVE_2012_3465\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Rails\\ 3\\.1\\.0\\ has\\ a\\ vulnerability\\ in\\ `strip/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_sql_injection_CVE_2013_0155\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2013-0155/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_remote_code_execution_CVE_2013_0156_fix\n    assert_no_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Rails\\ 3\\.1\\.0\\ has\\ a\\ remote\\ code\\ execution\\ /,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_denial_of_service_CVE_2013_0269\n    assert_warning :type => :warning,\n      :warning_type => \"Denial of Service\",\n      :message => /^json\\ gem\\ 1\\.5\\.4\\ has\\ a\\ symbol\\ crea/,\n      :confidence => 1,\n      :file => /Gemfile/\n  end\n\n  def test_xss_sanitize_CVE_2013_1857\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 64,\n      :message => /^Rails\\ 3\\.1\\.0\\ has\\ a\\ vulnerability\\ in\\ `sanit/,\n      :confidence => 0,\n      :file => /other_controller\\.rb/\n  end\n\n  def test_xss_sanitize_css_CVE_2013_1855\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 65,\n      :message => /^Rails\\ 3\\.1\\.0\\ has\\ a\\ vulnerability\\ in\\ `sanitize_css`/,\n      :confidence => 0,\n      :file => /other_controller\\.rb/\n  end\n\n  def test_xml_jruby_parsing_CVE_2013_1856_workaround\n    assert_no_warning :type => :warning,\n      :warning_type => \"File Access\",\n      :message => /^Rails\\ 3\\.1\\.0\\ with\\ JRuby\\ has\\ a\\ vulnerabili/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_denial_of_service_CVE_2013_1854\n    assert_warning :type => :warning,\n      :warning_code => 55,\n      :fingerprint => \"2aaf46791b1a8c520cd594aa0b6e382b81b9c8cd9728176a057208e412ec9962\",\n      :warning_type => \"Denial of Service\",\n      :message => /^Rails\\ 3\\.1\\.0\\ has\\ a\\ denial\\ of\\ service\\ vul/,\n      :confidence => 1,\n      :line => 69,\n      :relative_path => \"Gemfile.lock\"\n  end\n\n  def test_denial_of_service_CVE_2013_6414\n    assert_warning :type => :warning,\n      :warning_code => 64,\n      :fingerprint => \"ee4938ce7bc4aa6f37b3d993d6fed813de6b15e5c1ada41146563207c395b0c5\",\n      :warning_type => \"Denial of Service\",\n      :message => /^Rails\\ 3\\.1\\.0\\ has\\ a\\ denial\\ of\\ service\\ vuln/,\n      :confidence => 1,\n      :line => 69,\n      :relative_path => \"Gemfile.lock\"\n  end\n\n  def test_number_to_currency_CVE_2014_0081\n    assert_warning :type => :warning,\n      :warning_code => 73,\n      :fingerprint => \"86f945934ed965a47c30705141157c44ee5c546d044f8de7d573bfab456e97ce\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 69,\n      :message => /^Rails\\ 3\\.1\\.0\\ has\\ a\\ vulnerability\\ in\\ numbe/,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_sql_injection_CVE_2013_6417\n    assert_warning :type => :warning,\n      :warning_code => 69,\n      :fingerprint => \"2f63d663e9f35ba60ef81d56ffc4fbf0660fbc2067e728836176bc18f610f77f\",\n      :warning_type => \"SQL Injection\",\n      :line => 69,\n      :file => /Gemfile.lock/,\n      :message => /^Rails\\ 3\\.1\\.0\\ contains\\ a\\ SQL\\ injection\\ vul/,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_remote_code_execution_CVE_2014_0130\n    assert_warning :type => :warning,\n      :warning_code => 77,\n      :fingerprint => \"e833fd152ab95bf7481aada185323d97cd04c3e2322b90f3698632f4c4c04441\",\n      :warning_type => \"Remote Code Execution\",\n      :line => nil,\n      :message => /^Rails\\ 3\\.1\\.0\\ with\\ globbing\\ routes\\ is\\ vuln/,\n      :confidence => 1,\n      :relative_path => \"config/routes.rb\",\n      :user_input => nil\n  end\n\n  def test_xml_dos_CVE_2015_3227\n    assert_warning :type => :warning,\n      :warning_code => 88,\n      :fingerprint => \"ab42647fbdea61e25c4b794e82a8b315054e2fac4328bb3fd4be6a744889a987\",\n      :warning_type => \"Denial of Service\",\n      :line => 69,\n      :message => /^Rails\\ 3\\.1\\.0\\ is\\ vulnerable\\ to\\ denial\\ of\\ s/,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_basic_auth_CVE_2015_7576\n    assert_warning :type => :warning,\n      :warning_code => 93,\n      :fingerprint => \"b7dd886bf4767c0245001519f8d6c402a9a4cad9211ce6663fb6118c23962057\",\n      :warning_type => \"Timing Attack\",\n      :line => 4,\n      :message => /^Basic\\ authentication\\ in\\ Rails\\ 3\\.1\\.0\\ is\\ v/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, nil, :http_basic_authenticate_with, s(:hash, s(:lit, :name), s(:str, \"superduperadmin\"), s(:lit, :password), s(:str, \"superdupersecret\"), s(:lit, :only), s(:lit, :create))),\n      :user_input => nil\n  end\n\n  def test_denial_of_service_CVE_2016_0751_work_around\n    assert_no_warning :type => :warning,\n      :warning_code => 94,\n      :fingerprint => \"5945a9b096557ee5771c2dd12ea6cbec933b662d169e559f524ba01c44bf2452\",\n      :warning_type => \"Denial of Service\",\n      :line => 69,\n      :message => /^Rails\\ 3\\.1\\.0\\ is\\ vulnerable\\ to\\ denial\\ of\\ s/,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_to_json_with_overwritten_config\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Unescaped parameter value in JSON hash/,\n      :confidence => 0,\n      :line => 1,\n      :file => /json_test\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_in_haml_interp\n    assert_no_warning :type => :template,\n      :warning_code => 5,\n      :fingerprint => \"56acfae7db5bda36a971702c819899043e7f62c8623223f353a1ade876454712\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 2,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/views/users/interpolated_value.html.haml\"\n  end\n\n  def test_cross_site_scripting_escape_html_entities_json\n    assert_warning :type => :warning,\n      :warning_code => 114,\n      :fingerprint => \"c96eb07567e2a7b0ded7cda123645c4e736d3a1b124bb7c0ffaf5070f53dfcf3\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 2,\n      :message => /^HTML\\ entities\\ in\\ JSON\\ are\\ not\\ escaped\\ by/,\n      :confidence => 1,\n      :relative_path => \"config/environments/production.rb\",\n      :code => s(:attrasgn, s(:const, :ActiveSupport), :escape_html_entities_in_json=, s(:false)),\n      :user_input => nil\n  end\n\n  def test_arel_table_in_sql\n    assert_no_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 46,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /other_controller\\.rb/\n  end\n\n  def test_to_sql_interpolation\n    assert_no_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 181,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :file => /product\\.rb/\n  end\n\n  def test_sql_injection_update_all\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 140,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_sql_injection_update_all_interpolation\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 141,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_sql_injection_update_all_interp_array\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 142,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_sql_injection_update_all_order_param\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 143,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_sql_injection_update_all_on_where\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 145,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_sql_injection_update_all_on_where_interp\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 146,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_sql_injection_update_all_where_interp_array\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 147,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_sql_injection_in_pluck\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 177,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n\n  def test_sql_injection_with_interpolated_value\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"b4dff795ce1504c9273a13add535c895ceb920e9091ddfff614fd2ea4c3696ed\",\n      :warning_type => \"SQL Injection\",\n      :line => 33,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:lvar, :parent_id)\n  end\n\n  def test_sql_injection_with_id_call\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"b9ade31073676589cf3b6a88de30105f67cc8170e87f2c2fd1c972f50ad2a3b3\",\n      :warning_type => \"SQL Injection\",\n      :line => 34,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:call, nil, :child_id)\n  end\n\n  def test_sql_injection_primary_key\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"b9a4789e68bee09651fc948e3c78b60fb6b611a96b284eea2cb37b2ca9e83d97\",\n      :warning_type => \"SQL Injection\",\n      :line => 47,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:call, s(:const, :User), :primary_key)\n  end\n\n  def test_sql_injection_quoted_table_name\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"d62a8796ff7e8f7547cea5352112294354b0400b01ab55388fa802a655751ed3\",\n      :warning_type => \"SQL Injection\",\n      :line => 47,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:call, s(:const, :User), :quoted_table_name)\n  end\n\n  def test_sql_injection_table_name_prefix\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"841b2af00d0992f49b753a3a6c1118a95ec9a7519ec434d7b0613d40d9fd67fe\",\n      :warning_type => \"SQL Injection\",\n      :line => 47,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:call, nil, :table_name_prefix)\n  end\n\n  def test_sql_injection_dynamic_finders\n    assert_warning :type => :warning,\n      :warning_code => 92,\n      :fingerprint => \"911d08b750e5c583e0c1fcfffc229f284d10d69a2bb2f78ac068ef44585f8ae1\",\n      :warning_type => \"SQL Injection\",\n      :line => 197,\n      :message => /^MySQL\\ integer\\ conversion\\ may\\ cause\\ 0\\ to\\ /,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :find_by_name_and_password, s(:call, s(:params), :[], s(:lit, :name)), s(:call, s(:params), :[], s(:lit, :pass))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :name))\n\n    assert_warning :type => :warning,\n      :warning_code => 92,\n      :fingerprint => \"62dce25499ad7885cb6b838c44095707d045c40c474d09e862e82ba206f4837a\",\n      :warning_type => \"SQL Injection\",\n      :line => 198,\n      :message => /^MySQL\\ integer\\ conversion\\ may\\ cause\\ 0\\ to\\ /,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :find_by_reset_code, s(:call, s(:params), :[], s(:lit, :code))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :code))\n  end\n\n  def test_validates_format\n    assert_warning :type => :model,\n      :warning_type => \"Format Validation\",\n      :line => 2,\n      :message => /^Insufficient\\ validation\\ for\\ `username`\\ u/,\n      :confidence => 0,\n      :file => /account\\.rb/\n  end\n\n  def test_validates_format_with\n    assert_warning :type => :model,\n      :warning_type => \"Format Validation\",\n      :line => 3,\n      :message => /^Insufficient\\ validation\\ for\\ `phone`\\ usin/,\n      :confidence => 0,\n      :file => /account\\.rb/\n  end\n\n  def test_validates_format_with_short_regex\n    assert_warning :type => :model,\n      :warning_type => \"Format Validation\",\n      :line => 4,\n      :message => /^Insufficient\\ validation\\ for\\ `first_name`/,\n      :confidence => 0,\n      :file => /account\\.rb/\n  end\n\n  def test_session_secret_token\n    assert_warning :type => :warning,\n      :warning_type => \"Session Setting\",\n      :line => 7,\n      :message => /^Session\\ secret\\ should\\ not\\ be\\ included\\ in/,\n      :confidence => 0,\n      :file => /secret_token\\.rb/\n  end\n\n  def test_unsafe_reflection_constantize\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 9,\n      :message => /^Unsafe\\ reflection\\ method\\ `constantize`\\ called\\ on/,\n      :confidence => 0,\n      :file => /admin_controller\\.rb/\n  end\n\n\n  def test_unsafe_reflection_safe_constantize\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 12,\n      :message => /^Unsafe\\ reflection\\ method\\ `safe_constantize`\\ called\\ on/,\n      :confidence => 0,\n      :file => /admin_controller\\.rb/\n  end\n\n  def test_unsafe_reflection_qualified_const_get\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 14,\n      :message => /^Unsafe\\ reflection\\ method\\ `qualified_const_get`\\ called\\ with/,\n      :confidence => 0,\n      :file => /admin_controller\\.rb/\n  end\n\n\n  def test_unsafe_relection_const_get\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 16,\n      :message => /^Unsafe\\ reflection\\ method\\ `const_get`\\ calle/,\n      :confidence => 0,\n      :file => /admin_controller\\.rb/\n  end\n\n  def test_unsafe_reflection_constantize_indirect\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :line => 18,\n      :message => /^Unsafe\\ reflection\\ method\\ `constantize`\\ cal/,\n      :confidence => 1,\n      :file => /admin_controller\\.rb/\n  end\n\n  def test_csv_load\n    assert_warning :type => :warning,\n      :warning_code => 25,\n      :fingerprint => \"3b58b691bf7ef0b244ee463aa812e4e6ffe3fe1075c8bd138c0cb5a77f365f41\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 69,\n      :message => /^`CSV\\.load`\\ called\\ with\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/other_controller.rb\"\n  end\n\n  def test_marshal_load\n    assert_warning :type => :warning,\n      :warning_code => 25,\n      :fingerprint => \"ecdb984aa40fbe7d42a74ab474a412579b42b36c630bcac640d382e108109437\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 71,\n      :message => /^`Marshal\\.load`\\ called\\ with\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/other_controller.rb\"\n  end\n\n  def test_marshal_restore\n    assert_warning :type => :warning,\n      :warning_code => 25,\n      :fingerprint => \"78ef96a81c8b02f97992a7056e4d9696ab238e12bc8a7a3204df29ef11e0a3fe\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 73,\n      :message => /^`Marshal\\.restore`\\ called\\ with\\ model\\ attrib/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/other_controller.rb\"\n  end\n\n  def test_attr_accessible_with_role\n    assert_no_warning :type => :model,\n      :warning_code => 17,\n      :fingerprint => \"77c353ad8e5fc9880775ed436bbfa37b005b43aa2978186de92b6916f46fac39\",\n      :warning_type => \"Mass Assignment\",\n      :message => \"Potentially dangerous attribute available for mass assignment: :admin\",\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\"\n  end\n\n  def test_attr_accessible_not_matching_regex\n    assert_no_warning :type => :model,\n      :warning_code => 60,\n      :fingerprint => \"e933f99c33bece852891a466b5b0fc629d9f20ba80ff3bbc42adfd239d5a5b48\",\n      :warning_type => \"Mass Assignment\",\n      :message => \"Potentially dangerous attribute available for mass assignment: :blah_admin_blah\",\n      :confidence => 0,\n      :relative_path => \"app/models/account.rb\"\n  end\n\n  def test_wrong_model_attributes_in_haml\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"8851713f0af477e60090607b814ba68055e4ac1cf19df0628fddd961ff87e763\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/other/test_model_in_haml.html.haml\"\n  end\n\n  def test_right_model_attribute_in_haml\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"3310ef4a4bde8b120fd5d421565ee416af815404e7c116a8069052e8732589d0\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/other/test_model_in_haml.html.haml\"\n  end\n\n  def test_information_disclosure_detailed_exceptions_override\n    assert_warning :type => :warning,\n      :warning_code => 62,\n      :fingerprint => \"e427e61359aa0f4f1e1a689066ff1c5034a54c9518da46755e308252b35b054d\",\n      :warning_type => \"Information Disclosure\",\n      :line => 29,\n      :message => /^Detailed\\ exceptions\\ may\\ be\\ enabled\\ in\\ `s/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/admin_controller.rb\"\n  end\n\n  def test_command_injection_interpolation_inside_interpolation\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"e12b7628a5656be025d37569da24a10157702f541cae63eca5d4211ff1ce632a\",\n      :warning_type => \"Command Injection\",\n      :line => 34,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/admin_controller.rb\",\n      :user_input => s(:call, nil, :why?)\n  end\n\n  def test_command_injection_or_literal_system\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"7de48cc753c090a61ac49a6885bc87198b1a7a72e5629eb2a188b671b95c7f13\",\n      :warning_type => \"Command Injection\",\n      :line => 42,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/admin_controller.rb\"\n  end\n\n  def test_command_injection_or_literal_backticks\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"a9ec8db240351f05e084a6acc9f7980d97718eb4cb386d9ea8079d224dfecef9\",\n      :warning_type => \"Command Injection\",\n      :line => 43,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/admin_controller.rb\"\n  end\n\n  def test_command_injection_integer_command\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"44d7403b6d2dfe4b74c32b80d924fed3d034637f0e13b3c31193ef9279a674f3\",\n      :warning_type => \"Command Injection\",\n      :line => 45,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/admin_controller.rb\"\n  end\n\n  def test_command_injection_integer_exec\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"11ab37cedddb3b4c9cd1c29db6b6ab8cd8a6a0063862448075cc22e9cd8b0882\",\n      :warning_type => \"Command Injection\",\n      :line => 46,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/admin_controller.rb\"\n  end\n\n  def test_eval_from_lambda_filter\n    assert_warning :type => :warning,\n      :warning_code => 13,\n      :fingerprint => \"58e5c4088dc57057a112ab5c472633752f787b8f6b0437bbd19d82fa06afbddb\",\n      :warning_type => \"Dangerous Eval\",\n      :line => 53,\n      :message => /^Parameter\\ value\\ evaluated\\ as\\ code/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/admin_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :t))\n  end\n\n  def test_cross_site_scripting_CVE_2016_6316\n    assert_warning :type => :warning,\n      :warning_code => 102,\n      :fingerprint => \"1a1b3368951a20d02976c9207e5981df37d1bfa7dbbdb925eecd9013ecfeaa0f\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 69,\n      :message => /^Rails\\ 3\\.1\\.0\\ `content_tag`\\ does\\ not\\ escape\\ /,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_unmaintained_dependency_rails\n    assert_warning check_name: \"EOLRails\",\n      type: :warning,\n      warning_code: 120,\n      fingerprint: \"d84924377155b41e094acae7404ec2e521629d86f97b0ff628e3d1b263f8101c\",\n      warning_type: \"Unmaintained Dependency\",\n      line: 69,\n      message: /^Support\\ for\\ Rails\\ 3\\.1\\.0\\ ended\\ on\\ 2016\\-06/,\n      confidence: 0,\n      relative_path: \"Gemfile.lock\",\n      code: nil,\n      user_input: nil\n  end\nend\n"
  },
  {
    "path": "test/tests/rails32.rb",
    "content": "# NOTE: Please do not add any further tests to the Rails 3.2 application unless\n# the issue being tested specifically applies to Rails 3.2 and not the other\n# versions.\n# If possible, please use the rails5 app. \n\nrequire_relative '../test'\n\nclass Rails32Tests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def expected\n    @expected ||= {\n      :controller => 8,\n      :model => 5,\n      :template => 11,\n      :generic => 23 }\n\n    if RUBY_PLATFORM == 'java'\n      @expected[:generic] += 1\n    end\n\n    @expected\n  end\n\n  def report\n    @@report ||= BrakemanTester.run_scan \"rails3.2\", \"Rails 3.2\"\n  end\n\n  def test_rc_version_number\n    assert_equal \"3.2.9.rc2\", report[:config].rails_version\n  end\n\n  def test_sql_injection_CVE_2012_5664\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-5664/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_sql_injection_CVE_2013_0155\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2013-0155/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_remote_code_execution_CVE_2013_0156\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ has\\ a\\ remote\\ code\\ execut/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_remote_code_execution_CVE_2013_0269\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^json\\ gem\\ 1\\.7\\.5\\ has\\ a\\ remote\\ code/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_xss_sanitize_css_CVE_2013_1855\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 2,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ has\\ a\\ vulnerability\\ in\\ `s/,\n      :confidence => 0,\n      :file => /sanitized\\.html\\.erb/\n  end\n\n  def test_xml_jruby_parsing_CVE_2013_1856\n    if RUBY_PLATFORM == 'java'\n      assert_warning :type => :warning,\n        :warning_type => \"File Access\",\n        :message => /^Rails\\ 3\\.2\\.9\\.rc2 with\\ JRuby\\ has\\ a\\ vulnerabili/,\n        :confidence => 0,\n        :file => /Gemfile/\n    end\n  end\n\n  def test_denial_of_service_CVE_2013_1854\n    assert_warning :type => :warning,\n      :warning_code => 55,\n      :fingerprint => \"2aaf46791b1a8c520cd594aa0b6e382b81b9c8cd9728176a057208e412ec9962\",\n      :warning_type => \"Denial of Service\",\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ has\\ a\\ denial\\ of\\ service\\ vul/,\n      :confidence => 1,\n      :line => 64,\n      :relative_path => \"Gemfile.lock\"\n  end\n\n  def test_i18n_xss_CVE_2013_4491\n    assert_warning :type => :warning,\n      :warning_code => 63,\n      :fingerprint => \"7ef985c538fd302e9450be3a61b2177c26bbfc6ccad7a598006802b0f5f8d6ae\",\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ has\\ an\\ XSS\\ vulnerability/,\n      :file => /Gemfile\\.lock/,\n      :confidence => 1,\n      :relative_path => /Gemfile.lock/\n  end\n\n  def test_number_to_currency_CVE_2014_0081\n    assert_warning :type => :warning,\n      :warning_code => 73,\n      :fingerprint => \"86f945934ed965a47c30705141157c44ee5c546d044f8de7d573bfab456e97ce\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 64,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ has\\ a\\ vulnerability\\ in\\ n/,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_sql_injection_CVE_2013_6417\n    assert_warning :type => :warning,\n      :warning_code => 69,\n      :fingerprint => \"2f63d663e9f35ba60ef81d56ffc4fbf0660fbc2067e728836176bc18f610f77f\",\n      :warning_type => \"SQL Injection\",\n      :line => 64,\n      :file => /Gemfile.lock/,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2 contains\\ a\\ SQL\\ injection\\ vul/,\n      :confidence => 0,\n      :relative_path => /Gemfile.lock/,\n      :user_input => nil\n  end\n\n  def test_denial_of_service_CVE_2014_0082\n    assert_warning :type => :warning,\n      :warning_code => 75,\n      :fingerprint => \"99b6df435353f17dff4b0d7dfeb5f21e5c0e8045dc73533e456baf78f1fc2215\",\n      :warning_type => \"Denial of Service\",\n      :line => 64,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ has\\ a\\ denial\\ of\\ service\\ /,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_remote_code_execution_CVE_2014_0130\n    assert_warning :type => :warning,\n      :warning_code => 77,\n      :fingerprint => \"93393e44a0232d348e4db62276b18321b4cbc9051b702d43ba2fd3287175283c\",\n      :warning_type => \"Remote Code Execution\",\n      :line => nil,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ with\\ globbing\\ routes\\ is\\ /,\n      :confidence => 0,\n      :relative_path => \"config/routes.rb\",\n      :user_input => nil\n  end\n\n  def test_xml_dos_2015_3227\n    assert_warning :type => :warning,\n      :warning_code => 88,\n      :fingerprint => \"ab42647fbdea61e25c4b794e82a8b315054e2fac4328bb3fd4be6a744889a987\",\n      :warning_type => \"Denial of Service\",\n      :line => 64,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2 is vulnerable to denial of service via XML parsing \\(CVE-2015-3227\\). Upgrade to Rails 3.2.22/,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_denial_of_service_CVE_2015_0751\n    assert_warning :type => :warning,\n      :warning_code => 94,\n      :fingerprint => \"5945a9b096557ee5771c2dd12ea6cbec933b662d169e559f524ba01c44bf2452\",\n      :warning_type => \"Denial of Service\",\n      :line => 64,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ is\\ vulnerable\\ to\\ denial\\ /,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\"\n  end\n\n  def test_cross_site_scripting_CVE_2016_6316\n    assert_warning :type => :warning,\n      :warning_code => 102,\n      :fingerprint => \"1a1b3368951a20d02976c9207e5981df37d1bfa7dbbdb925eecd9013ecfeaa0f\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 64,\n      :message => /^Rails\\ 3\\.2\\.9\\.rc2\\ `content_tag`\\ does\\ not\\ esc/,\n      :confidence => 1,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_path_traversal_sprockets_CVE_2018_3760\n    assert_warning :type => :warning,\n      :warning_code => 108,\n      :fingerprint => \"f22053251239417f0571439b41f7ea8ff49a7e97f4147578f021a568c2c3ba16\",\n      :warning_type => \"Path Traversal\",\n      :line => 87,\n      :message => /^sprockets\\ 2\\.1\\.3\\ has\\ a\\ path\\ traversal\\ vul/,\n      :confidence => 2,\n      :relative_path => \"Gemfile.lock\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_redirect_1\n    assert_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 14,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :file => /removal_controller\\.rb/\n  end\n\n  def test_cross_site_scripting_2\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /_partial\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_3\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /controller_removed\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_4\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 2,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /implicit_render\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_5\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /_form\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_6\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /mixed_in\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_7\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 15,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /show\\.html\\.erb/\n  end\n\n  def test_escaped_params_to_json\n    assert_no_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 21,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /show\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_in_slim_param\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /slimming\\.html\\.slim/\n  end\n\n  def test_cross_site_scripting_in_slim_model\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /slimming\\.html\\.slim/\n  end\n\n  def test_cross_site_scripting_slim_partial_param\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 6,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :file => /_slimmer\\.html\\.slim/\n  end\n\n  def test_cross_site_scripting_slim_partial_model\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 8,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /_slimmer\\.html\\.slim/\n  end\n\n  def test_mass_assignment_default\n    assert_no_warning :type => :model,\n      :warning_type => \"Attribute Restriction\",\n      :message => /^Mass\\ assignment\\ is\\ not\\ restricted\\ using\\ /,\n      :confidence => 0,\n      :file => /account\\.rb/\n  end\n\n  def test_session_secret_token\n    assert_warning :type => :warning,\n      :warning_type => \"Session Setting\",\n      :line => 7,\n      :message => /^Session\\ secret\\ should\\ not\\ be\\ included\\ in/,\n      :confidence => 0,\n      :file => /secret_token\\.rb/\n  end\n\n  def test_model_attr_accessible_admin\n    assert_warning :type => :model,\n      :warning_code => 60,\n      :warning_type => \"Mass Assignment\",\n      :message => \"Potentially dangerous attribute available for mass assignment: :admin\",\n      :confidence => 0, #HIGH\n      :file => /user\\.rb/\n  end\n\n  def test_model_attr_accessible_account_id\n    assert_warning :type => :model,\n      :warning_code => 60,\n      :fingerprint => \"add78ac0c12cea9335ad3128f17fd0ff8b0f3772daca1d0d109f9dc02ea2df59\",\n      :warning_type => \"Mass Assignment\",\n      :message => \"Potentially dangerous attribute available for mass assignment: :account_id\",\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\"\n  end\n\n  def test_model_attr_accessible_account_banned\n    assert_warning :type => :model,\n      :warning_code => 60,\n      :warning_type => \"Mass Assignment\",\n      :message => \"Potentially dangerous attribute available for mass assignment: :banned\",\n      :confidence => 1, #MED\n      :file => /account\\.rb/\n  end\n\n  def test_model_attr_accessible_status_id\n    assert_warning :type => :model,\n      :warning_code => 60,\n      :warning_type => \"Mass Assignment\",\n      :message => \"Potentially dangerous attribute available for mass assignment: :status_id\",\n      :confidence => 2, #LOW\n      :file => /user\\.rb/\n  end\n\n  def test_model_attr_accessible_plan_id\n    assert_warning :type => :model,\n      :warning_type => \"Mass Assignment\",\n      :message => \"Potentially dangerous attribute available for mass assignment: :plan_id\",\n      :confidence => 2,\n      :file => /account\\.rb/\n  end\n\n  def test_two_distinct_warnings_cant_have_same_fingerprint\n    assert_equal report[:model_warnings].map(&:fingerprint), report[:model_warnings].map(&:fingerprint).uniq\n  end\n\n  def test_controller_command_injection_direct_from_dependency\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :line => 3,\n      :message => /^Possible command injection/,\n      :confidence => 0,\n      :file => /command_dependency\\.rb/,\n      :relative_path => \"app/controllers/exec_controller/command_dependency.rb\",\n      :format_code => /params\\[:user_input\\]/\n  end\n\n  def test_model_command_injection_direct_from_dependency\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :line => 3,\n      :message => /^Possible command injection/,\n      :confidence => 0,\n      :file => /command_dependency\\.rb/,\n      :relative_path => \"app/models/user/command_dependency.rb\",\n      :format_code => /params\\[:user_input\\]/\n  end\n\n  def test_controller_default_routes\n    # Test to ensure warnings are generated for loose routes\n    assert_warning :type => :controller,\n      :warning_type => \"Default Routes\",\n      :message => /`GlobGetController.*get` requests/,\n      :fingerprint => \"6550aaf3da845a600a9c8fb767d08489679a9e3d89554db3c920ddb4eafcfb8e\",\n      :file => /routes\\.rb/\n\n    assert_warning :type => :controller,\n      :warning_type => \"Default Routes\",\n      :message => /`GlobMatchController.*matched` requests/,\n      :fingerprint => \"fb878909d1635de22ddc819e33e6a75e7f2cce0ff1efd2b7e76b361be88bb73e\",\n      :file => /routes\\.rb/\n\n    assert_warning :type => :controller,\n      :warning_type => \"Default Routes\",\n      :message => /`GlobPostController.*post` requests/,\n      :fingerprint => \"e5364369e3c89e5632aac3645e183037cc18de49f1b67547dca0c7febb6c849f\",\n      :file => /routes\\.rb/\n\n    assert_warning :type => :controller,\n      :warning_type => \"Default Routes\",\n      :message => /`GlobPutController.*put` requests/,\n      :fingerprint => \"b85eeac90866fc04b4bea19c971aed4f2458afe53c908aa7162eb1e46b84f9b6\",\n      :file => /routes\\.rb/\n\n    assert_warning :type => :controller,\n      :warning_type => \"Default Routes\",\n      :message => /`FooPostController.*post` requests/,\n      :fingerprint => \"880355a0a87704aa0d615dea6a175ba78711d1593843a596935f95cac3abc8a5\",\n      :file => /routes\\.rb/\n\n    assert_warning :type => :controller,\n      :warning_type => \"Default Routes\",\n      :message => /`FooGetController.*get` requests/,\n      :fingerprint => \"e4f29dc75741f74327ce95678173d3d5fe296335275f062c06d1348678f6a339\",\n      :file => /routes\\.rb/\n\n    assert_warning :type => :controller,\n      :warning_type => \"Default Routes\",\n      :message => /`FooPutController.*put` requests/,\n      :fingerprint => \"05a92f06689436b7b8189c358baab371de5f0fb7936ab206a11b251b0e5f7570\",\n      :file => /routes\\.rb/\n\n    assert_warning :type => :controller,\n      :warning_type => \"Default Routes\",\n      :message => /`BarMatchController.*matched` requests/,\n      :fingerprint => \"857efc249dfd1b5086dcf79c35e31ef19a7782d03b3beaa12f55f8634b543d2d\",\n      :file => /routes\\.rb/\n  end\n\n  def test_command_injection_from_namespaced_model_1\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :class => :\"MultiModel::Model1\",\n      :line => 5,\n      :message => /^Possible command injection/,\n      :confidence => 0,\n      :file => /multi_model\\.rb/,\n      :relative_path => \"app/models/multi_model.rb\",\n      :format_code => /params\\[:user_input\\]/\n  end\n\n  def test_command_injection_from_namespaced_model_2\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :class => :\"MultiModel::Model2\",\n      :line => 13,\n      :message => /^Possible command injection/,\n      :confidence => 0,\n      :file => /multi_model\\.rb/,\n      :relative_path => \"app/models/multi_model.rb\",\n      :format_code => /params\\[:user_input2\\]/\n  end\n\n  def test_unmaintained_dependency_rails\n    assert_warning check_name: \"EOLRails\",\n      type: :warning,\n      warning_code: 120,\n      fingerprint: \"d84924377155b41e094acae7404ec2e521629d86f97b0ff628e3d1b263f8101c\",\n      warning_type: \"Unmaintained Dependency\",\n      line: 64,\n      message: /^Support\\ for\\ Rails\\ 3\\.2\\.9\\.rc2\\ ended\\ on\\ 201/,\n      confidence: 0,\n      relative_path: \"Gemfile.lock\"\n  end\nend\n"
  },
  {
    "path": "test/tests/rails4.rb",
    "content": "require_relative '../test'\n\nclass Rails4Tests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def report\n    external_checks_path = File.expand_path(File.join(File.dirname(__FILE__), \"..\", \"/apps/rails4/external_checks\"))\n    # There are additional options in config/brakeman.yml\n    @@report ||= BrakemanTester.run_scan \"rails4\", \"Rails 4\", {:additional_checks_path => [external_checks_path]}\n\n  ensure\n    # Cleanup - scan is already run, so we can remove this test check\n    if Brakeman::Checks.optional_checks.include? Brakeman::CheckExternalCheckTest\n      assert Brakeman::Checks.optional_checks.delete Brakeman::CheckExternalCheckTest\n    end\n  end\n\n  def expected\n    @expected ||= {\n      :controller => 0,\n      :model => 3,\n      :template => 8,\n      :generic => 92\n    }\n  end\n\n  def test_redirects_to_created_model_do_not_warn\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"fedba22f0fbcd96dcaa0b2628ccedba2c0880870992d05b817697efbb36e134f\",\n      :warning_type => \"Redirect\",\n      :line => 14,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/application_controller.rb\",\n      :user_input => s(:call, s(:const, :User), :create)\n\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"1d2d4b0a59ed26a6d591094714dbee81a60a3e686429a44fe2d80f87b94bc555\",\n      :warning_type => \"Redirect\",\n      :line => 18,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/application_controller.rb\",\n      :user_input => s(:call, s(:const, :User), :create!)\n  end\n\n  def test_redirects_with_explicit_host_do_not_warn\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"b5a1bf2d1634564c82436e569c9ea874e355d4538cdc4dc4a8e6010dc9a7c11e\",\n      :warning_type => \"Redirect\",\n      :line => 59,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => s(:params, s(:lit, :host), s(:str, \"example.com\"))\n\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"d04df9716ee4c8cadcb5f046e73ee06c3f1606e8b522f6e3130ac0a33fbc4d73\",\n      :warning_type => \"Redirect\",\n      :line => 61,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => s(:params, s(:lit, :host), s(:call, s(:const, :User), :canonical_url))\n\n    assert_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"25846ea0cd5178f2af4423a9fc1d7212983ee7f7ba4ca9f35f890e7ef00d9bf9\",\n      :warning_type => \"Redirect\",\n      :line => 63,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => s(:params, s(:lit, :host), s(:call, s(:params), :[], s(:lit, :host)))\n  end\n\n  def test_redirect_with_only_path_in_wrong_method\n    assert_warning :type => :warning,\n    :warning_code => 18,\n    :warning_type => \"Redirect\",\n    :line => 34,\n    :message => /^Possible\\ unprotected\\ redirect/,\n    :confidence => 0,\n    :relative_path => \"app/controllers/application_controller.rb\"\n  end\n\n  def test_redirect_with_unsafe_hash_and_only_path_do_not_warn\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :warning_type => \"Redirect\",\n      :line => 38,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/application_controller.rb\"\n\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :warning_type => \"Redirect\",\n      :line => 42,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/application_controller.rb\"\n  end\n\n  def test_session_secret_token\n    assert_warning :type => :generic,\n      :warning_type => \"Session Setting\",\n      :fingerprint => \"715ad9c0d76f57a6a657192574d528b620176a80fec969e2f63c88eacab0b984\",\n      :line => 12,\n      :message => /^Session\\ secret\\ should\\ not\\ be\\ included\\ in/,\n      :confidence => 0,\n      :file => /secret_token\\.rb/,\n      :relative_path => \"config/initializers/secret_token.rb\"\n  end\n\n  def test_session_secrets_yaml\n    assert_warning :type => :warning,\n      :warning_code => 29,\n      :fingerprint => \"f0ee1cc1980474c82a013645508f002dcc801e00db5592f7dd8cd6bdb93c73fe\",\n      :warning_type => \"Session Setting\",\n      :line => 22,\n      :message => /^Session\\ secret\\ should\\ not\\ be\\ included\\ in/,\n      :confidence => 0,\n      :relative_path => \"config/secrets.yml\",\n      :user_input => nil\n  end\n\n  def test_session_manipulation\n    assert_warning :type => :warning,\n      :warning_code => 89,\n      :fingerprint => \"7cb52cac7d8562181aab8f09a34f6393708261c60d2a485a0b89ebd3e8f4b2f4\",\n      :warning_type => \"Session Manipulation\",\n      :line => 92,\n      :message => /^Parameter\\ value\\ used\\ as\\ key\\ in\\ session\\ h/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_session_manipulation_indirect\n    assert_warning :type => :warning,\n      :warning_code => 89,\n      :fingerprint => \"08cbdbed8bcd19a7746434865a0e4a4dc363dd980953cc5b535a318970c95d20\",\n      :warning_type => \"Session Manipulation\",\n      :line => 93,\n      :message => /^Parameter\\ value\\ used\\ as\\ key\\ in\\ session\\ h/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :token))\n  end\n\n  def test_json_escaped_by_default_in_rails_4\n    assert_no_warning :type => :template,\n      :warning_code => 5,\n      :fingerprint => \"3eedfa40819ce95d1d999ad19464023688a0e8bb881fc3e7683b6c3fffb7e51f\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ model\\ attribute\\ in\\ JSON\\ hash/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/index.html.erb\"\n\n    assert_no_warning :type => :template,\n      :warning_code => 5,\n      :fingerprint => \"fb0cb7e94e9a4bebd81ef44b336e02f68bf24f2c40e28d4bb5c21641276ea6cf\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 2,\n      :relative_path => \"app/views/users/index.html.erb\"\n\n    assert_no_warning :type => :template,\n      :warning_code => 5,\n      :fingerprint => \"8ce0a9eacf25be1f862b9074e6ba477d2f0e2ac86955b8510052984570b92d14\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped\\ parameter\\ value\\ in\\ JSON\\ hash/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/index.html.erb\"\n\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"b107fcc7742084a766a31332ba5c126f1c1a1cc062884f879dc3204c5f7620c5\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/index.html.erb\"\n  end\n\n  def test_information_disclosure_local_request_config\n    assert_warning :type => :warning,\n      :warning_code => 61,\n      :fingerprint => \"081f5d87a244b41d3cf1d5994cb792d2cec639cd70e4e306ffe1eb8abf0f32f7\",\n      :warning_type => \"Information Disclosure\",\n      :message => /^Detailed\\ exceptions\\ are\\ enabled\\ in\\ produ/,\n      :confidence => 0,\n      :relative_path => \"config/environments/production.rb\"\n  end\n\n  def test_information_disclosure_detailed_exceptions_override\n    assert_warning :type => :warning,\n      :warning_code => 62,\n      :fingerprint => \"e023f55d7d83631e750435a7d8f432e7e6d0d87b0a82706f91f247ce004830c2\",\n      :warning_type => \"Information Disclosure\",\n      :line => 6,\n      :message => /^Detailed\\ exceptions\\ may\\ be\\ enabled\\ in\\ `s/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/application_controller.rb\"\n  end\n\n  def test_redirect_with_instance_variable_from_block\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"e024f0cf67432409ec4afc80216fb2f6c9929fbbd32c2421e8867cd254f22d04\",\n      :warning_type => \"Redirect\",\n      :line => 12,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\"\n  end\n\n  def test_try_and_send_collapsing_with_sqli\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"c96c2984c1ce4f9a0f1205c9e7ac4707253a0553ecb6c7e9d6d4b88c92db7098\",\n      :warning_type => \"SQL Injection\",\n      :line => 17,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :table))\n\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"004e5d6afb7ce520f1a67b65ace238f763ca2feb6a7f552f7dcc86ed3f67a189\",\n      :warning_type => \"SQL Injection\",\n      :line => 16,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :query))\n  end\n\n  def test_nested_send\n    assert_warning :type => :warning,\n      :warning_code => 23,\n      :fingerprint => \"8034183b1b7e4b3d7ad4d60c59e2de9252f277c8ab5dfb408f628b15f03645c3\",\n      :warning_type => \"Dangerous Send\",\n      :line => 72,\n      :message => /^User\\ controlled\\ method\\ execution/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_sql_injection_connection_execute\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"7e96859dfdd7755eaa51af9ee04731c739af364215a97673b7576f348e88fcf1\",\n      :warning_type => \"SQL Injection\",\n      :line => 8,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/account.rb\",\n      :user_input => s(:call, nil, :version)\n  end\n\n  def test_sql_injection_select_rows\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"f1a6d026c789b6a029812171695e88fc0de85116d6bba6df61235ccca8194827\",\n      :warning_type => \"SQL Injection\",\n      :line => 54,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :published))\n  end\n\n  def test_sql_injection_select_values\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"700c504a68786c6a8d7a7125b5a722ede615e0f998f3c385d65bd12189220a99\",\n      :warning_type => \"SQL Injection\",\n      :line => 47,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => s(:call, s(:iter, s(:call, s(:call, nil, :destinations), :map), s(:args, :d), s(:call, s(:lvar, :d), :id)), :join, s(:str, \",\"))\n  end\n\n  def test_sql_injection_exec_query\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"52da65996b98cd05c8a515f9cee489bb086448be56b7aa87a20d513afe47d7b8\",\n      :warning_type => \"SQL Injection\",\n      :line => 12,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/account.rb\",\n      :user_input => s(:call, s(:self), :type)\n  end\n\n  def test_sql_injection_exec_update\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"cf9a74253c027dafb7f6bc090b0c4a7c36e9982d15c46e40bda37eeee78966ef\",\n      :warning_type => \"SQL Injection\",\n      :line => 5,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/account.rb\",\n      :user_input => s(:call, s(:self), :type)\n  end\n\n  def test_sql_injection_in_select_args\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"bd8c539a645aa417d538cbe7b658cc1c9743f61d1e90c948afacc7e023b30a62\",\n      :warning_type => \"SQL Injection\",\n      :line => 68,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_sql_injection_sanitize\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"bf92408cc7306b3b2f74cac830b9328a1cc2cc8d7697eb904d04f5a2d46bc31c\",\n      :warning_type => \"SQL Injection\",\n      :line => 3,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :age))\n\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"83d8270fd90fb665f2174fe170f51e94945de02879ed617f2f45d4434d5e5593\",\n      :warning_type => \"SQL Injection\",\n      :line => 3,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:call, nil, :sanitize, s(:lvar, :x))\n  end\n\n  def test_sql_injection_chained_call_in_scope\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"aa073ab210f9f4a800b5595241a6274656d37087a4f433d4b596516e1227d91b\",\n      :warning_type => \"SQL Injection\",\n      :line => 6,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:lvar, :col)\n  end\n\n  def test_sql_injection_in_find_by\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"660d7f796162d23ff209f2d35bb647b3d0cc6ad280e9320ce9d3b2853c508730\",\n      :warning_type => \"SQL Injection\",\n      :line => 47,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :age_limit))\n  end\n\n  def test_sql_injection_in_find_by!\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"6f743b0a084c132d3bd074a0d22e197d6c81018028f6166324de1970616c4cbd\",\n      :warning_type => \"SQL Injection\",\n      :line => 48,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :user_search))\n  end\n\n  def test_sql_injection_exists_to_s\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"7161505aefba0df5f732d479904a220ea52f0aff3ab1bfa4d8b6170854943d7e\",\n      :warning_type => \"SQL Injection\",\n      :line => 125,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :exists?, s(:call, s(:call, s(:params), :[], s(:lit, :x)), :to_s)),\n      :user_input => s(:call, s(:call, s(:params), :[], s(:lit, :x)), :to_s)\n  end\n\n  def test_dynamic_render_path_with_before_action\n    assert_warning :type => :warning,\n      :warning_code => 99,\n      :fingerprint => \"4b5d8c35b22fbdfbfabfd07343c8466ec941d5b78afbd574cf6ce76c68080c85\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 14,\n      :message => /^Passing\\ query\\ parameters\\ to\\ `render`/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:params), :[], s(:lit, :page)), s(:hash)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :page))\n  end\n\n  def test_dynamic_render_path_with_prepend_before_action\n    assert_warning :type => :warning,\n      :warning_code => 99,\n      :fingerprint => \"dd7110db0e7948d5e7047029e73ad570435e62e3ef8f3091eef57e15a11b6654\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 19,\n      :message => /^Passing\\ query\\ parameters\\ to\\ `render`/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:params), :[], s(:lit, :page)), s(:hash)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :page))\n  end\n\n  def test_dynamic_render_path_1\n    assert_warning check_name: \"Render\",\n      type: :warning,\n      warning_code: 15,\n      fingerprint: \"5b2267a68b4bfada283b59bdb9f453489111a5f2c335737588f88135d99426fa\",\n      warning_type: \"Dynamic Render Path\",\n      line: 14,\n      message: /^Render\\ path\\ contains\\ parameter\\ value/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:render, :action, s(:call, s(:params), :[], s(:lit, :page)), s(:hash)),\n      user_input: s(:call, s(:params), :[], s(:lit, :page))\n  end\n\n  def test_dynamic_render_path_2\n    assert_warning check_name: \"Render\",\n      type: :warning,\n      warning_code: 15,\n      fingerprint: \"fa1ad77b62059d1aeeb48217a94cc03a0109b1f17d8332c0e3a5718360de9a8c\",\n      warning_type: \"Dynamic Render Path\",\n      line: 19,\n      message: /^Render\\ path\\ contains\\ parameter\\ value/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:render, :action, s(:call, s(:params), :[], s(:lit, :page)), s(:hash)),\n      user_input: s(:call, s(:params), :[], s(:lit, :page))\n  end\n\n  def test_dynamic_render_safeish_values\n    assert_no_warning :type => :warning,\n      :warning_code => 99,\n      :fingerprint => \"fe066eddb631b6ab1ab2baaad37e1f0a6aa6ae2c5611cb3b6bfcea1f279fe96b\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 60,\n      :message => /^Passing\\ query\\ parameters\\ to/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:params), :[], s(:lit, :action)), s(:hash)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :action))\n\n    assert_no_warning :type => :warning,\n      :warning_code => 99,\n      :fingerprint => \"7205e53a21cb8e2b33ae76f3c25a1b9e60b61169d19fe423e4b936d92966450b\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 61,\n      :message => /^Passing\\ query\\ parameters\\ to/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:params), :[], s(:lit, :controller)), s(:hash)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :controller))\n  end\n\n  def test_no_cross_site_scripting_in_case_value\n    assert_no_warning :type => :template,\n      :warning_code => 5,\n      :fingerprint => \"e9a2843313999c2e856065efaf0a84ffc56ed912112c34927f406339bb395715\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/views/users/case_statement.html.erb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_cross_site_request_forgery_with_skip_before_action\n    assert_warning :type => :warning,\n      :warning_code => 8,\n      :fingerprint => \"320daba73937ffd333f10e5b578520dd90ba681962079bb92a775fb602e2d185\",\n      :warning_type => \"Cross-Site Request Forgery\",\n      :line => 11,\n      :message => /^List specific actions \\(`:only\\ =>\\ \\[\\.\\.\\]`\\)\\ when\\ skipp/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => nil\n  end\n\n  def test_redirect_to_new_query_methods\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"410e22682c2ebd663204362aac560414233b5c225fbc4259d108d2c760bfcbe4\",\n      :warning_type => \"Redirect\",\n      :line => 38,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:const, :User), :find_by, s(:hash, s(:lit, :name), s(:call, s(:params), :[], s(:lit, :name))))\n\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"c01e127b45d9010c495c6fd731baaf850f9a5bbad288cf9df66697d23ec6de4a\",\n      :warning_type => \"Redirect\",\n      :line => 40,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:const, :User), :find_by!, s(:hash, s(:lit, :name), s(:call, s(:params), :[], s(:lit, :name))))\n\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"9dd39bc751eab84c5485fa35966357b6aacb8830bd6812c7a228a02c5ac598d0\",\n      :warning_type => \"Redirect\",\n      :line => 42,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:call, s(:const, :User), :where, s(:hash, s(:lit, :stuff), s(:lit, 1))), :take)\n  end\n\n  def redirect_to_current_user_query_methods\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"b4056de92c9abd844825526b971fe683a66dd79c1fefbdd58ad343d8aeb60f6b\",\n      :warning_type => \"Redirect\",\n      :line => 108,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, nil, :redirect_to, s(:call, s(:call, s(:call, nil, :current_user), :place), :find, s(:call, s(:params), :[], s(:lit, :p)))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :p))\n  end\n\n  def test_symbol_dos_with_safe_parameters\n    assert_no_warning :type => :warning,\n      :warning_code => 59,\n      :fingerprint => \"53a74ac0c23e934a1d439e0b53ce818a22db6b23a696a61cd7dfb5b19175240a\",\n      :warning_type => \"Denial of Service\",\n      :line => 52,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string\\ \\(pa/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :controller))\n\n    assert_no_warning :type => :warning,\n      :warning_code => 59,\n      :fingerprint => \"d569b240f71e4fc4cc6e91559923baea941a1341d1d70fd6c1c36813947e369d\",\n      :warning_type => \"Denial of Service\",\n      :line => 53,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string\\ \\(pa/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :action))\n  end\n\n  def test_symbol_dos_on_model_attributes\n    assert_no_warning :type => :warning,\n      :warning_code => 59,\n      :fingerprint => \"0bc13d07b15305ddff2b095ccaf49ab8301fc0d917e5a444bcfe418429324a68\",\n      :warning_type => \"Denial of Service\",\n      :line => 48,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string\\ \\(mo/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, s(:call, s(:call, s(:const, :User), :find, s(:lvar, :stuff)), :attributes), :symbolize_keys),\n      :user_input => s(:call, s(:call, s(:const, :User), :find, s(:lvar, :stuff)), :attributes)\n  end\n\n  def test_regex_denial_of_service\n    assert_warning :type => :warning,\n      :warning_code => 76,\n      :fingerprint => \"f162a94e8ba3c94a8e997b470687e9d5e44a7692b99248b8bc3e689c1a1b86ff\",\n      :warning_type => \"Denial of Service\",\n      :line => 38,\n      :message => /^Parameter\\ value\\ used\\ in\\ regular expression/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n\n    assert_no_warning :type => :template,\n      :warning_code => 76,\n      :fingerprint => \"7d3359e28705b6a4392a1dd6ab9c424e7f7c754cdf2df1d932168ab8a77840c2\",\n      :warning_type => \"Denial of Service\",\n      :line => 1,\n      :message => /^Parameter\\ value\\ used\\ in\\ regex/,\n      :confidence => 0,\n      :relative_path => \"app/views/another/use_params_in_regex.html.erb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_weak_hash_base64\n    assert_warning :type => :warning,\n      :warning_code => 90,\n      :fingerprint => \"c82b29233b0e3326b628ac74cadc99264c796153d7971e7be09f71bd847a303f\",\n      :warning_type => \"Weak Hash\",\n      :line => 97,\n      :message => /^Weak\\ hashing\\ algorithm\\ used: MD5/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:params)\n  end\n\n  def test_weak_hash_password_variable_nested\n    assert_warning :type => :warning,\n      :warning_code => 90,\n      :fingerprint => \"db7bbef4391043f40b09a052829d71540e6edbd9c89ea7b9e17e10e8c63cdc98\",\n      :warning_type => \"Weak Hash\",\n      :line => 42,\n      :message => /^Weak\\ hashing\\ algorithm\\ used: MD5/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:lvar, :password)\n  end\n\n  def test_weak_hash_creation\n    assert_warning :type => :warning,\n      :warning_code => 90,\n      :fingerprint => \"0b4a35f9ac4fbfa2b270aea8b325905f8654bbfa5d79d52bcde766655e385cdf\",\n      :warning_type => \"Weak Hash\",\n      :line => 99,\n      :message => /^Weak\\ hashing\\ algorithm\\ used: SHA1/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => nil\n  end\n\n  def test_weak_hash_with_password_attribute\n    assert_warning :type => :warning,\n      :warning_code => 90,\n      :fingerprint => \"79abd372d12d87347e5c6bb00d01a0e54994be7bb19765a0d5927702f40f829f\",\n      :warning_type => \"Weak Hash\",\n      :line => 100,\n      :message => /^Weak\\ hashing\\ algorithm\\ used: SHA1/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:call, nil, :current_user), :password)\n  end\n\n  def test_weak_hash_in_HMAC\n    assert_warning :type => :warning,\n      :warning_code => 91,\n      :fingerprint => \"f72bce61c2064a2c25b61db0699931b37770e0f3c174236ee77cabdedde01d94\",\n      :warning_type => \"Weak Hash\",\n      :line => 98,\n      :message => /^Weak\\ hashing\\ algorithm\\ used in HMAC: SHA1/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => nil\n  end\n\n  def test_weak_hash_openssl_digest\n    assert_warning :type => :warning,\n      :warning_code => 90,\n      :fingerprint => \"7c2030a3a6d98010dcc0b93b2e63cc940ec4a8fd505a3f8a3eece61cb924354d\",\n      :warning_type => \"Weak Hash\",\n      :line => 104,\n      :message => /^Weak\\ hashing\\ algorithm\\ used: MD5/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:colon2, s(:colon2, s(:const, :OpenSSL), :Digest), :MD5), :digest, s(:call, nil, :password)),\n      :user_input => s(:call, nil, :password)\n  end\n\n  def test_weak_hash_openssl_new_md5\n    assert_warning :type => :warning,\n      :warning_code => 90,\n      :fingerprint => \"62a0ab29b01aa673f3d9f0ea8a6535da6f44b3c47bb37b2e87b7418a1f49d6e2\",\n      :warning_type => \"Weak Hash\",\n      :line => 102,\n      :message => /^Weak\\ hashing\\ algorithm\\ used: MD5/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:colon2, s(:colon2, s(:const, :OpenSSL), :Digest), :Digest), :new, s(:str, \"md5\")),\n      :user_input => nil\n  end\n\n  def test_weak_hash_openssl_new_sha1\n    assert_warning :type => :warning,\n      :warning_code => 90,\n      :fingerprint => \"295adb8ca6d79e65e0b14b7a6f9a7326094b738c450f3aaa78122ac172619e31\",\n      :warning_type => \"Weak Hash\",\n      :line => 103,\n      :message => /^Weak\\ hashing\\ algorithm\\ used: SHA1/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:colon2, s(:const, :OpenSSL), :Digest), :new, s(:str, \"SHA1\")),\n      :user_input => nil\n  end\n\n  def test_missing_encryption_force_ssl\n    assert_warning :type => :warning,\n      :warning_code => 109,\n      :fingerprint => \"6a26086cd2400fbbfb831b2f8d7291e320bcc2b36984d2abc359e41b3b63212b\",\n      :warning_type => \"Missing Encryption\",\n      :line => 1,\n      :message => /^The application does not force use of HTTPS/,\n      :confidence => 0,\n      :relative_path => \"config/environments/production.rb\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_i18n_xss_CVE_2013_4491_workaround\n    assert_no_warning :type => :warning,\n      :warning_code => 63,\n      :fingerprint => \"7ef985c538fd302e9450be3a61b2177c26bbfc6ccad7a598006802b0f5f8d6ae\",\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Rails\\ 4\\.0\\.0\\ has\\ an\\ XSS\\ vulnerability\\ in\\ /,\n      :file => /Gemfile\\.lock/,\n      :confidence => 1,\n      :relative_path => /Gemfile/\n  end\n\n  def test_denial_of_service_CVE_2013_6414\n    assert_warning :type => :warning,\n      :warning_code => 64,\n      :fingerprint => \"a7b00f08e4a18c09388ad017876e3f57d18040ead2816a2091f3301b6f0e5a00\",\n      :warning_type => \"Denial of Service\",\n      :message => /^Rails\\ 4\\.0\\.0\\ has\\ a\\ denial\\ of\\ service\\ vuln/,\n      :confidence => 1,\n      :relative_path => \"Gemfile\"\n  end\n\n  def test_number_to_currency_CVE_2014_0081\n    assert_warning :type => :template,\n      :warning_code => 74,\n      :fingerprint => \"2d06291f03b443619407093e5921ee1e4eb77b1bf045607d776d9493da4a3f95\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 9,\n      :message => /^Format\\ options\\ in\\ `number_to_currency`\\ are/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/index.html.erb\",\n      :user_input => s(:call, s(:call, nil, :params), :[], s(:lit, :currency))\n\n    assert_warning :type => :template,\n      :warning_code => 74,\n      :fingerprint => \"c5f481595217e42fbeaf40f32e6407e66d64d246a9729c2c199053e64365ac96\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 13,\n      :message => /^Format\\ options\\ in\\ `number_to_percentage`\\ a/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/index.html.erb\",\n      :user_input => s(:call, s(:call, nil, :params), :[], s(:lit, :format))\n  end\n\n  def test_simple_format_xss_CVE_2013_6416\n    assert_warning :type => :warning,\n      :warning_code => 67,\n      :fingerprint => \"e950ee1043d7f66b7f6ce99c2bf0876bd3ce8cb12818b52565b905cdb6004bad\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.0\\.0 has\\ a\\ vulnerability\\ in/,\n      :confidence => 1,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_render_text\n    assert_warning :type => :warning,\n      :warning_code => 84,\n      :fingerprint => \"a0b38ce5204afaaddfb5ba121d286bade5fe56cacbae1eb6c6e08482729638dd\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 24,\n      :message => /^Unescaped\\ parameter\\ value\\ rendered\\ inlin/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :code => s(:render, :text, s(:dstr, \"Welcome back, \", s(:evstr, s(:call, s(:params), :[], s(:lit, :name))), s(:str, \"!}\")), s(:hash)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :name))\n\n    assert_warning :type => :warning,\n      :warning_code => 84,\n      :fingerprint => \"a8bac3d3d75f55126b8331b0c843c8e02154ebe61b1e7e88443c85c4c67c501e\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 25,\n      :message => /^Unescaped\\ model\\ attribute\\ rendered\\ inlin/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :code => s(:render, :text, s(:dstr, \"Welcome back, \", s(:evstr, s(:call, s(:call, s(:const, :User), :current_user), :name)), s(:str, \"!}\")), s(:hash)),\n      :user_input => s(:call, s(:call, s(:const, :User), :current_user), :name)\n\n    assert_warning :type => :warning,\n      :warning_code => 84,\n      :fingerprint => \"74de1c04495b3d230bc81868e420d2c6ca121a5cd6a721d2189ba81f4862010a\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 26,\n      :message => /^Unescaped\\ parameter\\ value\\ rendered\\ inlin/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :code => s(:render, :text, s(:call, s(:params), :[], s(:lit, :q)), s(:hash)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :q))\n\n    assert_warning :type => :warning,\n      :warning_code => 84,\n      :fingerprint => \"9f96bf32a7fa73d2ba20e1b838bfa133a94c3b7029d9aadf5b813c25e49a031f\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 27,\n      :message => /^Unescaped\\ model\\ attribute\\ rendered\\ inlin/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :code => s(:render, :text, s(:call, s(:call, s(:const, :User), :current_user), :name), s(:hash)),\n      :user_input => s(:call, s(:call, s(:const, :User), :current_user), :name)\n  end\n\n  def test_cross_site_scripting_render_inline\n    assert_warning :type => :warning,\n      :warning_code => 84,\n      :fingerprint => \"1cfc027040376a06bd45ee3ce473dcd36adfa54e052d08651098d1c1e09bacec\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 29,\n      :message => /^Unescaped\\ parameter\\ value\\ rendered\\ inlin/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :code => s(:render, :inline, s(:dstr, \"<%= \", s(:evstr, s(:call, s(:params), :[], s(:lit, :name))), s(:str, \" %>\")), s(:hash)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :name))\n\n    assert_warning :type => :warning,\n      :warning_code => 84,\n      :fingerprint => \"89c00a5b4a816c6cf0c4cd2618f1a76411c3fc55460bf9069bb7ca12abb75f75\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 30,\n      :message => /^Unescaped\\ model\\ attribute\\ rendered\\ inlin/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :code => s(:render, :inline, s(:dstr, \"<%= \", s(:evstr, s(:call, s(:call, s(:const, :User), :current_user), :name)), s(:str, \" %>\")), s(:hash)),\n      :user_input => s(:call, s(:call, s(:const, :User), :current_user), :name)\n  end\n\n  def test_cross_site_scripting_with_double_equals\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"046c3a770f455c30aa5e3a49bc1309e6511c142783e2f1d0c0eddcbcef366cef\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 17,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/index.html.erb\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_with_html_safe\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"b04cfd8d120b773a3e9f70af8762f7efa7c5ca5c7f83136131d6cc75259cd429\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/another/html_safe_is_not.html.erb\",\n      :user_input => nil\n  end\n\n  def test_xss_haml_line_number\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"104956d65ed7dbf771419455017d79ff8d2f718c7d383c6a3b343a15d2511ff2\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/haml_test.html.haml\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_warning_code_for_weak_xss\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/views/another/various_xss.html.erb\",\n      :user_input => s(:call, s(:call, nil, :params), :[], s(:lit, :x))\n  end\n\n  def test_cross_site_scripting_no_warning_on_helper_methods_with_targets\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/views/another/various_xss.html.erb\",\n      :user_input => s(:call, s(:call, nil, :params), :[], s(:lit, :t))\n  end\n\n  def test_cross_site_scripting_warn_on_url_methods_in_href\n    assert_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"31b5a196d06699ced844270d876e7af818f5487dd82d713a47790798c1d6effd\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 2,\n      :message => /^Potentially\\ unsafe\\ model\\ attribute\\ in\\ `li/,\n      :confidence => 2,\n      :relative_path => \"app/views/another/various_xss.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"stuff\"), s(:call, s(:call, s(:const, :User), :find, s(:call, s(:call, nil, :params), :[], s(:lit, :id))), :home_url)),\n      :user_input => s(:call, s(:call, s(:const, :User), :find, s(:call, s(:call, nil, :params), :[], s(:lit, :id))), :home_url)\n  end\n\n  def test_cross_site_scripting_no_warning_on_path_methods_in_href\n    assert_no_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"75956429768c8c53ee3f9932320db67a9d2e0d6fe87431eb290156d0d31d8dba\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unsafe\\ parameter\\ value\\ in\\ link_to\\ href/,\n      :confidence => 1,\n      :relative_path => \"app/views/another/various_xss.html.erb\",\n      :user_input => s(:call, nil, :params)\n  end\n\n  def test_xss_no_warning_on_model_finds_in_href\n    assert_no_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"ada8501c6761c382f47b0ecd03d653059f16aabd7ef1588900a916ae6fd877ef\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 18,\n      :message => /^Unsafe\\ parameter\\ value\\ in\\ link_to\\ href/,\n      :confidence => 1,\n      :relative_path => \"app/views/users/index.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"Bars\"), s(:call, s(:call, s(:call, nil, :current_user), :bars), :find, s(:call, s(:call, nil, :params), :[], s(:lit, :id)))),\n      :user_input => s(:call, nil, :params)\n  end\n\n  def test_cross_site_scripting_haml_interpolation\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"466b2c1ea4aa43dc6465d2b7815d529f7cfad6f333cc17dd90e3aae9697a6042\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 6,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/haml_test.html.haml\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_find_and_preserve_escape_javascript\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"d75b08fa4d1ef70aa2be54f4568b7486aaf91beae65c7adc1422d3582fdbf5b0\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 8,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/views/users/haml_test.html.haml\",\n      :user_input => s(:call, s(:call, nil, :params), :[], s(:lit, :id))\n  end\n\n  def test_cross_site_scripting_coffee_script\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"7027ca0313a2ca480f871890936e5d72f035cb7c27d25a5bf01afa784a9db10f\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 10,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/views/users/haml_test.html.haml\"\n  end\n\n  def test_cross_site_scripting_in_comparison_false_positive\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"70f4d1c73e97cdcc0581309169cf59bde767f9f02666421ae9f3b22604f8c37f\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 18,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/index.html.erb\",\n      :code => s(:call, s(:call, s(:call, nil, :params), :[], s(:lit, :x)), :==, s(:lit, 1)),\n      :user_input => nil\n  end\n\n  def test_sql_injection_in_chained_string_building\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"e60bf02af3884ea73227f05e0b5e00a8ed4466958c22223dbbc4fc4f828c6a1c\",\n      :warning_type => \"SQL Injection\",\n      :line => 34,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/models/account.rb\",\n      :user_input => s(:call, s(:call, s(:params), :[], s(:lit, :more_ids)), :join, s(:str, \",\"))\n  end\n\n  def test_no_sql_injection_due_to_skipped_filter\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"47709afc6e3ba4db08a7e6a6b9bda9644c0e8827437eb3489626b2471e0414b5\",\n      :warning_type => \"SQL Injection\",\n      :line => 14,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_sql_injection_ignore_to_sym\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"f2e6d5d952c841a148c086beb09ad2961ab9854215f3665babd574aaa4aaaf83\",\n      :warning_type => \"SQL Injection\",\n      :line => 13,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:call, s(:call, s(:const, :User), :table_name), :to_sym)\n\n    # This is a side effect of the test above\n    assert_warning :type => :warning,\n      :warning_code => 59,\n      :fingerprint => \"80fce17f43faed45ada3a85acd3902ab32478e585190b25dbb4d5ce483a463f7\",\n      :warning_type => \"Denial of Service\",\n      :line => 13,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string\\ in mo/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:call, s(:const, :User), :table_name)\n  end\n\n  def test_sql_injection_scope_alias_processing\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"a28e3653220903b78e2f00f1e571aa7afaa4f7db6f0789be8cf59c1b9eb583a1\",\n      :warning_type => \"SQL Injection\",\n      :line => 13,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/email.rb\",\n      :user_input => s(:lvar, :task_table)\n  end\n\n  def test_sql_injection_with_to_s_on_string_interp\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"06f9f76470ed1d4559c28258f29cab4ec28817a5414121a23b90ea6e9a564374\",\n      :warning_type => \"SQL Injection\",\n      :line => 39,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/account.rb\",\n      :user_input => s(:lvar, :locale)\n  end\n\n  def test_sql_injection_string_concat\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"b92b1e8d755fb15bed56f8e6f872f81784813b0eae3682b68dd0601e4fb9c0a6\",\n      :warning_type => \"SQL Injection\",\n      :line => 51,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :search))\n  end\n\n  def test_no_sql_injection_from_arel_methods\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"61d957cdeca70a82f53d7ec72287fc21f67c67c6e8dbc9c3c4cb2d115f3a5602\",\n      :warning_type => \"SQL Injection\",\n      :line => 30,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\"\n\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"46a08db9c5b2739027a34c37cbb79c0813247e5bba856705a56174173e230f4b\",\n      :warning_type => \"SQL Injection\",\n      :line => 32,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\"\n\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"64233e939bcef59cf6100c75cfefaf2968734305d4431622556e2f612b10a912\",\n      :warning_type => \"SQL Injection\",\n      :line => 33,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\"\n  end\n\n  def test_hash_keys_not_values\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :warning_type => \"SQL Injection\",\n      :line => 80,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :code => s(:call, s(:const, :User), :where, s(:hash, s(:str, \"stuff\"), s(:call, s(:params), :[], s(:lit, :stuff)))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :stuff))\n\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :warning_type => \"SQL Injection\",\n      :line => 81,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :code => s(:call, s(:const, :User), :where, s(:hash, s(:call, s(:params), :[], s(:lit, :key)), s(:call, s(:params), :[], s(:lit, :stuff)))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :stuff))\n\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"ec196cf3f65f4d45b41345d19cdec2ced1420781bd379a5223b9fa9318bec3d4\",\n      :warning_type => \"SQL Injection\",\n      :line => 81,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :code => s(:call, s(:const, :User), :where, s(:hash, s(:call, s(:params), :[], s(:lit, :key)), s(:call, s(:params), :[], s(:lit, :stuff)))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :key))\n  end\n\n  def test_sql_injection_with_permit\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"195c3ab08dd4b4f11a29afabb704cefe1d8987a9a7690e7c8299900c9e888a94\",\n      :warning_type => \"SQL Injection\",\n      :line => 119,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :find_by, s(:call, s(:params), :permit, s(:lit, :OMG))),\n      :user_input => s(:call, s(:params), :permit, s(:lit, :OMG))\n\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"1f92b0ca5290f5c4de78cfa33a72c2f845604062fa0d5c31f1800111cf191f36\",\n      :warning_type => \"SQL Injection\",\n      :line => 120,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :find_by, s(:call, s(:call, s(:params), :permit, s(:lit, :OMG)), :[], s(:lit, :OMG))),\n      :user_input => s(:call, s(:call, s(:params), :permit, s(:lit, :OMG)), :[], s(:lit, :OMG))\n\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"420d307a7e184dab8298c445acbf12df7cd106d38bc60886d9e2583972f3a6f5\",\n      :warning_type => \"SQL Injection\",\n      :line => 121,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :where, s(:dstr, \"\", s(:evstr, s(:call, s(:params), :permit, s(:lit, :OMG))))),\n      :user_input => s(:call, s(:params), :permit, s(:lit, :OMG))\n  end\n\n  def test_sql_injection_find_or_create_by\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"8fe189a91028c0718f3dc3791f628c102dd60874ecb80ea1354adfead9037147\",\n      :warning_type => \"SQL Injection\",\n      :line => 130,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :find_or_create_by, s(:call, s(:params), :[], s(:lit, :user))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :user))\n  end\n\n  def test_sql_injection_find_or_create_by!\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"9ecd0a3bb160b221bbe95ab201f53c1675561ffd11a6e6ff8fc0c3c95bd8b9e8\",\n      :warning_type => \"SQL Injection\",\n      :line => 131,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :find_or_create_by!, s(:call, s(:params), :[], s(:lit, :user))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :user))\n  end\n\n  def test_sql_injection_find_or_initialize_by\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"fc70a38d4ecc2220739b9075e790f2d433bb2d2b3484513ad99dea5236330c99\",\n      :warning_type => \"SQL Injection\",\n      :line => 132,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :find_or_initialize_by, s(:call, s(:params), :[], s(:lit, :user))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :user))\n  end\n\n  def test_format_validation_model_alias_processing\n    assert_warning :type => :model,\n      :warning_code => 30,\n      :fingerprint => \"d2bfa987fd0e59d1d515a0bc0baaf378d1dd75483184c945b662b96d370add28\",\n      :warning_type => \"Format Validation\",\n      :line => 8,\n      :message => /^Insufficient\\ validation\\ for\\ `email`\\ usin/,\n      :confidence => 0,\n      :relative_path => \"app/models/email.rb\",\n      :user_input => nil\n  end\n\n  def test_format_validation_with_multiline\n    assert_no_warning :type => :model,\n      :warning_type => \"Format Validation\",\n      :line => 11,\n      :message => /^Insufficient\\ validation\\ for\\ 'number/,\n      :confidence => 0,\n      :file => /phone\\.rb/\n  end\n\n  def test_additional_libs_option\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"e1bff55541ac57c8bae7b027e34c23bfe76f675d5a741d767d4a533bbce9ab4a\",\n      :warning_type => \"Command Injection\",\n      :line => 4,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/api/api.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :dir))\n  end\n\n  def test_command_injection_in_library\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"9a11e7271784d69c667ad82481596096781a4873297d3f7523d290f51465f9d6\",\n      :warning_type => \"Command Injection\",\n      :line => 3,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/sweet_lib.rb\",\n      :user_input => s(:lvar, :bad)\n  end\n\n  def test_command_injection_interpolated_string_in_library\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"83151193b403812e79a00a3bf8f1e8a01d0232b6b8ae5b1bccb2fd146299e8c6\",\n      :warning_type => \"Command Injection\",\n      :line => 8,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/sweet_lib.rb\",\n      :user_input => s(:ivar, :@bad)\n  end\n\n  def test_command_injection_from_not_skipping_before_filter\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"b4a4bfc1dd6f5f193c9cd3f0819abb936375eee379e5373c08d23957d3af1cd0\",\n      :warning_type => \"Command Injection\",\n      :line => 18,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_command_injection_in_open\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"e5316ae15f7db1b2232599d86859229ce01fb6eff1e6d273dbc154345c374d67\",\n      :warning_type => \"Command Injection\",\n      :line => 81,\n      :message => /^Possible\\ command\\ injection\\ in\\ `open`/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :url))\n\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"b35ce0b104d755d40bec760daf5ca33578036f737094e453b9c79e0c441ef2b7\",\n      :warning_type => \"Command Injection\",\n      :line => 83,\n      :message => /^Possible\\ command\\ injection\\ in\\ `open`/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_file_access_in_open\n    assert_warning :type => :warning,\n      :warning_code => 16,\n      :fingerprint => \"e860688d22411c19cb37652e2b41de6475ce094cf92dd6d66ce6b840e8e74c4b\",\n      :warning_type => \"File Access\",\n      :line => 81,\n      :message => /^Parameter\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :url))\n\n    assert_warning :type => :warning,\n      :warning_code => 16,\n      :fingerprint => \"259391ba0e21aa57fc0cadae71741c88b1cd86366e2f46f8347bf8ded1f3b526\",\n      :warning_type => \"File Access\",\n      :line => 82,\n      :message => /^Parameter\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :url))\n\n    assert_warning :type => :warning,\n      :warning_code => 16,\n      :fingerprint => \"2e235612f5ee3a6c20a094cec38c65ec4ae5f9550cd2cd31da69d5ef751967e6\",\n      :warning_type => \"File Access\",\n      :line => 83,\n      :message => /^Parameter\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n\n    assert_warning :type => :warning,\n      :warning_code => 16,\n      :fingerprint => \"a554792e632ec66fa9bee7a880a0ebf5b1938603ee2b673d240d03c4b9574ad9\",\n      :warning_type => \"File Access\",\n      :line => 84,\n      :message => /^Parameter\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n\n  def test_unsafe_reflection_comparison_false_positive\n    assert_no_warning :type => :warning,\n      :warning_code => 24,\n      :fingerprint => \"df957ee4f94d5c14f0ad24eb4b185b274721ac5edd72addd6ed54cf10a4c11bb\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 90,\n      :message => /^Unsafe\\ reflection\\ method\\ constantize\\ cal/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :code => s(:call, s(:iter, s(:call, s(:array, s(:str, \"Post\"), s(:str, \"Comments\")), :detect), s(:args, :k), s(:call, s(:lvar, :k), :==, s(:call, s(:params), :[], s(:lit, :a)))), :constantize),\n      :user_input => s(:call, s(:params), :[], s(:lit, :a))\n  end\n\n  def test_sql_injection_CVE_2013_6417\n    assert_warning :type => :warning,\n      :warning_code => 69,\n      :fingerprint => \"e1b66f4311771d714a13be519693c540d7e917511a758827d9b2a0a7f958e40f\",\n      :warning_type => \"SQL Injection\",\n      :line => 4,\n      :file => /Gemfile/,\n      :message => /^Rails\\ 4\\.0\\.0 contains\\ a\\ SQL\\ injection\\ vul/,\n      :confidence => 0,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_sql_injection_CVE_2014_0080\n    assert_warning :type => :warning,\n      :warning_code => 72,\n      :fingerprint => \"0ba20216bdda1cc067f9e4795bdb0d9224fd23c58317ecc09db67b6b38a2d0f0\",\n      :warning_type => \"SQL Injection\",\n      :line => 6,\n      :file => /Gemfile/,\n      :message => /^Rails\\ 4\\.0\\.0\\ contains\\ a\\ SQL\\ injection\\ vul/,\n      :confidence => 0,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_remote_code_execution_CVE_2014_0130\n    assert_warning :type => :warning,\n      :warning_code => 77,\n      :fingerprint => \"e833fd152ab95bf7481aada185323d97cd04c3e2322b90f3698632f4c4c04441\",\n      :warning_type => \"Remote Code Execution\",\n      :line => nil,\n      :message => /^Rails\\ 4\\.0\\.0\\ with\\ globbing\\ routes\\ is\\ vuln/,\n      :confidence => 1,\n      :relative_path => \"config/routes.rb\",\n      :user_input => nil\n  end\n\n  def test_sql_injection_CVE_2014_3482\n    assert_warning :type => :warning,\n      :warning_code => 78,\n      :fingerprint => \"5c9706393849d7de5125a3688562aea31e112a7b09d0abbb461ee5dc7c1751b8\",\n      :warning_type => \"SQL Injection\",\n      :line => 4,\n      :file => /Gemfile/,\n      :message => /^Rails\\ 4\\.0\\.0\\ contains\\ a\\ SQL\\ injection\\ vul/,\n      :confidence => 0,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_sql_injection_CVE_2014_3483\n    assert_warning :type => :warning,\n      :warning_code => 79,\n      :fingerprint => \"4a60c60c39e12b1dd1d8b490f228594f0a555aa5447587625df362327e86ad2f\",\n      :warning_type => \"SQL Injection\",\n      :line => 4,\n      :file => /Gemfile/,\n      :message => /^Rails\\ 4\\.0\\.0\\ contains\\ a\\ SQL\\ injection\\ vul/,\n      :confidence => 0,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_mass_assignment_CVE_2014_3514\n    assert_warning :type => :warning,\n      :warning_code => 81,\n      :fingerprint => \"c4a619b7316e45a5927b098294ff39d7206f84bac084402630318bf6f89f396d\",\n      :warning_type => \"Mass Assignment\",\n      :line => 57,\n      :message => /^`create_with`\\ is\\ vulnerable\\ to\\ strong\\ para/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => nil\n\n    assert_warning :type => :warning,\n      :warning_code => 81,\n      :fingerprint => \"c4a619b7316e45a5927b098294ff39d7206f84bac084402630318bf6f89f396d\",\n      :warning_type => \"Mass Assignment\",\n      :line => 58,\n      :message => /^`create_with`\\ is\\ vulnerable\\ to\\ strong\\ para/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => nil\n\n    assert_warning :type => :warning,\n      :warning_code => 81,\n      :fingerprint => \"8c55b05e3467934ac900567d47b4ac496e9761424b66b246585d14ba5b2b0240\",\n      :warning_type => \"Mass Assignment\",\n      :line => 61,\n      :message => /^`create_with`\\ is\\ vulnerable\\ to\\ strong\\ para/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => nil\n\n    assert_warning :type => :warning,\n      :warning_code => 81,\n      :fingerprint => \"aafdaf40064466b1eea16ca053072fb2ef20c999411108d606c8555ade2ce629\",\n      :warning_type => \"Mass Assignment\",\n      :line => 62,\n      :message => /^`create_with`\\ is\\ vulnerable\\ to\\ strong\\ para/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => nil\n\n  end\n\n  def test_CVE_2015_3227\n    assert_warning :type => :warning,\n      :warning_code => 88,\n      :fingerprint => \"6ad4464dbb2a999591c7be8346dc137c3372b280f4a8b0c024fef91dfebeeb83\",\n      :warning_type => \"Denial of Service\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.0\\.0\\ is\\ vulnerable\\ to\\ denial\\ of\\ s/,\n      :confidence => 1,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_denial_of_service_CVE_2016_0751\n    assert_warning :type => :warning,\n      :warning_code => 94,\n      :fingerprint => \"71fd8de94b502e46add9c8c9fad23096bb26e01e16fc5f23de56e6080e858c4a\",\n      :warning_type => \"Denial of Service\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.0\\.0\\ is\\ vulnerable\\ to\\ denial\\ of\\ s/,\n      :confidence => 1,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_nested_attributes_bypass_CVE_2015_7577\n    assert_warning :type => :model,\n      :warning_code => 95,\n      :fingerprint => \"04494b2a7fe6aff45ef9c1d72f4bcce132979a8725e8a3d313d17d5c3411c4d0\",\n      :warning_type => \"Nested Attributes\",\n      :line => 45,\n      :message => /^Rails\\ 4\\.0\\.0\\ does\\ not\\ call\\ `:reject_if`\\ opt/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => nil\n  end\n\n  def test_denial_of_service_CVE_2015_7581\n    assert_warning :type => :warning,\n      :warning_code => 100,\n      :fingerprint => \"5443fee81b56e41e116305465ddf3e2afc64e69a1a0119693dfd5368c6228d89\",\n      :warning_type => \"Denial of Service\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.0\\.0\\ has\\ a\\ denial\\ of\\ service\\ vuln/,\n      :confidence => 1,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_CVE_2016_6316\n    assert_warning :type => :warning,\n      :warning_code => 102,\n      :fingerprint => \"263bacc3390a9dd1ddec7a7f5bbb609a837de55725571234708d2a3b83a017fe\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.0\\.0\\ `content_tag`\\ does\\ not\\ escape\\ /,\n      :confidence => 1,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_mass_assignment_with_permit!\n    assert_warning :type => :warning,\n      :warning_code => 70,\n      :fingerprint => \"c2fdd36441441ef7d2aed764731c36fb9f16939ed4df582705f27d46c02fcbe3\",\n      :warning_type => \"Mass Assignment\",\n      :line => 22,\n      :message => /^Specify exact keys allowed for mass assignment/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => nil\n\n    assert_warning :type => :warning,\n      :warning_code => 70,\n      :fingerprint => \"2f2df4aef71799a6a441783b50e7a43a9bed7da6c8d50e07e73d9d165065ceec\",\n      :warning_type => \"Mass Assignment\",\n      :line => 28,\n      :message => /^Specify exact keys allowed for mass assignment/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => nil\n\n    assert_warning :type => :warning,\n      :warning_code => 70,\n      :fingerprint => \"4f6a0d82f6ddf5528f3d50545ce353f2f1658d5102a745107ea572af5c2eee4b\",\n      :warning_type => \"Mass Assignment\",\n      :line => 34,\n      :message => /^Specify exact keys allowed for mass assignment/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => nil\n\n    assert_warning :type => :warning,\n      :warning_code => 70,\n      :fingerprint => \"947bddec4cdd3ff8b2485eec1bd0078352c182a3bca18a5f68da0a64e87d4e80\",\n      :warning_type => \"Mass Assignment\",\n      :line => 40,\n      :message => /^Specify exact keys allowed for mass assignment/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :user_input => nil\n  end\n\n  def test_mass_assign_without_protection_with_hash_literal\n    assert_no_warning :type => :warning,\n      :warning_code => 54,\n      :fingerprint => \"b1fa7b124d251da5ade7f4fe22f158cd63894b91604d03f6faeef113036dad5a\",\n      :warning_type => \"Mass Assignment\",\n      :line => 115,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :new, s(:hash, s(:lit, :username), s(:str, \"jjconti\"), s(:lit, :admin), s(:false)), s(:hash, s(:lit, :without_protection), s(:true))),\n      :user_input => nil\n  end\n\n  def test_only_desired_attribute_is_ignored\n    assert_warning :type => :model,\n      :warning_code => 60,\n      :fingerprint => \"e543ea9186ed27e78ccfeee4e60ceee0c83163ffe0bf50e1ebf3d7b19793c5f4\",\n      :warning_type => \"Mass Assignment\",\n      :line => nil,\n      :message => \"Potentially dangerous attribute available for mass assignment: :account_id\",\n      :confidence => 0,\n      :relative_path => \"app/models/account.rb\",\n      :user_input => nil\n\n    assert_no_warning :type => :model,\n      :warning_code => 60,\n      :message => \"Potentially dangerous attribute available for mass assignment: :admin\",\n      :relative_path => \"app/models/account.rb\"\n  end\n\n  def test_ssl_verification_bypass\n    assert_warning :type => :warning,\n      :warning_code => 71,\n      :warning_type => \"SSL Verification Bypass\",\n      :line => 24,\n      :message => /^SSL\\ certificate\\ verification\\ was\\ bypassed/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/application_controller.rb\",\n      :user_input => nil\n  end\n\n  def test_ssl_verification_bypass_net_start\n    assert_warning :type => :warning,\n      :warning_code => 71,\n      :fingerprint => \"fed73f1d7511e72e158a7080eefe377c0c34ad18190471829216e9a2c4f7126d\",\n      :warning_type => \"SSL Verification Bypass\",\n      :line => 12,\n      :message => /^SSL\\ certificate\\ verification\\ was\\ bypasse/,\n      :confidence => 0,\n      :relative_path => \"lib/sweet_lib.rb\",\n      :user_input => nil\n  end\n\n  def test_unscoped_find_by_id_bang\n    assert_warning :type => :warning,\n      :warning_code => 82,\n      :fingerprint => \"4d88d42b82e11010ba1fb67f587bb756068caefe73bb74cc9c3e6f3b9842810f\",\n      :warning_type => \"Unscoped Find\",\n      :line => 66,\n      :message => /^Unscoped\\ call\\ to\\ `Email\\#find_by_id!`/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:call, s(:params), :[], s(:lit, :email)), :[], s(:lit, :id))\n  end\n\n  def test_unscoped_find_by\n    assert_warning check_name: \"UnscopedFind\",\n      type: :warning,\n      warning_code: 82,\n      fingerprint: \"c78bc503851a0f79fb64362eb65813be232c9441e1111e1145c5ca3f52bd9606\",\n      warning_type: \"Unscoped Find\",\n      line: 136,\n      message: /^Unscoped\\ call\\ to\\ `Email\\#find_by`/,\n      confidence: 2,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, s(:const, :Email), :find_by, s(:hash, s(:lit, :id), s(:call, s(:call, s(:params), :[], s(:lit, :email)), :[], s(:lit, :id)))),\n      user_input: s(:call, s(:call, s(:params), :[], s(:lit, :email)), :[], s(:lit, :id))\n  end\n\n  def test_unscoped_find_by_bang\n    assert_warning check_name: \"UnscopedFind\",\n      type: :warning,\n      warning_code: 82,\n      fingerprint: \"da4c77ce860d5567bfaf5e915b734e54712a276dfbee36694757754a49ed4e0c\",\n      warning_type: \"Unscoped Find\",\n      line: 137,\n      message: /^Unscoped\\ call\\ to\\ `Email\\#find_by!`/,\n      confidence: 2,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, s(:const, :Email), :find_by!, s(:hash, s(:lit, :id), s(:call, s(:call, s(:params), :[], s(:lit, :email)), :[], s(:lit, :id)))),\n      user_input: s(:call, s(:call, s(:params), :[], s(:lit, :email)), :[], s(:lit, :id))\n  end\n\n  def test_before_filter_block\n    assert_warning :type => :warning,\n      :warning_code => 13,\n      :fingerprint => \"f8081023e9a6026264eaee41a4a1f520fc98ee5dbcba2129245e6a3873cb6409\",\n      :warning_type => \"Dangerous Eval\",\n      :line => 7,\n      :message => /^Parameter\\ value\\ evaluated\\ as\\ code/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/another_controller.rb\",\n      :method => :before_filter,\n      :user_input => s(:call, s(:call, nil, :params), :[], s(:lit, :x))\n  end\n\n  def test_eval_duplicates\n    assert_warning :type => :warning,\n      :warning_code => 13,\n      :fingerprint => \"33067304aaa21c6a874fed3b9bb0084cb66b607cc620065cb8ab06a640d3ab14\",\n      :warning_type => \"Dangerous Eval\",\n      :line => 88,\n      :message => /^Parameter\\ value\\ evaluated\\ as\\ code/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n\n    assert_no_warning :type => :template,\n      :warning_code => 13,\n      :fingerprint => \"dc94bedbdf82991d7a356de94650325c256c5876227480b3b98e24aadaab1fd5\",\n      :warning_type => \"Dangerous Eval\",\n      :line => 1,\n      :message => /^Parameter\\ value\\ evaluated\\ as\\ code/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/eval_it.html.erb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_private_call\n    assert_warning :type => :warning,\n      :warning_code => 13,\n      :fingerprint => \"f0463ae920dc6ebbed7f66d0bdf1cc41b7c7257f7f724107377d7c59c5ee8707\",\n      :warning_type => \"Dangerous Eval\",\n      :line => 76,\n      :message => /^Parameter\\ value\\ evaluated\\ as\\ code/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/friendly_controller.rb\",\n      :code => s(:call, nil, :eval, s(:call, s(:call, nil, :params), :[], s(:lit, :what_is_this_java?))),\n      :user_input => s(:call, s(:call, nil, :params), :[], s(:lit, :what_is_this_java?))\n  end\n\n  def test_cross_site_request_forgery_setting_in_api_controller\n    assert_no_warning :type => :controller,\n      :warning_code => 7,\n      :fingerprint => \"6f5239fb87c64764d0c209014deb5cf504c2c10ee424bd33590f0a4f22e01d8f\",\n      :warning_type => \"Cross-Site Request Forgery\",\n      :line => nil,\n      :message => /^'protect_from_forgery'\\ should\\ be\\ called\\ /,\n      :confidence => 0,\n      :relative_path => \"app/controllers/application_controller.rb\",\n      :user_input => nil\n  end\n\n  def test_unmaintained_dependency_rails\n    assert_warning check_name: \"EOLRails\",\n      type: :warning,\n      warning_code: 120,\n      fingerprint: \"e9d00416c23870f08d30cfda6ad07e2138e0ce51ab6b684814eb69e789cfa631\",\n      warning_type: \"Unmaintained Dependency\",\n      line: 4,\n      message: /^Support\\ for\\ Rails\\ 4\\.0\\.0\\ ended\\ on\\ 2017\\-04/,\n      confidence: 0,\n      relative_path: \"Gemfile\"\n  end\n\n  def test_external_check\n    assert_warning :type => :warning,\n      :warning_code => 9090,\n      :fingerprint => \"07b0c22fbd40737617b517a9d896bf63870cf7942dff81ad8ae1d0b80ed6b998\",\n      :warning_type => \"Shady Call\",\n      :line => 16,\n      :message => /^Called\\ something\\ shady!/,\n      :confidence => 0,\n      :relative_path => \"lib/sweet_lib.rb\",\n      :code => s(:call, nil, :call_shady_method, s(:call, s(:params), :[], s(:lit, :x))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  #Verify checks external to Brakeman are loaded\n  def test_external_checks\n    #Initial \"Check\" removed from check names\n    assert report[:checks_run].include? \"ExternalCheckTest\"\n    assert defined? Brakeman::CheckExternalCheckTest\n  end\nend\n"
  },
  {
    "path": "test/tests/rails4_with_engines.rb",
    "content": "require_relative '../test'\n\nclass Rails4WithEnginesTests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def expected\n    @expected ||= {\n      :controller => 2,\n      :model => 5,\n      :template => 12,\n      :generic => 15 }\n  end\n\n  def report\n   @@report ||= BrakemanTester.run_scan \"rails4_with_engines\", \"Rails4WithEngines\"\n  end\n\n  def test_dangerous_send_in_engine\n    assert_warning :type => :warning,\n      :warning_code => 23,\n      :fingerprint => \"d32b6adf5f606b101ff7e1d47d76458f84fc8e3b5fbed7a7347fe7ae34cde9bb\",\n      :warning_type => \"Dangerous Send\",\n      :line => 3,\n      :message => /^User\\ controlled\\ method\\ execution/,\n      :confidence => 0,\n      :relative_path => \"alt_engines/admin_stuff/app/controllers/admin_controller.rb\",\n      :code => s(:call, s(:call, s(:call, s(:call, s(:params), :[], s(:lit, :class)), :classify), :constantize), :send, s(:call, s(:params), :[], s(:lit, :meth))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :meth))\n  end\n\n  def test_cross_site_scripting_in_engine\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"2cdecf9256c975d1c7a3cdb0f912eb8f660b8b5a9e343dd8f6e7e73c827b7549\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"alt_engines/admin_stuff/app/views/admin/debug.html.erb\",\n      :code => s(:call, s(:params), :[], s(:lit, :debug)),\n      :user_input => nil\n  end\n\n  def test_remote_code_execution_in_engine\n    assert_warning :type => :warning,\n      :warning_code => 24,\n      :fingerprint => \"5a59773cc5a29469202c4e8908e37cdb9ef7926af05f68de1c6e765854e869c0\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 3,\n      :message => /^Unsafe\\ reflection\\ method\\ `constantize`\\ cal/,\n      :confidence => 0,\n      :relative_path => \"alt_engines/admin_stuff/app/controllers/admin_controller.rb\",\n      :code => s(:call, s(:call, s(:call, s(:params), :[], s(:lit, :class)), :classify), :constantize),\n      :user_input => s(:call, s(:call, s(:params), :[], s(:lit, :class)), :classify)\n  end\n\n  def test_i18n_xss_CVE_2013_4491\n    assert_warning :type => :warning,\n      :warning_code => 63,\n      :fingerprint => \"f127f5b2c0fc6f49570a6a3ec2b79baa2d8c24df59f86926f7a83855af06f534\",\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^Rails\\ 4\\.0\\.0\\ has\\ an\\ XSS\\ vulnerability\\ in\\ /,\n      :file => /gems.rb/,\n      :confidence => 1,\n      :relative_path => \"gems.rb\"\n  end\n\n  def test_number_to_currency_CVE_2014_0081\n    assert_warning :type => :warning,\n      :warning_code => 73,\n      :fingerprint => \"d884628b046c0ac6267bffe01bf0017a29dd94065e10e564d337cd85e40550a1\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.0\\.0\\ has\\ a\\ vulnerability\\ in\\ numbe/,\n      :confidence => 1,\n      :relative_path => \"gems.rb\",\n      :user_input => nil\n  end\n\n  def test_xss_simple_format_CVE_2013_6416\n    assert_warning :type => :template,\n      :warning_code => 68,\n      :fingerprint => \"e5b270bcb5bf77069b7e4adf0c46221d1277f0b126c795e43b700a6b0f4747ae\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 20,\n      :message => /^Values\\ passed\\ to\\ `simple_format`\\ are\\ not\\ s/,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/views/users/show.html.erb\",\n      :user_input => s(:call, s(:call, s(:const, :User), :find, s(:call, s(:params), :[], s(:lit, :id))), :likes)\n\n    assert_warning :type => :template,\n      :warning_code => 68,\n      :fingerprint => \"e31d9365f0e99e55bb3d62deda2bf1ee0bc4e5970dd5791fcde8056f6558f51f\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 21,\n      :message => /^Values\\ passed\\ to\\ `simple_format`\\ are\\ not\\ s/,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/views/users/show.html.erb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :color))\n  end\n\n  def test_sql_injection_CVE_2013_6417\n    assert_warning :type => :warning,\n      :warning_code => 69,\n      :fingerprint => \"6526cbe210b51803986cddbfd567059c8036390eb697d64baa604b62940a3c55\",\n      :warning_type => \"SQL Injection\",\n      :message => /^Rails\\ 4\\.0\\.0\\ contains\\ a\\ SQL\\ injection\\ vul/,\n      :confidence => 0,\n      :relative_path => 'gems.rb',\n      :line => 4,\n      :file => /gems.rb/,\n      :user_input => nil\n  end\n\n  def test_remote_code_execution_CVE_2014_0130\n    assert_warning :type => :warning,\n      :warning_code => 77,\n      :fingerprint => \"e833fd152ab95bf7481aada185323d97cd04c3e2322b90f3698632f4c4c04441\",\n      :warning_type => \"Remote Code Execution\",\n      :line => nil,\n      :message => /^Rails\\ 4\\.0\\.0\\ with\\ globbing\\ routes\\ is\\ vuln/,\n      :confidence => 1,\n      :relative_path => \"config/routes.rb\",\n      :user_input => nil\n  end\n\n  def test_mass_assignment_CVE_2014_3514\n    assert_warning :type => :warning,\n      :warning_code => 80,\n      :fingerprint => \"98c76e0940c4e2ebb0dafd2b022c6818e7f620f196ce7e5c612af7d6ac06cd39\",\n      :warning_type => \"Mass Assignment\",\n      :line => 4,\n      :message => /^`create_with`\\ is\\ vulnerable\\ to\\ strong\\ para/,\n      :confidence => 1,\n      :relative_path => \"gems.rb\",\n      :user_input => nil\n  end\n\n  def test_redirect_1\n    assert_warning :type => :generic,\n      :warning_code => 18,\n      :fingerprint => \"6d27826e07e583ba9c6ae1f33843089fd1d8b1a2c359e00bf636e64a85a47feb\",\n      :warning_type => \"Redirect\",\n      :line => 14,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/controllers/removal_controller.rb\"\n  end\n\n  def test_session_setting_2\n    assert_warning :type => :generic,\n      :warning_code => 29,\n      :fingerprint => \"715ad9c0d76f57a6a657192574d528b620176a80fec969e2f63c88eacab0b984\",\n      :warning_type => \"Session Setting\",\n      :line => 12,\n      :message => /^Session\\ secret\\ should\\ not\\ be\\ included\\ in/,\n      :confidence => 0,\n      :relative_path => \"config/initializers/secret_token.rb\"\n  end\n\n  def test_cross_site_scripting_3\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"598b957fea4a202a75e1d8101a8c21332b10b2c0e9ca4ffad6c18407bde6615d\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/views/removal/_partial.html.erb\"\n  end\n\n  def test_cross_site_scripting_4\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"011d330ea62763eb61684cccc4169518b0876eadbab2b469e3526548f3da3795\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/views/removal/controller_removed.html.erb\"\n  end\n\n  def test_cross_site_scripting_5\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"26da712dc3289b873b7928b54bde6da038cbf891ec11076897e062f32939863e\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 2,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/views/removal/implicit_render.html.erb\"\n  end\n\n  def test_cross_site_scripting_6\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"52c513069319d44e03c5ac21806d47c1f05393fe35a5026314c8064f70ff0375\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/views/users/_form.html.erb\"\n  end\n\n  def test_cross_site_scripting_7\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"9d94ba6993f761ff688b5a8d428c793486cd8bf42f487a44d895a96f658dca50\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 6,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/views/users/_slimmer.html.slim\"\n  end\n\n  def test_cross_site_scripting_8\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"9d576795978cf6681a0cd17f7250ea267ab2ac7888dd5f6100331d5c0684beb3\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 8,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/views/users/_slimmer.html.slim\"\n  end\n\n  def test_cross_site_scripting_9\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"822a9a031ab38ae9e2b3580ce6eb28e6372852f289c9e65b347a9182c918d551\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 15,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/views/users/show.html.erb\"\n  end\n\n  def test_cross_site_scripting_10\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"aa06d3e4d8e00ccf6169d9293b1ef90365917c46fa21678d248494f7767d1d15\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/views/users/slimming.html.slim\"\n  end\n\n  def test_cross_site_scripting_11\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"6628d5e2059d14b31e7c37251ac7380ddbe44e937d78d4e955763d5d53df08fc\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/views/users/slimming.html.slim\"\n  end\n\n  def test_mass_assignment_12\n    assert_warning :type => :model,\n      :warning_code => 60,\n      :fingerprint => \"dbb51200329e5eadf073c7145497d0b18e33d903248426b6e8b97ec5d03ec23a\",\n      :warning_type => \"Mass Assignment\",\n      #noline,\n      :message => \"Potentially dangerous attribute available for mass assignment: :plan_id\",\n      :confidence => 2,\n      :relative_path => \"engines/user_removal/app/models/account.rb\"\n  end\n\n  def test_mass_assignment_13\n    assert_warning :type => :model,\n      :warning_code => 60,\n      :fingerprint => \"c505002e3567c74c8197586751d0cf9ab245aee0068f05c93589959b14dc40c8\",\n      :warning_type => \"Mass Assignment\",\n      #noline,\n      :message => \"Potentially dangerous attribute available for mass assignment: :banned\",\n      :confidence => 1,\n      :relative_path => \"engines/user_removal/app/models/account.rb\"\n  end\n\n  def test_mass_assignment_14\n    assert_warning :type => :model,\n      :warning_code => 60,\n      :fingerprint => \"962a14c66f5f83ece9a22700939111a0b71ed2c925980416f1b664a601e87070\",\n      :warning_type => \"Mass Assignment\",\n      #noline,\n      :message => \"Potentially dangerous attribute available for mass assignment: :account_id\",\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/models/user.rb\"\n  end\n\n  def test_mass_assignment_15\n    assert_warning :type => :model,\n      :warning_code => 60,\n      :fingerprint => \"fa154c3e50c02c70f4351dd6731085657dfb0b9ed73ee223ad5444b31bc1d31f\",\n      :warning_type => \"Mass Assignment\",\n      #noline,\n      :message => \"Potentially dangerous attribute available for mass assignment: :admin\",\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/models/user.rb\"\n  end\n\n  def test_mass_assignment_16\n    assert_warning :type => :model,\n      :warning_code => 60,\n      :fingerprint => \"98c24601f549d41e0d0367e8bcefc6083263fa175a2978ace0340c6446e57603\",\n      :warning_type => \"Mass Assignment\",\n      #noline,\n      :message => \"Potentially dangerous attribute available for mass assignment: :status_id\",\n      :confidence => 2,\n      :relative_path => \"engines/user_removal/app/models/user.rb\"\n  end\n\n  def test_csrf_without_exception\n    assert_warning :type => :controller,\n      :warning_code => 86,\n      :fingerprint => \"4d109bd02e4ccb3ea4c51485c947be435ee006a61af7d2cd37d1b358c7469189\",\n      :warning_type => \"Cross-Site Request Forgery\",\n      :message => \"`protect_from_forgery` should be configured with `with: :exception`\",\n      :confidence => 1,\n      :relative_path => \"app/controllers/application_controller.rb\"\n  end\n\n  def test_csrf_in_engine\n    assert_warning :type => :controller,\n      :warning_code => 7,\n      :fingerprint => \"bdd5f4f1cdd2e9fb24adc4e9333f2b2eb1d0325badcab7c0b89c25952a2454e8\",\n      :warning_type => \"Cross-Site Request Forgery\",\n      :line => 1,\n      :message => /^`protect_from_forgery`\\ should\\ be\\ called\\ /,\n      :confidence => 0,\n      :relative_path => \"engines/user_removal/app/controllers/base_controller.rb\",\n      :user_input => nil\n  end\n\n  def test_xml_dos_CVE_2015_3227\n    assert_warning :type => :warning,\n      :warning_code => 88,\n      :fingerprint => \"ea8edcadc48c04a69e0bee3bdb214ce61f7837b46e9c254b61993660653d1ec6\",\n      :warning_type => \"Denial of Service\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.0\\.0\\ is\\ vulnerable\\ to\\ denial\\ of\\ s/,\n      :confidence => 1,\n      :relative_path => \"gems.rb\",\n      :user_input => nil\n  end\n\n  def test_denial_of_service_CVE_2016_0751\n    assert_warning :type => :warning,\n      :warning_code => 94,\n      :fingerprint => \"13138593b5d3af97b9b674220c811229870f418c36717cb3c3df69928264bc95\",\n      :warning_type => \"Denial of Service\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.0\\.0\\ is\\ vulnerable\\ to\\ denial\\ of\\ s/,\n      :confidence => 1,\n      :relative_path => \"gems.rb\",\n      :user_input => nil\n  end\n\n  def test_nested_attributes_bypass_workaround_CVE_2015_7577\n    assert_no_warning :type => :model,\n      :warning_code => 95,\n      :fingerprint => \"2b1b6ac6e2348889ac0e1a7fdf0861dba7af91d794c454f8b4b07e7655a19610\",\n      :warning_type => \"Nested Attributes\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.0\\.0\\ does\\ not\\ call\\ `:reject_if`\\ opt/,\n      :confidence => 1,\n      :relative_path => \"engines/user_removal/app/models/user.rb\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_CVE_2016_6316\n    assert_warning :type => :warning,\n      :warning_code => 102,\n      :fingerprint => \"88b10c71ffa09afd9ec3dec09e08647caceb9977e23c80abc0de6bf024bb85b9\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Rails\\ 4\\.0\\.0\\ `content_tag`\\ does\\ not\\ escape\\ /,\n      :confidence => 1,\n      :relative_path => \"gems.rb\",\n      :user_input => nil\n  end\n\n  def test_unmaintained_dependency_rails\n    assert_warning check_name: \"EOLRails\",\n      type: :warning,\n      warning_code: 120,\n      fingerprint: \"918071d2713bdcc73e9cd9b5a1a3a59edf41d95fff50ab8c21f76f692fb5e0d7\",\n      warning_type: \"Unmaintained Dependency\",\n      line: 4,\n      message: /^Support\\ for\\ Rails\\ 4\\.0\\.0\\ ended\\ on\\ 2017\\-04/,\n      confidence: 0,\n      relative_path: \"gems.rb\",\n      code: nil,\n      user_input: nil\n  end\nend\n"
  },
  {
    "path": "test/tests/rails5.rb",
    "content": "require_relative '../test'\n\nclass Rails5Tests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def report\n    @@report ||= BrakemanTester.run_scan \"rails5\", \"Rails 5\", run_all_checks: true\n  end\n\n  def expected\n    @@expected ||= {\n      :controller => 0,\n      :model => 0,\n      :template => 19,\n      :generic => 26\n    }\n  end\n\n  def test_mass_assignment_with_safe_attrasgn\n    assert_warning :type => :warning,\n      :warning_code => 70,\n      :fingerprint => \"046f3c6cc9a55464d21837b583c672c26532cd46c1f719853a1a15b790baf8ea\",\n      :warning_type => \"Mass Assignment\",\n      :line => 78,\n      :message => /^Specify exact keys allowed for mass assignment/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:params), :permit!),\n      :user_input => nil\n  end\n\n  def test_mass_assignment_with_slice\n    assert_no_warning :type => :warning,\n      :warning_code => 70,\n      :fingerprint => \"79c472e032f2ff16f4688ea9d87ccc1c6def392c9b3e189ee1c4d1c079dd4fbf\",\n      :warning_type => \"Mass Assignment\",\n      :line => 87,\n      :message => /^Specify exact keys allowed for mass assignment instead/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:call, s(:params), :slice, s(:lit, :id)), :permit!),\n      :user_input => nil\n  end\n\n  def test_mass_assignment_permit_high\n    assert_warning :type => :warning,\n      :warning_code => 105,\n      :fingerprint => \"615627822842388859734b6124bf99e0db057a2572f35c92ff42b5ad46f4415f\",\n      :warning_type => \"Mass Assignment\",\n      :line => 90,\n      :message => /^Potentially\\ dangerous\\ key\\ allowed\\ for\\ ma/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/widget_controller.rb\",\n      :code => s(:call, s(:params), :permit, s(:lit, :admin)),\n      :user_input => s(:lit, :admin)\n  end\n\n  def test_mass_assignment_permit_medium\n    assert_no_warning :type => :warning,\n      :warning_code => 105,\n      :fingerprint => \"c4c89a39b0a2dc707027f47747312d27308ea219a009e4f0116a759a71ad561b\",\n      :warning_type => \"Mass Assignment\",\n      :line => 91,\n      :message => /^Potentially\\ dangerous\\ key\\ allowed\\ for\\ ma/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/widget_controller.rb\",\n      :code => s(:call, s(:params), :permit, s(:lit, :role_id)),\n      :user_input => s(:lit, :role_id)\n  end\n\n  def test_sql_injection_with_slice\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"d9f4fec5f738785ea1aed229d192a2d5d2eb0d8805f6ca58fd02416105e0f9db\",\n      :warning_type => \"SQL Injection\",\n      :line => 88,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :find_by, s(:call, s(:params), :slice, s(:lit, :id))),\n      :user_input => s(:call, s(:params), :slice, s(:lit, :id))\n  end\n\n  def test_sql_injection_with_quoted_primary_key\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"f9396dd572315e802eca1e03024a5b309ff006ede47b1aef6255236fcc37d2a9\",\n      :warning_type => \"SQL Injection\",\n      :line => 3,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/thing.rb\",\n      :user_input => s(:call, nil, :quoted_primary_key)\n  end\n\n  def test_divide_by_zero_1\n    assert_warning :type => :warning,\n      :warning_code => 104,\n      :fingerprint => \"f0729f7446e41e51883e58c74aa0789c0c7a48ab832f16c17b7eaba01a21ce6e\",\n      :warning_type => \"Divide by Zero\",\n      :line => 8,\n      :message => /^Potential\\ division\\ by\\ zero/,\n      :confidence => 2,\n      :relative_path => \"lib/a_lib.rb\",\n      :code => s(:call, s(:call, nil, :whatever), :/, s(:lit, 0)),\n      :user_input => s(:lit, 0)\n  end\n\n  def test_divide_by_zero_2\n    assert_warning :type => :warning,\n      :warning_code => 104,\n      :fingerprint => \"9ca769ad11ef57b7490caccc70af1bb8a623dfca2e84e5593d8dec901d3841f2\",\n      :warning_type => \"Divide by Zero\",\n      :line => 12,\n      :message => /^Potential\\ division\\ by\\ zero/,\n      :confidence => 1,\n      :relative_path => \"lib/a_lib.rb\",\n      :code => s(:call, s(:lit, 100), :/, s(:lit, 0)),\n      :user_input => s(:lit, 0)\n  end\n\n  def test_dangerous_send_with_safe_call\n    assert_warning :type => :warning,\n      :warning_code => 23,\n      :fingerprint => \"21c9eef1c001e48a0bfedfa11ff0f9d96b0c106f1016218712dabc088b2e69b6\",\n      :warning_type => \"Dangerous Send\",\n      :line => 76,\n      :message => /^User\\ controlled\\ method\\ execution/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:call, nil, :x), :send, s(:call, s(:params), :[], s(:lit, :x))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_dangerous_send_with_early_return\n    assert_no_warning :type => :warning,\n      :warning_code => 23,\n      :fingerprint => \"04f96cff9e890ab6c0a54c62465602eb92fe74f4fb91c10dcad51aaeb96ff7d7\",\n      :warning_type => \"Dangerous Send\",\n      :line => 16,\n      :message => /^User\\ controlled\\ method\\ execution/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/mixed_controller.rb\",\n      :code => s(:call, s(:colon2, s(:const, :Statistics), :AdminWithdrawal), :send, s(:dstr, \"export_\", s(:evstr, s(:call, s(:call, s(:params), :[], s(:lit, :filename)), :first)), s(:str, \"_#inc!\"))),\n      :user_input => s(:call, s(:call, s(:params), :[], s(:lit, :filename)), :first)\n  end\n\n  def test_dangerous_send_with_fail\n    assert_no_warning :type => :warning,\n      :warning_code => 23,\n      :fingerprint => \"e39bb04370762208b68068f4dc823ec897b75bb50f4a5dee02e329e2b6dda733\",\n      :warning_type => \"Dangerous Send\",\n      :line => 22,\n      :message => /^User\\ controlled\\ method\\ execution/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/mixed_controller.rb\",\n      :code => s(:call, s(:const, :Model), :public_send, s(:call, s(:call, s(:params), :[], s(:lit, :scope)), :presence)),\n      :user_input => s(:call, s(:call, s(:params), :[], s(:lit, :scope)), :presence)\n  end\n\n  def test_no_symbol_denial_of_service\n    assert_no_warning :type => :warning,\n      :warning_code => 59,\n      :fingerprint => \"78ba8fe2efc151bc8eca64f36940d1423a8fb92f17a8b7858bffba6cb372490b\",\n      :warning_type => \"Denial of Service\",\n      :line => 83,\n      :message => /^Symbol\\ conversion\\ from\\ unsafe\\ string\\ \\(pa/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:call, s(:params), :[], s(:lit, :x)), :to_sym),\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_secrets_in_source\n    assert_warning :type => :warning,\n      :warning_code => 101,\n      :fingerprint => \"eefde7320af81299c41d50840750b5cb509a1fe454ba9179076955bf53b6d966\",\n      :warning_type => \"Authentication\",\n      :line => 1,\n      :message => /^Hardcoded\\ value\\ for\\ `DB_PASSWORD`\\ in\\ sourc/,\n      :confidence => 1,\n      :user_input => nil,\n      :relative_path => \"config/initializers/secrets.rb\"\n  end\n\n  def test_skipping_rails_env_test\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"46cda22e00dca87a8715682bd7d8d52cc4a8e705257b27c5e36595ebd1f654f8\",\n      :warning_type => \"SQL Injection\",\n      :line => 7,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, s(:const, :User), :where, s(:params)),\n      :user_input => s(:params)\n  end\n\n  def test_default_routes_in_test\n    assert_no_warning :type => :warning,\n      :warning_code => 11,\n      :fingerprint => \"ff2b76e22c9fd2bc3930f9a935124b9ed9f6ea710bbb5bc7c51505d70ca0f2d5\",\n      :warning_type => \"Default Routes\",\n      :line => 8,\n      :message => /^All\\ public\\ methods\\ in\\ controllers\\ are\\ av/,\n      :confidence => 0,\n      :relative_path => \"config/routes.rb\",\n      :user_input => nil\n  end\n\n  def test_redirect_with_slice\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"b70fe6fa14df927bdfe80e0731c4c4170db0c3c80edad5a4462c7037acde93a4\",\n      :warning_type => \"Redirect\",\n      :line => 89,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, nil, :redirect_to, s(:call, s(:params), :slice, s(:lit, :back_to))),\n      :user_input => s(:call, s(:params), :slice, s(:lit, :back_to))\n  end\n\n  def test_redirect_with_return_guard\n    assert_no_warning :type => :warning,\n      :warning_code => 23,\n      :fingerprint => \"208deedcfef17a235e5c2139c74bbb408b2a948334880be58fcc441c09b9d799\",\n      :warning_type => \"Dangerous Send\",\n      :line => 82,\n      :message => /^User\\ controlled\\ method\\ execution/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/widget_controller.rb\",\n      :code => s(:call, nil, :send, s(:dstr, \"\", s(:evstr, s(:call, s(:params), :[], s(:lit, :goto))), s(:str, \"_event_path\")), s(:call, s(:params), :[], s(:lit, :event))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :goto))\n  end\n\n  def test_redirect_with_unsafe_permit_values\n    assert_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"9187972b879413888afcc0f94c02d9e5c47d56ecb15add404d6706f95efc08ee\",\n      :warning_type => \"Redirect\",\n      :line => 26,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/mixed_controller.rb\",\n      :code => s(:call, nil, :redirect_to, s(:call, s(:params), :permit, s(:lit, :domain))),\n      :user_input => s(:call, s(:params), :permit, s(:lit, :domain))\n  end\n\n  def test_redirect_with_safe_permit_values\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"b148908432d722a877a87c9c70e62cdf67328a2f25f6f62eefebce94ef01b7ec\",\n      :warning_type => \"Redirect\",\n      :line => 27,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/mixed_controller.rb\",\n      :code => s(:call, nil, :redirect_to, s(:call, s(:params), :permit, s(:lit, :page), s(:lit, :sort))),\n      :user_input => s(:call, s(:params), :permit, s(:lit, :page), s(:lit, :sort))\n  end\n\n  def test_redirect_with_path_on_model\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"1f0dba58823930667b1fbf060329a5ce7462b517b776d1985da014321f399362\",\n      :warning_type => \"Redirect\",\n      :line => 100,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/widget_controller.rb\",\n      :code => s(:call, nil, :redirect_to, s(:call, s(:call, s(:call, s(:const, :User), :find_by_token, s(:call, s(:params), :[], s(:lit, :session))), :user), :current_path)),\n      :user_input => s(:call, s(:call, s(:call, s(:const, :User), :find_by_token, s(:call, s(:params), :[], s(:lit, :session))), :user), :current_path)\n  end\n\n  def test_cross_site_scripting_with_slice\n    assert_no_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"0e7c3fed684f3152150e01986fbdde92741b2d69628156f3f28f30987456c018\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 25,\n      :message => /^Unsafe\\ parameter\\ value\\ in\\ `link_to`\\ href/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/index.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"slice\"), s(:call, s(:params), :slice, s(:lit, :url))),\n      :user_input => s(:call, s(:params), :slice, s(:lit, :url))\n  end\n\n  def test_cross_site_scripting_with_merge_in_link_to\n    assert_no_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"54043efc2da20930f636dedfef5b1e77dfed0957ebb0c285f0c0a71b68e046c5\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 6,\n      :message => /^Unsafe\\ parameter\\ value\\ in\\ `link_to`\\ href/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"good\"), s(:call, s(:call, nil, :params), :merge, s(:hash, s(:lit, :page), s(:lit, 2)))),\n      :user_input => s(:call, s(:call, nil, :params), :merge, s(:hash, s(:lit, :page), s(:lit, 2)))\n  end\n\n  def test_cross_site_scripting_link_to_url_for\n    assert_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"03fcddff701f15c976a229c2a814c817f81463b733c6d4925253488879d907e3\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unsafe\\ parameter\\ value\\ in\\ `link_to`\\ href/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"xss\"), s(:call, nil, :url_for, s(:call, s(:params), :[], s(:lit, :bad)))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :bad))\n  end\n\n  def test_cross_site_scripting_inline_erb\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"26b8b0ad586712d41ac6877e2292c6da7aa4760078add7fd23edf5b7a1bcb699\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/widget/show.html.erb\",\n      :code => s(:call, s(:params), :[], s(:lit, :x)),\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_in_layout\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"7aec2bbe8fa40f49f08b70dfc8c0b6afdc9be252eb0459a3d5313bc904d9ec77\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 2,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/layouts/users.html.erb\",\n      :code => s(:call, s(:call, s(:const, :User), :new), :name),\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_in_template_with_no_html_extension\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"dbec030dda82f21c5ea860e38746de64a6f3b9c49508ae2db947759a753a386c\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/widget/no_html.haml\",\n      :code => s(:call, s(:params), :[], s(:lit, :x)),\n      :user_input => nil\n  end\n\n  def test_if_expression_in_templates\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"26b8b0ad586712d41ac6877e2292c6da7aa4760078add7fd23edf5b7a1bcb699\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/widget/show.html.erb\",\n      :code => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_remote_code_execution_in_dynamic_constant\n    assert_warning :type => :warning,\n      :warning_code => 24,\n      :fingerprint => \"ed9f1dea97ba2929a0107fce64c3b4aa66010961ebbef36e1d11428067095cb6\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 7,\n      :message => /^Unsafe\\ reflection\\ method\\ `constantize`\\ cal/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/widget_controller.rb\",\n      :code => s(:call, s(:call, s(:params), :[], s(:lit, :IdentifierClass)), :constantize),\n      :user_input => s(:call, s(:params), :[], s(:lit, :IdentifierClass))\n  end\n\n  def test_dynamic_render_path_with_boolean\n    assert_no_warning :type => :warning,\n      :warning_code => 15,\n      :fingerprint => \"77503a2c10167a42ac4b40b81aa2cf3b737ad206f5a9c593ae9898c9915a5136\",\n      :warning_type => \"Dynamic Render Path\",\n      :line => 11,\n      :message => /^Render\\ path\\ contains\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/widget_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:call, s(:params), :[], s(:lit, :x)), :thing?), s(:hash)),\n      :user_input => s(:call, s(:call, s(:params), :[], s(:lit, :x)), :thing?)\n  end\n\n  def test_dynamic_render_path_template_exists\n    assert_no_warning :type => :warning,\n      :warning_code => 15,\n      :fingerprint => \"5c250fd85fe088bf628d517af37038fa516acc4b6103ee6d8a15e857079ad434\",\n      :warning_type => \"Dynamic Render Path\",\n      :line => 108,\n      :message => /^Render\\ path\\ contains\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/widget_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:call, s(:params), :[], s(:lit, :slug)), :to_s), s(:hash)),\n      :user_input => s(:call, s(:call, s(:params), :[], s(:lit, :slug)), :to_s)\n  end\n\n  def test_render_inline_cookies\n    assert_warning :type => :warning,\n      :warning_code => 84,\n      :fingerprint => \"8badd2e174576484eca32fb6015d903700d6694e9b3486be64d737aa215a36ea\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 86,\n      :message => /^Unescaped\\ cookie\\ value\\ rendered\\ inline/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/widget_controller.rb\",\n      :code => s(:render, :inline, s(:call, s(:call, s(:call, nil, :request), :cookies), :[], s(:str, \"value\")), s(:hash)),\n      :user_input => s(:call, s(:call, s(:call, nil, :request), :cookies), :[], s(:str, \"value\"))\n  end\n\n  def test_warning_in_helper_method\n    assert_warning :type => :warning,\n      :warning_code => 13,\n      :fingerprint => \"e90f8e364e35ed2f6a56b4597e7de8945c836c75ef673006d960a380ecdf47e8\",\n      :warning_type => \"Dangerous Eval\",\n      :line => 3,\n      :message => /^Parameter\\ value\\ evaluated\\ as\\ code/,\n      :confidence => 0,\n      :relative_path => \"app/helpers/users_helper.rb\",\n      :code => s(:call, nil, :eval, s(:call, s(:params), :[], s(:lit, :x))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_sql_injection_where_values_hash_fp\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"2a77f56c4c09590a4cac1fe68dd00c0fa0a7820ea6f0d4bad20451ecc07dc68e\",\n      :warning_type => \"SQL Injection\",\n      :line => 21,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, nil, :where, s(:call, s(:call, s(:const, :Thing), :canadian), :where_values_hash)),\n      :user_input => s(:call, s(:call, s(:const, :Thing), :canadian), :where_values_hash)\n  end\n\n  def test_sql_injection_from_model_call_fp\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"dcfa0c30b2d303c58bde5b376f423cff6282bbc71ed460077478ca97e1f4d0f7\",\n      :warning_type => \"SQL Injection\",\n      :line => 2,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, s(:const, :User), :where, s(:call, s(:const, :User), :access_condition, s(:lvar, :user))),\n      :user_input => s(:call, s(:const, :User), :access_condition, s(:lvar, :user))\n  end\n\n  def test_targetless_sql_injection_outside_of_AR_model\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"fe0098fc5ab1051854573b487855f348bd9320c8eb5ae55302b4649d0147d7dd\",\n      :warning_type => \"SQL Injection\",\n      :line => 3,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"lib/a_lib.rb\",\n      :code => s(:call, nil, :joins, s(:dstr, \"INNER JOIN things ON id = \", s(:evstr, s(:call, s(:params), :[], s(:lit, :id))))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :id))\n  end\n\n  def test_sql_injection_in_interp_branch\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"13c2dbdbce47c04755e5019dba4fc03729167c71a63e1d4bab81d672ff3975a0\",\n      :warning_type => \"SQL Injection\",\n      :line => 93,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:call, s(:const, :User), :connection), :execute, s(:dstr, \"SELECT * FROM foo WHERE \", s(:evstr, s(:if, s(:true), s(:dstr, \"bar = \", s(:evstr, s(:call, s(:call, s(:colon2, s(:const, :ActiveRecord), :Base), :connection), :quote, s(:true)))), nil)))),\n      :user_input => s(:if, s(:true), s(:dstr, \"bar = \", s(:evstr, s(:call, s(:call, s(:colon2, s(:const, :ActiveRecord), :Base), :connection), :quote, s(:true)))), nil)\n  end\n\n  def test_sql_injection_arel_sql\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"672fb59ead203af7b429e4efa722101b9246e60334470daa7c07a62078974350\",\n      :warning_type => \"SQL Injection\",\n      :line => 97,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :Arel), :sql, s(:dstr, \"select \", s(:evstr, s(:call, s(:params), :[], s(:lit, :s))))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :s))\n  end\n\n  def test_tempfile_access\n    assert_no_warning :type => :warning,\n      :warning_code => 16,\n      :fingerprint => \"4c1ffee385f7f0318d609a3675b13d9eeaf6da3ce6a7953df523c7d6ba4d24b5\",\n      :warning_type => \"File Access\",\n      :line => 8,\n      :message => /^Parameter\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 0,\n      :relative_path => \"lib/a_lib.rb\",\n      :code => s(:call, s(:const, :FileUtils), :move, s(:call, s(:call, s(:call, s(:call, s(:params), :permit, s(:hash, s(:lit, :my_upload), s(:array, s(:lit, :upload)))), :dig, s(:str, \"my_upload\"), s(:str, \"upload\")), :tempfile), :path), s(:str, \"/tmp/new_temp_file\")),\n      :user_input => s(:call, s(:call, s(:call, s(:call, s(:params), :permit, s(:hash, s(:lit, :my_upload), s(:array, s(:lit, :upload)))), :dig, s(:str, \"my_upload\"), s(:str, \"upload\")), :tempfile), :path)\n\n    assert_no_warning :type => :warning,\n      :warning_code => 16,\n      :fingerprint => \"9783fd98e5657e76e8337bc7b319b101e8cf5ab3b290d69d5f00eee3a86e4ebd\",\n      :warning_type => \"File Access\",\n      :line => 9,\n      :message => /^Parameter\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 0,\n      :relative_path => \"lib/a_lib.rb\",\n      :code => s(:call, s(:const, :FileUtils), :move, s(:call, s(:call, s(:call, s(:params), :permit, s(:hash, s(:lit, :my_upload), s(:array, s(:lit, :upload)))), :dig, s(:str, \"my_upload\"), s(:str, \"upload\")), :path), s(:str, \"/tmp/new_temp_file\")),\n      :user_input => s(:call, s(:call, s(:call, s(:params), :permit, s(:hash, s(:lit, :my_upload), s(:array, s(:lit, :upload)))), :dig, s(:str, \"my_upload\"), s(:str, \"upload\")), :path)\n\n    assert_no_warning :type => :warning,\n      :warning_code => 16,\n      :fingerprint => \"4aa7ea06f3d7c93776701ecfff0dcf8a9a4ef1789864f804efb9708539760d4c\",\n      :warning_type => \"File Access\",\n      :line => 3,\n      :message => /^Parameter\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/file_controller.rb\"\n  end\n\n  def test_activestorage_sanitized\n    assert_no_warning :type => :warning,\n      :warning_code => 16,\n      :fingerprint => \"9dae00164405a05fa97d79fcf1eedba0e9cd9767c94193ecda9cc99db0cec89a\",\n      :warning_type => \"File Access\",\n      :line => 7,\n      :message => /^Parameter\\ value\\ used\\ in\\ file\\ name/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/file_controller.rb\",\n      :code => s(:call, nil, :send_file, s(:call, s(:call, s(:colon2, s(:const, :ActiveStorage), :Filename), :new, s(:dstr, \"\", s(:evstr, s(:call, s(:params), :[], s(:lit, :file_name))), s(:str, \".jpg\"))), :sanitized)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :file_name))\n  end\n\n  def test_missing_encryption_force_ssl\n    assert_warning :type => :warning,\n      :warning_code => 109,\n      :fingerprint => \"6a26086cd2400fbbfb831b2f8d7291e320bcc2b36984d2abc359e41b3b63212b\",\n      :warning_type => \"Missing Encryption\",\n      :line => 1,\n      :message => /^The application does not force use of HTTPS/,\n      :confidence => 0,\n      :relative_path => \"config/environments/production.rb\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_CVE_2015_7578\n    assert_warning :type => :warning,\n      :warning_code => 96,\n      :fingerprint => \"7feea01d5ef6edbc300e34ecffd304a4d76cf306dbc71712a8340a3ac08b6dad\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 133,\n      :message => /^rails\\-html\\-sanitizer\\ 1\\.0\\.2\\ is\\ vulnerable/,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_CVE_2015_7580\n    assert_warning :type => :warning,\n      :warning_code => 97,\n      :fingerprint => \"f542035c0310ab2e76ec6dbccace0954f0d7c576d56d8cfcb03d9836f50bc7c9\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 133,\n      :message => /^rails\\-html\\-sanitizer\\ 1\\.0\\.2\\ is\\ vulnerable/,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_CVE_2015_7579\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"0d980d69bd0158cfa6a92c12bc49294fe32e9862a758e11fe3cf9e03b6c50489\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/sanitizing.html.erb\",\n      :code => s(:call, nil, :strip_tags, s(:call, s(:call, nil, :params), :[], s(:lit, :x))),\n      :user_input => s(:call, s(:call, nil, :params), :[], s(:lit, :x))\n  end\n\n  def test_cross_site_scripting_sanitize_cve\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"e203c837d65aad6ab63e09c2487beabf478534f77f0c20e946a28a38826ca657\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/sanitizing.html.erb\",\n      :code => s(:call, nil, :sanitize, s(:call, s(:call, nil, :params), :[], s(:lit, :x))),\n      :user_input => s(:call, s(:call, nil, :params), :[], s(:lit, :x))\n  end\n\n  def test_cross_site_scripting_strip_tags_cve\n    assert_warning :type => :warning,\n      :warning_code => 98,\n      :fingerprint => \"9f292c507e0f07fd0ffc7a3d000af464c522ae6a929015256f505f35fb75ac82\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 133,\n      :message => /^rails\\-html\\-sanitizer\\ 1\\.0\\.2\\ is\\ vulnerable/,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_xss_content_tag_CVE_2016_6316_html_safe\n    assert_warning :type => :template,\n      :warning_code => 53,\n      :fingerprint => \"956e3e4f494316c5f30cde009086fd7be0bddf80d85901cdb8e3d7b7d76d219b\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value\\ in\\ `content_tag`/,\n      :confidence => 0,\n      :relative_path => \"app/views/widget/content_tag.html.erb\",\n      :code => s(:call, nil, :content_tag, s(:lit, :div), s(:str, \"hi\"), s(:hash, s(:lit, :title), s(:call, s(:call, s(:call, nil, :params), :[], s(:lit, :stuff)), :html_safe))),\n      :user_input => s(:call, s(:call, s(:call, nil, :params), :[], s(:lit, :stuff)), :html_safe)\n  end\n\n  def test_xss_content_tag_CVE_2016_6316_sanitize\n    assert_warning :type => :template,\n      :warning_code => 53,\n      :fingerprint => \"a1ca8c0e91d159ddc920d3c9efc6942f6aa697c519b299b756810ac1ca977763\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ parameter\\ value\\ in\\ `content_tag`/,\n      :confidence => 1,\n      :relative_path => \"app/views/widget/content_tag.html.erb\",\n      :code => s(:call, nil, :content_tag, s(:lit, :div), s(:str, \"hi\"), s(:hash, s(:lit, :title), s(:call, nil, :sanitize, s(:call, s(:call, nil, :params), :[], s(:lit, :stuff))))),\n      :user_input => s(:call, s(:call, nil, :params), :[], s(:lit, :stuff))\n  end\n\n  def test_cross_site_scripting_CVE_2016_6316_general\n    assert_warning :type => :warning,\n      :warning_code => 102,\n      :fingerprint => \"331e69e4654f158601d9a0e124304f825da4e0156d2c94759eb02611e280feaa\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 115,\n      :message => /^Rails\\ 5\\.0\\.0\\ `content_tag`\\ does\\ not\\ escape\\ /,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_loofah_CVE_2018_8048\n    assert_warning :type => :warning,\n      :warning_code => 106,\n      :fingerprint => \"cdfb1541fdcc9cdcf0784ce5bd90013dc39316cb822eedea3f03b2521c06137f\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 99,\n      :message => /^loofah\\ gem 2\\.0\\.3\\ is\\ vulnerable\\ \\(CVE\\-2018\\-804/,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_CVE_2018_3741\n    assert_warning :type => :warning,\n      :warning_code => 107,\n      :fingerprint => \"3e35a6afcd1a8a14894cf26a7f00d4e895f0583bbc081d45e5bd28c4b541b7e6\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 133,\n      :message => /^rails\\-html\\-sanitizer\\ 1\\.0\\.2\\ is\\ vulnerable/,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :user_input => nil\n  end\n\n  def test_path_traversal_sprockets_CVE_2018_3760\n    assert_warning :type => :warning,\n      :warning_code => 108,\n      :fingerprint => \"d4c4f9fab93a12c4fabb5b5f9541700e94f4948bc1b90a1aea38f1425d8d0dc9\",\n      :warning_type => \"Path Traversal\",\n      :line => 144,\n      :message => /^sprockets\\ 3\\.5\\.2\\ has\\ a\\ path\\ traversal\\ vul/,\n      :confidence => 0,\n      :relative_path => \"Gemfile.lock\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_directory_traversal_caching_page_CVE_2020_8159\n    assert_warning :type => :warning,\n      :warning_code => 115,\n      :fingerprint => \"4022cb6c8f1bdeb4feec0fe01ba0ddc34441c551ae40c84845d76edb47464b8f\",\n      :warning_type => \"Directory Traversal\",\n      :line => 24,\n      :message => /^Directory\\ traversal\\ vulnerability\\ in\\ act/,\n      :confidence => 2,\n      :relative_path => \"Gemfile\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_CVE_2022_32209_rails_config\n    assert_warning check_name: \"SanitizeConfigCve\",\n      type: :warning,\n      warning_code: 124,\n      fingerprint: \"b9ac1b40c4e59a1e97e2beb039fdfa75c6fb97cf530161bd3c29939e83a513f4\",\n      warning_type: \"Cross-Site Scripting\",\n      line: 133,\n      message: /^rails\\-html\\-sanitizer\\ 1\\.0\\.2\\ is\\ vulnerable/,\n      confidence: 0,\n      relative_path: \"Gemfile.lock\",\n      code: nil,\n      user_input: nil\n  end\n\n  def test_dangerous_eval_in_prior_class_method_with_same_name\n    assert_warning :type => :warning,\n      :warning_code => 13,\n      :fingerprint => \"7fe3142d1d11b7118463e45a82b4b7a2b5b5bac95cf8904050c101fae16b8168\",\n      :warning_type => \"Dangerous Eval\",\n      :line => 7,\n      :message => /^Parameter\\ value\\ evaluated\\ as\\ code/,\n      :method => :\"User.evaluate_user_input\",\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :user_input => s(:params)\n  end\n\n  def test_template_injection_1\n    assert_warning :type => :warning,\n      :warning_code => 117,\n      :fingerprint => \"fba898ebe85a030856f8553a3329c184ad6f9e16b1ecc8eb862d75f8b48d8189\",\n      :warning_type => \"Template Injection\",\n      :line => 3,\n      :message => /^Parameter\\ value\\ used\\ directly\\ in\\ `ERB`\\ t/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, s(:const, :ERB), :new, s(:params)),\n      :user_input => s(:params)\n  end\n\n  def test_link_to_href_safe_interpolation\n    assert_no_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"840e79fc7cc526a9e744b8a3d49f6689aa572941f46b030d14cdec01f3675a4a\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unsafe\\ parameter\\ value\\ in\\ `link_to`\\ href/,\n      :confidence => 0,\n      :relative_path => \"app/views/widget/show.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"Thing\"), s(:dstr, \"\", s(:evstr, s(:call, s(:const, :ENV), :[], s(:str, \"SOME_URL\"))), s(:evstr, s(:call, s(:params), :[], s(:lit, :x))))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n\n    assert_no_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"ea91f7cfb339ae9522f00fb1f3bc176f789110b6e0cbc4f8704e95d0999b0e71\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unsafe\\ parameter\\ value\\ in\\ `link_to`\\ href/,\n      :confidence => 0,\n      :relative_path => \"app/views/widget/show.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"Email!\"), s(:dstr, \"mailto:\", s(:evstr, s(:call, s(:params), :[], s(:lit, :x))))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_cross_site_scripting_sanitize_in_link_to\n    assert_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"2247b0928591e951ddb428e97bf4174a36080a196a2f6d6fedd2d7c4428db2a9\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 9,\n      :message => /^Potentially\\ unsafe\\ model\\ attribute\\ in\\ `li/,\n      :confidence => 2,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, nil, :link_to, s(:call, nil, :image_tag, s(:str, \"icons/twitter-gray.svg\")), s(:call, nil, :sanitize, s(:call, s(:call, s(:const, :User), :new, s(:call, nil, :user_params)), :home_page)), s(:hash, s(:lit, :target), s(:str, \"_blank\"))),\n      :user_input => s(:call, s(:call, s(:const, :User), :new, s(:call, nil, :user_params)), :home_page)\n  end\n\n  def test_mixed_in_csrf_protection\n    assert_no_warning :type => :controller,\n      :warning_type => \"Cross-Site Request Forgery\",\n      :line => 1,\n      :message => /^'protect_from_forgery'\\ should\\ be\\ called\\ /,\n      :relative_path => \"app/controllers/mixed_controller.rb\"\n  end\n\n  def test_unscoped_find\n    assert_no_warning :type => :warning,\n      :warning_code => 82,\n      :fingerprint => \"21a836b647ac118baf1a63e5fa4c219f8d600760b05ff9b8927c39a97ebf1dd1\",\n      :warning_type => \"Unscoped Find\",\n      :line => 67,\n      :message => /^Unscoped\\ call\\ to\\ User\\#find/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :find, s(:call, s(:params), :[], s(:lit, :id))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :id))\n  end\n\n  def test_reverse_tabnabbing\n    assert_warning :type => :template,\n      :warning_code => 111,\n      :fingerprint => \"a72829f1e36e4d7c4fd71a1b9e39b011137dc3b317a17df2fc7795e08b37cf75\",\n      :warning_type => \"Reverse Tabnabbing\",\n      :line => 9,\n      :message => /^When\\ opening\\ a\\ link\\ in\\ a\\ new\\ tab\\ without\\ setting\\ `rel:\\ \"noopener\\ noreferrer\"`/,\n      :confidence => 1,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, nil, :link_to, s(:call, nil, :image_tag, s(:str, \"icons/twitter-gray.svg\")), s(:call, nil, :sanitize, s(:call, s(:call, s(:const, :User), :new, s(:call, nil, :user_params)), :home_page)), s(:hash, s(:lit, :target), s(:str, \"_blank\"))),\n      :user_input => nil\n\n    assert_no_warning :type => :template,\n      :warning_code => 111,\n      :warning_type => \"Reverse Tabnabbing\",\n      :line => 11,\n      :confidence => 1,\n      :relative_path => \"app/views/users/show.html.erb\"\n\n    assert_warning :type => :template,\n      :warning_code => 111,\n      :fingerprint => \"24b8934e896e96588d39e012e8ad1eb77d10a9daaaac1c08e9e0e920b6b82748\",\n      :warning_type => \"Reverse Tabnabbing\",\n      :line => 12,\n      :message => /^When\\ opening\\ a\\ link\\ in\\ a\\ new\\ tab\\ without\\ setting\\ `rel:\\ \"noopener\\ noreferrer\"`/,\n      :confidence => 1,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"\"), s(:call, nil, :some_url), s(:hash, s(:lit, :target), s(:str, \"_blank\"))),\n      :user_input => nil\n\n    assert_warning :type => :template,\n      :warning_code => 111,\n      :fingerprint => \"83d6722a5dca630fc2a8ba0bee5b457b3fd9399fabb4575df63ce25b280f5a19\",\n      :warning_type => \"Reverse Tabnabbing\",\n      :line => 13,\n      :message => /^When\\ opening\\ a\\ link\\ in\\ a\\ new\\ tab\\ without\\ setting\\ `rel:\\ \"noopener\\ noreferrer\"`/,\n      :confidence => 1,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"\"), s(:call, nil, :some_url), s(:hash, s(:lit, :target), s(:lit, :_blank))),\n      :user_input => nil\n\n    assert_warning :type => :template,\n      :warning_code => 111,\n      :fingerprint => \"d81b4e129ad9a43a905c335deb5ca98ef62ce9509cd29d536fcff70c2431dccf\",\n      :warning_type => \"Reverse Tabnabbing\",\n      :line => 14,\n      :message => /^When\\ opening\\ a\\ link\\ in\\ a\\ new\\ tab\\ without\\ setting\\ `rel:\\ \"noopener\\ noreferrer\"`/,\n      :confidence => 1,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, nil, :link_to, s(:call, nil, :some_url), s(:hash, s(:lit, :target), s(:str, \"_blank\"))),\n      :user_input => nil\n\n    assert_warning :type => :template,\n      :warning_code => 111,\n      :fingerprint => \"89231ee3d208edba36dd6c445d4172216c282887e3d9a7b22ab47767ca033b92\",\n      :warning_type => \"Reverse Tabnabbing\",\n      :line => 16,\n      :message => /^When\\ opening\\ a\\ link\\ in\\ a\\ new\\ tab\\ without\\ setting\\ `rel:\\ \"noopener\\ noreferrer\"`/,\n      :confidence => 1,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, nil, :link_to, s(:call, nil, :some_url), s(:hash, s(:lit, :target), s(:lit, :_blank))),\n      :user_input => nil\n\n    assert_warning :type => :template,\n      :warning_code => 111,\n      :fingerprint => \"7678f236bb756de38a16d0aeb753a47db32e44c1371aee64e86f04f7bcd7c067\",\n      :warning_type => \"Reverse Tabnabbing\",\n      :line => 18,\n      :message => /^When\\ opening\\ a\\ link\\ in\\ a\\ new\\ tab\\ without\\ setting\\ `rel:\\ \"noopener\\ noreferrer\"`/,\n      :confidence => 2,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"\"), s(:call, nil, :some_url), s(:hash, s(:lit, :target), s(:str, \"_blank\"), s(:lit, :rel), s(:str, \"noopener\"))),\n      :user_input => s(:str, \"noopener\")\n\n    assert_warning :type => :template,\n      :warning_code => 111,\n      :fingerprint => \"2a3657324d7d873ae9fb3667534ee2a4df0f7822ec0b379740828aecc2941d8c\",\n      :warning_type => \"Reverse Tabnabbing\",\n      :line => 19,\n      :message => /^When\\ opening\\ a\\ link\\ in\\ a\\ new\\ tab\\ without\\ setting\\ `rel:\\ \"noopener\\ noreferrer\"`/,\n      :confidence => 2,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, nil, :link_to, s(:str, \"\"), s(:call, nil, :some_url), s(:hash, s(:lit, :target), s(:str, \"_blank\"), s(:lit, :rel), s(:str, \"noreferrer\"))),\n      :user_input => s(:str, \"noreferrer\")\n\n    assert_no_warning :type => :template,\n      :warning_code => 111,\n      :warning_type => \"Reverse Tabnabbing\",\n      :line => 22,\n      :confidence => 2,\n      :relative_path => \"app/views/users/show.html.erb\"\n\n    assert_no_warning :type => :template,\n      :warning_code => 111,\n      :warning_type => \"Reverse Tabnabbing\",\n      :line => 23,\n      :confidence => 2,\n      :relative_path => \"app/views/users/show.html.erb\"\n\n    assert_no_warning :type => :template,\n      :warning_code => 111,\n      :warning_type => \"Reverse Tabnabbing\",\n      :line => 24,\n      :confidence => 1,\n      :relative_path => \"app/views/users/show.html.erb\"\n  end\n\n  def test_haml_attributes\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/views/widget/attributes.html.haml\",\n      :code => s(:call, s(:call, nil, :_hamlout), :attributes, s(:hash, s(:str, \"data-text\"), s(:dstr, \"\", s(:evstr, s(:call, s(:params), :[], s(:lit, :name))))), s(:nil)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :name))\n  end\n\n  def test_haml_interpolation\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"c16c1a10087e9e1b703a21ab8d9eac942f033c50be9afe9a56fff5ba5c62c739\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 13,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/widget/attributes.html.haml\",\n      :code => s(:call, s(:call, s(:call, s(:const, :User), :first), :name), :stuff_html),\n      :user_input => nil\n  end\n\n  def test_haml_textareas\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/views/widget/attributes.html.haml\",\n      :code => s(:call, s(:call, nil, :_hamlout), :fix_textareas!, s(:call, s(:colon2, s(:colon3, :Haml), :Helpers), :preserve, s(:call, s(:call, s(:colon2, s(:colon3, :Haml), :Helpers), :html_escape, s(:call, s(:call, s(:params), :[], s(:lit, :blah)), :to_s)), :strip)))\n  end\n\n  def test_cross_site_scripting_haml_interpolation\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"01ff71dc776c03921089d8559dabd1a75480411ec7f1de7f2886659085c26045\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 6,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/widget/haml_test.html.haml\",\n      :code => s(:call, s(:params), :[], s(:lit, :y)),\n      :user_input => nil\n  end\n\n  def test_unmaintained_dependency_rails\n    assert_warning check_name: \"EOLRails\",\n      type: :warning,\n      warning_code: 120,\n      fingerprint: \"d84924377155b41e094acae7404ec2e521629d86f97b0ff628e3d1b263f8101c\",\n      warning_type: \"Unmaintained Dependency\",\n      line: 115,\n      message: /^Support\\ for\\ Rails\\ 5\\.0\\.0\\ ended\\ on\\ 2018\\-05/,\n      confidence: 0,\n      relative_path: \"Gemfile.lock\",\n      code: nil,\n      user_input: nil\n  end\nend\n"
  },
  {
    "path": "test/tests/rails52.rb",
    "content": "require_relative '../test'\n\nclass Rails52Tests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def report\n    @@report ||= BrakemanTester.run_scan \"rails5.2\", \"Rails 5.2\", run_all_checks: true\n  end\n\n  def expected\n    @@expected ||= {\n      :controller => 0,\n      :model => 0,\n      :template => 7,\n      :generic => 24\n    }\n  end\n\n  def test_cross_site_request_forgery_false_positive\n    assert_no_warning :type => :controller,\n      :warning_code => 7,\n      :fingerprint => \"6f5239fb87c64764d0c209014deb5cf504c2c10ee424bd33590f0a4f22e01d8f\",\n      :warning_type => \"Cross-Site Request Forgery\",\n      :message => /^'protect_from_forgery'\\ should\\ be\\ called\\ /,\n      :confidence => 0,\n      :relative_path => \"app/controllers/application_controller.rb\"\n  end\n\n  def test_query_with_symbolize_keys\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"b7614315999c9f0db62649515da3bc9ce32ec418d69ea13af0564841392c98af\",\n      :warning_type => \"SQL Injection\",\n      :line => 9,\n      :message => /^Possible SQL injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\"\n  end\n\n  def test_sql_injection_not\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"659ce3a1ad4a44065f64f44e73c857c80c9505ecf74a3ebe40f3454dc7185845\",\n      :warning_type => \"SQL Injection\",\n      :line => 3,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 2,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, s(:call, nil, :where), :not, s(:dstr, \"blah == \", s(:evstr, s(:lvar, :thing)))),\n      :user_input => s(:lvar, :thing)\n  end\n\n  def test_sql_injection_string_freeze\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"b1ed6e8858db8a9a176fba44374a9a43c6277ea5df3ed04236a5870eed44e43c\",\n      :warning_type => \"SQL Injection\",\n      :line => 11,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, s(:call, s(:call, s(:self), :class), :select, s(:dstr, \"\", s(:evstr, s(:call, s(:str, \"my_table_alias\"), :freeze)), s(:str, \".*\"))), :from, s(:dstr, \"\", s(:evstr, s(:call, nil, :table_name)), s(:str, \" AS \"), s(:evstr, s(:call, s(:str, \"my_table_alias\"), :freeze)))),\n      :user_input => s(:call, s(:str, \"my_table_alias\"), :freeze)\n  end\n\n  def test_sql_injection_with_array_map\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"d3e36e5e530dc926b4fd38c605cf39341bf9e48169310f34ac439caf129e1f6f\",\n      :warning_type => \"SQL Injection\",\n      :line => 76,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 2,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:call, s(:lvar, :base_scope), :where, s(:dstr, \"\", s(:evstr, s(:lvar, :exp)), s(:str, \" ILIKE '%foo%'\"))),\n      :user_input => s(:lvar, :exp)\n  end\n\n  def test_sql_injection_safe_literal_to_s_singularize\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"4de07c156fa4b7694024871f100409e41fbcac4f65813a34ba749e1751b95204\",\n      :warning_type => \"SQL Injection\",\n      :line => 16,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 2,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, s(:call, nil, :articles), :sum, s(:dstr, \"calculated_\", s(:evstr, s(:call, s(:call, s(:lit, :BRAKEMAN_SAFE_LITERAL), :to_s), :singularize)), s(:str, \"_cents * quantity\"))),\n      :user_input => s(:call, s(:call, s(:lit, :BRAKEMAN_SAFE_LITERAL), :to_s), :singularize)\n  end\n\n  def test_sql_injection_foreign_key\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"efc0a7f6a2f171db9cd6369da3335f0250478f9f50603118884f4d2ca0ca5161\",\n      :warning_type => \"SQL Injection\",\n      :line => 24,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, s(:const, :User), :joins, s(:dstr, \"INNER JOIN <complex join involving custom SQL and \", s(:evstr, s(:call, s(:call, nil, :reflect_on_association, s(:lit, :foos)), :foreign_key)), s(:str, \" interpolation>\"))),\n      :user_input => s(:call, s(:call, nil, :reflect_on_association, s(:lit, :foos)), :foreign_key)\n  end\n\n  def test_sql_injection_polymorphic_name\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"76aa7211f677f4341babbadbed9d1c64c1cf3073303e94d565fe6cdb0124e4dc\",\n      :warning_type => \"SQL Injection\",\n      :line => 30,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 2,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, s(:const, :MediaFile), :joins, s(:dstr, \"JOIN \", s(:evstr, s(:call, nil, :table_name)), s(:str, \"\\n        ON media_files.parent_type = '\"), s(:evstr, s(:call, nil, :polymorphic_name)), s(:str, \"'\\n        AND media_files.parent_id = \"), s(:evstr, s(:call, nil, :table_name)), s(:str, \".id\"))),\n      :user_input => s(:call, nil, :polymorphic_name)\n  end\n\n  def test_sql_injection_user_input\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"f7affe2dfe9e3a48f39f1fb86224e150e60555a73f2e78fb499eadd298233625\",\n      :warning_type => \"SQL Injection\",\n      :line => 31,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :find_by_sql, s(:dstr, \"SELECT \", s(:evstr, s(:iter, s(:call, s(:iter, s(:call, s(:call, s(:const, :Something), :selection), :select), s(:args, :x), s(:call, nil, :some_condition?, s(:lvar, :x))), :map), s(:args, :x), s(:dstr, \"\", s(:evstr, s(:call, s(:const, :User), :table_name)), s(:str, \".\"), s(:evstr, s(:lvar, :x))))), s(:str, \".name\"), s(:str, \" where name = \"), s(:evstr, s(:call, s(:params), :[], s(:lit, :name))))),\n      :user_input => s(:iter, s(:call, s(:iter, s(:call, s(:call, s(:const, :Something), :selection), :select), s(:args, :x), s(:call, nil, :some_condition?, s(:lvar, :x))), :map), s(:args, :x), s(:dstr, \"\", s(:evstr, s(:call, s(:const, :User), :table_name)), s(:str, \".\"), s(:evstr, s(:lvar, :x))))\n  end\n\n  def test_sql_injection_splat\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"c869a301cba116872eac10eda8b3f99dff818c5014cd2552110a8eb4dcdfe661\",\n      :warning_type => \"SQL Injection\",\n      :line => 35,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :Person), :where, s(:splat, s(:call, s(:params), :[], s(:lit, :foo)))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :foo))\n  end\n\n  def test_sql_injection_kwsplat\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"624ecefdd9521b00ceb9ae845c523fe456c6494d59f8fa2217474a1d4d46e512\",\n      :warning_type => \"SQL Injection\",\n      :line => 39,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :User), :where, s(:hash, s(:kwsplat, s(:call, s(:params), :[], s(:lit, :foo))))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :foo))\n  end\n\n  def test_ignoring_freeze_generally\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"fdfde80ab27695c4a296f68f953391581df6e9b5568d921622982c32baffaa25\",\n      :warning_type => \"SQL Injection\",\n      :line => 18,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :Person), :where, s(:dstr, \"\", s(:evstr, s(:lvar, :foo)), s(:str, \" >= 1\"))),\n      :user_input => s(:lvar, :foo)\n  end\n\n  def test_treat_if_not_like_unless\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"c5788857ecda6ae28a0cf4db2823a49e5dbcd029a65c9a8d6d750e43d4596268\",\n      :warning_type => \"SQL Injection\",\n      :line => 24,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :Person), :where, s(:dstr, \"\", s(:evstr, s(:lvar, :foo)), s(:str, \" >= 1\"))),\n      :user_input => s(:lvar, :foo)\n  end\n\n  def test_command_injection_1\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"d8881688ca97faef7a0f300a902237ea201e52a511a45561dcd7462ef85ae720\",\n      :warning_type => \"Command Injection\",\n      :line => 7,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/initthing.rb\",\n      :code => s(:dxstr, \"\", s(:evstr, s(:ivar, :@blah))),\n      :user_input => s(:ivar, :@blah)\n  end\n\n  def test_command_injection_in_job\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"e712e2741ad78f4e947bec84f36a0d703849d3b0facdabd8cc74851d7b702a48\",\n      :warning_type => \"Command Injection\",\n      :line => 3,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/jobs/delete_stuff_job.rb\",\n      :code => s(:dxstr, \"rm -rf \", s(:evstr, s(:lvar, :file))),\n      :user_input => s(:lvar, :file)\n  end\n\n  def test_command_injection_shellwords\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"89b886281c6329f5c5f319932d98ea96527d50f1d188fde9fd85ff93130b7c50\",\n      :warning_type => \"Command Injection\",\n      :line => 9,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:dxstr, \"dig +short -x \", s(:evstr, s(:call, s(:const, :Shellwords), :shellescape, s(:lvar, :ip))), s(:str, \" @\"), s(:evstr, s(:call, s(:const, :Shellwords), :shellescape, s(:lvar, :one))), s(:str, \" -p \"), s(:evstr, s(:call, s(:const, :Shellwords), :escape, s(:lvar, :two)))),\n      :user_input => s(:call, s(:const, :Shellwords), :shellescape, s(:lvar, :ip))\n  end\n\n  def test_command_injection_nested_shellwords\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"acb19548a2a44c3070d35c62754216f4b3365f372d6004470417cca587af0f47\",\n      :warning_type => \"Command Injection\",\n      :line => 28,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:call, nil, :system, s(:dstr, \"echo \", s(:evstr, s(:call, s(:const, :Shellwords), :escape, s(:dstr, \"\", s(:evstr, s(:call, nil, :file_prefix)), s(:str, \".txt\")))))),\n      :user_input => s(:call, nil, :file_prefix)\n  end\n\n  def test_command_injection_backticks_as_target\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"9af991a12b23b815013ce0c69727b7a14cfb08e62f4e66a8851513af7cc6a757\",\n      :warning_type => \"Command Injection\",\n      :line => 18,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:dxstr, \"echo \", s(:evstr, s(:lvar, :path))),\n      :user_input => s(:lvar, :path)\n  end\n\n  def test_command_injection_array_join\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"478a39b6379df61bf0b016f435d054f279353e4fcd048304105152f6203fbdaa\",\n      :warning_type => \"Command Injection\",\n      :line => 33,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:call, nil, :system, s(:dstr, \"ruby \", s(:evstr, s(:call, nil, :method_that_returns_user_input)), s(:str, \" --some-flag\"))),\n      :user_input => s(:call, nil, :method_that_returns_user_input)\n  end\n\n  def test_command_injection_as_target\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"18e51f5a40dc0e63a90908e88ec5f2ed585fa3a645622f997026ada323cf7552\",\n      :warning_type => \"Command Injection\",\n      :line => 37,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:call, nil, :system, s(:dstr, \"echo \", s(:evstr, s(:call, nil, :foo)))),\n      :user_input => s(:call, nil, :foo)\n  end\n\n  def test_command_injection_interpolated_conditional_safe\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"eae19504b13ab3f112216fa589e1ec19dfce6df912bd43f00066b77c94c10568\",\n      :warning_type => \"Command Injection\",\n      :line => 41,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\"\n  end\n\n  def test_command_injection_interpolated_ternary_safe\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"007232cf2f1dc81f49d8ae2b3e1d77b6491b6a7fcf82cfc424982e05b1cab9b5\",\n      :warning_type => \"Command Injection\",\n      :line => 45,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\"\n  end\n\n  def test_command_injection_interpolated_conditional_dangerous\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"35e75c9db1462a5945016a6d4dbc195cba7b2d105a0ef71070bdd6f305a0efef\",\n      :warning_type => \"Command Injection\",\n      :line => 49,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:dxstr, \"echo \", s(:evstr, s(:if, s(:call, nil, :foo), s(:call, nil, :bar), nil)), s(:str, \" baz\")),\n      :user_input => s(:call, nil, :bar)\n  end\n\n  def test_command_injection_interpolated_ternary_dangerous\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"a88fd53a2f217af569c90edcef2d1b086a347b100d67cae52f519073050d48af\",\n      :warning_type => \"Command Injection\",\n      :line => 53,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:dxstr, \"echo \", s(:evstr, s(:if, s(:call, nil, :foo), s(:str, \"bar\"), s(:call, nil, :bar))), s(:str, \" baz\")),\n      :user_input => s(:call, nil, :bar)\n  end\n\n  def test_command_injection_with_hash_unknown_key_access\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"d24b0ba3ecb378e00bc0c8034eb2651f145ec6247f7471f8a41b31d44d4cdd33\",\n      :warning_type => \"Command Injection\",\n      :line => 66,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:dxstr, \"\", s(:evstr, s(:or, s(:call, s(:const, :COMMANDS), :[], s(:lvar, :arg)), s(:call, s(:const, :MORE_COMMANDS), :[], s(:lvar, :arg)))), s(:str, \" file1.txt\")),\n      :user_input => s(:call, s(:const, :COMMANDS), :[], s(:lvar, :arg))\n  end\n\n  def test_command_injection_with_array_each\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"760cf49f3f216b99e9720a8282ace8096c3b844ebd1da16cf20478d00449cd90\",\n      :warning_type => \"Command Injection\",\n      :line => 72,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:dxstr, \"echo \", s(:evstr, s(:lvar, :exp))),\n      :user_input => s(:lvar, :exp)\n  end\n\n  def test_command_injection_shell_escape_model\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"fc6e48ece7bd1e6a9e6d03cdedfcdf7818c86515b563a9a09bb49c5da00d8324\",\n      :warning_type => \"Command Injection\",\n      :line => 82,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:call, s(:const, :Open3), :capture2e, s(:str, \"ls\"), s(:call, s(:const, :Shellwords), :escape, s(:call, s(:call, s(:const, :User), :new), :z))),\n      :user_input => s(:call, s(:call, s(:const, :User), :new), :z)\n\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"781301c915efbc0f26482d8114744b062001946fb9450c969220fcf4f516ac2f\",\n      :warning_type => \"Command Injection\",\n      :line => 85,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:dxstr, \"ls \", s(:evstr, s(:call, s(:const, :Shellwords), :escape, s(:call, s(:call, s(:const, :User), :new), :z)))),\n      :user_input => s(:call, s(:call, s(:const, :User), :new), :z)\n  end\n\n  def test_command_injection_with__file__\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"8aeaa50052306c0c79e3b1ece079ba369e30356658b455b049da9543fd729d75\",\n      :warning_type => \"Command Injection\",\n      :line => 90,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:dxstr, \"cp lib/shell.rb \", s(:evstr, s(:call, nil, :somewhere_else))),\n      :user_input => s(:call, nil, :somewhere_else)\n  end\n\n  def test_command_injection_percent_W\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :warning_type => \"Command Injection\",\n      :line => 95,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:call, nil, :system, s(:splat, s(:array, s(:str, \"foo\"), s(:str, \"bar\"), s(:dstr, \"\", s(:evstr, s(:call, nil, :value)))))),\n      :user_input => s(:call, nil, :value)\n  end\n\n  def test_command_injection_with_concatenation\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"6a8f4711d1bbd58964c536edb77372c1d402ee18ed558390d46d60c57920d614\",\n      :warning_type => \"Command Injection\",\n      :line => 103,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:call, nil, :system, s(:call, s(:str, \"echo \"), :+, s(:call, nil, :foo))),\n      :user_input => s(:call, nil, :foo)\n  end\n\n  def test_dash_c_command_injection_with_concatenation\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"cbafca7ab394a7454815dc0c45e873ce35a23093431ef414f2cfad40ec37fb98\",\n      :warning_type => \"Command Injection\",\n      :line => 115,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:call, nil, :system, s(:str, \"bash\"), s(:str, \"-c\"), s(:call, s(:str, \"echo \"), :+, s(:call, nil, :foo))),\n      :user_input => s(:call, nil, :foo)\n  end\n\n  def test_dash_c_command_injection_with_popen\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"da6ffe8f2fadd19479a1ae5e060b76e04f517cbeb6c54c47b59d63196e7b05aa\",\n      :warning_type => \"Command Injection\",\n      :line => 123,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:call, s(:const, :IO), :popen, s(:array, s(:str, \"bash\"), s(:str, \"-c\"), s(:call, s(:params), :[], s(:lit, :foo)))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :foo))\n  end\n\n  def test_command_injection_concatenation_with_popen\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"b801bc63d43acc99bffcb4c669a1d0d6acc0724cc7267d5739f9ec31c4067467\",\n      :warning_type => \"Command Injection\",\n      :line => 127,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/shell.rb\",\n      :code => s(:call, s(:const, :IO), :popen, s(:call, s(:str, \"ls \"), :+, s(:call, nil, :foo))),\n      :user_input => s(:call, nil, :foo)\n  end\n\n  def test_command_injection_ignored_in_vendor_dir\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :warning_type => \"Command Injection\",\n      :line => 3,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"vendor/vendored_thing.rb\",\n      :code => s(:dxstr, \"rm -rf \", s(:evstr, s(:call, nil, :stuff))),\n      :user_input => s(:call, nil, :stuff)\n  end\n\n  def test_cross_site_scripting_haml_sass\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"45a1c26b6a6b4735351d7d6ce91e33e9b7295865e7e8e49cbafd5945c9429862\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 4,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/one.html.haml\",\n      :code => s(:call, s(:call, s(:const, :User), :find, s(:call, s(:params), :[], s(:lit, :id))), :name),\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_slim_sass\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"4a2b104710afbfb3861dc1e6f1b4b6e2459e422662561e009722b31e6e8f6d87\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 6,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/two.html.slim\",\n      :code => s(:call, s(:call, s(:const, :User), :find, s(:call, s(:params), :[], s(:lit, :id))), :name),\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_kwsplat_known_values\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"bc1e6c1ff0c94366d1936050698bad21b33cb7377528169a18ef609c39c373b9\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/_foo.html.haml\",\n      :code => s(:call, s(:call, nil, :params), :[], s(:lit, :x)),\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_kwsplat_unknown_values\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"f4393261a50f25e93f61ff8387643e110a2c386f9063806c99dadc8226eb6c0e\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/_foo2.html.haml\",\n      :code => s(:call, s(:call, nil, :params), :[], s(:lit, :x)),\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_link_to_with_block\n    assert_warning :type => :template,\n      :warning_code => 4,\n      :fingerprint => \"caefec37f50032631e8b0352437a13f792076ef2d7460040c96aa68c5ac1c863\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unsafe\\ parameter\\ value\\ in\\ `link_to`\\ href/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/link.html.erb\",\n      :code => s(:call, nil, :link_to, s(:call, s(:call, nil, :params), :[], s(:lit, :x))),\n      :user_input => s(:call, s(:call, nil, :params), :[], s(:lit, :x))\n  end\n\n  def test_cross_site_scripting_not_not_false_positive\n    assert_no_warning :type => :template,\n      :warning_code => 2,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/views/users/not_not.html.erb\",\n      :user_input => s(:call, s(:call, s(:call, s(:params), :[], s(:lit, :header_row)), :!), :!)\n  end\n\n  def test_remote_code_execution_oj_load\n    assert_warning :type => :warning,\n      :warning_code => 25,\n      :fingerprint => \"97ecaa5677c8eadaed09217a704e59092921fab24cc751e05dfb7b167beda2cf\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 51,\n      :message => /^`Oj\\.load`\\ called\\ with\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :Oj), :load, s(:call, s(:params), :[], s(:lit, :json))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :json))\n  end\n\n  def test_remote_code_execution_oj_load_mode\n    assert_warning :type => :warning,\n      :warning_code => 25,\n      :fingerprint => \"006ac5fe3834bf2e73ee51b67eb111066f618be46e391d493c541ea2a906a82f\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 52,\n      :message => /^`Oj\\.load`\\ called\\ with\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :Oj), :load, s(:call, s(:params), :[], s(:lit, :json)), s(:hash, s(:lit, :mode), s(:lit, :object))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :json))\n  end\n\n  def test_remote_code_execution_oj_object_load\n    assert_warning :type => :warning,\n      :warning_code => 25,\n      :fingerprint => \"3bc375c9cb79d8bcd9e7f1c09a574fa3deeab17f924cf20455cbd4c15e9c66eb\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 53,\n      :message => /^`Oj\\.object_load`\\ called\\ with\\ parameter\\ v/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:const, :Oj), :object_load, s(:call, s(:params), :[], s(:lit, :json)), s(:hash, s(:lit, :mode), s(:lit, :strict))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :json))\n  end\n\n  def test_remote_code_execution_cookie_serialization_config\n    assert_warning :type => :warning,\n      :warning_code => 110,\n      :fingerprint => \"9ae68e59cfee3e5256c0540dadfeb74e6b72c91997fdb60411063a6e8518144a\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 5,\n      :message => /^Use\\ of\\ unsafe\\ cookie\\ serialization\\ strat/,\n      :confidence => 1,\n      :relative_path => \"config/initializers/cookies_serializer.rb\",\n      :code => s(:attrasgn, s(:call, s(:call, s(:call, s(:const, :Rails), :application), :config), :action_dispatch), :cookies_serializer=, s(:lit, :hybrid)),\n      :user_input => nil\n  end\n\n  def test_missing_encryption_force_ssl\n    assert_warning :type => :warning,\n      :warning_code => 109,\n      :fingerprint => \"6a26086cd2400fbbfb831b2f8d7291e320bcc2b36984d2abc359e41b3b63212b\",\n      :warning_type => \"Missing Encryption\",\n      :line => 50,\n      :message => /^The\\ application\\ does\\ not\\ force\\ use\\ of\\ HT/,\n      :confidence => 0,\n      :relative_path => \"config/environments/production.rb\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_loofah_CVE_2018_8048\n    assert_warning check_name: \"SanitizeMethods\",\n      type: :warning,\n      warning_code: 106,\n      fingerprint: \"cdfb1541fdcc9cdcf0784ce5bd90013dc39316cb822eedea3f03b2521c06137f\",\n      warning_type: \"Cross-Site Scripting\",\n      line: 90,\n      message: /^loofah\\ gem\\ 2\\.1\\.1\\ is\\ vulnerable\\ \\(CVE\\-2018/,\n      confidence: 0,\n      relative_path: \"Gemfile.lock\",\n      code: nil,\n      user_input: nil\n  end\n\n  def test_cross_site_scripting_CVE_2018_3741\n    assert_warning check_name: \"SanitizeMethods\",\n      type: :warning,\n      warning_code: 107,\n      fingerprint: \"3e35a6afcd1a8a14894cf26a7f00d4e895f0583bbc081d45e5bd28c4b541b7e6\",\n      warning_type: \"Cross-Site Scripting\",\n      line: 125,\n      message: /^rails\\-html\\-sanitizer\\ 1\\.0\\.3\\ is\\ vulnerable/,\n      confidence: 0,\n      relative_path: \"Gemfile.lock\",\n      code: nil,\n      user_input: nil\n  end\n\n  def test_cross_site_scripting_CVE_2022_32209_sanitize_call\n    assert_warning check_name: \"SanitizeConfigCve\",\n      type: :template,\n      warning_code: 124,\n      fingerprint: \"381dbd3ff41d8e8a36bc13ea1943fbf8f8d70774724c9f1be7b0581b88d1d3f5\",\n      warning_type: \"Cross-Site Scripting\",\n      line: 9,\n      message: /^rails\\-html\\-sanitizer\\ 1\\.0\\.3\\ is\\ vulnerable/,\n      confidence: 0,\n      relative_path: \"app/views/users/one.html.haml\",\n      code: s(:call, nil, :sanitize, s(:call, s(:call, s(:const, :User), :find, s(:call, s(:params), :[], s(:lit, :id))), :bio), s(:hash, s(:lit, :tags), s(:array, s(:str, \"style\"), s(:lit, :select)))),\n      user_input: nil\n  end\n\n  def test_command_injection_ignored_in_stdin\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"98e72a863c59b1a9dd0024ef645ede1677e87123ac57b7b2d54ca704aba8f8e1\",\n      :warning_type => \"Command Injection\",\n      :line => 135,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"lib/shell.rb\"\n\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"a8ac4ba6f5c56040bb21bb8a2a69a003a1a37b17822209133accb92bbaf1a19b\",\n      :warning_type => \"Command Injection\",\n      :line => 136,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"lib/shell.rb\"\n\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"775dbae94ae590e818d94bcef5d035b46cbc0f72c5c9cb568cfca20a746ed290\",\n      :warning_type => \"Command Injection\",\n      :line => 137,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"lib/shell.rb\"\n  end\n\n  def test_unmaintained_dependency_ruby\n    assert_warning check_name: \"EOLRuby\",\n      type: :warning,\n      warning_code: 121,\n      fingerprint: \"edf687f759ec9765bd5db185dbc615c80af77d6e7e19386fc42934e7a80307af\",\n      warning_type: \"Unmaintained Dependency\",\n      line: 1,\n      message: /^Support\\ for\\ Ruby\\ 2\\.3\\.1\\ ended\\ on\\ 2019\\-03\\-/,\n      confidence: 0,\n      relative_path: \".ruby-version\",\n      code: nil,\n      user_input: nil\n  end\nend\n\nclass Rails52WithVendorTests < Minitest::Test\n  include BrakemanTester::FindWarning\n\n  def report\n    @@report ||= BrakemanTester.run_scan \"rails5.2\", \"Rails 5.2\", skip_vendor: false \n  end\n\n  def test_command_injection_ignored_vendor_dir\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :warning_type => \"Command Injection\",\n      :line => 3,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"vendor/vendored_thing.rb\",\n      :code => s(:dxstr, \"rm -rf \", s(:evstr, s(:call, nil, :stuff))),\n      :user_input => s(:call, nil, :stuff)\n  end\nend\n"
  },
  {
    "path": "test/tests/rails52_csrf.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/rescanner'\n\nclass Rails52CSRFTest < Minitest::Test\n  include BrakemanTester::RescanTestHelper\n  include BrakemanTester::FindWarning\n\n  def report\n    @report\n  end\n\n  def test_csrf_with_no_load_defaults\n    tracker = nil\n\n    # Terribly abusing the rescan functionality here.\n    # Actually don't want the rescan, just want to run a regular scan\n    # because we don't have the capability to rescan with on config changes\n    # like this and I don't feel like building it right now.\n    before_rescan_of ['config/application.rb'], 'rails5.2' do |app_dir|\n      replace 'config/application.rb', 'config.load_defaults 5.2', ''\n      tracker = Brakeman.run(app_path: app_dir, parallel_checks: false)\n    end\n\n    @report = tracker.report.to_hash\n\n    assert_warning check_name: \"ForgerySetting\",\n      type: :controller,\n      warning_code: 7,\n      fingerprint: \"6f5239fb87c64764d0c209014deb5cf504c2c10ee424bd33590f0a4f22e01d8f\",\n      warning_type: \"Cross-Site Request Forgery\",\n      line: 1,\n      message: /^`protect_from_forgery`\\ should\\ be\\ called\\ /,\n      confidence: 0,\n      relative_path: \"app/controllers/application_controller.rb\",\n      code: nil,\n      user_input: nil\n  end\nend\n"
  },
  {
    "path": "test/tests/rails6.rb",
    "content": "require_relative '../test'\n\nclass Rails6Tests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def report\n    @@report ||= BrakemanTester.run_scan \"rails6\", \"Rails 6\", :run_all_checks => true, :sql_safe_methods => [:sanitize_s]\n  end\n\n  def expected\n    @@expected ||= {\n      :controller => 0,\n      :model => 0,\n      :template => 4,\n      :generic => 37\n    }\n  end\n\n  def test_sql_injection_delete_by\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"02ad62a4e0cc17d972701be99e1d1ba4761b9176acc36e41498eac3a8d853a8a\",\n      :warning_type => \"SQL Injection\",\n      :line => 66,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:ivar, :@user), :delete_by, s(:call, s(:params), :[], s(:lit, :user))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :user))\n  end\n\n  def test_sql_injection_destroy_by\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"5049d89b5d867ce8c2e602746575b512f147b0ff4eca18ac1b2a3a308204180e\",\n      :warning_type => \"SQL Injection\",\n      :line => 65,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:ivar, :@user), :destroy_by, s(:call, s(:params), :[], s(:lit, :user))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :user))\n  end\n\n  def test_sql_injection_strip_heredoc\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"c567289064ac39d277b33a5b860641b79a8139cf85a9a079bc7bb36130784a93\",\n      :warning_type => \"SQL Injection\",\n      :line => 11,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, nil, :where, s(:call, s(:dstr, \"      name = '\", s(:evstr, s(:lvar, :name)), s(:str, \"'\\n\")), :strip_heredoc)),\n      :user_input => s(:lvar, :name)\n  end\n\n  def test_sql_injection_squish_string\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"061b514e5a37df58d8b64ec0c9c10002dcd9d7253d0e1c1a9bd61bdb27be158f\",\n      :warning_type => \"SQL Injection\",\n      :line => 19,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:call, s(:colon2, s(:const, :ActiveRecord), :Base), :connection), :execute, s(:call, s(:dstr, \"SELECT * FROM \", s(:evstr, s(:call, nil, :user_input))), :squish)),\n      :user_input => s(:call, nil, :user_input)\n  end\n\n  def test_sql_injection_strip_string\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"07fe35888f23cd4125c862d041b9ab3257c01c1263b66cd8c63804d55b8e1549\",\n      :warning_type => \"SQL Injection\",\n      :line => 20,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:call, s(:colon2, s(:const, :ActiveRecord), :Base), :connection), :execute, s(:call, s(:dstr, \"SELECT * FROM \", s(:evstr, s(:call, nil, :user_input))), :strip)),\n      :user_input => s(:call, nil, :user_input)\n  end\n\n  def test_sql_injection_chomp_string\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"e5f6e40868c9046e5c1433e4975e49b65967de1dcf3add7ba35248b897eeea1c\",\n      :warning_type => \"SQL Injection\",\n      :line => 20,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, s(:call, s(:colon2, s(:const, :ActiveRecord), :Base), :connection), :delete, s(:call, s(:dstr, \"DELETE FROM \", s(:evstr, s(:call, nil, :table)), s(:str, \" WHERE updated_at < now() - interval '\"), s(:evstr, s(:call, nil, :period)), s(:str, \"'\\n\")), :chomp)),\n      :user_input => s(:call, nil, :table)\n  end\n\n  def test_sql_injection_nonstandard_directory\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"633da061d9412e2a270133526a45933e29553846c677254abfd0d0955e69f064\",\n      :warning_type => \"SQL Injection\",\n      :line => 3,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/widgets/widget.rb\",\n      :code => s(:call, nil, :where, s(:dstr, \"direction = \", s(:evstr, s(:lvar, :direction)), s(:str, \")\"))),\n      :user_input => s(:lvar, :direction)\n  end\n\n  def test_sql_injection_uuid_false_positive\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"8ebbafd2b7d6aa2d6ab639c6678ae1f5489dc3166747bbad8919e95156592321\",\n      :warning_type => \"SQL Injection\",\n      :line => 3,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/models/group.rb\",\n      :code => s(:call, s(:call, s(:colon2, s(:const, :ActiveRecord), :Base), :connection), :exec_query, s(:dstr, \"select * where x = \", s(:evstr, s(:call, s(:const, :User), :uuid)))),\n      :user_input => s(:call, s(:const, :User), :uuid)\n  end\n\n  def test_sql_injection_safe_sql_methods_false_postitive\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"205c9aaba1e719acc4f7026c0f65f172d4ac8dd8d036d31a94d7d446c7c874e7\",\n      :warning_type => \"SQL Injection\",\n      :line => 60,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:call, s(:colon2, s(:const, :ActiveRecord), :Base), :connection), :execute, s(:dstr, \"SELECT * FROM \", s(:evstr, s(:call, nil, :sanitize_s, s(:call, nil, :user_input))))),\n      :user_input => s(:call, nil, :sanitize_s, s(:call, nil, :user_input))\n  end\n\n  def test_sql_injection_date_integer_target_false_positive\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"5ec829ba8790c01a39faf6788f0754d39879a6e68a9de8804c6f25ac9c2f1ee6\",\n      :warning_type => \"SQL Injection\",\n      :line => 8,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/group.rb\",\n      :code => s(:call, s(:const, :Arel), :sql, s(:dstr, \"created_at > '\", s(:evstr, s(:call, s(:call, s(:lit, 30), :days), :ago)), s(:str, \"'\"))),\n      :user_input => s(:call, s(:call, s(:lit, 30), :days), :ago)\n  end\n\n  def test_sql_injection_sanitize_sql_like\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"8dde11c95a0f3acb4f982ff6554ac3ba821334ee04aee7f1fb0ea01c8919baad\",\n      :warning_type => \"SQL Injection\",\n      :line => 13,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/group.rb\",\n      :code => s(:call, s(:const, :Arel), :sql, s(:dstr, \"name ILIKE '%\", s(:evstr, s(:call, s(:colon2, s(:const, :ActiveRecord), :Base), :sanitize_sql_like, s(:lvar, :query))), s(:str, \"%'\"))),\n      :user_input => s(:call, s(:colon2, s(:const, :ActiveRecord), :Base), :sanitize_sql_like, s(:lvar, :query))\n  end\n\n  def test_sql_injection_hash_fetch_all_literals\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"1b9a04c9fdc4b8c7f215387fb726dc542c2d35dde2f29b48a76248443524a5fa\",\n      :warning_type => \"SQL Injection\",\n      :line => 14,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/group.rb\",\n      :code => s(:call, s(:const, :Arel), :sql, s(:dstr, \"role = '\", s(:evstr, s(:call, s(:hash, s(:lit, :admin), s(:lit, 1), s(:lit, :moderator), s(:lit, 2)), :fetch, s(:lvar, :role_name))), s(:str, \"'\"))),\n      :user_input => s(:call, s(:hash, s(:lit, :admin), s(:lit, 1), s(:lit, :moderator), s(:lit, 2)), :fetch, s(:lvar, :role_name))\n  end\n\n  def test_sql_injection_with_date\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"d8eec07773f66d6818a9dab2533bda5b295fbe261de5df9675dbf3213c1dcfa2\",\n      :warning_type => \"SQL Injection\",\n      :line => 25,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, nil, :where, s(:dstr, \"date > \", s(:evstr, s(:call, s(:call, s(:const, :Date), :today), :-, s(:lit, 1))))),\n      :user_input => s(:call, s(:call, s(:const, :Date), :today), :-, s(:lit, 1))\n  end\n\n  def test_sql_injection_rewhere\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"7f5154ba5124c5ae26ec23f364239311df959acb9b2e4d09f4867c2fbd954dd6\",\n      :warning_type => \"SQL Injection\",\n      :line => 69,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:call, s(:const, :User), :where, s(:str, \"x = 1\")), :rewhere, s(:dstr, \"x = \", s(:evstr, s(:call, s(:params), :[], s(:lit, :x))))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_sql_injection_reselect\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"e4fdd9614cff8e8f8a70cd983c55d36acd6da219048faf1530de9dc43d58aa71\",\n      :warning_type => \"SQL Injection\",\n      :line => 68,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:call, s(:const, :User), :select, s(:str, \"stuff\")), :reselect, s(:call, s(:params), :[], s(:lit, :columns))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :columns))\n  end\n\n  def test_sql_injection_pluck\n    # Not in Rails 6.1 though\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"69a7e516b2b409dc8d74f6a26b44d62f4b842ce9c73e96c3910f9206c6fc50f5\",\n      :warning_type => \"SQL Injection\",\n      :line => 70,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:const, :User), :pluck, s(:call, s(:params), :[], s(:lit, :column))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :column))\n  end\n\n  def test_sql_injection_order\n    # Not in Rails 6.1 though\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"47e9c6316ae9b2937121298ebc095bac4c4c8682779a0be95ce32c3fc4ba3118\",\n      :warning_type => \"SQL Injection\",\n      :line => 71,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:const, :User), :order, s(:dstr, \"name \", s(:evstr, s(:call, s(:params), :[], s(:lit, :direction))))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :direction))\n  end\n\n  def test_sql_injection_reorder\n    # Not in Rails 6.1 though\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"c6b303b67a5de261d9faaa84e02b29987b57fb443691d7ad77956bbecf41a1d0\",\n      :warning_type => \"SQL Injection\",\n      :line => 72,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:call, s(:const, :User), :order, s(:lit, :name)), :reorder, s(:call, s(:params), :[], s(:lit, :column))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :column))\n  end\n\n  def test_sql_injection_enum\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"b2071137eba7ef6ecbcc1c6381a428e5c576a5fadf73dc04b2e155c41043e1d2\",\n      :warning_type => \"SQL Injection\",\n      :line => 31,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, nil, :where, s(:dstr, \"state = \", s(:evstr, s(:call, s(:call, s(:const, :User), :states), :[], s(:str, \"pending\"))))),\n      :user_input => s(:call, s(:call, s(:const, :User), :states), :[], s(:str, \"pending\"))\n  end\n\n  def test_sql_injection_locale\n    assert_no_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"843143d5a9dcfa097c66d80a6a72ba151c0332f1ea8c8bc852e418d4f0e2cb7b\",\n      :warning_type => \"SQL Injection\",\n      :line => 37,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, s(:const, :User), :where, s(:dstr, \"lower(slug_\", s(:evstr, s(:call, s(:call, s(:call, s(:call, s(:const, :I18n), :locale), :to_s), :split, s(:str, \"-\")), :first)), s(:str, \") = :country_id\")), s(:hash, s(:lit, :country_id), s(:call, s(:params), :[], s(:lit, :new_country_id)))),\n      :user_input => s(:call, s(:call, s(:call, s(:call, s(:const, :I18n), :locale), :to_s), :split, s(:str, \"-\")), :first)\n  end\n\n  def test_sql_injection_tr_method\n    assert_warning check_name: \"SQL\",\n      type: :warning,\n      warning_code: 0,\n      fingerprint: \"5d5e33e109c52601027f20eb706d6f7688dffaaebc3b62e92a57bb74f7dab451\",\n      warning_type: \"SQL Injection\",\n      line: 37,\n      message: /^Possible\\ SQL\\ injection/,\n      confidence: 1,\n      relative_path: \"app/controllers/accounts_controller.rb\",\n      code: s(:call, s(:const, :Arel), :sql, s(:call, s(:dstr, \"CASE\\nWHEN \", s(:evstr, s(:call, s(:call, nil, :user_params), :[], s(:lit, :field))), s(:str, \" IS NULL\\n  OR TRIM(\"), s(:evstr, s(:call, s(:call, nil, :user_params), :[], s(:lit, :field))), s(:str, \") = ''\\nTHEN 'Untitled'\\nELSE TRIM(\"), s(:evstr, s(:call, s(:call, nil, :user_params), :[], s(:lit, :field))), s(:str, \")\\nEND\\n\")), :tr, s(:str, \"\\n\"), s(:str, \" \"))),\n      user_input: s(:call, s(:call, nil, :user_params), :[], s(:lit, :field))\n  end\n\n  def test_dangerous_send_enum\n    assert_no_warning :type => :warning,\n      :warning_code => 23,\n      :fingerprint => \"483fa36e41f5791e86f345a19b517a61859886d685ce40ef852871bb7a935f2d\",\n      :warning_type => \"Dangerous Send\",\n      :line => 80,\n      :message => /^User\\ controlled\\ method\\ execution/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:const, :Group), :send, s(:call, s(:dstr, \"\", s(:evstr, s(:call, s(:params), :[], s(:lit, :status)))), :to_sym)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :status))\n  end\n\n  def test_cross_site_scripting_sanity\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"9e949d88329883f879b7ff46bdb096ba43e791aacb6558f47beddc34b9d42c4c\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 5,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, s(:call, s(:const, :User), :new, s(:call, nil, :user_params)), :name),\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_2\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"9e949d88329883f879b7ff46bdb096ba43e791aacb6558f47beddc34b9d42c4c\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 6,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, s(:call, s(:const, :User), :new, s(:call, nil, :user_params)), :name),\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_3\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"9e949d88329883f879b7ff46bdb096ba43e791aacb6558f47beddc34b9d42c4c\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 7,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, s(:call, s(:const, :User), :new, s(:call, nil, :user_params)), :name),\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_4\n    assert_warning :type => :template,\n      :warning_code => 2,\n      :fingerprint => \"9e949d88329883f879b7ff46bdb096ba43e791aacb6558f47beddc34b9d42c4c\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 8,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/show.html.erb\",\n      :code => s(:call, s(:call, s(:const, :User), :new, s(:call, nil, :user_params)), :name),\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_json_escape_config\n    assert_warning :type => :warning,\n      :warning_code => 113,\n      :fingerprint => \"fea6a166c0704d9525d109c17d6ee95eda217dfb1ef56a4d4c91ec9bd384cbf8\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^HTML\\ entities\\ in\\ JSON\\ are\\ not\\ escaped\\ by/,\n      :confidence => 1,\n      :relative_path => \"config/environments/production.rb\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_json_escape_module\n    assert_warning :type => :warning,\n      :warning_code => 114,\n      :fingerprint => \"8275f584e7cced41c26890e574cdbf6804bddff54374058834a562294c99d6f6\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 2,\n      :message => /^HTML\\ entities\\ in\\ JSON\\ are\\ not\\ escaped\\ by/,\n      :confidence => 1,\n      :relative_path => \"config/environments/production.rb\",\n      :code => s(:attrasgn, s(:colon2, s(:colon2, s(:const, :ActiveSupport), :JSON), :Encoding), :escape_html_entities_in_json=, s(:false)),\n      :user_input => nil\n  end\n\n  def test_remote_code_execution_cookie_serialization\n    assert_warning :type => :warning,\n      :warning_code => 110,\n      :fingerprint => \"d882f63ce96c28fb6c6e0982f2a171460e4b933bfd9b9a5421dca21eef3f76da\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 5,\n      :message => /^Use\\ of\\ unsafe\\ cookie\\ serialization\\ strat/,\n      :confidence => 1,\n      :relative_path => \"config/initializers/cookies_serializer.rb\",\n      :code => s(:attrasgn, s(:call, s(:call, s(:call, s(:const, :Rails), :application), :config), :action_dispatch), :cookies_serializer=, s(:lit, :marshal)),\n      :user_input => nil\n  end\n\n  def test_remote_code_execution_method\n    assert_warning :type => :warning,\n      :warning_code => 119,\n      :fingerprint => \"f4c435cdf78761be48879a05c84db905558d192cb6693640174ff3c0f18b61cd\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 48,\n      :message => /^Unsafe\\ reflection\\ method\\ `method`\\ called/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:call, s(:call, s(:params), :[], s(:lit, :klass)), :to_s), :method, s(:call, s(:params), :[], s(:lit, :method))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :method))\n  end\n\n  def test_remote_code_execution_tap\n    assert_warning :type => :warning,\n      :warning_code => 119,\n      :fingerprint => \"988c82365b897a118c1c2b49059dc2b7202333ecc8bdd3a182ae0c126db2fca4\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 49,\n      :message => /^Unsafe\\ reflection\\ method\\ `tap`\\ called\\ wi/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:const, :Kernel), :tap, s(:block_pass, s(:call, s(:call, s(:params), :[], s(:lit, :method)), :to_sym))),\n      :user_input => s(:call, s(:call, s(:params), :[], s(:lit, :method)), :to_sym)\n  end\n\n  def test_remote_code_execution_to_proc\n    assert_warning :type => :warning,\n      :warning_code => 119,\n      :fingerprint => \"0eceb89cbf8d71f0aa8ada268bb0042f6efefee746e015adaa656d33e87c2f6e\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 47,\n      :message => /^Unsafe\\ reflection\\ method\\ `to_proc`\\ calle/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:call, s(:call, s(:params), :[], s(:lit, :method)), :to_sym), :to_proc),\n      :user_input => s(:call, s(:call, s(:params), :[], s(:lit, :method)), :to_sym)\n  end\n\n  def test_remote_code_execution_not_query_parameters\n    assert_warning :type => :warning,\n      :warning_code => 119,\n      :fingerprint => \"78e2d9010374d26ef8fe31ed22f10a6de7dfc428e0387dd8502cd5833ffe4aa6\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 50,\n      :message => /^Unsafe\\ reflection\\ method\\ `method`\\ called/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:const, :User), :method, s(:dstr, \"\", s(:evstr, s(:call, s(:call, s(:const, :User), :first), :some_method_thing)), s(:str, \"_stuff\"))),\n      :user_input => s(:call, s(:call, s(:const, :User), :first), :some_method_thing)\n  end\n\n  def test_safe_yaml_load_option\n    assert_no_warning :type => :warning,\n      :warning_code => 25,\n      :fingerprint => \"bf38405dcc489a459957bf515cb9f078686bb9316cc3d1f421c61c330a9005ec\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 41,\n      :message => /^`YAML\\.load`\\ called\\ with\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:const, :YAML), :load, s(:call, s(:params), :[], s(:lit, :yaml_stuff)), s(:hash, s(:lit, :safe), s(:true))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :yaml_stuff))\n  end\n\n  def test_safe_yaml_load_option_false\n    assert_warning :type => :warning,\n      :warning_code => 25,\n      :fingerprint => \"2798cec372112fdecfadf2cb30b41635742d93c5b0bfc0ba71a3f69eb21b7f48\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 42,\n      :message => /^`YAML\\.load`\\ called\\ with\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:const, :YAML), :load, s(:call, s(:params), :[], s(:lit, :yaml_stuff)), s(:hash, s(:lit, :safe), s(:false))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :yaml_stuff))\n  end\n\n  def test_safe_yaml_load_option_missing\n    assert_warning :type => :warning,\n      :warning_code => 25,\n      :fingerprint => \"baffe1ec42a14c076b7c7bf676f833a397b879ee8c8ae4bc697b2bcef0355399\",\n      :warning_type => \"Remote Code Execution\",\n      :line => 43,\n      :message => /^`YAML\\.load`\\ called\\ with\\ parameter\\ value/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:const, :YAML), :load, s(:call, s(:params), :[], s(:lit, :yaml_stuff))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :yaml_stuff))\n  end\n\n  def test_dup_call\n    assert_no_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"5c2a887ac2e7ba5ae8d27160c0b4540d9ddb93ae8cde64f84558544e2235c83e\",\n      :warning_type => \"Redirect\",\n      :line => 6,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, nil, :redirect_to, s(:call, s(:call, s(:const, :Group), :find, s(:call, s(:params), :[], s(:lit, :id))), :dup)),\n      :user_input => s(:call, s(:call, s(:const, :Group), :find, s(:call, s(:params), :[], s(:lit, :id))), :dup)\n  end\n\n  def test_redirect_request_params\n    assert_warning :type => :warning,\n      :warning_code => 18,\n      :fingerprint => \"1d18e872e5f74ff0fd445008fd00ea2f04d5b3086f18682e301621779cd609a2\",\n      :warning_type => \"Redirect\",\n      :line => 88,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, nil, :redirect_to, s(:call, s(:call, nil, :request), :params)),\n      :user_input => s(:call, s(:call, nil, :request), :params)\n  end\n\n  def test_basic_dash_c_command_injection\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"22f0226c43eeb59bff49e4f0ea10014c2882c8be2f51e4d36876a26960b100d9\",\n      :warning_type => \"Command Injection\",\n      :line => 70,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, nil, :system, s(:str, \"bash\"), s(:str, \"-c\"), s(:call, s(:params), :[], s(:lit, :script))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :script))\n  end\n\n  def test_complex_dash_c_command_injection\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"d5b5eeed916c878c897bcde8b922bb18cdcf9fc1acfb8e37b30eb02422e8c43e\",\n      :warning_type => \"Command Injection\",\n      :line => 75,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, nil, :exec, s(:str, \"zsh\"), s(:str, \"-c\"), s(:dstr, \"\", s(:evstr, s(:call, s(:params), :[], s(:lit, :script))), s(:str, \" -e ./\"))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :script))\n  end\n\n  def test_without_shell_dash_c_is_not_command_injection\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :warning_type => \"Command Injection\",\n      :line => 84,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, nil, :system, s(:str, \"bash\"), s(:str, \"-c\"), s(:call, s(:params), :[], s(:lit, :argument))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :argument))\n  end\n\n  def test_command_injection_in_render_1\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"6c1c068078e37b94af882d43bd6e239dd8e28912b7f0a56f11b82a504915c064\",\n      :warning_type => \"Command Injection\",\n      :line => 10,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:dxstr, \"\", s(:evstr, s(:call, s(:params), :require, s(:str, \"name\"))), s(:str, \" some optional text\")),\n      :user_input => s(:call, s(:params), :require, s(:str, \"name\"))\n  end\n\n  def test_command_injection_in_render_2\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"6c1c068078e37b94af882d43bd6e239dd8e28912b7f0a56f11b82a504915c064\",\n      :warning_type => \"Command Injection\",\n      :line => 11,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:dxstr, \"\", s(:evstr, s(:call, s(:params), :require, s(:str, \"name\"))), s(:str, \" some optional text\")),\n      :user_input => s(:call, s(:params), :require, s(:str, \"name\"))\n  end\n\n  def test_command_injection_nonstandard_directory\n    assert_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"374fcec0e44933e90ee710b9a3975e29134d8a3725e3d7d7ab5e0e8f0c09f5a4\",\n      :warning_type => \"Command Injection\",\n      :line => 3,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"another_lib_dir/some_lib.rb\",\n      :code => s(:dxstr, \"rm -rf \", s(:evstr, s(:lvar, :thing))),\n      :user_input => s(:lvar, :thing)\n  end\n\n  def test_command_injection_with_temp_file_path\n    assert_no_warning :type => :warning,\n      :warning_code => 14,\n      :fingerprint => \"0e28d1629630acaf62f873043ad128de709ac423f86256bed8fa73fdc8756f20\",\n      :warning_type => \"Command Injection\",\n      :line => 4,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 1,\n      :relative_path => \"lib/run_stuff.rb\",\n      :code => s(:dxstr, \"cat \", s(:evstr, s(:call, s(:lvar, :temp_file), :path))),\n      :user_input => s(:call, s(:lvar, :temp_file), :path)\n  end\n\n  def test_dynamic_render_path_renderable\n    assert_no_warning :type => :warning,\n      :warning_code => 15,\n      :fingerprint => \"50c648276617a57eafc0402e7a2f2c02a3d25d8b6c9ced62cd84294163595ff5\",\n      :warning_type => \"Dynamic Render Path\",\n      :line => 12,\n      :message => /^Render\\ path\\ contains\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:const, :TestComponent), :new, s(:call, s(:params), :require, s(:str, \"name\"))), s(:hash)),\n      :user_input => s(:call, s(:params), :require, s(:str, \"name\"))\n  end\n\n  def test_dynamic_render_path_known_renderable_class\n    assert_no_warning :type => :warning,\n      :warning_code => 15,\n      :fingerprint => \"7e8ad12b494ba3e8617fb6a8d27aa10a4f944498c6236d07c31e6063b3c83fc5\",\n      :warning_type => \"Dynamic Render Path\",\n      :line => 13,\n      :message => /^Render\\ path\\ contains\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:const, :TestViewComponent), :new, s(:call, s(:params), :require, s(:str, \"name\"))), s(:hash)),\n      :user_input => s(:call, s(:params), :require, s(:str, \"name\"))\n  end\n\n  def test_dynamic_render_path_fully_qualified_known_renderable_class\n    assert_no_warning :check_name => \"Render\",\n      :type => :warning,\n      :warning_code => 15,\n      :fingerprint => \"2b82e2d4056b1d82a41245f21d22ea1e03ebdbbe1bd84df8e6095d23e43515fc\",\n      :warning_type => \"Dynamic Render Path\",\n      :line => 14,\n      :message => /^Render\\ path\\ contains\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:colon3, :TestViewComponent), :new, s(:call, s(:params), :require, s(:str, \"name\"))), s(:hash)),\n      :user_input => s(:call, s(:params), :require, s(:str, \"name\"))\n  end\n\n  def test_dynamic_render_path_fully_qualified_ancestor_known_renderable_class\n    assert_no_warning :check_name => \"Render\",\n      :type => :warning,\n      :warning_code => 15,\n      :fingerprint => \"224aa68d0c57766bfdfef3df66f361c2eecc95533d07b4a31fe08d1530854d7c\",\n      :warning_type => \"Dynamic Render Path\",\n      :line => 15,\n      :message => /^Render\\ path\\ contains\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:const, :TestViewComponentFullyQualifiedAncestor), :new, s(:call, s(:params), :require, s(:str, \"name\"))), s(:hash)),\n      :user_input => s(:call, s(:params), :require, s(:str, \"name\"))\n  end\n\n  def test_dynamic_render_path_phlex_component\n    assert_no_warning :type => :warning,\n      :warning_code => 15,\n      :warning_type => \"Dynamic Render Path\",\n      :line => 87,\n      :message => /^Render\\ path\\ contains\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:const, :TestPhlexComponent), :new, s(:call, s(:params), :require, s(:str, \"name\"))), s(:hash)),\n      :user_input => s(:call, s(:params), :require, s(:str, \"name\"))\n  end\n\n  def test_dynamic_render_view_component_contrib\n    assert_no_warning :type => :warning,\n      :warning_code => 15,\n      :warning_type => \"Dynamic Render Path\",\n      :line => 90,\n      :message => /^Render\\ path\\ contains\\ parameter\\ value/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:const, :TestViewComponentContrib), :new, s(:call, s(:params), :require, s(:str, \"name\"))), s(:hash)),\n      :user_input => s(:call, s(:params), :require, s(:str, \"name\"))\n  end\n\n  def test_dynamic_render_path_view_component_with_content\n    # No more low confidence render path warnings\n    assert_no_warning :type => :warning,\n      :warning_code => 15,\n      :warning_type => \"Dynamic Render Path\",\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:render, :action, s(:call, s(:call, s(:const, :TestViewComponent), :new, s(:call, s(:params), :require, s(:str, \"name\"))), :with_content, s(:str, \"string\")), s(:hash)),\n      :user_input => s(:call, s(:params), :require, s(:str, \"name\"))\n  end\n\n  def test_dynamic_render_path_dir_glob_filter\n    assert_no_warning :type => :warning,\n      :warning_code => 15,\n      :fingerprint => \"3ca5600705cf1e73b6213275bb2206480867176a80f0f1135a100019a29cb850\",\n      :warning_type => \"Dynamic Render Path\",\n      :line => 29,\n      :message => /^Render\\ path\\ contains\\ parameter\\ value/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:render, :action, s(:dstr, \"groups/\", s(:evstr, s(:call, s(:params), :[], s(:lit, :template)))), s(:hash)),\n      :user_input => s(:call, s(:params), :[], s(:lit, :template))\n  end\n\n  def test_mass_assignment_permit_bang_1\n    assert_warning :type => :warning,\n      :warning_code => 70,\n      :fingerprint => \"58e42d4ef79c278374a8456b1c034c7768e28b9a156e5602bb99a1105349f350\",\n      :warning_type => \"Mass Assignment\",\n      :line => 93,\n      :message => /^Specify exact keys allowed for mass assignment/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:params), :permit!),\n      :user_input => nil\n  end\n\n  def test_mass_assignment_permit_bang_2\n    assert_warning :type => :warning,\n      :warning_code => 70,\n      :fingerprint => \"58e42d4ef79c278374a8456b1c034c7768e28b9a156e5602bb99a1105349f350\",\n      :warning_type => \"Mass Assignment\",\n      :line => 94,\n      :message => /^Specify exact keys allowed for mass assignment/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :code => s(:call, s(:params), :permit!),\n      :user_input => nil\n  end\n\n  def test_mass_assignment_in_path_helper_false_positive\n    assert_no_warning :type => :warning,\n      :warning_code => 70,\n      :fingerprint => \"7802220237ed1e4c030fdc71d59bffd33fa8800adeb699e9d2105b00bc048d38\",\n      :warning_type => \"Mass Assignment\",\n      :line => 32,\n      :message => /^Parameters\\ should\\ be\\ whitelisted\\ for\\ mas/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:params), :permit!),\n      :user_input => nil\n  end\n\n  def test_mass_assignment_global_allow_all_parameters\n    assert_warning :type => :warning,\n      :warning_code => 112,\n      :fingerprint => \"a02bb53bb433ffd7e52cfd58f9a3fdf20f53d082db36d2e47bf3c0aee32458ae\",\n      :warning_type => \"Mass Assignment\",\n      :line => 2,\n      :message => /^Mass assignment is globally enabled/,\n      :confidence => 0,\n      :relative_path => \"config/initializers/allow_all_parameters.rb\",\n      :code => s(:attrasgn, s(:colon2, s(:const, :ActionController), :Parameters), :permit_all_parameters=, s(:true)),\n      :user_input => nil\n  end\n\n  def test_mass_assignment_permit_bang_slice_false_positive\n    assert_no_warning :type => :warning,\n      :warning_code => 70,\n      :fingerprint => \"5c1aa277503e9c28dda97731136240ab07800348c4ac296c25789d65bd158373\",\n      :warning_type => \"Mass Assignment\",\n      :line => 36,\n      :message => /^Parameters\\ should\\ be\\ whitelisted\\ for\\ mas/,\n      :confidence => 1,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, s(:params), :permit!),\n      :user_input => nil\n  end\n\n  def test_secrets_file_1\n    assert_warning :type => :warning,\n      :warning_code => 101,\n      :fingerprint => \"6036cfd256d955c52298c798e37b363f923d9c38f0a77599bfae942839a1dc4e\",\n      :warning_type => \"Authentication\",\n      :line => 3,\n      :message => /^Hardcoded\\ value\\ for\\ `DEFAULT_PASSWORD`\\ i/,\n      :confidence => 1,\n      :relative_path => \"app/models/user.rb\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_template_injection_1\n    assert_warning :type => :warning,\n      :warning_code => 117,\n      :fingerprint => \"fba898ebe85a030856f8553a3329c184ad6f9e16b1ecc8eb862d75f8b48d8189\",\n      :warning_type => \"Template Injection\",\n      :line => 15,\n      :message => /^Parameter\\ value\\ used\\ directly\\ in\\ `ERB`\\ t/,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :code => s(:call, s(:const, :ERB), :new, s(:params)),\n      :user_input => s(:params)\n  end\n\n  def test_http_verb_confusion_1\n    assert_warning :type => :warning,\n      :warning_code => 118,\n      :fingerprint => \"25c3c56a6e2026101731748e855b72413e91afb0fcc5c1d250253ede9d8ce6d9\",\n      :warning_type => \"HTTP Verb Confusion\",\n      :line => 3,\n      :message => /^Potential\\ HTTP\\ verb\\ confusion\\.\\ `HEAD`\\ is/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/accounts_controller.rb\",\n      :code => s(:if, s(:call, s(:call, nil, :request), :get?), nil, nil),\n      :user_input => s(:call, s(:call, nil, :request), :get?)\n  end\n\n  def test_skip_dev_environment\n    assert_no_warning :type => :warning,\n      :warning_code => 13,\n      :fingerprint => \"a7759c4ad34056fffc847aff31c9b40d90803cd5637a7189b0edfd7615132f37\",\n      :warning_type => \"Dangerous Eval\",\n      :line => 54,\n      :message => /^Parameter\\ value\\ evaluated\\ as\\ code/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/groups_controller.rb\",\n      :code => s(:call, nil, :eval, s(:call, s(:params), :[], s(:lit, :x))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_dangerous_eval_as_method_target\n    assert_warning :type => :warning,\n      :warning_code => 13,\n      :fingerprint => \"3c4b94f3fc4ff4cfb005299349eb4f9a89832f35fc33ed9edc8481b98a047edb\",\n      :warning_type => \"Dangerous Eval\",\n      :line => 27,\n      :message => /^Parameter\\ value\\ evaluated\\ as\\ code/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/accounts_controller.rb\",\n      :code => s(:call, nil, :eval, s(:call, s(:params), :[], s(:lit, :x))),\n      :user_input => s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_unmaintained_dependency_ruby\n    assert_warning check_name: \"EOLRuby\",\n      type: :warning,\n      warning_code: 121,\n      fingerprint: \"81776f151be34b9c42a5fc3bec249507a2acd9b64338e6f544a68559976bc5d5\",\n      warning_type: \"Unmaintained Dependency\",\n      line: 4,\n      message: /^Support\\ for\\ Ruby\\ 2\\.5\\.3\\ ended\\ on\\ 2021\\-03\\-/,\n      confidence: 0,\n      relative_path: \"Gemfile\"\n  end\nend\n"
  },
  {
    "path": "test/tests/rails7.rb",
    "content": "require_relative '../test'\n\nclass Rails7Tests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def report\n    @@report ||=\n      Date.stub :today, Date.parse('2023-02-10') do\n        BrakemanTester.run_scan \"rails7\", \"Rails 7\", :run_all_checks => true, gemfile: \"MyGemfile\"\n      end\n  end\n\n  def expected\n    @@expected ||= {\n      :controller => 0,\n      :model => 0,\n      :template => 0,\n      :warning => 26\n    }\n  end\n\n  def test_ruby_2_7_eol\n    assert_warning check_name: \"EOLRuby\",\n      type: :warning,\n      warning_code: 123,\n      fingerprint: \"92f8b4d79a8f4abeffed8ce6683ca85c4c42df26c7e5ec4378fafa7b728044ce\",\n      warning_type: \"Unmaintained Dependency\",\n      line: 235,\n      message: /^Support\\ for\\ Ruby\\ 2\\.7\\.0\\ ends\\ on\\ 2023\\-03\\-3/,\n      confidence: 2,\n      relative_path: \"MyGemfile.lock\",\n      code: nil,\n      user_input: nil\n  end\n\n  def test_missing_encryption_1\n    assert_warning :type => :warning,\n      :warning_code => 109,\n      :fingerprint => \"6a26086cd2400fbbfb831b2f8d7291e320bcc2b36984d2abc359e41b3b63212b\",\n      :warning_type => \"Missing Encryption\",\n      :line => 1,\n      :message => /^The\\ application\\ does\\ not\\ force\\ use\\ of\\ HT/,\n      :confidence => 0,\n      :relative_path => \"config/environments/production.rb\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_path_traversal_1\n    assert_warning check_name: \"Pathname\",\n      type: :warning,\n      warning_code: 125,\n      fingerprint: \"1797967f82af2ce9213b465cd77c98bd08b36b6ed748a50e2d72a5e1c5c83461\",\n      warning_type: \"Path Traversal\",\n      line: 30,\n      message: /^Absolute\\ paths\\ in\\ `Pathname\\#join`\\ cause\\ /,\n      confidence: 0,\n      relative_path: \"app/controllers/application_controller.rb\",\n      code: s(:call, s(:call, s(:const, :Rails), :root), :join, s(:str, \"a\"), s(:str, \"b\"), s(:dstr, \"\", s(:evstr, s(:call, s(:params), :[], s(:lit, :c))))),\n      user_input: s(:call, s(:params), :[], s(:lit, :c))\n  end\n\n  def test_path_traversal_2\n    assert_warning check_name: \"Pathname\",\n      type: :warning,\n      warning_code: 125,\n      fingerprint: \"b5ff15be27c93ea4b88c3affe638affb2a255d1b6ac6c8e90ee1536f6001e73d\",\n      warning_type: \"Path Traversal\",\n      line: 27,\n      message: /^Absolute\\ paths\\ in\\ `Pathname\\#join`\\ cause\\ /,\n      confidence: 0,\n      relative_path: \"app/controllers/application_controller.rb\",\n      code: s(:call, s(:call, s(:const, :Pathname), :new, s(:str, \"a\")), :join, s(:call, s(:params), :[], s(:lit, :x)), s(:str, \"z\")),\n      user_input: s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_redirect_to_last\n    assert_no_warning check_name: \"Redirect\",\n      type: :warning,\n      warning_code: 18,\n      fingerprint: \"86a37d9ade23cec9901c80ad7c6fa7581d6257783dd56f2cddfd6adda4efc95a\",\n      warning_type: \"Redirect\",\n      line: 3,\n      message: /^Possible\\ unprotected\\ redirect/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, nil, :redirect_to, s(:call, s(:const, :User), :last!)),\n      user_input: s(:call, s(:const, :User), :last!)\n  end\n\n  def test_weak_cryptography_1\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 128,\n      fingerprint: \"4c4db18f4142dac0b271136f6bcf8bee08f0585bd9640676a12cdb80b1d7f02d\",\n      warning_type: \"Weak Cryptography\",\n      line: 16,\n      message: /^RSA\\ key\\ with\\ size\\ `512`\\ is\\ considered\\ ve/,\n      confidence: 0,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :generate, s(:lit, 512)),\n      user_input: s(:lit, 512)\n  end\n\n  def test_weak_cryptography_2\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 128,\n      fingerprint: \"0f23edef18a0d092581daff053a88b523a56f50c03367907c0167af50d01dec2\",\n      warning_type: \"Weak Cryptography\",\n      line: 17,\n      message: /^RSA\\ key\\ with\\ size\\ `1024`\\ is\\ considered\\ w/,\n      confidence: 1,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :new, s(:lit, 1024)),\n      user_input: s(:lit, 1024)\n  end\n\n  def test_weak_cryptography_3\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 128,\n      fingerprint: \"74dd38e229f0343ce80891b7530c4ecf3446c2f214917f70a1044006c885a6b0\",\n      warning_type: \"Weak Cryptography\",\n      line: 22,\n      message: /^RSA\\ key\\ with\\ size\\ `1024`\\ is\\ considered\\ w/,\n      confidence: 1,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:colon2, s(:const, :OpenSSL), :PKey), :generate_key, s(:str, \"rsa\"), s(:hash, s(:lit, :rsa_keygen_bits), s(:lit, 1024))),\n      user_input: s(:lit, 1024)\n  end\n\n  def test_weak_cryptography_4\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 126,\n      fingerprint: \"cc38689724cb70423c57d575290423054f0c998a7b897b2985e96da96f51e77e\",\n      warning_type: \"Weak Cryptography\",\n      line: 4,\n      message: /^Use\\ of\\ padding\\ mode\\ PKCS1\\ \\(default\\ if\\ no/,\n      confidence: 0,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:call, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :new, s(:str, \"grab the public 4096 bit key\")), :public_encrypt, s(:call, s(:call, nil, :payload), :to_json)),\n      user_input: nil\n  end\n\n  def test_weak_cryptography_5\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 126,\n      fingerprint: \"53df5254e251a0ab8f6159df3dbdb1a77ff92c96589a213adb9847c2f255a479\",\n      warning_type: \"Weak Cryptography\",\n      line: 5,\n      message: /^Use\\ of\\ padding\\ mode\\ PKCS1\\ \\(default\\ if\\ no/,\n      confidence: 0,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:call, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :new, s(:str, \"grab the public 4096 bit key\")), :private_decrypt, s(:call, s(:const, :Base64), :decode64, s(:call, s(:const, :Base64), :encode64, s(:call, s(:call, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :new, s(:str, \"grab the public 4096 bit key\")), :public_encrypt, s(:call, s(:call, nil, :payload), :to_json))))),\n      user_input: nil\n  end\n\n  def test_weak_cryptography_6\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 126,\n      fingerprint: \"c8a3c3c409f64eae926ce9b60d85d243f86bc8448d1ba7b5880f192eb54089d7\",\n      warning_type: \"Weak Cryptography\",\n      line: 10,\n      message: /^Use\\ of\\ padding\\ mode\\ PKCS1\\ \\(default\\ if\\ no/,\n      confidence: 0,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:call, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :new, s(:str, \"grab the public 4096 bit key\")), :public_decrypt, s(:call, nil, :data), s(:colon2, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :PKCS1_PADDING)),\n      user_input: s(:colon2, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :PKCS1_PADDING)\n  end\n\n  def test_weak_cryptography_7\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 126,\n      fingerprint: \"bf3a313e24667f5839385b4ad0e90bc51a4f6bf8b489dab152c03242641ebad9\",\n      warning_type: \"Weak Cryptography\",\n      line: 11,\n      message: /^No\\ padding\\ mode\\ used\\ for\\ RSA\\ key\\.\\ A\\ safe/,\n      confidence: 0,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:call, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :new, s(:str, \"grab the public 4096 bit key\")), :private_encrypt, s(:call, nil, :data), s(:colon2, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :NO_PADDING)),\n      user_input: s(:colon2, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :NO_PADDING)\n  end\n\n  def test_weak_cryptography_8\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 126,\n      fingerprint: \"7692aefd6fc53891734025f079ac062bf5b4ca69d1447f53de8f7e0cd389ae19\",\n      warning_type: \"Weak Cryptography\",\n      line: 12,\n      message: /^Use\\ of\\ padding\\ mode\\ SSLV23\\ for\\ RSA\\ key,\\ /,\n      confidence: 0,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:call, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :new, s(:str, \"grab the public 4096 bit key\")), :private_encrypt, s(:call, nil, :data), s(:colon2, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :SSLV23_PADDING)),\n      user_input: s(:colon2, s(:colon2, s(:colon2, s(:const, :OpenSSL), :PKey), :RSA), :SSLV23_PADDING)\n  end\n\n  def test_weak_cryptography_9\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 126,\n      fingerprint: \"386909718cfc8427e4509912c7c22b0f99ce2e052bb505ccfe6b400e3fd21632\",\n      warning_type: \"Weak Cryptography\",\n      line: 23,\n      message: /^Use\\ of\\ padding\\ mode\\ PKCS1\\ \\(default\\ if\\ no/,\n      confidence: 0,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:call, s(:colon2, s(:const, :OpenSSL), :PKey), :generate_key, s(:str, \"rsa\"), s(:hash, s(:lit, :rsa_keygen_bits), s(:lit, 1024))), :encrypt, s(:str, \"data\"), s(:hash, s(:str, \"rsa_padding_mode\"), s(:str, \"pkcs1\"))),\n      user_input: s(:str, \"pkcs1\")\n  end\n\n  def test_weak_cryptography_10\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 126,\n      fingerprint: \"3a3f24bb81d480515081aee1ebdf76d34c79b6e0c3c1946513158164512f9130\",\n      warning_type: \"Weak Cryptography\",\n      line: 25,\n      message: /^Use\\ of\\ padding\\ mode\\ PKCS1\\ \\(default\\ if\\ no/,\n      confidence: 0,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:call, s(:colon2, s(:const, :OpenSSL), :PKey), :generate_key, s(:str, \"rsa\"), s(:hash, s(:lit, :rsa_keygen_bits), s(:lit, 1024))), :sign, s(:str, \"SHA256\"), s(:str, \"data\"), s(:hash, s(:lit, :rsa_padding_mode), s(:str, \"PKCS1\"))),\n      user_input: s(:str, \"pkcs1\")\n  end\n\n  def test_weak_cryptography_11\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 126,\n      fingerprint: \"0b6b1f354c2380be841134447c315a24c2919d61fbb4de51af3dafc66e2144c3\",\n      warning_type: \"Weak Cryptography\",\n      line: 26,\n      message: /^No\\ padding\\ mode\\ used\\ for\\ RSA\\ key\\.\\ A\\ safe/,\n      confidence: 0,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:call, s(:colon2, s(:const, :OpenSSL), :PKey), :generate_key, s(:str, \"rsa\"), s(:hash, s(:lit, :rsa_keygen_bits), s(:lit, 1024))), :verify, s(:str, \"SHA256\"), s(:str, \"data\"), s(:hash, s(:lit, :rsa_padding_mode), s(:str, \"none\"))),\n      user_input: s(:str, \"none\")\n  end\n\n  def test_weak_cryptography_12\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 126,\n      fingerprint: \"cf7d2b90d591ca7a442992caf39b858c4e599c9f2f4d82fa09e40b250f9c8e78\",\n      warning_type: \"Weak Cryptography\",\n      line: 27,\n      message: /^No\\ padding\\ mode\\ used\\ for\\ RSA\\ key\\.\\ A\\ safe/,\n      confidence: 0,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:call, s(:colon2, s(:const, :OpenSSL), :PKey), :generate_key, s(:str, \"rsa\"), s(:hash, s(:lit, :rsa_keygen_bits), s(:lit, 1024))), :sign_raw, s(:nil), s(:str, \"data\"), s(:hash, s(:lit, :rsa_padding_mode), s(:str, \"none\"))),\n      user_input: s(:str, \"none\")\n  end\n\n  def test_weak_cryptography_13\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 126,\n      fingerprint: \"6a9835fa708e6f92797c4c1164b32446fe028672ba7ad652d3a474072658e271\",\n      warning_type: \"Weak Cryptography\",\n      line: 28,\n      message: /^No\\ padding\\ mode\\ used\\ for\\ RSA\\ key\\.\\ A\\ safe/,\n      confidence: 0,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:call, s(:colon2, s(:const, :OpenSSL), :PKey), :generate_key, s(:str, \"rsa\"), s(:hash, s(:lit, :rsa_keygen_bits), s(:lit, 1024))), :verify_raw, s(:nil), s(:str, \"data\"), s(:hash, s(:lit, :rsa_padding_mode), s(:str, \"none\"))),\n      user_input: s(:str, \"none\")\n  end\n\n  def test_weak_cryptography_14\n    assert_warning check_name: \"WeakRSAKey\",\n      type: :warning,\n      warning_code: 126,\n      fingerprint: \"a7c85f295d9ea5356afbdf9165eb5bcfb892646f5f9a5a73b514a835456b419b\",\n      warning_type: \"Weak Cryptography\",\n      line: 29,\n      message: /^Use\\ of\\ padding\\ mode\\ PKCS1\\ \\(default\\ if\\ no/,\n      confidence: 0,\n      relative_path: \"lib/some_lib.rb\",\n      code: s(:call, s(:call, s(:colon2, s(:const, :OpenSSL), :PKey), :generate_key, s(:str, \"rsa\"), s(:hash, s(:lit, :rsa_keygen_bits), s(:lit, 1024))), :encrypt, s(:str, \"data\")),\n      user_input: nil\n  end\n\n  def test_presence_in_with_render_path_false_positive\n    assert_no_warning check_name: \"Render\",\n      type: :warning,\n      warning_code: 15,\n      fingerprint: \"32762c066cbafafce28947fb91f24cd547c52f184084fb4dc05ac9ff81def638\",\n      warning_type: \"Dynamic Render Path\",\n      line: 9,\n      message: /^Render\\ path\\ contains\\ parameter\\ value/,\n      confidence: 1,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:render, :action, s(:dstr, \"admin2/fields/\", s(:evstr, s(:or, s(:call, s(:call, s(:params), :[], s(:lit, :field)), :presence_in, s(:array, s(:str, \"foo\"))), s(:call, nil, :raise, s(:colon2, s(:const, :ActionController), :BadRequest))))), s(:hash)),\n      user_input: s(:call, s(:call, s(:params), :[], s(:lit, :field)), :presence_in, s(:array, s(:str, \"foo\")))\n  end\n\n  def test_cross_site_scripting_CVE_2022_32209_allowed_tags_initializer\n    assert_warning check_name: \"SanitizeConfigCve\",\n      type: :warning,\n      warning_code: 124,\n      fingerprint: \"c2cc471a99036432e03d83e893fe748c2b1d5c40a39e776475faf088717af97d\",\n      warning_type: \"Cross-Site Scripting\",\n      line: 1,\n      message: /^rails\\-html\\-sanitizer\\ 1\\.4\\.2\\ is\\ vulnerable/,\n      confidence: 0,\n      relative_path: \"config/initializers/sanitizers.rb\",\n      code: s(:attrasgn, s(:colon2, s(:colon2, s(:const, :Rails), :Html), :SafeListSanitizer), :allowed_tags=, s(:array, s(:str, \"select\"), s(:str, \"a\"), s(:str, \"style\"))),\n      user_input: nil\n  end\n\n  def test_cross_site_scripting_content_tag\n    assert_no_warning check_name: \"ContentTag\",\n      type: :template,\n      warning_code: 53,\n      warning_type: \"Cross-Site Scripting\",\n      line: 2,\n      message: /^Unescaped\\ parameter\\ value\\ in\\ `content_ta/,\n      confidence: 0,\n      relative_path: \"app/views/users/index.html.erb\",\n      code: s(:call, nil, :content_tag, s(:lit, :b), s(:call, nil, :cool_content), s(:hash, s(:call, s(:call, nil, :params), :[], s(:lit, :stuff)), s(:call, s(:call, nil, :params), :[], s(:lit, :things)))),\n      user_input: s(:call, s(:call, nil, :params), :[], s(:lit, :stuff))\n  end\n\n  def test_redirect_1\n    assert_warning check_name: \"Redirect\",\n      type: :warning,\n      warning_code: 18,\n      fingerprint: \"0e6b36e8598a024ef8832d7af1a5b0089f6b00f96c17e2ccdb87aca012e6f76f\",\n      warning_type: \"Redirect\",\n      line: 13,\n      message: /^Possible\\ unprotected\\ redirect/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, nil, :redirect_to, s(:or, s(:call, s(:params), :[], s(:lit, :redirect_url)), s(:str, \"/\"))),\n      user_input: s(:call, s(:params), :[], s(:lit, :redirect_url))\n  end\n\n  def test_redirect_2\n    assert_no_warning check_name: \"Redirect\",\n      type: :warning,\n      warning_code: 18,\n      fingerprint: \"f6ddaf32c99db9912fb0a78d79f81701a893e6b283ddad5709393b09c6c693bc\",\n      warning_type: \"Redirect\",\n      line: 17,\n      message: /^Possible\\ unprotected\\ redirect/,\n      confidence: 2,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, nil, :redirect_to, s(:or, s(:call, nil, :url_from, s(:call, s(:params), :[], s(:lit, :redirect_url))), s(:str, \"/\"))),\n      user_input: s(:call, s(:params), :[], s(:lit, :redirect_url))\n  end\n\n  def test_redirect_disallow_other_host\n    assert_no_warning check_name: \"Redirect\",\n      type: :warning,\n      warning_code: 18,\n      fingerprint: \"cef6913575a0db9d43acdb945fd8d7f5b1ea3e0a19c457d9c85bf673e67a4a85\",\n      warning_type: \"Redirect\",\n      line: 25,\n      message: /^Possible\\ unprotected\\ redirect/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, nil, :redirect_to, s(:call, s(:params), :[], s(:lit, :x)), s(:hash, s(:lit, :allow_other_host), s(:false))),\n      user_input: s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_redirect_allow_other_host\n    assert_warning check_name: \"Redirect\",\n      type: :warning,\n      warning_code: 18,\n      fingerprint: \"a1fdd251c91d225a187a41b9b4acf88412d86a834b597fa58a17d208681b8a00\",\n      warning_type: \"Redirect\",\n      line: 21,\n      message: /^Possible\\ unprotected\\ redirect/,\n      confidence: 2,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, nil, :redirect_to, s(:call, s(:params), :[], s(:lit, :x)), s(:hash, s(:lit, :allow_other_host), s(:true))),\n      user_input: s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_redirect_back\n    assert_warning check_name: \"Redirect\",\n      type: :warning,\n      warning_code: 18,\n      fingerprint: \"81ee1b43b1a16a2e143669adb3259407bb462f1963d339717662d9271a154909\",\n      warning_type: \"Redirect\",\n      line: 29,\n      message: /^Possible\\ unprotected\\ redirect/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, nil, :redirect_back, s(:hash, s(:lit, :fallback_location), s(:call, s(:params), :[], s(:lit, :x)))),\n      user_input: s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_redirect_back_or_to\n    assert_warning check_name: \"Redirect\",\n      type: :warning,\n      warning_code: 18,\n      fingerprint: \"e5aed5eb26b588f3cb6f9f7d34c63ceffcb574348c4fd3c8464e11cab16ed3e3\",\n      warning_type: \"Redirect\",\n      line: 33,\n      message: /^Possible\\ unprotected\\ redirect/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, nil, :redirect_back_or_to, s(:call, s(:params), :[], s(:lit, :x))),\n      user_input: s(:call, s(:params), :[], s(:lit, :x))\n  end\n\n  def test_missing_authorization_ransack\n    assert_warning check_name: \"Ransack\",\n      type: :warning,\n      warning_code: 129,\n      fingerprint: \"ae28bfeb8423952ffae97149292175b2d10c36c4904ee198ab7b2eda4e05c3e0\",\n      warning_type: \"Missing Authorization\",\n      line: 41,\n      message: /^Unrestricted\\ search\\ using\\ `ransack`\\ libr/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, s(:const, :User), :ransack, s(:call, s(:params), :[], s(:lit, :q))),\n      user_input: s(:call, s(:params), :[], s(:lit, :q))\n  end\n\n  def test_missing_authorization_ransack_admin\n    assert_warning check_name: \"Ransack\",\n      type: :warning,\n      warning_code: 129,\n      fingerprint: \"cb03f7424bc739b26e3789e2d9bb6893b4f2f517dbe10d4d3f3f19b4cf845459\",\n      warning_type: \"Missing Authorization\",\n      line: 4,\n      message: /^Unrestricted\\ search\\ using\\ `ransack`\\ libr/,\n      confidence: 1,\n      relative_path: \"app/controllers/admin_controller.rb\",\n      code: s(:call, s(:const, :User), :ransack, s(:call, s(:params), :[], s(:lit, :q))),\n      user_input: s(:call, s(:params), :[], s(:lit, :q))\n  end\n\n  def test_missing_authorization_ransack_2\n    assert_no_warning check_name: \"Ransack\",\n      type: :warning,\n      warning_code: 129,\n      warning_type: \"Missing Authorization\",\n      line: 46,\n      message: /^Unrestricted\\ search\\ using\\ `ransack`\\ libr/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\"\n  end\n\n  def test_missing_authorization_ransack_low\n    assert_warning check_name: \"Ransack\",\n      type: :warning,\n      warning_code: 129,\n      fingerprint: \"50e236d8fbc9db0f67e0011941b92b08d0ece176ce4b8caea89d372f007a4873\",\n      warning_type: \"Missing Authorization\",\n      line: 49,\n      message: /^Unrestricted\\ search\\ using\\ `ransack`\\ libr/,\n      confidence: 2,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, s(:call, s(:call, nil, :some_book), :things), :ransack, s(:call, s(:params), :[], s(:lit, :q))),\n      user_input: s(:call, s(:params), :[], s(:lit, :q))\n  end\nend\n"
  },
  {
    "path": "test/tests/rails7_redirect.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/rescanner'\n\nclass RailsConfiguration < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::RescanTestHelper\n\n  def report\n    @rescanner.tracker.report.to_hash\n  end\n\n  def test_rails7_default_no_open_redirects\n    before_rescan_of ['config/application.rb'], 'rails7' do\n      replace 'config/application.rb', 'config.action_controller.raise_on_open_redirects = false', 'config.action_controller.raise_on_open_redirects = true'\n    end\n\n    assert_fixed 3\n\n    assert_no_warning check_name: \"Redirect\",\n      type: :warning,\n      warning_code: 18,\n      fingerprint: \"0e6b36e8598a024ef8832d7af1a5b0089f6b00f96c17e2ccdb87aca012e6f76f\",\n      warning_type: \"Redirect\",\n      line: 13,\n      message: /^Possible\\ unprotected\\ redirect/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, nil, :redirect_to, s(:or, s(:call, s(:params), :[], s(:lit, :redirect_url)), s(:str, \"/\"))),\n      user_input: s(:call, s(:params), :[], s(:lit, :redirect_url))\n\n    assert_no_warning check_name: \"Redirect\",\n      type: :warning,\n      warning_code: 18,\n      fingerprint: \"81ee1b43b1a16a2e143669adb3259407bb462f1963d339717662d9271a154909\",\n      warning_type: \"Redirect\",\n      line: 29,\n      message: /^Possible\\ unprotected\\ redirect/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, nil, :redirect_back, s(:hash, s(:lit, :fallback_location), s(:call, s(:params), :[], s(:lit, :x)))),\n      user_input: s(:call, s(:params), :[], s(:lit, :x))\n\n    assert_no_warning check_name: \"Redirect\",\n      type: :warning,\n      warning_code: 18,\n      fingerprint: \"e5aed5eb26b588f3cb6f9f7d34c63ceffcb574348c4fd3c8464e11cab16ed3e3\",\n      warning_type: \"Redirect\",\n      line: 33,\n      message: /^Possible\\ unprotected\\ redirect/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, nil, :redirect_back_or_to, s(:call, s(:params), :[], s(:lit, :x))),\n      user_input: s(:call, s(:params), :[], s(:lit, :x))\n  end\nend\n"
  },
  {
    "path": "test/tests/rails8.rb",
    "content": "require_relative \"../test\"\n\nclass Rails8Tests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def report\n    @@report ||=\n      Date.stub :today, Date.parse(\"2024-05-13\") do\n        BrakemanTester.run_scan \"rails8\", \"Rails 8\", run_all_checks: true, use_prism: false\n      end\n  end\n\n  def expected\n    @@expected ||= {\n      controller: 0,\n      model:      0,\n      template:   2,\n      warning:    9\n    }\n  end\n\n  def test_dangerous_eval_1\n    assert_warning check_name: \"Evaluation\",\n      type: :warning,\n      warning_code: 13,\n      fingerprint: \"00a0720a22562960da1d793140ccae5987da210d45fb8478daabad13a5901130\",\n      warning_type: \"Dangerous Eval\",\n      line: 7,\n      message: /^Dynamic\\ code\\ evaluation/,\n      confidence: 2,\n      relative_path: \"lib/evals.rb\",\n      code: s(:call, nil, :eval, s(:call, nil, :anything)),\n      user_input: nil\n  end\n\n  def test_dangerous_eval_2\n    assert_warning check_name: \"Evaluation\",\n      type: :warning,\n      warning_code: 13,\n      fingerprint: \"c9b3bb7b8898b84a0d82bf59a110b8259d4a7796a092aecda9910c8a02d7ae5e\",\n      warning_type: \"Dangerous Eval\",\n      line: 4,\n      message: /^Dynamic\\ string\\ evaluated\\ as\\ code/,\n      confidence: 2,\n      relative_path: \"lib/evals.rb\",\n      code: s(:call, nil, :instance_eval, s(:dstr, \"interpolated \", s(:evstr, s(:call, nil, :string)), s(:str, \" - warning\"))),\n      user_input: nil\n  end\n\n  def test_dangerous_eval_3\n    assert_warning check_name: \"Evaluation\",\n      type: :warning,\n      warning_code: 13,\n      fingerprint: \"6e08d62e8ad637151c2d28872e9e55fd872d4ae19ba511f72d80be7525f1509c\",\n      warning_type: \"Dangerous Eval\",\n      line: 17,\n      message: /^Dynamic\\ string\\ evaluated\\ as\\ code/,\n      confidence: 2,\n      relative_path: \"lib/evals.rb\",\n      code: s(:call, nil, :eval, s(:dstr, \"interpolate \", s(:evstr, s(:lvar, :something)))),\n      user_input: nil\n  end\n\n  def test_dangerous_eval_4\n    assert_warning check_name: \"Evaluation\",\n      type: :warning,\n      warning_code: 13,\n      fingerprint: \"3f08e08ea36320517ee869bd7f6f6f150efe38f6e02667f5907e9964881b46be\",\n      warning_type: \"Dangerous Eval\",\n      line: 32,\n      message: /^Dynamic\\ string\\ evaluated\\ as\\ code/,\n      confidence: 2,\n      method: :\"self.defs_eval\",\n      relative_path: \"lib/evals.rb\",\n      code: s(:call, nil, :eval, s(:dstr, \"foo \", s(:evstr, s(:lvar, :string)))),\n      user_input: nil\n  end\n\n  def test_dangerous_eval_5\n    assert_warning check_name: \"Evaluation\",\n      type: :warning,\n      warning_code: 13,\n      fingerprint: \"a4284b65b029c04b9e786bf70d7a1bb88a9e7d35a4c0258b0e0e7f8011524994\",\n      warning_type: \"Dangerous Eval\",\n      line: 37,\n      message: /^Dynamic\\ string\\ evaluated\\ as\\ code/,\n      confidence: 2,\n      method: :\"Object.object_defs_eval\",\n      relative_path: \"lib/evals.rb\",\n      code: s(:call, nil, :eval, s(:dstr, \"foo \", s(:evstr, s(:lvar, :string)))),\n      user_input: nil\n  end\n\n  def test_dangerous_eval_6\n    assert_warning check_name: \"Evaluation\",\n      type: :warning,\n      warning_code: 13,\n      fingerprint: \"bcd52e91053d1e52c90c9a30992e255bb5602e6f06740b9473e1c4b0b6110430\",\n      warning_type: \"Dangerous Eval\",\n      line: 41,\n      message: /^Dynamic\\ string\\ evaluated\\ as\\ code/,\n      confidence: 2,\n      method: :\"@ivar.ivar_def_eval\",\n      relative_path: \"lib/evals.rb\",\n      code: s(:call, nil, :eval, s(:dstr, \"foo \", s(:evstr, s(:lvar, :string)))),\n      user_input: nil\n  end\n\n  def test_dangerous_eval_7\n    assert_warning check_name: \"Evaluation\",\n      type: :warning,\n      warning_code: 13,\n      fingerprint: \"a261e54367af8c1621ab2612ca8b7d0c67f67edb46295aeb3a68ca90035b29f6\",\n      warning_type: \"Dangerous Eval\",\n      line: 46,\n      message: /^Dynamic\\ string\\ evaluated\\ as\\ code/,\n      confidence: 2,\n      method: :\"lvar.lvar_def_eval\",\n      relative_path: \"lib/evals.rb\",\n      code: s(:call, nil, :eval, s(:dstr, \"foo \", s(:evstr, s(:lvar, :string)))),\n      user_input: nil\n  end\n\n  def test_plain_marshal_load_weak_warning\n    assert_warning check_name: \"Deserialize\",\n      type: :warning,\n      warning_code: 25,\n      fingerprint: \"458ac9bba693eae0b1d311627d59101dceac803c578bd1da7d808cb333c75068\",\n      warning_type: \"Remote Code Execution\",\n      line: 6,\n      message: /^Use\\ of\\ `Marshal\\.load`\\ may\\ be\\ dangerous/,\n      confidence: 2,\n      relative_path: \"app/controllers/application_controller.rb\",\n      code: s(:call, s(:const, :Marshal), :load, s(:call, nil, :something)),\n      user_input: nil\n  end\n\n  def test_dangerous_eval_plain_strings\n    assert_no_warning check_name: \"Evaluation\",\n      type: :warning,\n      warning_code: 13,\n      warning_type: \"Dangerous Eval\",\n      line: 22,\n      message: /^Dynamic\\ string\\ evaluated\\ as\\ code/,\n      confidence: 2,\n      relative_path: \"lib/evals.rb\"\n      # code: s(:call, nil, :class_eval, s(:dstr, \"        def method_that_is_\", s(:evstr, s(:lit, :BRAKEMAN_SAFE_LITERAL)), s(:str, \"\\n          puts suffix\\n        end\\n\"))),\n  end\n\n  def test_cross_site_scripting_render_model_partial\n    assert_warning check_name: \"CrossSiteScripting\",\n      type: :template,\n      warning_code: 2,\n      fingerprint: \"08a968d826da16ddaffcf8393d6ed50d30a3caea4b625dabf888a5a8b699453d\",\n      warning_type: \"Cross-Site Scripting\",\n      line: 4,\n      message: /^Unescaped\\ model\\ attribute/,\n      confidence: 0,\n      relative_path: \"app/views/users/_user.html.erb\",\n      code: s(:call, s(:call, s(:const, :User), :new, s(:call, nil, :user_params)), :name),\n      user_input: nil\n  end\n\n  def test_cross_site_scripting_render_model_as_collection\n    assert_warning check_name: \"CrossSiteScripting\",\n      type: :template,\n      warning_code: 2,\n      fingerprint: \"27817dcbdd924e1772bd98bcdd6063486a633cf8c1b84353dcbf1dde23904a94\",\n      warning_type: \"Cross-Site Scripting\",\n      line: 1,\n      message: /^Unescaped\\ model\\ attribute/,\n      confidence: 0,\n      relative_path: \"app/views/things/_thing.html.erb\",\n      code: s(:call, s(:call, s(:const, :Thing), :new), :name),\n      user_input: nil\n  end\n\n  def test_cross_site_scripting_haml6_attribute_builder\n    assert_no_warning check_name: \"CrossSiteScripting\",\n      type: :template,\n      warning_code: 2,\n      fingerprint: \"26ce9d920c75d29438b84cd3a45efdf4a7235d883d133937445c31c627bbc791\",\n      warning_type: \"Cross-Site Scripting\",\n      line: 1,\n      message: /^Unescaped\\ model\\ attribute/,\n      confidence: 2,\n      relative_path: \"app/views/users/dom_id.haml\",\n      code: s(:call, s(:colon2, s(:colon3, :Haml), :AttributeBuilder), :build_id, s(:true), s(:call, nil, :dom_id, s(:call, s(:const, :User), :first))),\n      user_input: s(:call, s(:const, :User), :first)\n  end\n\n  def test_sql_injection_permit_or_false_positive\n    assert_no_warning check_name: \"SQL\",\n      type: :warning,\n      warning_code: 0,\n      fingerprint: \"ad3963321ca8cbee045dce7efd357265432d14700f6cfb791b22da0395ad4fb9\",\n      warning_type: \"SQL Injection\",\n      line: 74,\n      message: /^Possible\\ SQL\\ injection/,\n      confidence: 0,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, s(:const, :Thing), :find_by, s(:or, s(:call, s(:params), :permit, s(:lit, :foo_uid)), s(:hash, s(:lit, :id), s(:call, s(:params), :require, s(:lit, :model_id))))),\n      user_input: s(:call, s(:params), :permit, s(:lit, :foo_uid))\n  end\n\n  def test_sql_injection_count_false_positive\n    assert_no_warning check_name: \"SQL\",\n      type: :warning,\n      warning_code: 0,\n      fingerprint: \"cd09d75803f54d86ed5479758dcbb82c2ca9a29626fecd01a2bb5000b63b3aa6\",\n      warning_type: \"SQL Injection\",\n      line: 78,\n      message: /^Possible\\ SQL\\ injection/,\n      confidence: 2,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, s(:gvar, :$stats), :count, s(:dstr, \"thing.\", s(:evstr, s(:call, nil, :variable))), s(:hash, s(:lit, :tags), s(:hash, s(:lit, :cool), s(:str, \"stuff\")))),\n      user_input: s(:call, nil, :variable)\n  end\n\n  def test_sql_injection_count_less_false_positive\n    assert_warning check_name: \"SQL\",\n      type: :warning,\n      warning_code: 0,\n      fingerprint: \"3f3281fe16319fd58d1bbd66515c61aa0962c1b7ab5f79421ed1c113271c9204\",\n      warning_type: \"SQL Injection\",\n      line: 79,\n      message: /^Possible\\ SQL\\ injection/,\n      confidence: 1,\n      relative_path: \"app/controllers/users_controller.rb\",\n      code: s(:call, s(:call, nil, :not_ar_model), :count, s(:dstr, \"something - \", s(:evstr, s(:call, s(:params), :[], s(:lit, :x))))),\n      user_input: s(:call, s(:params), :[], s(:lit, :x))\n  end\nend\n"
  },
  {
    "path": "test/tests/rails_61_sql.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/rescanner'\n\nclass Rails61SQLTests < Minitest::Test\n  include BrakemanTester::RescanTestHelper\n\n  # Test that `pluck`/`order`/`reorder` no longer warn about SQL injection\n  # in Rails 6.1\n  def test_pluck_safe_in_rails_6_1\n    before_rescan_of ['app/controllers/groups_controller.rb', 'Gemfile'], 'rails6' do\n      replace \"Gemfile\", \"gem 'rails', '~> 6.0.0.beta2'\", \"gem 'rails', '6.1.0'\"\n    end\n\n    assert_equal '6.1.0', @rescanner.tracker.config.rails_version\n    assert_fixed 3\n  end\nend\n"
  },
  {
    "path": "test/tests/rails_lts.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/rescanner'\n\nclass RailsLTSTests < Minitest::Test\n  include BrakemanTester::RescanTestHelper\n\n  def test_gemfile_lock_rails_lts\n    gemfile = \"Gemfile.lock\"\n\n    before_rescan_of gemfile, \"rails_with_xss_plugin\" do\n      append gemfile, \"railslts-version (2.3.18.6)\"\n    end\n\n    assert @rescanner.tracker.config.gem_version(:'railslts-version'), \"2.3.18.6\"\n    assert_new 0\n    assert_fixed 2\n  end\n\n  def test_rails_lts_CVE_2012_1099\n    gemfile = \"Gemfile.lock\"\n\n    before_rescan_of gemfile, \"rails_with_xss_plugin\" do\n      append gemfile, \"railslts-version (2.3.18.7)\"\n    end\n\n    assert @rescanner.tracker.config.gem_version(:'railslts-version'), \"2.3.18.7\"\n    assert_new 0\n    assert_fixed 3 # 2 + CVE-2012-1099\n  end\n\n  def test_rails_lts_CVE_2014_0081\n    gemfile = \"Gemfile.lock\"\n\n    before_rescan_of gemfile, \"rails_with_xss_plugin\" do\n      append gemfile, \"railslts-version (2.3.18.8)\"\n    end\n\n    assert @rescanner.tracker.config.gem_version(:'railslts-version'), \"2.3.18.8\"\n    assert_new 0\n    assert_fixed 4 # 2 + CVE-2012-1099 + CVE_2014_0081\n  end\n\n  def test_rails_lts_CVE_2014_0130\n    gemfile = \"Gemfile.lock\"\n\n    before_rescan_of gemfile, \"rails_with_xss_plugin\" do\n      append gemfile, \"railslts-version (2.3.18.9)\"\n    end\n\n    assert @rescanner.tracker.config.gem_version(:'railslts-version'), \"2.3.18.9\"\n    assert_new 0\n    assert_fixed 5\n  end\nend\n"
  },
  {
    "path": "test/tests/rails_with_xss_plugin.rb",
    "content": "require_relative '../test'\n\nclass RailsWithXssPluginTests < Minitest::Test\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def expected\n    @expected ||= {\n      :controller => 1,\n      :model => 4,\n      :template => 4,\n      :generic => 32 }\n  end\n\n  def report\n    @@report ||= BrakemanTester.run_scan(\n      \"rails_with_xss_plugin\",\n      \"RailsWithXssPlugin\",\n      :absolute_paths => true,\n      :run_all_checks => true,\n      :collapse_mass_assignment => true\n    )\n  end\n\n  def test_default_routes_1\n    assert_warning :type => :warning,\n      :warning_type => \"Default Routes\",\n      :line => 52,\n      :message => /^All\\ public\\ methods\\ in\\ controllers\\ are\\ av/,\n      :confidence => 0,\n      :file => /routes\\.rb/\n  end\n\n\n  def test_command_injection_2\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :line => 48,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n\n  def test_command_injection_3\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :line => 68,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n\n  def test_command_injection_4\n    assert_warning :type => :warning,\n      :warning_type => \"Command Injection\",\n      :line => 102,\n      :message => /^Possible\\ command\\ injection/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n\n  def test_mass_assignment_5\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 47,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 0,\n      :file => /posts_controller\\.rb/\n  end\n\n\n  def test_mass_assignment_6\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 47,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n\n  def test_mass_assignment_7\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 67,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 0,\n      :file => /posts_controller\\.rb/\n  end\n\n\n  def test_mass_assignment_8\n    assert_warning :type => :warning,\n      :warning_type => \"Mass Assignment\",\n      :line => 71,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n  def test_mass_assignment_with_string\n    assert_no_warning :type => :warning,\n      :warning_code => 17,\n      :fingerprint => \"2893b1a48ec56548a5a48d38324c5d78f7845066713ad79bb0ec17032672c862\",\n      :warning_type => \"Mass Assignment\",\n      :line => 97,\n      :message => /^Unprotected\\ mass\\ assignment/,\n      :confidence => 2,\n      :relative_path => \"app/controllers/other_controller.rb\",\n      :user_input => nil\n  end\n\n  def test_redirect_to_model_instance\n    assert_no_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 68,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 2,\n      :file => /posts_controller\\.rb/\n  end\n\n\n  def test_another_redirect_to_model_instance\n    assert_no_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 72,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 2,\n      :file => /users_controller\\.rb/\n  end\n\n\n  def test_redirect_11\n    assert_warning :type => :warning,\n      :warning_type => \"Redirect\",\n      :line => 95,\n      :message => /^Possible\\ unprotected\\ redirect/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n\n  def test_rails_cve_2012_2660\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-2660/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_rails_cve_2012_2695\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-2695/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_sql_injection_12\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :line => 126,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /users_controller\\.rb/\n  end\n\n\n  def test_cross_site_scripting_13\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      #noline,\n      :message => /^Rails\\ 2\\.3\\.x\\ using\\ the\\ rails_xss\\ plugin\\ h/,\n      :confidence => 1,\n      :file => /Gemfile/\n  end\n\n\n  def test_cross_site_scripting_14\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 13,\n      :message => /^Unescaped\\ model\\ attribute/,\n      :confidence => 0,\n      :file => /show\\.html\\.erb/\n  end\n\n  def test_cross_site_scripting_single_quotes_CVE_2012_3464\n    assert_no_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^All\\ Rails\\ 2\\.x\\ versions\\ do\\ not\\ escape\\ sin/,\n      :confidence => 1,\n      :file => /environment\\.rb/\n  end\n\n  def test_dynamic_render_path_15\n    assert_no_warning :type => :template,\n      :warning_type => \"Dynamic Render Path\",\n      :line => 8,\n      :message => /^Render\\ path\\ is\\ dynamic/,\n      :confidence => 0,\n      :file => /results\\.html\\.erb/\n  end\n\n\n  def test_sql_injection_16\n    assert_no_warning :type => :template,\n      :warning_type => \"SQL Injection\",\n      :line => 4,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /results\\.html\\.erb/\n  end\n\n\n  def test_sql_injection_17\n    assert_no_warning :type => :template,\n      :warning_type => \"SQL Injection\",\n      :line => 7,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :file => /results\\.html\\.erb/\n  end\n\n  def test_sql_injection_select_value\n    assert_warning :type => :warning,\n      :warning_code => 0,\n      :fingerprint => \"fbf3545b52e589a9f9c25449b3505fadbdec63010664504e7366fbcc5fe6b43a\",\n      :warning_type => \"SQL Injection\",\n      :line => 134,\n      :message => /^Possible\\ SQL\\ injection/,\n      :confidence => 0,\n      :relative_path => \"app/controllers/users_controller.rb\",\n      :user_input => s(:call, s(:params), :[], s(:lit, :name))\n  end\n\n  def test_cross_site_request_forgery_18\n    assert_warning :type => :controller,\n      :warning_type => \"Cross-Site Request Forgery\",\n      #noline,\n      :message => /^`protect_from_forgery`\\ should\\ be\\ called\\ /,\n      :confidence => 0,\n      :file => /application_controller\\.rb/\n  end\n\n  def test_cross_site_scripting\n    assert_warning :type => :template,\n      :warning_code => 58,\n      :fingerprint => \"3ec8749301aa7cdb1d3ec5610120492138060f05d65af0aa53dbb1a3b7c493ac\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Rails\\ 2\\.3\\.14\\ has\\ a\\ vulnerability\\ in\\ `sani/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/test_sanitize.html.erb\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_sanitize_dupe\n    assert_no_warning :type => :template,\n      :warning_code => 58,\n      :fingerprint => \"9d90d446941026c42502e1213ef6d9122a2ad587266cdb002d9f30bb3c77523d\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Rails\\ 2\\.3\\.14\\ has\\ a\\ vulnerability\\ in\\ `sani/,\n      :confidence => 0,\n      :relative_path => \"app/views/users/test_sanitize.html.erb\",\n      :user_input => nil\n  end\n\n  def test_attribute_restriction_1\n    assert_warning :type => :model,\n      :warning_code => 19,\n      :fingerprint => \"180c16a1a0b36556203c87328d46531665774859d07c95d6e31e94322b5395d3\",\n      :warning_type => \"Attribute Restriction\",\n      :line => 1,\n      :message => /^Mass\\ assignment\\ is\\ not\\ restricted\\ using\\ /,\n      :confidence => 0,\n      :relative_path => \"app/models/post.rb\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_attribute_restriction_2\n    assert_warning :type => :model,\n      :warning_code => 19,\n      :fingerprint => \"b325ae8a4570599cde146875ae86427506befae36a3b4a97ce2223930846fec5\",\n      :warning_type => \"Attribute Restriction\",\n      :line => 1,\n      :message => /^Mass\\ assignment\\ is\\ not\\ restricted\\ using\\ /,\n      :confidence => 0,\n      :relative_path => \"app/models/user.rb\",\n      :code => nil,\n      :user_input => nil\n  end\n\n  def test_format_validation_20\n    assert_warning :type => :model,\n      :warning_type => \"Format Validation\",\n      :line => 5,\n      :message => /^Insufficient\\ validation\\ for\\ `user_name`\\ /,\n      :confidence => 0,\n      :file => /user\\.rb/\n  end\n\n\n  def test_format_validation_21\n    assert_warning :type => :model,\n      :warning_type => \"Format Validation\",\n      :line => 7,\n      :message => /^Insufficient\\ validation\\ for\\ `display_nam/,\n      :confidence => 0,\n      :file => /user\\.rb/\n  end\n\n  def test_strip_tags_CVE_2012_3465\n    assert_warning :type => :warning,\n      :warning_type => \"Cross-Site Scripting\",\n      :message => /^All\\ Rails\\ 2\\.x\\ versions\\ have\\ a\\ vulnerabil/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_sql_injection_CVE_2012_5664\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2012-5664/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_to_json\n    assert_warning :type => :template,\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 1,\n      :message => /^Unescaped parameter value in JSON hash/,\n      :confidence => 0,\n      :file => /users\\/to_json\\.html\\.erb/\n  end\n\n  def test_session_secret_token\n    assert_warning :type => :warning,\n      :warning_type => \"Session Setting\",\n      :line => 9,\n      :message => /^Session\\ secret\\ should\\ not\\ be\\ included\\ in/,\n      :confidence => 0,\n      :file => /session_store\\.rb/\n  end\n\n  def test_absolute_paths\n    assert report[:generic_warnings].all? { |w| (Pathname.new w.file).absolute? }\n  end\n\n  def test_cross_site_scripting_CVE_2012_1099\n    assert_warning :type => :template,\n      :warning_code => 22,\n      :fingerprint => \"4ad12198bfbc84a389e439d3c4cc9c2551e3c2aa7b36182336463ca87e45ec5b\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 19,\n      :message => /^Upgrade\\ to\\ Rails\\ 3\\ or\\ use\\ options_for_se/,\n      :confidence => 1,\n      :relative_path => \"app/views/users/index.html.erb\",\n      :user_input => nil\n  end\n\n  def test_cross_site_scripting_html_entities_in_json\n    assert_warning :type => :warning,\n      :warning_code => 114,\n      :fingerprint => \"c96eb07567e2a7b0ded7cda123645c4e736d3a1b124bb7c0ffaf5070f53dfcf3\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 21,\n      :message => /^HTML\\ entities\\ in\\ JSON\\ are\\ not\\ escaped\\ by/,\n      :confidence => 1,\n      :relative_path => \"config/environments/production.rb\",\n      :code => s(:attrasgn, s(:const, :ActiveSupport), :escape_html_entities_in_json=, s(:false)),\n      :user_input => nil\n  end\n\n  def test_sql_injection_CVE_2013_0155\n    assert_warning :type => :warning,\n      :warning_type => \"SQL Injection\",\n      :message => /CVE-2013-0155/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_parsing_disable_CVE_2013_0156\n    assert_no_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Rails\\ 2\\.3\\.14\\ has\\ a\\ remote\\ code\\ execution/,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_remote_code_execution_CVE_2013_0156\n    assert_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Parsing\\ YAML\\ request\\ parameters\\ enables\\ /,\n      :confidence => 0\n  end\n\n  def test_denial_of_service_CVE_2013_0269\n    assert_warning :type => :warning,\n      :warning_type => \"Denial of Service\",\n      :message => /^json\\ gem\\ 1\\.1\\.0\\ has\\ a\\ symbol\\ crea/,\n      :confidence => 2,\n      :file => /Gemfile/\n  end\n\n  def test_json_parsing_workaround_CVE_2013_0333\n    assert_no_warning :type => :warning,\n      :warning_type => \"Remote Code Execution\",\n      :message => /^Rails\\ 2\\.3\\.14\\ has\\ a\\ serious\\ JSON\\ parsing\\ /,\n      :confidence => 0,\n      :file => /Gemfile/\n  end\n\n  def test_denial_of_service_CVE_2013_1854\n    assert_warning :type => :warning,\n      :warning_type => \"Denial of Service\",\n      :message => /^Rails\\ 2\\.3\\.14\\ has\\ a\\ denial\\ of\\ service\\ vul/,\n      :confidence => 1,\n      :file => /Gemfile/\n  end\n\n  def test_sql_injection_CVE_2013_6417\n    assert_warning :type => :warning,\n      :warning_code => 69,\n      :fingerprint => \"e1b66f4311771d714a13be519693c540d7e917511a758827d9b2a0a7f958e40f\",\n      :warning_type => \"SQL Injection\",\n      :line => 3,\n      :file => /Gemfile/,\n      :message => /^Rails\\ 2\\.3\\.14\\ contains\\ a\\ SQL\\ injection\\ vu/,\n      :confidence => 0,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_number_to_currency_CVE_2014_0081\n    assert_warning :type => :warning,\n      :warning_code => 73,\n      :fingerprint => \"f6981b9c24727ef45040450a1f4b158ae3bc31b4b0343efe853fe12c64881695\",\n      :warning_type => \"Cross-Site Scripting\",\n      :line => 3,\n      :message => /^Rails\\ 2\\.3\\.14\\ has\\ a\\ vulnerability\\ in\\ numb/,\n      :confidence => 1,\n      :relative_path => \"Gemfile\"\n  end\n\n  def test_remote_code_execution_CVE_2014_0130\n    assert_warning :type => :warning,\n      :warning_code => 77,\n      :fingerprint => \"93393e44a0232d348e4db62276b18321b4cbc9051b702d43ba2fd3287175283c\",\n      :warning_type => \"Remote Code Execution\",\n      :line => nil,\n      :message => /^Rails\\ 2\\.3\\.14\\ with\\ globbing\\ routes\\ is\\ vul/,\n      :confidence => 0,\n      :relative_path => \"config/routes.rb\",\n      :user_input => nil\n  end\n\n  def test_xml_dos_CVE_2015_3227\n    assert_warning :type => :warning,\n      :warning_code => 88,\n      :fingerprint => \"6ad4464dbb2a999591c7be8346dc137c3372b280f4a8b0c024fef91dfebeeb83\",\n      :warning_type => \"Denial of Service\",\n      :line => 3,\n      :message => /^Rails\\ 2\\.3\\.14\\ is\\ vulnerable\\ to\\ denial\\ of\\ /,\n      :confidence => 1,\n      :relative_path => \"Gemfile\",\n      :user_input => nil\n  end\n\n  def test_unmaintained_dependency_rails\n    assert_warning check_name: \"EOLRails\",\n      type: :warning,\n      warning_code: 120,\n      fingerprint: \"e9d00416c23870f08d30cfda6ad07e2138e0ce51ab6b684814eb69e789cfa631\",\n      warning_type: \"Unmaintained Dependency\",\n      line: 3,\n      message: /^Support\\ for\\ Rails\\ 2\\.3\\.14\\ ended\\ on\\ 2013\\-0/,\n      confidence: 0,\n      relative_path: \"Gemfile\"\n  end\nend\n"
  },
  {
    "path": "test/tests/render_path.rb",
    "content": "require_relative '../test'\n\nclass RenderPathTests < Minitest::Test\n  def setup\n    @r = Brakeman::RenderPath.new\n    @at = BrakemanTester.new_tracker.app_tree\n  end\n\n  def fp path\n    @at.file_path path\n  end\n\n  def test_include_controller\n    @r.add_controller_render :TestController, :test, 1, fp('app/controllers/test_controller.rb')\n\n    assert @r.include_controller? :TestController\n  end\n\n  def test_rendered_from_controller\n    @r.add_controller_render :TestController, :test, 1, fp('app/controllers/test_controller.rb')\n\n    assert @r.rendered_from_controller?\n  end\n\n  def test_include_template\n    @r.add_template_render 'some/template', 1, fp('app/views/some/template.html.erb')\n\n    assert @r.include_template? :'some/template'\n  end\n\n  def test_include_any_method\n    @r.add_controller_render :TestController, :test, 10, fp('app/controllers/test_controller.rb')\n    @r.add_controller_render :TestController, :test2, 20, fp('app/controllers/test_controller.rb')\n    @r.add_controller_render :TestController, :test3, 30, fp('app/controllers/test_controller.rb')\n\n    assert @r.include_any_method? ['test']\n  end\n\n  def test_each\n    @r.add_controller_render :TestController, :test, 1, fp('app/controllers/test_controller.rb')\n    @r.add_template_render 'some/template', 2, fp('app/views/some/template.html.erb')\n\n    @r.each do |loc|\n      case loc[:type]\n      when :template\n        assert_equal :'some/template', loc[:name]\n      when :controller\n        assert_equal :TestController, loc[:class]\n        assert_equal :test, loc[:method]\n      end\n    end\n  end\n\n  def test_dup\n    @r.add_controller_render :TestController, :test, 1, fp('app/controllers/test_controller.rb')\n\n    s = @r.dup\n    s.add_template_render 'some/template', 2, fp('app/views/some/template.html.erb')\n\n    assert_equal 1, @r.length\n    assert_equal 2, s.length\n  end\n\n  def test_with_relative_paths\n    @r.add_controller_render :TestController, :test, 1, fp('app/controllers/test_controller.rb')\n    @r.add_template_render 'some/template', 2, fp('app/views/some/template.html.erb')\n\n    @r.with_relative_paths.each do |loc|\n      assert_relative loc[:file]\n\n      if loc[:rendered]\n        assert_relative loc[:rendered][:file]\n      end\n    end\n  end\n\n  private\n\n  def assert_relative path\n    assert Pathname.new(path).relative?, \"#{path} is not relative\"\n  end\nend\n"
  },
  {
    "path": "test/tests/report_generation.rb",
    "content": "require 'json'\nrequire_relative '../test'\n\nclass TestReportGeneration < Minitest::Test\n  def setup\n    @@tracker ||= Brakeman.run(:app_path => \"#{TEST_PATH}/apps/rails4\", :quiet => true, :report_routes => true, :output_color => false)\n    @@report ||= @@tracker.report\n  end\n\n  def test_html_sanity\n    report = @@report.to_html\n\n    assert report.is_a? String\n    assert report.match(/\\A<!DOCTYPE HTML SYSTEM>.*<\\/html>\\z/m)\n    report.scan(/<a[^>]+>/).each do |a|\n      assert a.include?(\"noreferrer\"), \"#{a} does not include 'noreferrer'\"\n    end\n  end\n\n  def test_table_sanity\n    require 'highline/io_console_compatible' # For StringIO compatibility\n    @@report.to_table\n  end\n\n  def test_json_sanity\n    report = @@report.to_json\n    expected_keys = [\"scan_info\", \"warnings\", \"errors\"]\n\n    assert report.is_a? String\n\n    report_hash = JSON.parse report\n\n    assert (expected_keys - report_hash.keys).empty?, \"Expected #{expected_keys - report_hash.keys} to be empty\"\n  end\n\n  def test_codeclimate_sanity\n    report = @@report.to_codeclimate\n\n    assert report.is_a? String\n    refute report.include? Dir.pwd # Ensure output does not include absolute paths\n  end\n\n  def test_csv_sanity\n    headers = [\"Confidence\", \"Warning Type\", \"CWE\", \"File\", \"Line\", \"Message\", \"Code\", \"User Input\", \"Check Name\", \"Warning Code\", \"Fingerprint\", \"Link\"]\n    report = @@report.to_csv\n    parsed = CSV.parse report, headers: true\n    row = parsed.first\n\n    assert_equal row.headers, headers\n    assert_equal parsed.length, @@report.tracker.filtered_warnings.length\n  end\n\n  def test_csv_report_no_warnings\n    assert_nothing_raised do\n      Brakeman.run(:app_path => \"#{TEST_PATH}/apps/rails4_non_standard_structure\", :quiet => true, :report_routes => true).report.to_csv\n    end\n  end\n\n  def test_obsolete_reporting\n    report = @@report.to_s\n\n    assert report.include? \"Obsolete Ignore Entries\"\n    assert report.include? \"abcdef01234567890ba28050e7faf1d54f218dfa9435c3f65f47cb378c18cf98\"\n  end\n\n  def test_tabs_sanity\n    report = @@report.to_tabs\n\n    assert report.is_a? String\n  end\n\n  def test_text_sanity\n    report = @@report.to_s\n\n    assert report.is_a? String\n  end\n\n  def test_text_debug_sanity\n    @@tracker.options[:debug] = true\n    report = @@report.to_s\n\n    assert report.is_a? String\n  ensure\n    @@tracker.options[:debug] = false\n  end\n\n  def test_text_format_all\n    require 'brakeman/options'\n\n    options, _ = Brakeman::Options.parse([\"--text-fields\", \"all\"])\n    tracker = Brakeman.run(:app_path => \"#{TEST_PATH}/apps/rails5\", :quiet => true, :report_routes => true, :text_fields => options[:text_fields], :output_color => false)\n    report = tracker.report.to_s\n\n    assert_includes report, \"Confidence:\"\n    assert_includes report, \"Category:\"\n    assert_includes report, \"Category ID:\"\n    assert_includes report, \"Check:\"\n    assert_includes report, \"Code:\"\n    assert_includes report, \"CWE:\"\n    assert_includes report, \"File:\"\n    assert_includes report, \"Fingerprint:\"\n    assert_includes report, \"Line:\"\n    assert_includes report, \"Link:\"\n    assert_includes report, \"Message:\"\n    assert_includes report, \"Render Path:\"\n  end\n\n  def test_text_format\n    @@tracker.options[:text_fields] =\n      [:confidence, :category, :category_id, :code, :fingerprint, :line]\n\n    report = @@report.to_s\n\n    assert_includes report, \"Confidence:\"\n    assert_includes report, \"Category:\"\n    assert_includes report, \"Category ID:\"\n    assert_includes report, \"Code:\"\n    assert_includes report, \"Fingerprint:\"\n    assert_includes report, \"Line:\"\n    refute_includes report, \"Check:\"\n    refute_includes report, \"File:\"\n    refute_includes report, \"Link:\"\n    refute_includes report, \"Message:\"\n    refute_match /^Render Path:/, report\n  ensure\n    @@tracker.options.delete(:text_fields)\n  end\n\n  def test_markdown_sanity\n    report = @@report.to_markdown\n\n    assert report.is_a? String\n    assert report.include? \"High\"\n    assert report.include? \"Medium\"\n    assert report.include? \"Weak\"\n  end\n\n  def test_markdown_debug_sanity\n    @@tracker.options[:debug] = true\n    report = @@report.to_markdown\n\n    assert report.is_a?(String), \"Report wasn't a String, it was a #{report.class}\"\n  ensure\n    @@tracker.options[:debug] = false\n  end\n\n  def test_github_sanity\n    report = @@report.to_github\n\n    assert report.is_a? String\n    assert report.include? \"::warning\"\n  end\n\n  def test_bad_format_type\n    assert_raises RuntimeError do\n      @@report.format(:to_something_else)\n    end\n  end\n\n  def test_controller_output\n    text_report = @@report.to_s\n\n    assert text_report.include? \"Controller Overview\"\n\n    html_report = @@report.to_html\n\n    assert html_report.include? \"<h2>Controllers</h2>\"\n  end\n\n  def test_plain_debug_sanity\n    @@tracker.options[:debug] = true\n    report = @@report.to_plain\n\n    assert report.is_a? String\n    assert report.match(/Overview.*Warning Types.*Controller Overview.*Template Output.*Warnings/m)\n  ensure\n    @@tracker.options[:debug] = false\n  end\n\n  def test_github_markdown_sanity\n    @@tracker.options[:github_url] = \"https://github.com/presidentbeef/brakeman/blob/master/\"\n\n    report = @@report.to_markdown\n\n    assert report.is_a? String\n    assert report.include? @@tracker.options[:github_url]\n  ensure\n    @@tracker.options[:github_url] = nil\n  end\n\n  def test_junit_sanity\n    report = @@report.to_junit\n\n    assert report.is_a? String\n    assert report.match(/\\A.*<\\/testsuites>\\z/m)\n  end\nend\n"
  },
  {
    "path": "test/tests/rescanner.rb",
    "content": "require_relative '../test'\nrequire 'tmpdir'\nrequire 'brakeman/rescanner'\n\nclass RescannerTests < Minitest::Test\n  include BrakemanTester::RescanTestHelper\n\n  def test_no_change_no_warnings\n    before_rescan_of []\n\n    assert_fixed 0\n    assert_new 0\n    assert_equal false, rescan.warnings_changed?\n  end\n\n  def test_no_change\n    before_rescan_of []\n\n    assert rescan.any_warnings?\n    assert_fixed 0\n    assert_new 0\n  end\n\n  def test_irrelavent_new_file\n    before_rescan_of \"IRRELEVANT\" do\n      write_file \"IRRELEVANT\", \"Nothing special here\"\n    end\n\n    assert_new 0\n    assert_fixed 0\n  end\n\n  def test_irrelevant_deleted_file\n    before_rescan_of \"README.rdoc\" do\n      remove \"README.rdoc\"\n    end\n\n    assert_new 0\n    assert_fixed 0\n  end\n\n  def test_delete_template\n    template = \"app/views/users/show.html.erb\"\n\n    before_rescan_of template do\n      remove template\n    end\n\n    assert_new 0\n    assert_fixed 1\n  end\n\n  def test_controller_remove_method\n    controller = \"app/controllers/removal_controller.rb\"\n\n    before_rescan_of controller do\n      remove_method controller, :remove_this\n    end\n\n    assert_new 0\n    assert_fixed 1\n  end\n\n  def test_controller_remove_method_for_line_numbers_only\n    controller = \"app/controllers/removal_controller.rb\"\n\n    before_rescan_of controller do\n      remove_method controller, :change_lines\n    end\n\n    assert_new 0\n    assert_fixed 0\n  end\n\n  def test_delete_controller\n    controller = \"app/controllers/removal_controller.rb\"\n\n    before_rescan_of controller do\n      remove controller\n    end\n\n    assert_new 0\n    assert_fixed 4\n  end\n\n  def test_delete_controller_dependency\n    controller = \"app/controllers/exec_controller/command_dependency.rb\"\n\n    before_rescan_of controller do\n      remove controller\n    end\n\n    assert_new 0\n    assert_fixed 1\n  end\n\n  def test_controller_escape_params\n    controller = \"app/controllers/users_controller.rb\"\n\n    before_rescan_of controller do\n      replace controller, \"@user_data = raw params[:user_data]\", \"@user_data = params[:user_data]\"\n    end\n\n    assert_new 0\n    assert_fixed 1\n  end\n\n  def test_template_add_line\n    template = \"app/views/users/show.html.erb\"\n\n    before_rescan_of template do\n      append template, \"<%= raw params[:bad] %>\"\n    end\n\n    assert_new 1\n    assert_fixed 0\n  end\n\n  def test_partial_template_add_line\n    template = \"app/views/users/_form.html.erb\"\n\n    before_rescan_of template do\n      append template, \"<%= raw @user.thing %>\"\n    end\n\n    assert_new 1\n    assert_fixed 0\n  end\n\n  def test_delete_model\n    model = \"app/models/user.rb\"\n\n    before_rescan_of model do\n      # So actually there is another definition of User in\n      # app/models/user/command_dependency.rb\n      # so this does not completely delete the model\n      remove model\n    end\n\n    assert_new 0\n    assert_fixed 3\n  end\n\n  def test_delete_model_and_dependency\n    model = \"app/models/user.rb\"\n    dependency = \"app/models/user/command_dependency.rb\"\n\n    before_rescan_of [model, dependency] do\n      remove model\n      remove dependency\n    end\n\n    assert_new 6 #User is no longer a model, causing MORE warnings\n    assert_fixed 8\n  end\n\n  def test_add_method_to_model\n    model = \"app/models/user.rb\"\n\n    before_rescan_of model do\n      add_method model, <<-'RUBY'\n      def bad_sql input\n        User.find(:all, :conditions => \"x > #{input}\")\n      end\n      RUBY\n    end\n\n    assert_new 1\n    assert_fixed 0\n  end\n\n  def test_change_config\n    config = \"config/environments/production.rb\"\n\n    before_rescan_of config do\n      replace config, \"config.active_record.whitelist_attributes = true\",\n        \"config.active_record.whitelist_attributes = false\"\n    end\n\n    assert_new 3\n    assert_fixed 0\n  end\n\n  def test_remove_route\n    routes = \"config/routes.rb\"\n\n    before_rescan_of routes, \"rails3.2\", :assume_all_routes => false do\n      replace routes, \"match 'implicit' => 'removal#implicit_render'\", \"\"\n    end\n\n    assert_new 0\n    assert_fixed 1\n  end\n\n  def test_remove_initializer\n    #Should probably remove initializer that actually affects something\n    initializer = \"config/initializers/wrap_parameters.rb\"\n\n    before_rescan_of initializer do\n      remove initializer\n    end\n\n    assert_new 0\n    assert_fixed 0\n  end\n\n  def test_remove_mixin\n    lib = 'lib/user_controller_mixin.rb'\n\n    before_rescan_of lib do\n      remove lib\n    end\n\n    assert_new 0\n    assert_fixed 1\n  end\n\n  def test_remove_route_from_mixin\n    lib = 'lib/user_controller_mixin.rb'\n\n    before_rescan_of lib do\n      remove_method lib, :mixed_in\n    end\n\n    assert_new 0\n    assert_fixed 1\n  end\n\n  def test_gemfile_rails_version_change\n    gemfile = \"Gemfile.lock\"\n\n    before_rescan_of gemfile do\n      replace gemfile, \"rails (3.2.9.rc2)\", \"rails (3.2.6)\"\n    end\n\n    #@original is actually modified\n    assert @original.config.rails_version, \"3.2.6\"\n    assert_new 1\n    assert_fixed 0\n  end\n\n  def test_gemfile_rails_version_fix_CVE_2014_0082\n    gemfile = \"Gemfile.lock\"\n\n    before_rescan_of gemfile do\n      replace gemfile, \"rails (3.2.9.rc2)\", \"rails (3.2.17)\"\n    end\n\n    #@original is actually modified\n    assert @original.config.rails_version, \"3.2.17\"\n    assert_new 0\n    if RUBY_PLATFORM == \"java\"\n      assert_fixed 10\n    else\n      assert_fixed 9\n    end\n  end\n\n  def test_gitignore_session_secret_subdir\n    gitignore = \"config/initializers/.gitignore\"\n\n    before_rescan_of gitignore do\n      append gitignore, \"secret_token.rb\"\n    end\n\n    assert_fixed 1\n    assert_new 0\n  end\nend\n"
  },
  {
    "path": "test/tests/routes_error.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/rescanner'\n\nclass BrakemanTests < Minitest::Test\n  include BrakemanTester::RescanTestHelper\n\n  def test_parse_error_in_routes_rb\n    before_rescan_of \"config/routes.rb\", \"rails5.2\" do\n      write_file \"config/routes.rb\", \"x = \"\n    end\n\n    @rescanner.tracker.errors.select { |e| e[:exception].is_a? Racc::ParseError and e[:error].include? \"routes.rb\" }.each do |e|\n      # Check that file path is being reported correctly by parser\n      assert_includes e[:error], \"config/routes.rb\"\n    end\n  end\nend\n"
  },
  {
    "path": "test/tests/sarif_output.rb",
    "content": "require_relative '../test'\nrequire 'json'\n\nclass SARIFOutputTests < Minitest::Test\n\n  def tracker_3_2\n    @@tracker_3_2 ||= Brakeman.run(\"#{TEST_PATH}/apps/rails3.2\") # has no brakeman.ignore\n  end\n\n  def setup\n    @@sarif ||= JSON.parse(tracker_3_2.report.to_sarif)\n    @@sarif_with_ignore ||= JSON.parse(Brakeman.run(File.join(TEST_PATH, 'apps', 'rails4')).report.to_sarif) # has ignored warnings\n  end\n\n  def test_render_message\n    report = Brakeman::Report::SARIF.new tracker_3_2\n    assert_nil report.render_message(nil)\n    assert_equal 'Very serious sentence.', report.render_message('Very serious sentence')\n    assert_equal 'Nothing to see here.', report.render_message('Nothing to see here.')\n  end\n\n  def test_log_shape\n    assert_equal '2.1.0', @@sarif['version']\n    assert_equal 'https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json', @@sarif['$schema']\n  end\n\n  def test_runs_shape\n    # Log includes runs, an array, of length 1\n    assert runs = @@sarif['runs']\n    assert_equal 1, runs.length\n\n    # The single run contains some data\n    assert_equal ['tool', 'results', 'originalUriBaseIds'], runs[0].keys\n\n    # The single run contains a single tool\n    assert_equal 1, runs[0]['tool'].length\n  end\n\n  def test_driver_shape\n    # Tool includes a driver\n    assert driver = @@sarif.dig('runs', 0, 'tool', 'driver')\n\n    # Driver has a name, informationUri, semanticVersion, and rules\n    assert_equal driver.keys, ['name', 'informationUri', 'semanticVersion', 'rules']\n\n    # Driver name is 'Brakeman'\n    assert_equal driver['name'], 'Brakeman'\n\n    # Driver informationUri is 'https://brakemanscanner.org'\n    assert_equal driver['informationUri'], 'https://brakemanscanner.org'\n\n    # Driver semanticVersion is Brakeman::Version\n    assert_equal driver['semanticVersion'], Brakeman::Version\n  end\n\n  def test_rules_shape\n    assert rules = @@sarif.dig('runs', 0, 'tool', 'driver', 'rules')\n    rules.each do |rule|\n      # Each rule id starts with BRAKE\n      assert rule['id'].start_with? 'BRAKE'\n\n      # Each rule has a name, ...\n      assert rule['name']\n\n      # ... fullDescription, ...\n      assert rule['fullDescription']['text']\n\n      # ... helpUri, ...\n      assert rule['helpUri']\n\n      # ... help, ...\n      assert rule['help']['text']\n      assert rule['help']['markdown']\n\n      # ... and a property bag containing tags\n      assert rule['properties']['tags']\n    end\n\n    # Each rule id is unique\n    assert_equal rules.length, rules.map{ |rule| rule['id'] }.uniq.length\n  end\n\n  def test_results_shape\n    assert results = @@sarif.dig('runs', 0, 'results')\n    results.each do |result|\n      # Each result has message, ...\n      assert result['message']['text']\n\n      # ... ruleId, ...\n      assert result['ruleId']\n\n      # ... ruleIndex, ...\n      assert result['ruleIndex']\n\n      # ... level, ...\n      assert ['error', 'warning', 'note'].include? result['level']\n\n      # (and ruleIndex maps correctly onto the corresponding rule), ...\n      assert_equal result['ruleId'], @@sarif.dig('runs', 0, 'tool', 'driver', 'rules', result['ruleIndex'], 'id')\n\n      # ... locations, ...\n      assert locations = result['locations']\n      locations.each do |location|\n        # Each location has a physical location, ...\n        assert location['physicalLocation']\n\n        # Each physical location has an artifact location, ...\n        assert location['physicalLocation']['artifactLocation']\n\n        # Each artifact location has a relative URI\n        assert location['physicalLocation']['artifactLocation']['uri']\n        refute location['physicalLocation']['artifactLocation']['uri'].start_with? 'file://'\n\n\n        # and a uriBaseId\n        assert_equal '%SRCROOT%', location['physicalLocation']['artifactLocation']['uriBaseId']\n\n        # Each location has a region\n        assert location['physicalLocation']['region']['startLine']\n      end\n    end\n  end\n\n  def test_with_ignore_has_one_suppressed_finding\n    assert_equal(\n      1,\n      @@sarif_with_ignore.dig('runs', 0, 'results').\n        select { |f| f['suppressions'] }.count\n    )\n  end\n\n  def test_with_ignore_results_suppression_shape\n    @@sarif_with_ignore.dig('runs', 0, 'results').each do |finding|\n      suppressions = finding['suppressions']\n      next unless suppressions\n\n      # Each finding with suppressions has exactly one...\n      assert_equal 1, suppressions.count\n      assert suppression = suppressions[0]\n\n      # ...external suppression...\n      assert_equal 'external', suppression['kind']\n\n      # ...with a valid physical location...\n      assert suppression['location']['physicalLocation']['artifactLocation']['uri']\n      assert_equal '%SRCROOT%', suppression['location']['physicalLocation']['artifactLocation']['uriBaseId']\n\n      # ...and a justification (will be nil if no notes is set).\n      assert suppression['justification']\n    end\n  end\n\n  def test_uri_base_ids_with_absolute_app_path\n    assert base_uris = @@sarif.dig('runs', 0, 'originalUriBaseIds')\n    assert_equal ['%SRCROOT%'], base_uris.keys\n\n    # Only %SRCROOT% with no URI\n    assert base_uris['%SRCROOT%']\n    assert_equal ['description'], base_uris['%SRCROOT%'].keys\n  end\n\n  def test_uri_base_ids_with_relative_app_path\n    original_app_path = tracker_3_2.options[:app_path] # Horrible hack\n    tracker_3_2.options[:app_path] = 'something/relative'\n    sarif = JSON.parse(tracker_3_2.report.to_sarif)\n\n    assert base_uris = sarif.dig('runs', 0, 'originalUriBaseIds')\n    assert_equal ['PROJECTROOT', '%SRCROOT%'], base_uris.keys\n\n    # %SRCROOT% should have the relative path and point to PROJECTROOT\n    # as its base\n    assert base_uris['%SRCROOT%']\n    assert_equal ['uri', 'uriBaseId', 'description'], base_uris['%SRCROOT%'].keys\n    assert_equal 'something/relative/', base_uris['%SRCROOT%']['uri']\n    assert_equal 'PROJECTROOT', base_uris['%SRCROOT%']['uriBaseId']\n\n    # PROJECTROOT should not have a URI\n    assert base_uris['PROJECTROOT']\n    assert_equal ['description'], base_uris['PROJECTROOT'].keys\n  ensure\n    tracker_3_2.options[:app_path] = original_app_path\n  end\n\n  def test_uri_base_ids_with_absolute_app_path_and_absolute_path_option\n    tracker_3_2.options[:absolute_paths] = true\n    sarif = JSON.parse(tracker_3_2.report.to_sarif)\n\n    assert base_uris = sarif.dig('runs', 0, 'originalUriBaseIds')\n    assert_equal ['%SRCROOT%'], base_uris.keys\n\n    # Only %SRCROOT% with absolute URI\n    assert base_uris['%SRCROOT%']\n    assert_equal ['uri','description'], base_uris['%SRCROOT%'].keys\n    assert base_uris['%SRCROOT%']['uri'].start_with? 'file://'\n    assert base_uris['%SRCROOT%']['uri'].end_with? '/'\n  ensure\n    tracker_3_2.options[:absolute_paths] = false\n  end\n\n  def test_uri_base_ids_with_relative_app_path_and_absolute_path_option\n    original_app_path = tracker_3_2.options[:app_path] # Horrible hack\n    tracker_3_2.options[:app_path] = 'something/relative'\n    tracker_3_2.options[:absolute_paths] = true\n    sarif = JSON.parse(tracker_3_2.report.to_sarif)\n\n    assert base_uris = sarif.dig('runs', 0, 'originalUriBaseIds')\n    assert_equal ['PROJECTROOT', '%SRCROOT%'], base_uris.keys\n\n    # %SRCROOT% should have the relative path and point to PROJECTROOT\n    # as its base\n    assert base_uris['%SRCROOT%']\n    assert_equal ['uri', 'uriBaseId', 'description'], base_uris['%SRCROOT%'].keys\n    assert_equal 'something/relative/', base_uris['%SRCROOT%']['uri']\n    assert_equal 'PROJECTROOT', base_uris['%SRCROOT%']['uriBaseId']\n\n    # PROJECTROOT should have an absolute URI\n    assert base_uris['PROJECTROOT']\n    assert_equal ['uri', 'description'], base_uris['PROJECTROOT'].keys\n    assert base_uris['PROJECTROOT']['uri'].start_with? 'file://'\n    assert base_uris['PROJECTROOT']['uri'].end_with? '/'\n  ensure\n    tracker_3_2.options[:app_path] = original_app_path\n    tracker_3_2.options[:absolute_paths] = false\n  end\n\n  def test_uri_base_ids_with_default_app_path_and_absolute_path_option\n    original_app_path = tracker_3_2.options[:app_path] # Horrible hack\n    tracker_3_2.options[:app_path] = '.'\n    tracker_3_2.options[:absolute_paths] = true\n    sarif = JSON.parse(tracker_3_2.report.to_sarif)\n\n    assert base_uris = sarif.dig('runs', 0, 'originalUriBaseIds')\n    assert_equal ['%SRCROOT%'], base_uris.keys\n\n    # Only %SRCROOT% with absolute URI\n    assert base_uris['%SRCROOT%']\n    assert_equal ['uri', 'description'], base_uris['%SRCROOT%'].keys\n    assert base_uris['%SRCROOT%']['uri'].start_with? 'file://'\n    assert base_uris['%SRCROOT%']['uri'].end_with? '/'\n  ensure\n    tracker_3_2.options[:app_path] = original_app_path\n    tracker_3_2.options[:absolute_paths] = false\n  end\nend\n"
  },
  {
    "path": "test/tests/sexp.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/processors/base_processor'\n\nclass SexpTests < Minitest::Test\n  def setup\n    @ruby_parser = ::RubyParser\n  end\n\n  def parse string\n    Brakeman::BaseProcessor.new(nil).process @ruby_parser.new.parse string\n  end\n\n  def test_method_call_with_no_args\n    exp = parse \"x.y\"\n\n    assert_equal s(:call, nil, :x), exp.target\n    assert_equal :y, exp.method\n    assert_equal s(), exp.args\n    assert_equal s(:arglist), exp.arglist\n    assert_nil exp.first_arg\n    assert_nil exp.second_arg\n    assert_nil exp.last_arg\n  end\n\n  def test_method_call_with_args\n    exp = parse 'x.y(1, 2, 3)'\n\n    assert_equal s(:call, nil, :x), exp.target\n    assert_equal :y, exp.method\n    assert_equal s(s(:lit, 1), s(:lit, 2), s(:lit, 3)), exp.args\n    assert_equal s(:arglist, s(:lit, 1), s(:lit, 2), s(:lit, 3)), exp.arglist\n    assert_equal s(:lit, 1), exp.first_arg\n    assert_equal s(:lit, 2), exp.second_arg\n    assert_equal s(:lit, 3), exp.last_arg\n  end\n\n  def test_method_call_no_target\n    exp = parse 'x 1, 2, 3'\n\n    assert_nil exp.target\n    assert_equal :x, exp.method\n    assert_equal s(s(:lit, 1), s(:lit, 2), s(:lit, 3)), exp.args\n    assert_equal s(:arglist, s(:lit, 1), s(:lit, 2), s(:lit, 3)), exp.arglist\n    assert_equal s(:lit, 1), exp.first_arg\n    assert_equal s(:lit, 2), exp.second_arg\n    assert_equal s(:lit, 3), exp.last_arg\n  end\n\n  def test_method_call_set_target\n    exp = parse 'x.y'\n    exp.target = :z\n\n    assert_equal :z, exp.target\n  end\n\n  def test_method_call_set_arglist\n    exp = parse 'x.y'\n    exp.arglist = s(:arglist, s(:lit, 1), s(:lit, 2))\n\n    assert_equal s(:lit, 1), exp.first_arg\n    assert_equal s(:lit, 2), exp.second_arg\n    assert_equal s(:lit, 2), exp.last_arg\n    assert_equal s(:arglist, s(:lit, 1), s(:lit, 2)), exp.arglist\n    assert_equal s(s(:lit, 1), s(:lit, 2)), exp.args\n  end\n\n  def test_method_call_set_args\n    exp = parse \"x.y\"\n\n    assert_equal s(), exp.args\n\n    exp.set_args s(:lit, 1), s(:lit, 2)\n\n    assert_equal s(s(:lit, 1), s(:lit, 2)), exp.args\n    assert_equal s(:lit,1), exp.first_arg\n    assert_equal s(:lit, 2), exp.second_arg\n    assert_equal s(:lit, 2), exp.last_arg\n  end\n\n  def test_method_call_set_method\n    exp = parse \"x.y\"\n\n    assert_equal :y, exp.method\n    \n    exp.method = :z\n\n    assert_equal :z, exp.method\n  end\n\n  def test_method_call_with_block\n    exp = parse \"x do |z|; blah z; end\"\n    block = exp.block\n    call = exp.block_call\n    args = exp.block_args\n\n    assert_equal s(:call, nil, :x), call\n    assert_equal s(:args, :z), args\n    assert_equal s(:call, nil, :blah, s(:lvar, :z)), block\n  end\n\n  def test_stabby_lambda_no_args\n    exp = parse \"->{ hi }\"\n\n    assert_equal s(:call, nil, :lambda), exp.block_call\n    assert_equal s(:args), exp.block_args\n    assert_equal s(:call, nil, :hi), exp.block\n  end\n\n  def test_or\n    exp = parse '1 or 2'\n\n    assert_equal s(:lit, 1), exp.lhs\n    assert_equal s(:lit, 2), exp.rhs\n  end\n\n  def test_and\n    exp = parse '1 and 2'\n\n    assert_equal s(:lit, 1), exp.lhs\n    assert_equal s(:lit, 2), exp.rhs\n  end\n\n  def test_if_expression\n    exp = parse <<-RUBY\n    if x\n      y\n    else\n      z\n    end\n    RUBY\n\n    assert_equal s(:call, nil, :x), exp.condition\n    assert_equal s(:call, nil, :y), exp.then_clause\n    assert_equal s(:call, nil, :z), exp.else_clause\n  end\n\n  def test_local_assignment\n    exp = parse 'x = 1'\n\n    assert_equal :x, exp.lhs\n    assert_equal s(:lit, 1), exp.rhs\n  end\n\n  def test_instance_assignment\n    exp = parse '@x = 1'\n\n    assert_equal :@x, exp.lhs\n    assert_equal s(:lit, 1), exp.rhs\n  end\n\n  def test_attribute_index_assignment\n    exp = parse 'y[:x] = 1'\n\n    assert_equal s(:lit, 1), exp.rhs\n  end\n\n  def test_global_assignment\n    exp = parse '$x = 1'\n\n    assert_equal :$x, exp.lhs\n    assert_equal s(:lit, 1), exp.rhs\n  end\n\n  def test_constant_assignment\n    exp = parse 'X = 1'\n\n    assert_equal :X, exp.lhs\n    assert_equal s(:lit, 1), exp.rhs\n  end\n\n  def test_class_variable_declaration\n    exp = parse '@@x = 1'\n\n    assert_equal :cvdecl, exp.node_type\n    assert_equal :@@x, exp.lhs\n    assert_equal s(:lit, 1), exp.rhs\n  end\n\n  def test_class_variable_assignment\n    exp = parse 'def x; @@a = 1; end'\n    asgn = exp.last\n\n    assert_equal :cvasgn, asgn.node_type\n    assert_equal :@@a, asgn.lhs\n    assert_equal s(:lit, 1), asgn.rhs\n  end\n\n  def test_method_def_name\n    exp = parse <<-RUBY\n    def x(y)\n      z\n      y\n    end\n    RUBY\n\n    assert_equal :x, exp.method_name\n  end\n\n  def test_method_self_def_name\n    exp = parse <<-RUBY\n    def self.x(y)\n      z\n      y\n    end\n    RUBY\n\n    assert_equal :x, exp.method_name\n  end\n\n  def test_method_def_body\n    exp = parse <<-RUBY\n    def x(y)\n      z\n      y\n    end\n    RUBY\n\n    assert_equal s(s(:call, nil, :z), s(:lvar, :y)), exp.body\n  end\n\n  def test_method_def_body_single_line\n    exp = parse <<-RUBY\n    def x(y)\n      y\n    end\n    RUBY\n\n    assert_equal s(s(:lvar, :y)), exp.body\n  end\n\n  def test_class_body\n    exp = parse <<-RUBY\n    class X\n      def y\n      end\n    end\n    RUBY\n\n    assert_equal s(s(:defn, :y, s(:args), s(:nil))), exp.body\n  end\n\n  def test_module_body\n    exp = parse <<-RUBY\n    module X\n      def y\n      end\n    end\n    RUBY\n\n    assert_equal s(s(:defn, :y, s(:args), s(:nil))), exp.body\n  end\n\n  def test_class_name\n    exp = parse 'class X < Y; end'\n\n    assert_equal :X, exp.class_name\n  end\n\n  def test_parent_name\n    exp = parse 'class X < Y; end'\n\n    assert_equal s(:const, :Y), exp.parent_name\n  end\n\n  def test_module_name\n    exp = parse 'module X; end'\n\n    assert_equal :X, exp.module_name\n  end\n\n  def test_wrong_sexp_error\n    exp = parse 'true ? false : true'\n\n    assert_raises WrongSexpError do\n      exp.method\n    end\n  end\n\n  def test_zsuper_call\n    exp = parse 'super'\n\n    assert_equal :super, exp.method\n    assert_equal s(:arglist), exp.arglist\n    assert_equal s(), exp.args\n  end\n\n  def test_super_call\n    exp = parse 'super 1'\n\n    assert_equal :super, exp.method\n    assert_equal s(:arglist, s(:lit, 1)), exp.arglist\n    assert_equal s(s(:lit, 1)), exp.args\n  end\n\n  def test_resbody_block\n    #Ruby2Ruby has calls like this which need to be supported\n    #for Brakeman::OutputProcessor\n    exp = parse \"begin; rescue; end\"\n\n    assert_nil exp.resbody.block\n  end\n\n  def test_lasgn\n    #Ruby2Ruby has calls like this which need to be supported\n    #for Brakeman::OutputProcessor\n    exp = parse \"blah; x = 1\"\n\n    assert_equal s(:lasgn, :x, s(:lit, 1)), exp.lasgn(true)\n    assert_nil exp.lasgn #Was deleted\n  end\n\n  def test_iasgn\n    #Ruby2Ruby has calls like this which need to be supported\n    #for Brakeman::OutputProcessor\n    exp = parse \"blah; @x = 1\"\n\n    assert_equal s(:iasgn, :@x, s(:lit, 1)), exp.iasgn(true)\n    assert_nil exp.iasgn #Was deleted\n  end\n\n  def test_each_arg\n    exp = parse \"blah 1, 2, 3\"\n\n    args = []\n    exp.each_arg do |a|\n      args << a.value\n    end\n\n    assert_equal [1,2,3], args\n  end\n\n  def test_each_arg!\n    exp = parse \"blah 1, 2\"\n    exp.each_arg! do |a|\n      s(:lit, a.value + 1)\n    end\n\n    assert_equal s(:lit, 2), exp.first_arg\n    assert_equal s(:lit, 3), exp.second_arg\n  end\n\n  def test_num_args\n    assert_equal 3, parse('x(1, 2, 3)').num_args\n    assert_equal 2, parse('x(1, 2)').num_args\n    assert_equal 1, parse('x(1)').num_args\n    assert_equal 0, parse('x()').num_args\n\n\n    assert_equal 1, parse('x.y = 1').num_args\n    assert_equal 2, parse('super 1, 2').num_args\n    assert_equal 0, parse('super').num_args\n    assert_equal 0, parse('super()').num_args\n\n    assert_raises do\n      parse('1').num_args\n    end\n  end\n\n  # For performance reasons, Sexps cache their hash value. This was not being\n  # invalidated on <<\n  def test_hash_invalidation_on_push\n    s = Sexp.new(:blah)\n    s_hash = s.hash\n    s << :blah\n\n    refute_equal s_hash, s.hash\n  end\n\n  # Since Sexp is subclassed from Array, only changing the contents\n  # of the Sexp actually change the hash value.\n  def test_hash_invalidation_on_line_number_change\n    s = Sexp.new(:blah).line(1)\n    s_hash = s.hash\n    s.line(10)\n\n    refute_nil s.instance_variable_get(:@my_hash_value)\n    assert_equal s_hash, s.hash\n  end\n\n  def test_sexp_line_set\n    s = Sexp.new(:blah).line(10)\n    assert_equal 10, s.line\n\n    s.line = 100\n    assert_equal 100, s.line\n\n    s.line(0)\n    assert_equal 0, s.line\n  end\n\n  def test_sexp_original_line_set\n    s = Sexp.new(:blah)\n    s.original_line = 10\n    assert_equal 10, s.original_line\n\n    s.original_line = 100\n    assert_equal 100, s.original_line\n  end\n\n  def test_combine_and_or_depth\n    e = Sexp.new(:lit, 0)\n\n    3.times do |i|\n      e = e.combine(Sexp.new(:lit, i))\n    end\n\n    assert_equal s(:or, s(:or, s(:or, s(:lit, 0), s(:lit, 0)), s(:lit, 1)), s(:lit, 2)), e\n    assert_equal 3, e.or_depth\n  end\n\n  def test_inspect_recursive\n    s = Sexp.new(:s)\n    s << s\n    assert_equal \"s(:s, s(...))\", s.inspect\n  end\n\n  def test_value\n    assert_equal 1, s(:lit, 1).value\n    assert_nil s(:blah).value\n    assert_raises do\n      s(:blah, 1, 2).value\n    end\n  end\n\n  def test_call_chain\n    s = RubyParser.new.parse \"w.new.x.y(:stuff).z.to_s(1)\"\n    cc = [:w, :new, :x, :y, :z, :to_s]\n\n    assert_equal cc, s.call_chain\n  end\n\n  def test_short_call_chain\n    s = Sexp.new(:call, nil, :x)\n\n    assert_equal [:x], s.call_chain\n  end\n\n  def test_local_call_chain\n    s = Sexp.new(:call, s(:lvar, :z), :x)\n\n    assert_equal [:x], s.call_chain\n  end\n\n  def test_body_list_set\n    exp = parse <<-RUBY\n    def x(y)\n      z\n      y\n    end\n    RUBY\n\n    exp2 = parse <<-RUBY\n    def z\n      z\n    end\n    RUBY\n\n    assert_equal s(:rlist, s(:call, nil, :z)), exp2.body_list\n\n    exp.body = exp2.body_list\n\n    assert_equal s(s(:call, nil, :z)), exp.body\n  end\nend\n"
  },
  {
    "path": "test/tests/sonar_output.rb",
    "content": "require_relative '../test'\nrequire 'json'\n\nclass SonarOutputTests < Minitest::Test\n  def setup\n    @@sonar ||= JSON.parse(Brakeman.run(\"#{TEST_PATH}/apps/rails3.2\").report.to_sonar)\n  end\n\n  def test_for_expected_keys\n    assert (@@sonar.keys - [\"issues\"]).empty?\n  end\n\n  def test_for_issues_keys\n    issues_keys = [\"engineId\", \"ruleId\", \"severity\", \"type\", \"primaryLocation\", \"effortMinutes\"]\n    @@sonar[\"issues\"].each do |warning|\n      assert (warning.keys - issues_keys).empty?\n    end\n    \n  end\n\nend\n"
  },
  {
    "path": "test/tests/tabs_output.rb",
    "content": "require_relative '../test'\n\nclass TestTabsOutput < Minitest::Test\n  def setup\n    @@report ||= Brakeman.run(\n      :app_path       => \"#{TEST_PATH}/apps/rails2\",\n      :quiet          => true,\n      :run_all_checks => true\n    ).report.to_tabs\n  end\n\n  def test_reported_warnings\n    assert_equal 113, @@report.lines.to_a.count, 'Did you add new vulnerabilities to the Rails 2 app?'\n  end\nend\n"
  },
  {
    "path": "test/tests/tracker.rb",
    "content": "require_relative '../test'\n\nclass TrackerTests < Minitest::Test\n  def setup\n    @tracker = BrakemanTester.new_tracker\n  end\n\n  def test_exception_in_error_list\n    @tracker.error Exception.new\n\n    assert_equal 1, @tracker.errors.length\n\n    @tracker.errors.each do |e|\n      assert e.has_key? :exception\n      assert e.has_key? :error\n      assert e.has_key? :backtrace\n\n      assert e[:exception].is_a? Exception\n      assert e[:error].is_a? String\n      assert e[:backtrace].is_a? Array\n    end\n  end\n\n  def test_method_lookup_default_type\n    parse_class\n    assert @tracker.find_method(:boo, :Example)\n  end\n\n  def test_method_lookup_instance\n    parse_class\n    assert @tracker.find_method(:boo, :Example, :instance)\n  end\n\n  def test_method_lookup_class\n    parse_class\n    assert @tracker.find_method(:far, :Example, :class)\n  end\n\n  def test_method_lookup_wrong_type\n    parse_class\n    assert_nil @tracker.find_method(:far, :Example, :instance)\n  end\n\n  def test_method_lookup_no_method\n    parse_class\n    assert_nil @tracker.find_method(:farther, :Example, :class)\n  end\n\n  def test_method_lookup_in_parent\n    parse_class\n    assert @tracker.find_method(:zoop, :Example, :instance)\n  end\n\n  def test_method_lookup_in_mixin\n    parse_class\n    assert @tracker.find_method(:mixed, :Example)\n  end\n\n  def test_method_lookup_in_module\n    parse_class\n    assert @tracker.find_method(:mixed, :Mixin)\n  end\n\n  def test_method_lookup_invalid_type\n    parse_class\n    assert_raises do\n      assert @tracker.find_method(:far, :Example, :invalid_type)\n    end\n  end\n\n  def test_method_inside_sclass\n    parse_class\n    assert @tracker.find_method(:class_method, :Example, :class)\n  end\n\n  def test_class_method_in_parent\n    parse_class\n    assert @tracker.find_method(:parent_class_method, :Example, :class)\n  end\n\n  def test_invalid_method_info_src\n    assert_raises do\n      Brakeman::MethodInfo.new(:blah, s(:not_a_defn), nil, nil)\n    end\n  end\n\n  def test_module_includes_in_same_class\n    ast = RubyParser.new.parse <<~RUBY\n      module Mixin\n        def self.builds_self\n          Class.new { include Mixin }\n        end\n      end\n    RUBY\n\n    Brakeman::LibraryProcessor.new(@tracker).process_library(ast, 'fake_file_name.rb')\n    assert_nil @tracker.find_method(:builds_self, :Mixin)\n  end\n\n  private\n\n  def parse_class\n    ast = RubyParser.new.parse <<-RUBY\n    module Mixin\n      def mixed\n      end\n    end\n\n    class Parent\n      def zoop\n      end\n\n      def self.parent_class_method\n      end\n    end\n\n    class Example < Parent\n      include Mixin\n\n      def boo\n      end\n\n      def self.far\n      end\n\n      class << self\n        def class_method\n        end\n      end\n    end\n    RUBY\n\n    Brakeman::LibraryProcessor.new(@tracker).process_library(ast, 'fake_file_name.rb')\n  end\nend\n"
  },
  {
    "path": "test/tests/warning.rb",
    "content": "require_relative '../test'\nrequire 'brakeman/warning'\n\nclass WarningTests < Minitest::Test\n  def test_confidence_symbols\n    [:high, :med, :medium, :low, :weak].each do |c|\n      w = Brakeman::Warning.new(confidence: c) \n      assert_equal Brakeman::Warning::CONFIDENCE[c], w.confidence\n    end\n  end\n\n  def test_confidence_integers\n    [0, 1, 2].each do |c|\n      w = Brakeman::Warning.new(confidence: c) \n      assert_equal c, w.confidence\n    end\n  end\n\n  def test_bad_confidence_symbol\n    assert_raises do\n      Brakeman::Warning.new(confidence: :blah)\n    end\n  end\n\n  def test_bad_confidence_integer\n    assert_raises do\n      Brakeman::Warning.new(confidence: 10)\n    end\n  end\n\n  def test_relative_path\n    tracker = BrakemanTester.new_tracker\n    path = tracker.app_tree.file_path(\"app/controllers/some_controller.rb\")\n\n    w = Brakeman::Warning.new(file: path, confidence: :high)\n\n    refute w.relative_path.start_with? \"/\"\n  end\nend\n"
  },
  {
    "path": "test/to_test.rb",
    "content": "#This is a utility script for generating tests from reported warnings.\n#\n#It is not heavily tested. It is mostly for the convenience of coders. Sometimes\n#it generates broken code which will need to be fixed manually.\n#\n#Usage:\n#\n#  ruby to_test.rb apps/some_app > tests/test_some_app.rb`\n\n# Set paths\n$LOAD_PATH.unshift \"#{File.expand_path(File.dirname(__FILE__))}/../lib\"\n\nrequire 'brakeman'\nrequire 'ruby_parser'\nrequire 'ruby_parser/bm_sexp'\nrequire 'brakeman/options'\nrequire 'brakeman/report/report_base'\n\nclass Brakeman::Report::Tests < Brakeman::Report::Base\n  def generate_report\n    counter = 0\n\n    name = camelize File.basename(tracker.app_path)\n\n    output = <<-RUBY\nabort \"Please run using test/test.rb\" unless defined? BrakemanTester\n\n#{name} = BrakemanTester.run_scan \"#{File.basename tracker.app_path}\", \"#{name}\"\n\nclass #{name}Tests < Test::Unit::TestCase\n  include BrakemanTester::FindWarning\n  include BrakemanTester::CheckExpected\n\n  def expected\n    @expected ||= {\n      :controller => #{@checks.controller_warnings.length},\n      :model => #{@checks.model_warnings.length},\n      :template => #{@checks.template_warnings.length},\n      :warning => #{@checks.warnings.length} }\n  end\n\n  def report\n    #{name}\n  end\n\n    RUBY\n\n    output << @checks.all_warnings.map do |w|\n      counter += 1\n\n      <<-RUBY\n  def test_#{w.warning_type.to_s.downcase.tr(\" -\", \"__\")}_#{counter}\n    assert_warning check_name: #{w.check_name.inspect},\n      type: #{w.warning_set.inspect},\n      warning_code: #{w.warning_code},\n      fingerprint: #{w.fingerprint.inspect},\n      warning_type: #{w.warning_type.inspect},\n      line: #{w.line.inspect},\n      message: /^#{Regexp.escape w.message.to_s[0,40]}/,\n      confidence: #{w.confidence},\n      relative_path: #{w.file.relative.inspect},\n      code: #{w.code.inspect},\n      user_input: #{w.user_input.inspect}\n  end\n      RUBY\n    end.join(\"\\n\")\n\n    output << \"\\nend\"\n  end\nend\n\noptions, _ = Brakeman::Options.parse!(ARGV)\n\nunless options[:app_path]\n  if ARGV[-1].nil?\n    options[:app_path] = \".\"\n  else\n    options[:app_path] = ARGV[-1]\n  end\nend\n\ntracker = Brakeman.run options\n\nputs Brakeman::Report::Tests.new(tracker).generate_report\n"
  }
]