├── .github └── stale.yml ├── .gitignore ├── .rspec ├── .ruby-version ├── .rvmrc ├── .travis.yml ├── Apache2_License.html ├── CHANGES ├── DEVOPS.md ├── DOCKER.md ├── FEATURES ├── Gemfile ├── Gemfile.lock ├── OPTIONS.md ├── PLAYGROUND.md ├── README.md ├── Rakefile ├── TOOLS.md ├── bin └── glue ├── config.yml.example ├── docker ├── README.md ├── glue │ ├── Dockerfile │ ├── Dockerfile-raw │ └── build.sh ├── java │ └── Dockerfile ├── phpcs │ └── Dockerfile ├── retire.js │ └── Dockerfile └── zap │ ├── Dockerfile │ ├── build.sh │ └── run.sh ├── docs └── dynamic_task.md ├── glue.gemspec ├── glue.png ├── integrations ├── hooks │ └── pre-commit └── jenkins │ ├── glue-active.sh │ ├── glue-code.sh │ └── glue-pipeline.groovy ├── lib ├── glue.rb └── glue │ ├── event.rb │ ├── filters.rb │ ├── filters │ ├── base_filter.rb │ ├── contrast_severity_filter.rb │ ├── file_filter.rb │ ├── jira_one_time_filter.rb │ ├── pivotal_one_time_filter.rb │ ├── remove_all_filter.rb │ └── zap_consdensing_filter.rb │ ├── finding.rb │ ├── mappings │ ├── mobsf.json │ ├── schema.json │ ├── snyk.json │ └── zaproxy.json │ ├── mounters.rb │ ├── mounters │ ├── base_mounter.rb │ ├── docker_mounter.rb │ ├── filesystem_mounter.rb │ ├── git_mounter.rb │ ├── iso_mounter.rb │ └── url_mounter.rb │ ├── options.rb │ ├── reporters.rb │ ├── reporters │ ├── base_reporter.rb │ ├── csv_reporter.rb │ ├── jira_reporter.rb │ ├── json_reporter.rb │ ├── pivotal_reporter.rb │ ├── slack_reporter.rb │ ├── teamcity_reporter.rb │ └── text_reporter.rb │ ├── scanner.rb │ ├── tasks.rb │ ├── tasks │ ├── bandit.rb │ ├── base_task.rb │ ├── brakeman.rb │ ├── bundle-audit.rb │ ├── burp.rb │ ├── checkmarx.rb │ ├── clamav.rb │ ├── contrast.rb │ ├── dawnscanner.rb │ ├── dynamic.rb │ ├── eslint.rb │ ├── fim.rb │ ├── findsecbugs.rb │ ├── npm.rb │ ├── nsp.rb │ ├── owasp-dep-check.rb │ ├── patterns.json │ ├── pmd.rb │ ├── retirejs.rb │ ├── scanjs-eslintrc │ ├── scanjs.rb │ ├── scout2.rb │ ├── sfl.rb │ ├── snyk.rb │ ├── test.rb │ ├── trufflehog.rb │ └── zap.rb │ ├── tracker.rb │ ├── util.rb │ └── version.rb └── spec ├── awsscout_spec.rb ├── cli_spec.rb ├── eicar.com ├── filters └── file_filter │ ├── file_filter_spec.rb │ └── targets │ ├── finding_ignore.json │ ├── finding_partial.json │ ├── finding_postpone.json │ └── finding_postpone_passed.json ├── parse_scout_spec.rb ├── reporters ├── jira_reporter_spec.rb ├── slack_reporter_spec.rb └── teamcity_reporter_spec.rb ├── scout_data.json ├── spec_helper.rb ├── support └── aruba.rb └── tasks ├── bundle-audit ├── README.md ├── bundle-audit_spec.rb ├── generate_reports.sh └── targets │ ├── finding_1 │ ├── Gemfile.lock │ └── report.txt │ ├── finding_2 │ ├── Gemfile.lock │ └── report.txt │ ├── finding_2_unknown │ ├── Gemfile.lock │ └── report.txt │ ├── finding_3 │ ├── Gemfile.lock │ └── report.txt │ ├── no_findings │ ├── Gemfile.lock │ └── report.txt │ └── no_findings_no_gemfile_lock │ └── example.txt ├── dynamic ├── dynamic_spec.rb └── targets │ ├── dummy │ ├── invalid_report.json │ ├── invalid_schema.json │ ├── mapping.json │ └── report.json │ └── tools_samples │ ├── mobsf.json │ ├── snyk.json │ └── zaproxy.json ├── owasp-dep-check ├── generate_reports.sh ├── owasp-dep-check_spec.rb └── targets │ ├── findings_1 │ ├── dependency-check-report.xml │ └── kibana-2.2.335.jar │ ├── findings_1_nested │ ├── dependency-check-report.xml │ └── findings_1 │ │ └── jackson-databind-2.1.4.jar │ ├── findings_2 │ ├── dependency-check-report.xml │ └── limesurvey-rc-0.6.jar │ └── no_findings │ ├── dependency-check-report.xml │ └── jinjava-2.4.15.jar ├── retirejs ├── README.md ├── generate_reports.sh ├── retirejs_spec.rb ├── targets │ ├── finding_1 │ │ ├── node_modules │ │ │ └── cli │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── finding_1_nested │ │ └── finding_1 │ │ │ ├── node_modules │ │ │ └── cli │ │ │ │ └── package.json │ │ │ ├── package.json │ │ │ └── report.json │ ├── finding_2 │ │ ├── node_modules │ │ │ └── cookie-signature │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── finding_3 │ │ ├── node_modules │ │ │ └── pivottable │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── finding_f1 │ │ ├── file_1.js │ │ ├── package.json │ │ └── report.json │ ├── finding_f1_nested │ │ ├── js_files │ │ │ └── file_1.js │ │ ├── package.json │ │ └── report.json │ ├── findings_1-2 │ │ ├── node_modules │ │ │ ├── cli │ │ │ │ └── package.json │ │ │ └── cookie-signature │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── findings_123 │ │ ├── node_modules │ │ │ ├── cli │ │ │ │ └── package.json │ │ │ ├── cookie-signature │ │ │ │ └── package.json │ │ │ └── pivottable │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── findings_123_2-1_3-1 │ │ ├── node_modules │ │ │ ├── cli │ │ │ │ └── package.json │ │ │ ├── cookie-signature │ │ │ │ └── package.json │ │ │ └── pivottable │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── findings_123_2-1_3-12 │ │ ├── node_modules │ │ │ ├── cli │ │ │ │ └── package.json │ │ │ ├── cookie-signature │ │ │ │ └── package.json │ │ │ └── pivottable │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── findings_1_2_3 │ │ ├── finding_1 │ │ │ ├── node_modules │ │ │ │ └── cli │ │ │ │ │ └── package.json │ │ │ ├── package.json │ │ │ └── report.json │ │ ├── finding_2 │ │ │ ├── node_modules │ │ │ │ └── cookie-signature │ │ │ │ │ └── package.json │ │ │ ├── package.json │ │ │ └── report.json │ │ └── finding_3 │ │ │ ├── node_modules │ │ │ └── pivottable │ │ │ │ └── package.json │ │ │ ├── package.json │ │ │ └── report.json │ ├── findings_1f1 │ │ ├── file_1.js │ │ ├── node_modules │ │ │ └── cli │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── findings_4 │ │ ├── node_modules │ │ │ └── uglify-js │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── findings_5f5 │ │ ├── file_5.js │ │ ├── node_modules │ │ │ └── jquery │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── findings_f12 │ │ ├── file_12.js │ │ ├── package.json │ │ └── report.json │ ├── findings_f1_components_wo_vulnerabilities │ │ ├── file_1.js │ │ ├── node_modules │ │ │ └── example.txt │ │ ├── package.json │ │ └── report.json │ ├── findings_f1f1 │ │ ├── file_1.js │ │ ├── file_2.js │ │ ├── package.json │ │ └── report.json │ ├── findings_f1f2 │ │ ├── file_1.js │ │ ├── file_2.js │ │ ├── package.json │ │ └── report.json │ ├── findings_f3 │ │ ├── file_3.js │ │ ├── package.json │ │ └── report.json │ ├── malformed │ │ ├── package.json │ │ └── report.json │ ├── malformed_nested │ │ ├── finding_1 │ │ │ ├── node_modules │ │ │ │ └── cli │ │ │ │ │ └── package.json │ │ │ ├── package.json │ │ │ └── report.json │ │ ├── malformed │ │ │ ├── package.json │ │ │ └── report.json │ │ └── zz_finding_1 │ │ │ ├── node_modules │ │ │ └── cli │ │ │ │ └── package.json │ │ │ ├── package.json │ │ │ └── report.json │ ├── no_findings │ │ ├── node_modules │ │ │ └── example.txt │ │ ├── package.json │ │ └── report.json │ └── no_findings_no_package_json │ │ └── example.txt └── test_targets │ └── README.md ├── sfl ├── malformed_patterns_file.json ├── sfl_patterns_spec.rb ├── sfl_spec.rb └── targets │ ├── no_findings │ └── example.txt │ ├── no_findings_password_subdir │ └── password │ │ └── example.txt │ ├── one_finding_extension_match │ └── test.pkcs12 │ ├── one_finding_extension_regex │ └── test.keypair │ ├── one_finding_filename_match │ └── secret_token.rb │ ├── one_finding_filename_regex │ └── .id_rsa │ ├── one_finding_path_regex │ └── purple │ │ └── accounts.xml │ ├── two_findings │ ├── .id_rsa │ ├── example.txt │ └── secret_token.rb │ ├── two_findings_difft_dirs │ ├── dir1 │ │ └── secret_token.rb │ └── dir2 │ │ └── secret_token.rb │ └── two_findings_one_file │ └── password_backup.txt ├── snyk ├── README.md ├── generate_reports.sh ├── snyk_spec.rb └── targets │ ├── finding_1 │ ├── node_modules │ │ └── cli │ │ │ └── package.json │ ├── package.json │ └── report.json │ ├── finding_1_nested │ └── finding_1 │ │ ├── node_modules │ │ └── cli │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── finding_2 │ ├── node_modules │ │ └── cookie-signature │ │ │ └── package.json │ ├── package.json │ └── report.json │ ├── finding_3 │ ├── node_modules │ │ └── pivottable │ │ │ └── package.json │ ├── package.json │ └── report.json │ ├── findings_123_2-1_3-12 │ ├── node_modules │ │ ├── cli │ │ │ └── package.json │ │ ├── cookie-signature │ │ │ └── package.json │ │ └── pivottable │ │ │ └── package.json │ ├── package.json │ └── report.json │ ├── findings_1_2_3 │ ├── finding_1 │ │ ├── node_modules │ │ │ └── cli │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── finding_2 │ │ ├── node_modules │ │ │ └── cookie-signature │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ └── finding_3 │ │ ├── node_modules │ │ └── pivottable │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── malformed │ ├── package.json │ └── report.json │ ├── malformed_nested │ ├── finding_1 │ │ ├── node_modules │ │ │ └── cli │ │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── malformed │ │ ├── package.json │ │ └── report.json │ └── zz_finding_1 │ │ ├── node_modules │ │ └── cli │ │ │ └── package.json │ │ ├── package.json │ │ └── report.json │ ├── no_findings │ ├── package.json │ └── report.json │ ├── no_findings_no_package_json │ └── example.txt │ └── snyk-sample-data │ ├── SKIP.txt │ ├── findings_snapshot.json │ ├── generate_report.sh │ ├── package.json │ └── report.json ├── trufflehog ├── generate_reports.sh ├── reports │ ├── mult_findings.json │ ├── one_finding.json │ └── zero_findings.json ├── targets │ ├── mult_findings │ │ ├── sub_one_finding │ │ │ └── example_m1.txt │ │ ├── sub_two_findings │ │ │ └── example_m2.txt │ │ └── sub_zero_findings │ │ │ └── example_m0.txt │ ├── one_finding │ │ └── example.txt │ └── zero_findings │ │ └── example.txt └── trufflehog_spec.rb └── zap ├── alerts.json └── zap_spec.rb /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 60 5 | 6 | # Number of days of inactivity before a stale Issue or Pull Request is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: false 9 | 10 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 11 | exemptLabels: 12 | - pinned 13 | - security 14 | - "[Status] Maybe Later" 15 | 16 | # Set to true to ignore issues in a project (defaults to false) 17 | exemptProjects: false 18 | 19 | # Set to true to ignore issues in a milestone (defaults to false) 20 | exemptMilestones: false 21 | 22 | # Label to use when marking as stale 23 | staleLabel: stale 24 | 25 | # Comment to post when marking as stale. Set to `false` to disable 26 | markComment: > 27 | This issue has been automatically marked as stale because it has not had 28 | recent activity. It will be closed if no further activity occurs. Thank you 29 | for your contributions. 30 | 31 | # Comment to post when removing the stale label. 32 | # unmarkComment: > 33 | # Your comment here. 34 | 35 | # Comment to post when closing a stale Issue or Pull Request. 36 | # closeComment: > 37 | # Your comment here. 38 | 39 | # Limit the number of actions per hour, from 1-30. Default is 30 40 | limitPerRun: 30 41 | 42 | # Limit to only `issues` or `pulls` 43 | # only: issues 44 | 45 | # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': 46 | # pulls: 47 | # daysUntilStale: 30 48 | # markComment: > 49 | # This pull request has been automatically marked as stale because it has not had 50 | # recent activity. It will be closed if no further activity occurs. Thank you 51 | # for your contributions. 52 | 53 | # issues: 54 | # exemptLabels: 55 | # - confirmed 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | vendor/** 3 | coverage 4 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.1 2 | -------------------------------------------------------------------------------- /.rvmrc: -------------------------------------------------------------------------------- 1 | rvm use 2.3.1@glue 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | cache: bundler 3 | sudo: required 4 | 5 | services: 6 | - docker 7 | rvm: 8 | - 2.4 9 | before_install: 10 | - sudo apt-get -qq update 11 | - sudo apt-get install -y build-essential libcurl4-openssl-dev 12 | before_script: 13 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 14 | > ./cc-test-reporter 15 | - chmod +x ./cc-test-reporter 16 | - "./cc-test-reporter before-build" 17 | script: 18 | - bundle exec rspec 19 | - docker build -t owasp/glue . -f docker/glue/Dockerfile-raw --label COMMIT_SHA:$TRAVIS_COMMIT 20 | after_script: 21 | - "./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT --debug" 22 | before_deploy: 23 | - docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD 24 | after_deploy: 25 | - docker logout 26 | deploy: 27 | - provider: rubygems 28 | api_key: 29 | secure: F/HpV5oaF0W0d0yhHd1oOgzJUxPXW/IGxLBXieyiqoyfae2kA6VoTvCijSz6tY4Fo7kYXQvwWouLWr/MokTRISO4pD3iwghfFQONtqjqBAB99+wGwJFARdIhVGAHTTlDoykgmh/nIYRwWMYvo/9sg+cq7RN6guMC88suaZ9AF3sweFNOiMiDvks7E0Id/6yyE2USlK8Lef03LaoTgylpB2raogKmDwQ6yx5JVBWUAzZnshSvQWst+o1CKQUGC8/U/1shjpPRmUlEAxLwdrkk9BYffHX2QdMYPzaBYU4zool/YypbjOHIws7izgJI3zUrbYVNyAwOfyaMQjYpnu2C9XGYVA7Fm5xCnJXkzfIP0jNhcoA7d55NedVLn3tFbsE1HgkC8tG3QephOmHJdW5oQy6+L4c1jkAQzV5HQPodBMHkSG0Zw7H28R67DSoIFIo76i6ak8Ina0KTZ7/mtu/O2UKN8sRLQW4+AE6yHRXQp3DsZxwdjoC+TyWEqpthFVHb9PeEJpxldd9r82lRyBGX8C4Gia265c4A4WfY675tzR6S9X6JxUnl0HkrLpOeg5lv5EfF2eg5ypM4sb5cVhD3dnyLY1EWVaJ8kZAb33JL75TFR8jQ+TVM0HqCMTiAw5dNQXxAMvGmcpOOVvHB/GpZMIDuxRfHZmgGQ9iJggGMl/I= 30 | gem: owasp-glue 31 | on: 32 | tags: true 33 | branch: master 34 | repo: OWASP/glue 35 | - provider: script 36 | script: docker tag owasp/glue owasp/glue:raw-latest && docker push owasp/glue:raw-latest 37 | on: 38 | branch: master 39 | repo: OWASP/glue 40 | - provider: script 41 | script: docker tag owasp/glue owasp/glue:$TRAVIS_TAG && docker push owasp/glue:$TRAVIS_TAG 42 | on: 43 | branch: master 44 | tags: true 45 | repo: OWASP/glue 46 | env: 47 | global: 48 | - secure: idBRF6n/XLA25Z46aHBI4KaM+ljbzzD9L58JuASp/VBuViM07r0ASO0HDMMafiVuYpyBUXeo8f7/Kzb88z8fQUi3aTnMkOxsAl5PiygR0on7DQie1G78OKJ+wZ6NiEzYwFLrzVhjEXvFIsu8zZWtLfRNC+54HignxhIrdw0PEcsXbAE1UT1zR9zuBJfWrCtHGH6sQ6a57qiyGoOkjBnScDef5Tn0LgPT8WISy4Qb1AiAJDAnW9D8lF3sSWRbwysMjbIUTlTMqaZGpGVtTGiW75Vx60kY1qZ1dRZNm5U/QLlD3T8SAig6gYXGBXHgV1CR3S1JVNhX//Fk26HzTI5S3lNCw/h+qpSevdfqXZMaLSOJ5QF0920V1i+veUxSnd7BeFcCG6sxpVaHr/ztzptMSqyceYMEzmdpcDqXuPC+wn/U3QA4LWFW0PyfbWxrH9ibP4BaMEQNIHG1mByIDFLgZzB6zIQUQWPoB55DkmlRA1QC5Te+8JntvbVZF8qMk0Sv7lrg2ilTkFON7cHPE9y9wkaM5EhbdAwAl8cg6tDVeLrvmB6W+AuCFt1wgm46UEn8soSBNdYZThtfw7DvmUoFuMP/Nnmr9see6OFFnrSO4oslbbxLQAowWjnR1JzwXbrJcwrvA6d0Oy3C5byAduQoZCxN8d7SdnUuQeeSi1xVAzc= 49 | - DOCKER_USERNAME=owaspglueci 50 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | ## 0.9.2 2 | * Add Bandit 3 | ## 0.9.1 4 | * Clean up directories for java tooling. 5 | ## 0.9.0 6 | * Rename to Glue 7 | ## 0.8.0 8 | * Java Tooling 9 | ## 0.6.0 10 | * Docker image. 11 | * JavaScript tools (retire.js, nodesecurity, eslint) 12 | * Java tools (SonarCube, Findbugs) 13 | ## 0.5.3 14 | * Checkmarx 15 | * JIRA Integration 16 | * Search for secrets (a la gitrob) 17 | * OWASP dependency-check 18 | ## 0.1.0 19 | * Structure for pipeline and initial tool series. 20 | * gem packaging 21 | * Finding / reporting 22 | * docker mounting 23 | * Support for task labels. Run with 'pipeline -l label' and only tasks that identify with that label will run. 24 | * Move to only keep options in tracker. 25 | 26 | ## FUTURE 27 | * TODO: add scap 28 | * TODO: Devtools: pmd, brakeman, dependency checker, codescan, scanjs, bandit, etc. 29 | * TODO: Misc OS checks 30 | * TODO: Active: ZAP, gauntlt 31 | * TODO: .NET 32 | -------------------------------------------------------------------------------- /DOCKER.md: -------------------------------------------------------------------------------- 1 | # Glue with Docker 2 | 3 | Glue is distributed in a Docker image for each of use. 4 | This has the advantage of coming with tools already 5 | configured and ready to go. 6 | 7 | # Installation 8 | 9 | ``` 10 | docker pull owasp/glue 11 | ``` 12 | 13 | # Usage 14 | 15 | ## Help 16 | 17 | This is one way to get help. 18 | ``` 19 | docker run --rm owasp/glue --help 20 | ``` 21 | 22 | ## Typical Usage 23 | 24 | Most basic starting point. Will analyze a predetermined codebase. 25 | ``` 26 | docker run --rm --name=Glue owasp/glue 27 | ``` 28 | 29 | Here is an example that runs on a github repo. 30 | ``` 31 | docker run --rm --name=Glue owasp/glue https://github.com/YourOrg/YourProject.git 32 | ``` 33 | 34 | This example only runs code analysis tools and outputs JSON. 35 | ``` 36 | docker run --rm --name=Glue owasp/glue -l code -f json https://github.com/YourOrg/YourProject.git 37 | ``` 38 | 39 | Example: 40 | ``` 41 | docker run --rm --name=Glue owasp/glue -l code -f json https://github.com/Owasp/triage.git 42 | ``` 43 | 44 | ## On the File System 45 | 46 | Running against a local file system: 47 | ``` 48 | docker run --rm --name=Glue -v /code/location:/tmp/directory owasp/glue -d -f json /tmp/directory/ 49 | ``` 50 | 51 | Example: 52 | ``` 53 | docker run --rm --name=Glue -v /Users/mk/line/tmp/triage:/tmp/triage owasp/glue -l code -f json /tmp/triage/ 54 | ``` 55 | 56 | Note that the folder sharing on Windows and Mac is constrained by [Docker Volumes](https://docs.docker.com/engine/userguide/dockervolumes/). 57 | To summarize those for Mac, it is easy to share directories in the Users home directory but if you want to share 58 | a different directory you have to make it shared through VirtualBox or whatever container controls your base image. 59 | 60 | ## Running Specific Tools 61 | 62 | Glue supports running specific tools using the -t flag. For example the following command only runs retire.js on the project. 63 | ``` 64 | docker run --rm --name=Glue -v /Users/mk/line/tmp/NodeGoat:/tmp/nodegoat owasp/glue:0.7 -t retirejs -f csv /tmp/nodegoat/ 65 | ``` 66 | 67 | The tools include: 68 | - brakeman 69 | - bundler-audit 70 | - retirejs 71 | - nodesecurityproject 72 | - eslint 73 | - sfl (Sensitive file lookup - part of Glue) 74 | 75 | 76 | # Dependencies 77 | 78 | - Docker: https://get.docker.com/ 79 | - Mac: https://docs.docker.com/mac/step_one/ 80 | - Linux: https://docs.docker.com/linux/step_one/ 81 | - Windows: https://docs.docker.com/windows/step_one/ 82 | 83 | # Development 84 | 85 | To run the code from the docker image by hand or debug issues there, run the following: 86 | 87 | ``` 88 | docker run --name=Glue --rm -i -t --entrypoint=bash owasp/glue 89 | ``` 90 | 91 | Then, you will be in the root of the project. You can run the tool as though you were developing it. 92 | 93 | # Configuration files 94 | 95 | For advanced usage scenarios, you can save your configuration 96 | and use it at runtime. 97 | 98 | # License 99 | 100 | [Apache 2](http://www.apache.org/licenses/LICENSE-2.0) 101 | -------------------------------------------------------------------------------- /FEATURES: -------------------------------------------------------------------------------- 1 | # Capabilities 2 | * AV 3 | * FIM 4 | 5 | FUTURE: 6 | * oscap - https://www.redhat.com/archives/open-scap-list/2013-May/msg00007.html 7 | * pmd 8 | * brakeman 9 | * dependency checker 10 | * codescan 11 | 12 | # Images 13 | * Raw directory on file system. 14 | * git 15 | * docker 16 | 17 | FUTURE: 18 | * iso 19 | * vmdk, vdi, ami -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec :name => "owasp-glue" 4 | 5 | gem 'rspec-collection_matchers', :group => :test 6 | gem 'rake', :group => :test 7 | gem 'rspec', :group => :test 8 | gem 'webmock', :group => :test 9 | gem 'simplecov', require: false, group: :test 10 | gem 'rspec_junit_formatter', :group => :test 11 | gem 'aruba', '~> 0.14.2', :group => :test 12 | gem 'httparty' 13 | gem 'slack-ruby-client' -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Twitter](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/owaspglue) 3 | [![Ruby Gem](https://img.shields.io/gem/dtv/rails.svg)](https://rubygems.org/gems/owasp-glue) [![Dockerhub](https://img.shields.io/docker/stars/owasp/glue.svg)](https://hub.docker.com/r/owasp/glue/) 4 | [![Travis](https://img.shields.io/travis/USER/REPO.svg)](https://travis-ci.org/OWASP/glue) 5 | 6 | 7 | 8 | 9 | # OWASP Glue 10 | 11 | Glue is a framework for running a series of tools. Generally, it is intended as a backbone for automating a security analysis pipeline of tools. 12 | 13 | # Recommended Usage 14 | 15 | For those wishing to run Glue, we recommend using the docker image because 16 | it should have the other tools it uses available already and configured. 17 | See the documentation for more info. [Glue Docker Documentation](./DOCKER.md) 18 | 19 | For those interested in how to use Glue in a DevOps context, see 20 | [Glue DevOps Integration Options](./DEVOPS.md) 21 | 22 | Checkout the [Playground](./PLAYGROUND.md) to get a better understanding of Glue's features and how you can use them. 23 | 24 | # Local run 25 | 26 | `docker run owasp/glue` 27 | 28 | # Installation for Development 29 | 30 | ``` 31 | git clone https://github.com/owasp/glue 32 | cd glue -- RVM will set to 2.3.1 with Gemset Glue 33 | gem install bundler 34 | bundle install 35 | ``` 36 | 37 | ## Running in Development 38 | 39 | ``` 40 | cd lib 41 | ../bin/glue -h 42 | ``` 43 | 44 | # Extending Glue 45 | 46 | Glue is intended to be extended through added "tasks". To add a new tool, 47 | copy an existing task and tweak to make it work for the tool in question. 48 | 49 | # Usage 50 | 51 | `Glue ` 52 | 53 | ## Options 54 | 55 | Common options include: 56 | ``` 57 | -d for debug 58 | -f for format (takes "json", "csv", "jira") 59 | ``` 60 | 61 | For a full list of options, use `Glue --help` or see the [OPTIONS.md](./OPTIONS.md) file. 62 | 63 | ## Target 64 | 65 | The target can be: 66 | * Filesystem (which is analyzed in place) 67 | * Git repo (which is cloned for analysis) 68 | * Other types of images (.iso, docker, etc. are experimental) 69 | 70 | 71 | # Dependencies 72 | 73 | * clamav 74 | * hashdeep 75 | * rm (*nix) 76 | * git 77 | * mount (*nix) 78 | * docker 79 | 80 | # Development 81 | 82 | To run the code, run the following from the root directory: 83 | 84 | `>ruby bin/Glue target` 85 | 86 | To build a gem, just run: 87 | 88 | `gem build Glue.gemspec` 89 | 90 | 91 | # Integration 92 | 93 | ## Git Hooks 94 | 95 | First, grab the hook from the code. 96 | ``` 97 | meditation:hooks mk$ cp /area53/owasp/Glue/hooks/pre-commit . 98 | ``` 99 | 100 | Then make it executable. 101 | ``` 102 | meditation:hooks mk$ chmod +x pre-commit 103 | ``` 104 | 105 | Make sure the shell you are committing in can see docker. 106 | ``` 107 | meditation:hooks mk$ eval "$(docker-machine env default)" 108 | ``` 109 | 110 | Now go test and make a change and commit a file. 111 | The result should be that Glue runs against your 112 | code and will not allow commits unless the results 113 | are clean. (Which is not necessarily a reasonable 114 | expectation) 115 | 116 | 117 | # Configuration files 118 | 119 | For advanced usage scenarios, you can save your configuration and use it at runtime. 120 | 121 | # Authors 122 | 123 | Matt Konda 124 | Alex Lock 125 | Rafa Perez 126 | 127 | # License 128 | 129 | Apache 2: http://www.apache.org/licenses/LICENSE-2.0 130 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new 5 | 6 | task :default => :spec 7 | task :test => :spec 8 | -------------------------------------------------------------------------------- /bin/glue: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #Adjust path in case called directly and not through gem 3 | $:.unshift "#{File.expand_path(File.dirname(__FILE__))}/../lib" 4 | 5 | require 'glue' 6 | require 'glue/options' 7 | require 'glue/version' 8 | 9 | #Parse options 10 | begin 11 | options, parser = Glue::Options.parse! ARGV 12 | rescue OptionParser::ParseError => e 13 | $stderr.puts e.message.capitalize 14 | $stderr.puts "Please see `glue --help` for valid options" 15 | exit -1 16 | end 17 | 18 | #Exit early for these options 19 | if options[:list_checks] or options[:list_optional_checks] 20 | Glue.list_checks options 21 | exit 22 | elsif options[:create_config] 23 | Glue.dump_config options 24 | exit 25 | elsif options[:show_help] 26 | puts parser 27 | exit 28 | elsif options[:show_version] 29 | puts "Glue #{Glue::Version}" 30 | exit 31 | end 32 | 33 | #Set application path according to the commandline arguments 34 | unless options[:target] 35 | if ARGV[-1].nil? 36 | options[:target] = "." 37 | else 38 | options[:target] = ARGV[-1] 39 | end 40 | end 41 | 42 | trap("INT") do 43 | $stderr.puts "\nInterrupted - exiting." 44 | 45 | if options[:debug] 46 | $stderr.puts caller 47 | end 48 | 49 | exit! 50 | end 51 | 52 | if options[:quiet].nil? 53 | options[:quiet] = :command_line 54 | end 55 | 56 | begin 57 | #Run scan and output a report 58 | tracker = Glue.run options.merge(:print_report => true, :quiet => options[:quiet]) 59 | worst_finding = tracker.get_worst_finding 60 | 61 | #Return error code if --exit-on-warn is used and warnings were found 62 | if options[:exit_on_warn] and not tracker.findings.empty? 63 | if options[:severity_threshold] 64 | if worst_finding.severity >= options[:severity_threshold].to_i 65 | puts "Worst finding (#{worst_finding.severity}) meets severity threshold (#{options[:severity_threshold]})" 66 | exit worst_finding.severity 67 | else 68 | puts "Worst finding (#{worst_finding.severity}) did not meet severity threshold (#{options[:severity_threshold]})" 69 | end 70 | else 71 | exit Glue::Warnings_Found_Exit_Code 72 | end 73 | end 74 | rescue Glue::NoTargetError => e 75 | $stderr.puts e.message 76 | exit 1 77 | end 78 | -------------------------------------------------------------------------------- /config.yml.example: -------------------------------------------------------------------------------- 1 | # Glue config. 2 | # 3 | # Generally this file wants to be in ~/.glue/ 4 | # or at the root of the files. 5 | # 6 | # 7 | # Configuration for local JIRA. 8 | jira_project: "APPS" 9 | jira_component: "Automated Findings" 10 | jira_api_url: "https://jira.yourdomain.com/rest/api/2" 11 | # This is auth for some environments ... need to find alternative. 12 | jira_cookie: "" 13 | 14 | pivotal_token: abc323234343431c5cd5f83 15 | pivotal_api: https://www.pivotaltracker.com/services/v5/projects/ 16 | pivotal_project: 18343407 17 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | In Glue, we are using docker to handle configuration of our dependent tools. 2 | 3 | 1. Ensure you have a docker environment. 4 | E.g. boot2docker shell-init 5 | 6 | 2. Make sure tooks here are built: 7 | 1. docker/zap/build.sh 8 | 2. ... 9 | 3. Ensure they run with the command in: 10 | 1. /docker/zap/run.sh 11 | 2. 12 | -------------------------------------------------------------------------------- /docker/glue/Dockerfile-raw: -------------------------------------------------------------------------------- 1 | FROM ruby:2.4-alpine 2 | 3 | LABEL maintainer="mkonda@jemurai.com" 4 | LABEL maintainer="omer.levihevroni@owasp.org" 5 | 6 | WORKDIR /glue 7 | 8 | RUN apk add --update build-base curl-dev 9 | 10 | COPY Gemfile Gemfile.lock glue.gemspec /glue/ 11 | COPY ./bin/glue /glue/bin/glue 12 | COPY ./lib/glue/version.rb /glue/lib/glue/ 13 | 14 | RUN bundle install --without development test 15 | 16 | COPY /lib /glue/lib 17 | 18 | CMD ./bin/glue -------------------------------------------------------------------------------- /docker/glue/build.sh: -------------------------------------------------------------------------------- 1 | #docker build -f Dockerfile -t owasp/glue:0.6 . 2 | docker build --no-cache -f Dockerfile -t owasp/glue:0.9.4 -t owasp/glue:latest . 3 | -------------------------------------------------------------------------------- /docker/java/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # Java Based Tools 3 | # Dependency Check 4 | RUN mkdir /depcheck 5 | WORKDIR /depcheck 6 | RUN wget http://dl.bintray.com/jeremy-long/owasp/dependency-check-1.3.1-release.zip 7 | RUN unzip dependency-check-1.3.1-release.zip 8 | 9 | # ZAP 10 | RUN mkdir /zap 11 | WORKDIR /zap 12 | RUN wget https://github.com/zaproxy/zaproxy/releases/download/2.4.2/ZAP_2.4.2_Linux.tar.gz /zap 13 | RUN tar -zxvf /zap/*.gz 14 | 15 | # Node JS Tools 16 | -------------------------------------------------------------------------------- /docker/phpcs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | MAINTAINER Matt Konda 3 | # Environment 4 | RUN apt-get update && apt-get install -y ruby ruby-dev nodejs openjdk-7-jre 5 | 6 | # Pipeline App 7 | 8 | # Working Dir 9 | RUN mkdir /pipeline 10 | WORKDIR /pipeline 11 | 12 | # Core Pipeline 13 | RUN git clone https://github.com/owasp/pipeline.git 14 | RUN gem install bundler 15 | RUN bundle install -j20 16 | 17 | 18 | # Java Based Tools 19 | # Dependency Check 20 | RUN mkdir /depcheck 21 | WORKDIR /depcheck 22 | RUN wget http://dl.bintray.com/jeremy-long/owasp/dependency-check-1.3.1-release.zip 23 | RUN unzip dependency-check-1.3.1-release.zip 24 | 25 | # ZAP 26 | RUN mkdir /zap 27 | WORKDIR /zap 28 | RUN wget https://github.com/zaproxy/zaproxy/releases/download/2.4.2/ZAP_2.4.2_Linux.tar.gz /zap 29 | RUN tar -zxvf /zap/*.gz 30 | 31 | # Node JS Tools 32 | -------------------------------------------------------------------------------- /docker/retire.js/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | MAINTAINER Matt Konda 3 | RUN apt-get update && apt-get install -y nodejs npm 4 | RUN npm install -g retire 5 | RUN ln -s /usr/bin/nodejs /usr/bin/node 6 | CMD [ "retire" , "retire -v" ] 7 | 8 | -------------------------------------------------------------------------------- /docker/zap/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM owasp/zap2docker-weekly 2 | MAINTAINER Matt Konda 3 | RUN apt-get install python-pip 4 | RUN pip install --upgrade git+https://github.com/Grunny/zap-cli.git 5 | RUN chown -R zap /zap/ 6 | ENV ZAP_PORT 8080 7 | -------------------------------------------------------------------------------- /docker/zap/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker build -t pipeline/zap:v1 . -------------------------------------------------------------------------------- /docker/zap/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # docker run -t -i pipeline/zap:v1 zap-cli quick-scan http://localhost:4000/ 4 | 5 | docker run -u zap -i pipeline/zap:v1 zap-cli --api-key 123 quick-scan --spider -l Medium -sc -r -o '-config api.key=123' http://www.jemurai.com 6 | 7 | -------------------------------------------------------------------------------- /docs/dynamic_task.md: -------------------------------------------------------------------------------- 1 | # Dynamic Task 2 | The dynamic task allows you to integrate new security tools into Glue without writing new code. 3 | This is done by creating a mapping files that instruct Glue how to create findings from the tool report. 4 | Currently, there is only support for JSON reports, but this can be extended in the future. 5 | 6 | ## Integrating a New Tool 7 | ### Create the mapping file 8 | The mapping file should map between the issues in the report to the matching fields in Glue's finding. 9 | THis is a JSON file, and you can find the schema [here](/lib/glue/mappings/schema.json). 10 | Here is the mapping file for [MobSF](https://github.com/MobSF/Mobile-Security-Framework-MobSF): 11 | ```JSON 12 | { 13 | "task_name": "MobSF", 14 | "app_name": "name", 15 | "mappings": [ 16 | { 17 | "key": "manifest", 18 | "properties": { 19 | "description": "desc", 20 | "detail": "title", 21 | "source": "title", 22 | "severity": "stat", 23 | "fingerprint": "title" 24 | } 25 | } 26 | ] 27 | } 28 | ``` 29 | You'll need to create a similar file in order to process the report of your tool. 30 | Checkout the schema, it contains the documentation for all the fields. 31 | ### Using the new schema 32 | After creating the mapping file, run Glue in the following format: 33 | ``` 34 | ruby bin/glue -t Dynamic -T report.json --mapping-file mapping.json 35 | ``` 36 | Replace `report.json` and `mapping.json` with the paths to the relevant files. 37 | 38 | ## Built-In Tools 39 | The dynamic task support built-in mappings, that are shipped with Glue. 40 | Those mapping files aims to help others to use the mapping your created. 41 | 42 | ### MobSF 43 | 44 | To parse MobSF report, use the following format: 45 | ``` 46 | ruby bin/glue -t Dynamic -T report.json --mapping-file mobsf 47 | ``` 48 | where `report.json` is your report 49 | 50 | ### Zaproxy 51 | To parse Zaproxy report, you first need to generate it by using the API: 52 | ``` 53 | curl --fail $PROXY_URL/OTHER/core/other/jsonreport/?formMethod=GET --output report.json 54 | ``` 55 | Than, use [jq](https://stedolan.github.io/jq/) to flatten the report so Glue can parse it: 56 | ``` 57 | jq '{ "@name" : .site."@name", 58 | "alerts": 59 | [.site.alerts[] as $in 60 | | $in.instances[] as $h 61 | | $in 62 | | $h * $in 63 | | { 64 | "description": $in.desc, 65 | "source": "URI: \($h.uri) Method: \($h.method)", 66 | "detail": "\($in.name) \n Evidence: \($h.evidence) \n Solution: \($in.solution) \n Other info: \($in.otherinfo) \n Reference: \($in.reference)", 67 | "severity": $in.riskdesc | split(" ") | .[0], 68 | "fingerprint": "\($in.pluginid)_\($h.uri)_\($h.method)" 69 | } 70 | ] 71 | } ' report.json > output.json 72 | ``` 73 | Now use Glue to process the report: 74 | ``` 75 | ruby bin/glue -t Dynamic -T report.json --mapping-file zaproxy 76 | ``` 77 | You can modify the jq pattern to modify the fields in Glue's results. For example, you might want to remove `otherinfo`, or use something else for the fingerprint. 78 | ## Adding a new tool 79 | First, create the mapping file. 80 | After you have a working mapping file, open a PR and add it under `/lib/glue/mappings/`. 81 | Also add a test to `dynamic_spec`, see mobsf tests for reference. -------------------------------------------------------------------------------- /glue.gemspec: -------------------------------------------------------------------------------- 1 | require './lib/glue/version' 2 | 3 | Gem::Specification.new do |s| 4 | s.name = %q{owasp-glue} 5 | s.version = Glue::Version 6 | s.authors = ["Matt Konda", "Alex Lock", "Rafa Perez", "Runako Godfrey", "Reuben Swartz", "Varun Yeldandi"] 7 | s.email = "matt.konda@owasp.org" 8 | s.summary = "Security toolchain for software build automation." 9 | s.description = "Glue detects security vulnerabilities in code." 10 | s.homepage = "http://github.com/OWASP/glue" 11 | s.files = ["bin/glue", "CHANGES", "FEATURES", "README.md"] + Dir["lib/**/*"] 12 | s.executables = ["glue"] 13 | s.license = "Apache 2" 14 | s.add_dependency "terminal-table", ">= 1.4" 15 | s.add_dependency "fastercsv", ">= 1.5" 16 | s.add_dependency "highline", ">= 1.6.20" 17 | s.add_dependency "multi_json", ">= 1.2" 18 | s.add_dependency "bundler-audit", ">= 0.3.1" 19 | s.add_dependency "brakeman", ">= 3.0.5" 20 | s.add_dependency "curb", ">= 0.8.8" 21 | s.add_dependency "jsonpath", ">= 0.5.7" 22 | s.add_dependency "nokogiri", ">=1.6.6.2" 23 | s.add_dependency "rake" 24 | s.add_dependency "redcarpet" 25 | s.add_dependency "jira-ruby" 26 | s.add_dependency "httparty" 27 | s.add_dependency "json-schema" 28 | 29 | s.add_development_dependency "pry" 30 | s.add_development_dependency "pry-byebug" 31 | end 32 | -------------------------------------------------------------------------------- /glue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/glue.png -------------------------------------------------------------------------------- /integrations/hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "Pre-commit: Testing for security issues ..." 3 | 4 | # -z tells glue to emit error on warnings 5 | # -v mount volumes 6 | # -l code stage 7 | docker run --name="glue-precommit" --rm=true -v /Users/mk/line/tmp/triage/:/tmp/triage/ owasp/glue:0.9.0 -z -l code /tmp/triage/ 8 | RESULT=$? 9 | if [$RESULT -eq 0] 10 | then 11 | echo "pre-commit: No detected security errors \o/" 12 | exit 0 13 | else 14 | echo "pre-commit: Aborting commit due to security errors" 15 | echo "3 suggests security warning. 127 suggests failure to run." 16 | exit $RESULT 17 | fi 18 | -------------------------------------------------------------------------------- /integrations/jenkins/glue-active.sh: -------------------------------------------------------------------------------- 1 | # Use existing managed scripts to add this to your Jenkins build 2 | # Obviously depends on having docker-machine and docker set up. 3 | 4 | echo "Starting Glue" 5 | echo "Script executed from: ${PWD}" 6 | 7 | eval $(docker-machine env patched) 8 | GUID="$RANDOM" 9 | docker run --rm=true --name=glue_ci_active owasp/glue:0.9.0 -z -t zap -d https://staging.jemurai.com/ 10 | -------------------------------------------------------------------------------- /integrations/jenkins/glue-code.sh: -------------------------------------------------------------------------------- 1 | # Use existing managed scripts to add this to your Jenkins build 2 | # Obviously depends on having docker-machine and docker set up. 3 | 4 | echo "Starting Glue" 5 | echo "Script executed from: ${PWD}" 6 | 7 | eval $(docker-machine env patched) 8 | GUID="$RANDOM" 9 | docker run --rm=true --name=glue_ci_code -v ${PWD}:/tmp/$GUID/ owasp/glue:0.9.0 -z -l code -d /tmp/$GUID/ 10 | -------------------------------------------------------------------------------- /integrations/jenkins/glue-pipeline.groovy: -------------------------------------------------------------------------------- 1 | node { 2 | stage("Checkout"){ 3 | git url: "https://github.com/Jemurai/triage.git" 4 | } 5 | stage("Glue-Static"){ 6 | sh '''echo "Starting Glue" 7 | echo "Script executed from: ${PWD}" 8 | 9 | eval $(docker-machine env default) 10 | GUID="$RANDOM" 11 | docker run --rm=true --name=glue_ci_code -v ${PWD}:/tmp/$GUID/ owasp/glue -z -t brakeman,sfl -d /tmp/$GUID/''' 12 | } 13 | stage("Deploy"){ 14 | echo "TODO: DEPLOY" 15 | } 16 | stage("Glue-Dynamic"){ 17 | echo "TODO: ZAP" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/glue/event.rb: -------------------------------------------------------------------------------- 1 | # Tracks internal glue events. 2 | # Can be used for control, but also tracking what happens. 3 | class Glue::Event 4 | attr_reader :parent 5 | attr_accessor :path 6 | attr_accessor :appname 7 | 8 | def initialize appname, parent = nil 9 | @appname = appname 10 | @parent = parent 11 | @timestamp = Time.now 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /lib/glue/filters.rb: -------------------------------------------------------------------------------- 1 | class Glue::Filters 2 | @filters = [] 3 | 4 | #Add a task. This will call +_klass_.new+ when running tests 5 | def self.add klass 6 | @filters << klass unless @filters.include? klass 7 | end 8 | 9 | def self.filters 10 | @filters 11 | end 12 | 13 | def self.initialize_filters filters_directory = "" 14 | Dir.glob(File.join(filters_directory, "*.rb")).sort.each do |f| 15 | require f 16 | end 17 | end 18 | 19 | #No need to use this directly. 20 | def initialize options = { } 21 | end 22 | 23 | #Run all the tasks on the given Tracker. 24 | #Returns a new instance of tasks with the results. 25 | def self.filter(tracker) 26 | @filters.each do |c| 27 | filter = c.new() 28 | begin 29 | filter.filter tracker 30 | rescue => e 31 | Glue.error e.message 32 | tracker.error e 33 | end 34 | end 35 | end 36 | end 37 | 38 | #Load all files in filters/ directory 39 | Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/filters/*.rb").sort.each do |f| 40 | require f.match(/(glue\/filters\/.*)\.rb$/)[0] 41 | end 42 | -------------------------------------------------------------------------------- /lib/glue/filters/base_filter.rb: -------------------------------------------------------------------------------- 1 | class Glue::BaseFilter 2 | attr_accessor :name 3 | attr_accessor :description 4 | 5 | def initialize() 6 | end 7 | 8 | def name 9 | @name 10 | end 11 | 12 | def description 13 | @description 14 | end 15 | 16 | def filter 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /lib/glue/filters/contrast_severity_filter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/filters/base_filter' 2 | require 'jira-ruby' 3 | 4 | class Glue::ContrastSeverityFilter < Glue::BaseFilter 5 | 6 | Glue::Filters.add self 7 | 8 | SEVERITIES = { 9 | 'NOTE' => 1, 10 | 'LOW' => 2, 11 | 'MEDIUM' => 3, 12 | 'HIGH' => 4, 13 | 'CRITICAL' => 5 14 | } 15 | 16 | def initialize 17 | @name = "Contrast Severity Filter" 18 | @description = "Checks that each issue meets a minimum specified threshold (when specified)." 19 | @format = :to_jira 20 | end 21 | 22 | def filter tracker 23 | Glue.debug "Starting Contrast Severity Filter" 24 | Glue.debug "Minimum: #{tracker.options[:minimum_contrast_severity]}" 25 | if tracker.options[:minimum_contrast_severity].nil? || tracker.options[:minimum_contrast_severity].blank? 26 | Glue.debug "No minimum found, skipping filter." 27 | return # Bail in the case where a minimum isn't specified. 28 | end 29 | 30 | begin 31 | Glue.debug "Have #{tracker.findings.count} items pre Contrast severity filter." 32 | 33 | potential_findings = Array.new(tracker.findings) 34 | tracker.findings.clear 35 | 36 | minimum = tracker.options[:minimum_contrast_severity] ? SEVERITIES[tracker.options[:minimum_contrast_severity].upcase] : nil 37 | if minimum.nil? 38 | # Warn and bail if not found. 39 | Glue.debug "Specified minimum doesn't match any of the Contrast severities, skipping filter." 40 | return 41 | end 42 | 43 | #potential_findings.each { |f| Glue.debug "Checking Finding -> Task: #{f.task} | Contrast: #{contrast?(f)} | sev: #{f.severity} | below_minimum?: #{below_minimum?(f, minimum)}" } 44 | filtered = potential_findings.reject { |finding| contrast?(finding) && below_minimum?(finding, minimum) } 45 | filtered.each { |finding| tracker.report finding } 46 | 47 | Glue.debug "Have #{tracker.findings.count} items post Contrast severity filter." 48 | rescue Exception => e 49 | puts e 50 | puts e.backtrace.inspect 51 | end 52 | end 53 | 54 | private 55 | def contrast? finding 56 | finding.task == "Contrast" 57 | end 58 | 59 | def below_minimum? finding, minimum 60 | if is_number?(finding.severity) 61 | finding_severity = Float(finding.severity) 62 | else 63 | finding_severity = SEVERITIES[finding.severity.upcase] 64 | end 65 | 66 | finding_severity < minimum 67 | end 68 | 69 | def is_number? string 70 | true if Float(string) rescue false 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/glue/filters/file_filter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/filters/base_filter' 2 | require 'jira-ruby' 3 | require 'date' 4 | 5 | class Glue::FileFilter < Glue::BaseFilter 6 | 7 | Glue::Filters.add self 8 | 9 | def initialize 10 | @name = "File Filter" 11 | @description = "Checks that each issue against exisiting issues status file" 12 | end 13 | 14 | def filter tracker 15 | if (tracker.options[:finding_file_path].nil?) 16 | return 17 | end 18 | exisiting_finding = {} 19 | if (File.exist? tracker.options[:finding_file_path]) 20 | exisiting_finding = JSON.parse!(File.read tracker.options[:finding_file_path]) 21 | end 22 | 23 | potential_findings = Array.new(tracker.findings) 24 | tracker.findings.clear 25 | 26 | for finding in potential_findings do 27 | if (!exisiting_finding.key? finding.fingerprint) 28 | exisiting_finding[finding.fingerprint] = "new" 29 | end 30 | 31 | if exisiting_finding[finding.fingerprint] == "new" 32 | tracker.report finding 33 | elsif exisiting_finding[finding.fingerprint].starts_with? "postpone:" 34 | date_raw = exisiting_finding[finding.fingerprint].split("postpone:")[1] 35 | begin 36 | date = Date.strptime(date_raw, "%d-%m-%Y") 37 | if (date <= Date.today) 38 | tracker.report finding 39 | end 40 | rescue => e 41 | Glue.error "failed to parse date: #{e}" 42 | end 43 | end 44 | end 45 | 46 | File.open(tracker.options[:finding_file_path], 'w') {|f| f << JSON.pretty_generate(exisiting_finding)} 47 | 48 | return tracker.findings 49 | end 50 | 51 | 52 | end 53 | -------------------------------------------------------------------------------- /lib/glue/filters/jira_one_time_filter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/filters/base_filter' 2 | require 'jira-ruby' 3 | 4 | class Glue::JiraOneTimeFilter < Glue::BaseFilter 5 | 6 | Glue::Filters.add self 7 | 8 | def initialize 9 | @name = "Jira One Time Filter" 10 | @description = "Checks that each issue that will be reported doesn't already exist in JIRA." 11 | @format = :to_jira 12 | end 13 | 14 | def filter tracker 15 | 16 | if !tracker.options[:output_format].include?(@format) 17 | return # Bail in the case where JIRA isn't being used. 18 | end 19 | Glue.debug "Have #{tracker.findings.count} items pre JIRA One Time filter." 20 | options = { 21 | :username => tracker.options[:jira_username], 22 | :password => tracker.options[:jira_password], 23 | :site => tracker.options[:jira_api_url], 24 | :context_path => tracker.options[:jira_api_context], 25 | :auth_type => :basic, 26 | :http_debug => :true 27 | } 28 | @project = tracker.options[:jira_project] 29 | @component = tracker.options[:jira_component] 30 | @jira = JIRA::Client.new(options) 31 | 32 | potential_findings = Array.new(tracker.findings) 33 | tracker.findings.clear 34 | potential_findings.each do |finding| 35 | if confirm_new finding 36 | tracker.report finding 37 | end 38 | end 39 | Glue.debug "Have #{tracker.findings.count} items post JIRA One Time filter." 40 | end 41 | 42 | private 43 | def confirm_new finding 44 | count = 0 45 | 46 | @jira.Issue.jql("project=#{@project} AND description ~ '#{finding.fingerprint}' AND resolution is EMPTY").each do |issue| 47 | count = count + 1 # Must have at least 1 issue with fingerprint. 48 | end 49 | Glue.debug "Found #{count} items for #{finding.description}" 50 | if count > 0 then 51 | return false 52 | else 53 | return true # New! 54 | end 55 | end 56 | 57 | end 58 | -------------------------------------------------------------------------------- /lib/glue/filters/pivotal_one_time_filter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/filters/base_filter' 2 | require 'curb' 3 | require 'json' 4 | 5 | class Glue::PivotalOneTimeFilter < Glue::BaseFilter 6 | 7 | Glue::Filters.add self 8 | 9 | def initialize 10 | @name = "Pivotal One Time Filter" 11 | @description = "Checks that each issue that will be reported doesn't already exist in Pivotal." 12 | @format = :to_pivotal 13 | end 14 | 15 | # export TOKEN='your Pivotal Tracker API token' 16 | # export PROJECT_ID=99 17 | # curl -X GET -H "X-TrackerToken: $TOKEN" "https://www.pivotaltracker.com/services/v5/projects/$PROJECT_ID/stories?date_format=millis&filter=label%3Aplans" 18 | 19 | def filter tracker 20 | if !tracker.options[:output_format].include?(@format) 21 | return # Bail in the case where JIRA isn't being used. 22 | end 23 | @project = tracker.options[:pivotal_project] 24 | @token = tracker.options[:pivotal_token] 25 | @api = tracker.options[:pivotal_api_url] 26 | @url = "#{@api}/#{@project}/stories" 27 | 28 | Glue.debug "Have #{tracker.findings.count} items pre Pivotal One Time filter." 29 | 30 | potential_findings = Array.new(tracker.findings) 31 | tracker.findings.clear 32 | potential_findings.each do |finding| 33 | if confirm_new finding 34 | tracker.report finding 35 | end 36 | end 37 | Glue.debug "Have #{tracker.findings.count} items post Pivotal One Time filter." 38 | end 39 | 40 | private 41 | def confirm_new finding 42 | count = 0 43 | filter_url = "#{@url}?filter=label:#{finding.fingerprint}" 44 | c = Curl.get(filter_url) do |curl| 45 | curl.headers['Content-Type'] = 'application/json' 46 | curl.headers['X-TrackerToken'] = "#{@token}" 47 | #curl.verbose=true 48 | end 49 | json = c.body_str 50 | Glue.debug "JSON response: #{json}" 51 | items = JSON.parse(json) 52 | count = items.count 53 | Glue.debug "Found #{count} items for #{finding.description}" 54 | if count > 0 then 55 | return false 56 | else 57 | return true # New! 58 | end 59 | end 60 | 61 | end 62 | -------------------------------------------------------------------------------- /lib/glue/filters/remove_all_filter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/filters/base_filter' 2 | 3 | class Glue::RemoveAllFilter < Glue::BaseFilter 4 | 5 | #Glue::Filters.add self 6 | 7 | def initialize 8 | @name = "Remove All Filter" 9 | @description = "Remove all the things..." 10 | end 11 | 12 | def filter tracker 13 | tracker.findings.clear 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /lib/glue/filters/zap_consdensing_filter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/filters/base_filter' 2 | 3 | class Glue::ZAPCondensingFilter < Glue::BaseFilter 4 | 5 | Glue::Filters.add self 6 | 7 | def initialize 8 | @name = "ZAP Condensing Filter" 9 | @description = "Consolidate N ZAP warnings to one per issue type." 10 | end 11 | 12 | def filter tracker 13 | Glue.debug "Have #{tracker.findings.count} items pre ZAP filter." 14 | tracker.findings.each do |finding| 15 | if zap? finding 16 | if xframe? finding 17 | record tracker,finding 18 | elsif xcontenttypeoptions? finding 19 | record tracker, finding 20 | elsif dirbrowsing? finding 21 | record tracker, finding 22 | elsif xxssprotection? finding 23 | record tracker, finding 24 | end 25 | end 26 | end 27 | Glue.debug "Have #{tracker.findings.count} items post ZAP filter." 28 | end 29 | 30 | def zap? finding 31 | if finding.source =~ /\AZAP/ 32 | return true 33 | end 34 | return false 35 | end 36 | 37 | def xframe? finding 38 | if finding.description == "X-Frame-Options header is not included in the HTTP response to protect against 'ClickJacking' attacks." 39 | return true 40 | end 41 | return false 42 | end 43 | 44 | def xcontenttypeoptions? finding 45 | if finding.description == "The Anti-MIME-Sniffing header X-Content-Type-Options was not set to 'nosniff'. This allows older versions of Internet Explorer and Chrome to perform MIME-sniffing on the response body, potentially causing the response body to be interpreted and displayed as a content type other than the declared content type. Current (early 2014) and legacy versions of Firefox will use the declared content type (if one is set), rather than performing MIME-sniffing." 46 | return true 47 | end 48 | return false 49 | end 50 | 51 | def dirbrowsing? finding 52 | if finding.description == "It is possible to view the directory listing. Directory listing may reveal hidden scripts, include files , backup source files etc which be accessed to read sensitive information." 53 | return true 54 | end 55 | return false 56 | end 57 | 58 | def xxssprotection? finding 59 | if finding.description == "Web Browser XSS Protection is not enabled, or is disabled by the configuration of the 'X-XSS-Protection' HTTP response header on the web server" 60 | return true 61 | end 62 | return false 63 | end 64 | 65 | # Is it always true that the findings will be on the same site? 66 | # For now, we can assume that. 67 | # Note that this may not be an efficient way to to do this, but it seems to work as a first pass. 68 | def record tracker, finding 69 | tracker.findings.delete_if do |to_delete| 70 | to_delete.description == finding.description 71 | end 72 | finding.detail << "\n\t** Consolidated ** - Potentially identified in > 1 spot." # Make sure to note that there were 73 | tracker.findings.push finding 74 | end 75 | 76 | end 77 | -------------------------------------------------------------------------------- /lib/glue/finding.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | class Glue::Finding 4 | attr_reader :task 5 | attr_reader :appname 6 | attr_reader :timestamp 7 | #Number between 1 to 3, when 1 is the lowest severity. 8 | attr_reader :severity 9 | attr_reader :source 10 | attr_reader :description 11 | attr_reader :detail 12 | attr_reader :fingerprint 13 | 14 | def initialize appname, description, detail, source, severity, fingerprint, task 15 | unless severity.is_a? Integer 16 | raise ArgumentError.new("Severity should be a number, got: #{severity}") 17 | end 18 | unless severity > 0 && severity < 4 19 | raise ArgumentError.new("Severity should be between 1 to 3, not #{severity}") 20 | end 21 | 22 | @task = task 23 | @task.sub!(/^Glue::/, '') if @task 24 | 25 | @appname = appname 26 | @timestamp = Time.now 27 | @description = description 28 | @detail = detail 29 | @source = source 30 | @stringsrc = source.to_s 31 | @severity = severity 32 | @fingerprint = fingerprint 33 | end 34 | 35 | def to_string 36 | s = "\n\tDescription: #{@description}" 37 | s << "\n\n\tTimestamp: #{@timestamp}" 38 | s << "\n\n\tSource: #{@stringsrc}" 39 | s << "\n\n\tSeverity: #{@severity}" 40 | s << "\n\n\tFingerprint: #{@fingerprint}" 41 | s << "\n\n\tFound by: #{@task}" 42 | s << "\n\n\tDetail: #{@detail}" 43 | s 44 | end 45 | 46 | def to_csv 47 | [@appname, @description, @timestamp, @source.to_s, @severity, @fingerprint, @detail] 48 | end 49 | 50 | def to_json 51 | json = { 52 | 'task' => @task, 53 | 'appname' => @appname, 54 | 'description' => @description, 55 | 'fingerprint' => @fingerprint, 56 | 'detail' => @detail, 57 | 'source' => @source, 58 | 'severity' => @severity, 59 | 'timestamp' => @timestamp 60 | }.to_json 61 | json 62 | end 63 | 64 | end 65 | -------------------------------------------------------------------------------- /lib/glue/mappings/mobsf.json: -------------------------------------------------------------------------------- 1 | { 2 | "task_name": "MobSF", 3 | "app_name": "app_name", 4 | "mappings": [ 5 | { 6 | "key": "manifest_analysis", 7 | "properties": { 8 | "description": "desc", 9 | "detail": "title", 10 | "source": "title", 11 | "severity": "stat", 12 | "fingerprint": "title" 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /lib/glue/mappings/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-06/schema#", 3 | "title": "Mapping", 4 | "description": "A mapping used by Glue to extract findings from a security tool report", 5 | "type": "object", 6 | "required": ["task_name", "app_name", "mappings"], 7 | "properties": { 8 | "task_name": { 9 | "type": "string", 10 | "description": "The name of the task (tool) the produce those findings" 11 | }, 12 | "app_name": { 13 | "type": "string", 14 | "description": "Path to the property in the report file that contains the target app name" 15 | }, 16 | "mappings": { 17 | "type": "array", 18 | "description": "An array of mappings", 19 | "items": { 20 | "type": "object", 21 | "required": ["key"], 22 | "properties": { 23 | "key": { 24 | "type": "string", 25 | "description": "The key in the JSON report to an array that contains findings" 26 | }, 27 | "properties": { 28 | "type": "object", 29 | "description": "The mapping between the items in the array and Glue's findings", 30 | "required": ["description", "detail", "source", "severity", "fingerprint"], 31 | "properties": { 32 | "description": { 33 | "type": "string", 34 | "description": "The path on the array item to extract the description" 35 | }, 36 | "detail": { 37 | "type": "string", 38 | "description": "The path on the array item to extract the details" 39 | }, 40 | "source": { 41 | "type": "string", 42 | "description": "The path on the array item to extract the source" 43 | }, 44 | "severity": { 45 | "type": "string", 46 | "description": "The path on the array item to extract the severity" 47 | }, 48 | "fingerprint": { 49 | "type": "string", 50 | "description": "The path on the array item to extract the fingerprint" 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /lib/glue/mappings/snyk.json: -------------------------------------------------------------------------------- 1 | { 2 | "task_name": "Snyk", 3 | "app_name": "path", 4 | "mappings": [ 5 | { 6 | "key": "vulnerabilities", 7 | "properties": { 8 | "description": "title", 9 | "detail": "description", 10 | "source": "packageName", 11 | "severity": "severity", 12 | "fingerprint": "id" 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /lib/glue/mappings/zaproxy.json: -------------------------------------------------------------------------------- 1 | { 2 | "task_name": "OWASP Zaproxy", 3 | "app_name": "@name", 4 | "mappings": [ 5 | { 6 | "key": "alerts", 7 | "properties": { 8 | "description": "description", 9 | "detail": "detail", 10 | "source": "source", 11 | "severity": "severity", 12 | "fingerprint": "fingerprint" 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /lib/glue/mounters.rb: -------------------------------------------------------------------------------- 1 | # Knows how to test file types and then defer to the helper. 2 | 3 | require 'glue/event' 4 | 5 | class Glue::Mounters 6 | @mounters = [] 7 | 8 | attr_reader :target 9 | attr_reader :warnings 10 | 11 | def self.add klass 12 | @mounters << klass unless @mounters.include? klass 13 | end 14 | 15 | def self.mounters 16 | @mounters 17 | end 18 | 19 | def initialize 20 | @warnings = [] 21 | end 22 | 23 | def add_warning warning 24 | @warnings << warning 25 | end 26 | 27 | def self.mount tracker 28 | target = tracker.options[:target] 29 | Glue.debug "Mounting target: #{target}" 30 | trigger = Glue::Event.new(tracker.options[:appname]) 31 | @mounters.each do | c | 32 | mounter = c.new trigger, tracker.options 33 | begin 34 | Glue.debug "Checking about mounting #{target} with #{mounter}" 35 | if mounter.supports? target 36 | Glue.notify "Mounting #{target} with #{mounter}" 37 | path = mounter.mount target 38 | Glue.notify "Mounted #{target} with #{mounter}" 39 | return path 40 | end 41 | rescue => e 42 | Glue.notify e.message 43 | end 44 | end 45 | end 46 | 47 | def self.get_mounter_name mounter_class 48 | mounter_class.to_s.split("::").last 49 | end 50 | end 51 | 52 | #Load all files in mounters/ directory 53 | Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/mounters/*.rb").sort.each do |f| 54 | require f.match(/(glue\/mounters\/.*)\.rb$/)[0] 55 | end 56 | -------------------------------------------------------------------------------- /lib/glue/mounters/base_mounter.rb: -------------------------------------------------------------------------------- 1 | 2 | class Glue::BaseMounter 3 | attr_reader :errors 4 | attr_reader :trigger 5 | attr_accessor :name 6 | attr_accessor :description 7 | 8 | def initialize(trigger) 9 | @errors = [] 10 | @trigger = trigger 11 | end 12 | 13 | def error error 14 | @errors << error 15 | end 16 | 17 | def name 18 | @name 19 | end 20 | 21 | def description 22 | @description 23 | end 24 | 25 | def mount 26 | end 27 | 28 | def supports? target 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /lib/glue/mounters/docker_mounter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/mounters/base_mounter' 2 | 3 | class Glue::DockerMounter < Glue::BaseMounter 4 | 5 | Glue::Mounters.add self 6 | 7 | #Pass in path to the root of the Rails application 8 | def initialize trigger, options 9 | super(trigger) 10 | @options = options 11 | end 12 | 13 | def mount target 14 | base = @options[:working_dir] 15 | target = target.slice(0, target.length - 7) 16 | working_target = base + "/docker/" + target + "/" 17 | Glue.notify "Cleaning directory: #{working_target}" 18 | if ! working_target.match(/\A.*\/line\/tmp\/.*/) 19 | Glue.notify "Bailing in case #{working_target} is malicious." 20 | else 21 | result = `rm -rf #{working_target}` 22 | Glue.debug result 23 | result = `mkdir -p #{working_target}` 24 | Glue.debug result 25 | result = `docker export #{target} > #{working_target}#{target}.tar` 26 | Glue.debug result 27 | result = `tar -C #{working_target} -xf #{working_target}#{target}.tar` 28 | Glue.debug result 29 | result = `rm #{working_target}#{target}.tar` 30 | Glue.debug result 31 | end 32 | return working_target 33 | end 34 | 35 | def supports? target 36 | last = target.slice(-7,target.length) 37 | Glue.debug "In Docker mounter, target: #{target} became: #{last} ... wondering if it matched .docker" 38 | if last === ".docker" 39 | return true 40 | else 41 | return false 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/glue/mounters/filesystem_mounter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/mounters/base_mounter' 2 | 3 | class Glue::FileSystemMounter < Glue::BaseMounter 4 | Glue::Mounters.add self 5 | 6 | def initialize trigger, options 7 | super(trigger) 8 | @options = options 9 | @name = "FileSystem" 10 | @description = "Mount a file via normal file system commands." 11 | end 12 | 13 | def mount target 14 | return target 15 | end 16 | 17 | def supports? target 18 | File.directory? target 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/glue/mounters/git_mounter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/mounters/base_mounter' 2 | require 'fileutils' 3 | 4 | class Glue::GitMounter < Glue::BaseMounter 5 | 6 | Glue::Mounters.add self 7 | 8 | def initialize trigger, options 9 | super(trigger) 10 | @options = options 11 | @name = "Git" 12 | @description = "Pull a repo." 13 | end 14 | 15 | def mount target 16 | base = @options[:working_dir] 17 | 18 | Glue.debug "Making base." 19 | FileUtils.mkdir_p base 20 | 21 | # Grap the path part of the git url. 22 | protocol, path, suffix = target.match(/\A(.*\/\/)(.*)(.git)\z/i).captures 23 | working_target = File.expand_path(base + "" + path + "/") 24 | 25 | Glue.notify "Cleaning directory: #{working_target}" 26 | if ! Dir.exists? working_target 27 | Glue.notify "#{working_target} is not a directory." 28 | FileUtils.mkdir_p working_target 29 | else 30 | Glue.debug "Removing : #{working_target}" 31 | FileUtils.rm_rf working_target 32 | FileUtils.mkdir_p working_target 33 | end 34 | # result = `rm -rf #{working_target}` 35 | # puts result 36 | Glue.debug "Cloning into: #{working_target}" 37 | result = `git clone -q #{target} #{working_target}` 38 | # puts result 39 | #end 40 | return working_target 41 | end 42 | 43 | def supports? target 44 | last = target.slice(-4,target.length) 45 | if last === ".git" 46 | return true 47 | else 48 | return false 49 | end 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /lib/glue/mounters/iso_mounter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/mounters/base_mounter' 2 | 3 | class Glue::ISOMounter < Glue::BaseMounter 4 | 5 | # THIS DOESN'T WORK SO DON'T REGISTER FOR NOW 6 | # Glue::Mounters.add self 7 | 8 | def initialize trigger, options 9 | super(trigger) 10 | @options = options 11 | @name = "ISO" 12 | @description = "Mount an iso image." 13 | end 14 | 15 | def mount target 16 | base = @options[:working_dir] 17 | working_target = base + "/" + target + "/" 18 | Glue.notify "Cleaning directory: #{working_target}" 19 | 20 | if ! working_target.match(/\A.*\/line\/tmp\/.*/) 21 | Glue.notify "Bailing in case #{working_target} is malicious." 22 | else 23 | result = `rm -rf #{working_target}` 24 | # puts result 25 | result = `mkdir -p #{working_target}` 26 | # puts result 27 | Glue.notify "Mounting #{target} to #{working_target}" 28 | result = `mount -t iso9660 #{target} #{working_target}` 29 | # puts result 30 | end 31 | return working_target 32 | end 33 | 34 | def supports? target 35 | last = target.slice(-4,target.length) 36 | if last === ".iso" 37 | return true 38 | else 39 | return false 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/glue/mounters/url_mounter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/mounters/base_mounter' 2 | 3 | class Glue::URLMounter < Glue::BaseMounter 4 | Glue::Mounters.add self 5 | 6 | def initialize trigger, options 7 | super(trigger) 8 | @options = options 9 | @name = "URL" 10 | @description = "Mount a url - typically for a live attack." 11 | end 12 | 13 | def mount target 14 | return target 15 | end 16 | 17 | def supports? target 18 | start = target.slice(0,4) 19 | last = target.slice(-4,target.length) 20 | if last === ".git" 21 | return false 22 | elsif start === "http" 23 | return true 24 | else 25 | return false 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/glue/reporters.rb: -------------------------------------------------------------------------------- 1 | class Glue::Reporters 2 | @reporters = [] 3 | 4 | #Add a task. This will call +_klass_.new+ when running tests 5 | def self.add klass 6 | @reporters << klass unless @reporters.include? klass 7 | end 8 | 9 | def self.reporters 10 | @reporters 11 | end 12 | 13 | def self.initialize_reporters reporters_directory = "" 14 | #Load all files in task_directory 15 | Dir.glob(File.join(reporters_directory, "*.rb")).sort.each do |f| 16 | require f 17 | end 18 | end 19 | 20 | #No need to use this directly. 21 | def initialize options = { } 22 | end 23 | 24 | #Run all the tasks on the given Tracker. 25 | #Returns a new instance of tasks with the results. 26 | def self.run_report(tracker) 27 | @reporters.each do |c| 28 | reporter = c.new() 29 | 30 | if tracker.options[:output_format].include?(reporter.format) 31 | begin 32 | output = reporter.run_report(tracker) 33 | if tracker.options[:output_file] 34 | file = File.open(tracker.options[:output_file], 'w'){ |f| f.write(output)} 35 | else 36 | Glue.notify output unless tracker.options[:quiet] 37 | end 38 | rescue => e 39 | Glue.error e.message 40 | tracker.error e 41 | end 42 | end 43 | end 44 | end 45 | 46 | end 47 | 48 | #Load all files in reporters/ directory 49 | Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/reporters/*.rb").sort.each do |f| 50 | require f.match(/(glue\/reporters\/.*)\.rb$/)[0] 51 | end 52 | -------------------------------------------------------------------------------- /lib/glue/reporters/base_reporter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/finding' 2 | 3 | class Glue::BaseReporter 4 | attr_accessor :name, :format 5 | 6 | def initialize() 7 | end 8 | 9 | def run_report(tracker) 10 | Glue.notify "Running base report..." 11 | reports = [ ] 12 | tracker.findings.each do |finding| 13 | reports << out(finding) 14 | end 15 | 16 | combine_reports(reports) 17 | end 18 | 19 | def out(finding) 20 | end 21 | 22 | def combine_reports(reports) 23 | reports.join 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/glue/reporters/csv_reporter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/finding' 2 | require 'glue/reporters/base_reporter' 3 | 4 | class Glue::CSVReporter < Glue::BaseReporter 5 | 6 | Glue::Reporters.add self 7 | 8 | attr_accessor :name, :format 9 | 10 | def initialize() 11 | @name = "CSVReporter" 12 | @format = :to_csv 13 | end 14 | 15 | def out(finding) 16 | finding.to_csv 17 | end 18 | 19 | def combine_reports(reports) 20 | csv_string = CSV.generate do |csv| 21 | reports.each do |report| 22 | csv << report 23 | end 24 | end 25 | 26 | csv_string 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/glue/reporters/json_reporter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/finding' 2 | require 'glue/reporters/base_reporter' 3 | 4 | require 'json' 5 | 6 | class Glue::JSONReporter < Glue::BaseReporter 7 | 8 | Glue::Reporters.add self 9 | 10 | attr_accessor :name, :format 11 | 12 | def initialize() 13 | @name = "JSONReporter" 14 | @format = :to_json 15 | end 16 | 17 | def out(finding) 18 | finding.to_json 19 | end 20 | 21 | def combine_reports(reports) 22 | '[' << reports.join(', ') << ']' 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/glue/reporters/pivotal_reporter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/finding' 2 | require 'glue/reporters/base_reporter' 3 | require 'curb' 4 | require 'json' 5 | 6 | class Glue::PivotalReporter < Glue::BaseReporter 7 | 8 | Glue::Reporters.add self 9 | 10 | attr_accessor :name, :format 11 | 12 | def initialize() 13 | @name = "PivotalReporter" 14 | @format = :to_pivotal 15 | end 16 | 17 | # export TOKEN='your Pivotal Tracker API token' 18 | # export PROJECT_ID=99 19 | # curl -X POST -H "X-TrackerToken: $TOKEN" -H "Content-Type: application/json" -d '{"current_state":"started","estimate":1,"name":"Exhaust ports are ray shielded"}' "https://www.pivotaltracker.com/services/v5/projects/$PROJECT_ID/stories" 20 | 21 | def run_report(tracker) 22 | @project = tracker.options[:pivotal_project] 23 | @token = tracker.options[:pivotal_token] 24 | @api = tracker.options[:pivotal_api_url] 25 | @url = "#{@api}/#{@project}/stories" 26 | 27 | tracker.findings.each do |finding| 28 | begin 29 | Glue.debug "Posting to #{@url}" 30 | Glue.debug get_pivotal_json(finding) 31 | c = Curl.post(@url, get_pivotal_json(finding)) do |curl| 32 | curl.headers['Content-Type'] = 'application/json' 33 | curl.headers['X-TrackerToken'] = "#{@token}" 34 | #curl.verbose=true 35 | end 36 | Glue.debug c.body_str 37 | rescue Exception => e 38 | puts "Issue #{e.message}" 39 | end 40 | end 41 | "Results are in Pivotal" 42 | end 43 | 44 | private 45 | def get_pivotal_json(finding) 46 | json = {"current_state": "unscheduled", 47 | "estimate": 1, 48 | "name": "#{finding.appname} - #{finding.description}", 49 | "description": "#{finding.to_string}\n\nFINGERPRINT: #{finding.fingerprint}", 50 | "labels": ["Glue", "#{finding.appname}","#{finding.fingerprint}"] 51 | } 52 | json.to_json 53 | end 54 | 55 | # 56 | 57 | end 58 | -------------------------------------------------------------------------------- /lib/glue/reporters/slack_reporter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/finding' 2 | require 'glue/reporters/base_reporter' 3 | require 'jira-ruby' 4 | require 'slack-ruby-client' 5 | 6 | class Glue::SlackReporter < Glue::BaseReporter 7 | Glue::Reporters.add self 8 | 9 | attr_accessor :name, :format 10 | 11 | def initialize() 12 | @name = "SlackReporter" 13 | @format = :to_slack 14 | end 15 | 16 | def run_report(tracker) 17 | post_as_user = false 18 | if (tracker.options[:slack_post_as_user]) 19 | post_as_user = true 20 | end 21 | 22 | mandatory = [:slack_token, :slack_channel] 23 | missing = mandatory.select{ |param| tracker.options[param].nil? } 24 | unless missing.empty? 25 | Glue.fatal "missing one or more required params: #{missing}" 26 | return 27 | end 28 | 29 | Slack.configure do |config| 30 | config.token = tracker.options[:slack_token] 31 | end 32 | 33 | client = Slack::Web::Client.new 34 | 35 | begin 36 | client.auth_test 37 | rescue Slack::Web::Api::Error => error 38 | Glue.fatal "Slack authentication failed: " << error.to_s 39 | end 40 | 41 | reports = [] 42 | tracker.findings.each do |finding| 43 | reports << finding.to_string 44 | end 45 | 46 | puts tracker.options[:slack_channel] 47 | 48 | begin 49 | client.chat_postMessage(channel: tracker.options[:slack_channel], text: "OWASP Glue test run completed - See attachment.", attachments: reports.join << "\n", as_user: post_as_user) 50 | rescue Slack::Web::Api::Error => error 51 | Glue.fatal "Post to slack failed: " << error.to_s 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/glue/reporters/teamcity_reporter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/finding' 2 | 3 | class Glue::TeamCityReporter < Glue::BaseReporter 4 | 5 | Glue::Reporters.add self 6 | 7 | def initialize() 8 | @name = "TeamCityReporter" 9 | @format = :to_teamcity 10 | end 11 | 12 | def run_report(tracker) 13 | min_level = 3 14 | 15 | if (tracker.options[:teamcity_min_level]) 16 | unless tracker.options[:teamcity_min_level].is_a? Integer 17 | Glue.fatal "min level should be a number, got: #{tracker.options[:teamcity_min_level]}" 18 | end 19 | unless tracker.options[:teamcity_min_level] >= 1 && tracker.options[:teamcity_min_level] <= 3 20 | Glue.fatal "min level should be between 1 to 3, not #{tracker.options[:teamcity_min_level]}" 21 | end 22 | min_level = tracker.options[:teamcity_min_level] 23 | end 24 | reports = [ ] 25 | 26 | output = "" 27 | 28 | output << "##teamcity[message text='Report failed tests for each finding with severity equal or above #{printSeverity(min_level)}' status='NORMAL']" << "\n" 29 | 30 | tracker.findings.group_by{|finding| finding.task}.each do |task, task_findings| 31 | output << "##teamcity[testSuiteStarted name='#{task}']" << "\n" 32 | task_findings.each do |finding| 33 | if finding.severity < min_level 34 | output << "##teamcity[testIgnored name='#{escapeString(finding.fingerprint)}' message='Severity #{printSeverity(finding.severity)}']" << "\n" 35 | next 36 | end 37 | 38 | output << "##teamcity[testStarted name='#{escapeString(finding.fingerprint)}' captureStandardOutput='true']" << "\n" 39 | output << "Source: #{finding.source}" << "\n" 40 | output << "Details: #{finding.detail}" << "\n" 41 | output << "##teamcity[testFailed name='#{escapeString(finding.fingerprint)}' message='Severity #{printSeverity(finding.severity)}' details='#{escapeString(finding.description)}']" << "\n" 42 | output << "##teamcity[testFinished name='#{escapeString(finding.fingerprint)}']" << "\n" 43 | end 44 | output << "##teamcity[testSuiteFinished name='#{task}']" << "\n" 45 | end 46 | 47 | return output 48 | end 49 | 50 | def out(finding) 51 | end 52 | 53 | def printSeverity(severity) 54 | case severity 55 | when 1 56 | return "Low" 57 | when 2 58 | return "Medium" 59 | when 3 60 | return "High" 61 | return "Not supported" 62 | end 63 | end 64 | 65 | def escapeString(text) 66 | return text.gsub('|', '||').gsub('\n', '|n').gsub('\r', '|r').gsub('\'', '|\'').gsub('[', '|[').gsub(']', '|]') 67 | end 68 | 69 | def combine_reports(reports) 70 | reports.join 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/glue/reporters/text_reporter.rb: -------------------------------------------------------------------------------- 1 | require 'glue/finding' 2 | require 'glue/reporters/base_reporter' 3 | 4 | class Glue::TextReporter < Glue::BaseReporter 5 | 6 | Glue::Reporters.add self 7 | 8 | attr_accessor :name, :format 9 | 10 | def initialize() 11 | @name = "TextReporter" 12 | @format = :to_s 13 | end 14 | 15 | def out(finding) 16 | finding.to_string << "\n" 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /lib/glue/scanner.rb: -------------------------------------------------------------------------------- 1 | require 'glue/event' 2 | require 'glue/tracker' 3 | require 'glue/tasks' 4 | 5 | class Glue::Scanner 6 | attr_reader :tracker 7 | attr_reader :mounter 8 | 9 | #Pass in path to the root of the Rails application 10 | def initialize 11 | @stage = :wait 12 | @stages = [ :wait, :mount, :file, :code, :live, :done] 13 | end 14 | 15 | #Process everything in the Rails application 16 | def process target, tracker 17 | @stages.each do |stage| 18 | Glue.notify "Running tasks in stage: #{stage}" 19 | @stage = stage 20 | begin 21 | Glue::Tasks.run_tasks(target, stage, tracker) 22 | rescue Exception => e 23 | Glue.warn e.message 24 | raise e 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/glue/tasks.rb: -------------------------------------------------------------------------------- 1 | require 'thread' 2 | 3 | #Collects up results from running different tasks. 4 | # 5 | #tasks can be added with +task.add(task_class)+ 6 | # 7 | #All .rb files in tasks/ will be loaded. 8 | class Glue::Tasks 9 | @tasks = [] 10 | @optional_tasks = [] 11 | 12 | attr_reader :tasks_run 13 | 14 | #Add a task. This will call +_klass_.new+ when running tests 15 | def self.add klass 16 | @tasks << klass unless @tasks.include? klass 17 | end 18 | 19 | #Add an optional task 20 | def self.add_optional klass 21 | @optional_tasks << klass unless @tasks.include? klass 22 | end 23 | 24 | def self.tasks 25 | @tasks + @optional_tasks 26 | end 27 | 28 | def self.optional_tasks 29 | @optional_tasks 30 | end 31 | 32 | def self.initialize_tasks task_directory = "" 33 | #Load all files in task_directory 34 | Dir.glob(File.join(task_directory, "*.rb")).sort.each do |f| 35 | require f 36 | end 37 | end 38 | 39 | #No need to use this directly. 40 | def initialize options = { } 41 | @warnings = [] 42 | @tasks_run = [] 43 | end 44 | 45 | #Add Warning to list of warnings to report. 46 | def add_warning warning 47 | @warnings << warning 48 | end 49 | 50 | #Run all the tasks on the given Tracker. 51 | #Returns a new instance of tasks with the results. 52 | def self.run_tasks(target, stage, tracker) 53 | task_runner = self.new 54 | 55 | trigger = Glue::Event.new(tracker.options[:appname]) 56 | trigger.path = target 57 | 58 | self.tasks_to_run(tracker).each do |c| 59 | task_name = get_task_name c 60 | 61 | #Run or don't run task based on options 62 | #Now case-insensitive specifiers: nodesecurityproject = Glue::NodeSecurityProject 63 | 64 | if tracker.options[:skip_tasks] 65 | skip_tasks = tracker.options[:skip_tasks].map {|task| task.downcase} 66 | end 67 | if (tracker.options[:run_tasks]) 68 | run_tasks = tracker.options[:run_tasks].map {|task| task.downcase} 69 | end 70 | 71 | unless skip_tasks.include? task_name.downcase or 72 | (run_tasks and not run_tasks.include? task_name.downcase) 73 | 74 | task = c.new(trigger, tracker) 75 | begin 76 | if task.stage == stage and task.supported? 77 | if task.labels.intersect? tracker.options[:labels] or # Only run tasks with labels 78 | ( run_tasks and run_tasks.include? task_name.downcase ) # or that are explicitly requested. 79 | Glue.notify "#{stage} - #{task_name} - #{task.labels}" 80 | task.run 81 | task.analyze 82 | task.findings.each do | finding | 83 | tracker.report finding 84 | end 85 | end 86 | end 87 | rescue => e 88 | Glue.notify e.message 89 | tracker.error e 90 | end 91 | 92 | task.warnings.each do |w| 93 | task_runner.add_warning w 94 | end 95 | 96 | #Maintain list of which tasks were run 97 | #mainly for reporting purposes 98 | task_runner.tasks_run << task_name[5..-1] 99 | end 100 | end 101 | 102 | task_runner 103 | end 104 | 105 | 106 | private 107 | 108 | def self.get_task_name task_class 109 | task_class.to_s.split("::").last 110 | end 111 | 112 | def self.tasks_to_run tracker 113 | if tracker.options[:run_all_tasks] or tracker.options[:run_tasks] 114 | @tasks + @optional_tasks 115 | else 116 | @tasks 117 | end 118 | end 119 | end 120 | 121 | #Load all files in tasks/ directory 122 | Dir.glob("#{File.expand_path(File.dirname(__FILE__))}/tasks/*.rb").sort.each do |f| 123 | require f.match(/(glue\/tasks\/.*)\.rb$/)[0] 124 | end 125 | -------------------------------------------------------------------------------- /lib/glue/tasks/bandit.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'json' 3 | require 'glue/util' 4 | require 'pathname' 5 | 6 | class Glue::Bandit < Glue::BaseTask 7 | 8 | Glue::Tasks.add self 9 | include Glue::Util 10 | 11 | def initialize(trigger, tracker) 12 | super(trigger, tracker) 13 | @name = "Bandit" 14 | @description = "Source analysis for Python" 15 | @stage = :code 16 | @labels << "code" << "python" 17 | end 18 | 19 | def run 20 | rootpath = @trigger.path 21 | @result=runsystem(true, "bandit", "-f", "json", "-r", "#{rootpath}") 22 | end 23 | 24 | def analyze 25 | begin 26 | parsed = JSON.parse(@result) 27 | parsed["results"].each do |warning| 28 | file = relative_path(warning['filename'], @trigger.path) 29 | detail = "#{warning['issue_text']}\n#{warning['code']}" 30 | if ! warning['line'] 31 | warning['line'] = "0" 32 | end 33 | if ! warning['code'] 34 | warning['code'] = "" 35 | end 36 | source = { :scanner => @name, 37 | :file => file, 38 | :line => warning['line_number'], 39 | :code => warning['test_id'].lstrip } 40 | report warning["test_name"], 41 | detail, 42 | source, 43 | severity(warning["issue_severity"]), 44 | fingerprint("#{source}") 45 | end 46 | rescue Exception => e 47 | Glue.warn e.message 48 | Glue.warn e.backtrace 49 | Glue.warn "Raw result: #{@result}" 50 | end 51 | end 52 | 53 | def supported? 54 | supported=runsystem(true, "bandit", "-v") 55 | if supported =~ /command not found/ 56 | Glue.notify "Install python and pip." 57 | Glue.notify "Run: pip install bandit" 58 | Glue.notify "See: https://github.com/openstack/bandit" 59 | return false 60 | else 61 | return true 62 | end 63 | end 64 | 65 | end 66 | -------------------------------------------------------------------------------- /lib/glue/tasks/base_task.rb: -------------------------------------------------------------------------------- 1 | require 'glue/finding' 2 | require 'set' 3 | require 'digest' 4 | 5 | class Glue::BaseTask 6 | attr_reader :findings, :warnings, :trigger, :labels 7 | attr_accessor :name 8 | attr_accessor :description 9 | attr_accessor :stage 10 | attr_accessor :appname 11 | attr_accessor :result 12 | 13 | def initialize(trigger, tracker) 14 | @findings = [] 15 | @warnings = [] 16 | @labels = Set.new 17 | @trigger = trigger 18 | @tracker = tracker 19 | @severity_filter = { 20 | :low => ['low','weak', 'informational', 'info'], 21 | :medium => ['medium','med','average'], 22 | :high => ['high','severe','critical'] 23 | } 24 | end 25 | 26 | def report description, detail, source, severity, fingerprint 27 | finding = Glue::Finding.new( @trigger.appname, description, detail, source, severity, fingerprint, self.class.name ) 28 | @findings << finding 29 | end 30 | 31 | def warn warning 32 | @warnings << warning 33 | end 34 | 35 | def name 36 | @name 37 | end 38 | 39 | def description 40 | @description 41 | end 42 | 43 | def stage 44 | @stage 45 | end 46 | 47 | def directories_with? file, exclude_dirs = [] 48 | exclude_dirs = @tracker.options[:exclude_dirs] if exclude_dirs == [] and @tracker.options[:exclude_dirs] 49 | results = [] 50 | 51 | Find.find(@trigger.path) do |path| 52 | if FileTest.directory? path 53 | Find.prune if exclude_dirs.include? File.basename(path) or exclude_dirs.include? File.basename(path) + '/' 54 | next 55 | end 56 | 57 | Find.prune unless File.basename(path) == file 58 | 59 | results << File.dirname(path) 60 | end 61 | return results 62 | end 63 | 64 | def run 65 | end 66 | 67 | def analyze 68 | end 69 | 70 | def supported? 71 | end 72 | 73 | def severity sev 74 | sev = '' if sev.nil? 75 | return 1 if @severity_filter[:low].include?(sev.strip.chomp.downcase) 76 | return 2 if @severity_filter[:medium].include?(sev.strip.chomp.downcase) 77 | return 3 if @severity_filter[:high].include?(sev.strip.chomp.downcase) 78 | Glue.warn "unsupperted severity found: " + sev 79 | return 0 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /lib/glue/tasks/brakeman.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'json' 3 | require 'glue/util' 4 | require 'pathname' 5 | 6 | class Glue::Brakeman < Glue::BaseTask 7 | 8 | Glue::Tasks.add self 9 | include Glue::Util 10 | 11 | def initialize(trigger, tracker) 12 | super(trigger, tracker) 13 | @name = "Brakeman" 14 | @description = "Source analysis for Ruby" 15 | @stage = :code 16 | @labels << "code" << "ruby" << "rails" 17 | end 18 | 19 | def run 20 | rootpath = @trigger.path 21 | @result=runsystem(true, "brakeman", "-A", "-q", "-f", "json", "#{rootpath}") 22 | end 23 | 24 | def analyze 25 | # puts @result 26 | begin 27 | parsed = JSON.parse(@result) 28 | parsed["warnings"].each do |warning| 29 | file = relative_path(warning['file'], @trigger.path) 30 | 31 | detail = "#{warning['message']}\n#{warning['link']}" 32 | if ! warning['line'] 33 | warning['line'] = "0" 34 | end 35 | if ! warning['code'] 36 | warning['code'] = "" 37 | end 38 | source = { :scanner => @name, :file => file, :line => warning['line'], :code => warning['code'].lstrip } 39 | 40 | report warning["warning_type"], detail, source, severity(warning["confidence"]), fingerprint("#{warning['message']}#{warning['link']}#{severity(warning["confidence"])}#{source}") 41 | end 42 | rescue Exception => e 43 | Glue.warn e.message 44 | Glue.warn e.backtrace 45 | Glue.warn "Raw result: #{@result}" 46 | end 47 | end 48 | 49 | def supported? 50 | supported=runsystem(true, "brakeman", "-v") 51 | if supported =~ /command not found/ 52 | Glue.notify "Run: gem install brakeman" 53 | return false 54 | else 55 | return true 56 | end 57 | end 58 | 59 | end 60 | -------------------------------------------------------------------------------- /lib/glue/tasks/bundle-audit.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'json' 3 | require 'glue/util' 4 | require 'digest' 5 | 6 | class Glue::BundleAudit < Glue::BaseTask 7 | Glue::Tasks.add self 8 | include Glue::Util 9 | 10 | def initialize(trigger, tracker) 11 | super(trigger, tracker) 12 | @name = "BundleAudit" 13 | @description = "Dependency Checker analysis for Ruby" 14 | @stage = :code 15 | @labels << "code" << "ruby" 16 | @results = {} 17 | end 18 | 19 | def run 20 | directories_with?('Gemfile.lock').each do |dir| 21 | Glue.notify "#{@name} scanning: #{dir}" 22 | @results[dir] = runsystem(true, "bundle-audit", "check", :chdir => dir) 23 | end 24 | end 25 | 26 | def analyze 27 | # puts @result 28 | begin 29 | get_warnings 30 | rescue Exception => e 31 | Glue.warn e.message 32 | Glue.notify "Appears not to be a project with Gemfile.lock or there was another problem ... bundle-audit skipped." 33 | Glue.warn "Results look like: #{@results}" 34 | end 35 | end 36 | 37 | def supported? 38 | supported=runsystem(false, "bundle-audit", "update") 39 | if supported =~ /command not found/ 40 | Glue.notify "Run: gem install bundler-audit" 41 | return false 42 | else 43 | return true 44 | end 45 | end 46 | 47 | private 48 | def normalize_unknown_severity sev 49 | sev = '' if sev.nil? 50 | return sev if sev.strip.chomp.downcase != "unknown" 51 | Glue.warn "Criticality: Unknown. Setting it to medium" 52 | return "medium" 53 | end 54 | 55 | private 56 | def get_warnings 57 | @results.each do |dir, result| 58 | detail, jem, source, sev, hash = '','',{},'','' 59 | result.each_line do | line | 60 | 61 | if /\S/ !~ line 62 | # Signal section is over. Reset variables and report. 63 | if detail != '' 64 | report "Gem #{jem} has known security issues.", detail, source, sev, fingerprint(hash) 65 | end 66 | 67 | detail, jem, source, sev, hash = '','', {},'','' 68 | end 69 | 70 | name, value = line.chomp.split(':') 71 | case name 72 | when 'Name' 73 | jem << value 74 | hash << value 75 | when 'Version' 76 | jem << value 77 | hash << value 78 | when 'Advisory' 79 | source = { :scanner => @name, :file => "#{relative_path(dir, @trigger.path)}/Gemfile.lock", :line => nil, :code => nil } 80 | hash << value 81 | when 'Criticality' 82 | sev = severity(normalize_unknown_severity(value)) 83 | hash << sev 84 | when 'URL' 85 | detail += line.chomp.split('URL:').last 86 | when 'Title' 87 | detail += ",#{value}" 88 | when 'Solution' 89 | detail += ": #{value}" 90 | when 'Insecure Source URI found' 91 | report "Insecure GEM Source", "#{line.chomp} - use git or https", {:scanner => @name, :file => 'Gemfile.lock', :line => nil, :code => nil}, severity('high'), fingerprint("bundlerauditgemsource#{line.chomp}") 92 | else 93 | if line =~ /\S/ and line !~ /Unpatched versions found/ 94 | Glue.debug "Not sure how to handle line: #{line}" 95 | end 96 | end 97 | end 98 | end 99 | end 100 | 101 | 102 | end 103 | -------------------------------------------------------------------------------- /lib/glue/tasks/burp.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'glue/util' 3 | require 'rexml/document' 4 | require 'rexml/streamlistener' 5 | include REXML 6 | 7 | # SAX Like Parser for Burp Suite Pro Report XML. 8 | class Glue::BurpListener 9 | include StreamListener 10 | 11 | def initialize(task) 12 | @task = task 13 | @text = '' 14 | @cdata = '' 15 | @type = '' 16 | @serial_number = '' 17 | @issue_name = '' 18 | @url = '' 19 | @path = '' 20 | @location = '' 21 | @severity = '' 22 | @confidence = '' 23 | @issue_background = '' 24 | @remediation_background = '' 25 | @references = '' 26 | end 27 | 28 | def tag_start(name, attrs) 29 | end 30 | 31 | def tag_end(name) 32 | case name 33 | when 'name' 34 | # Only take the first name tag, or we may end up with a tag from a child node (e.g. ) 35 | if @issue_name.blank? && @text =~ /\D/ 36 | @issue_name = @text 37 | end 38 | when 'serialNumber' 39 | @serial_number = @text 40 | when 'type' 41 | @type = @text 42 | when 'host' 43 | @url = @text 44 | when 'path' 45 | @path = @text 46 | when 'location' 47 | @location = @cdata 48 | when 'severity' 49 | @severity = @text 50 | when 'confidence' 51 | @confidence = @text 52 | when 'issueBackground' 53 | @issue_background = @cdata 54 | when 'remediationBackground' 55 | @remediation_background = @cdata 56 | when 'references' 57 | @references = @text 58 | when 'issue' 59 | background = @issue_background.gsub(/<\/?[^>]*>/, "").gsub("\n", '').strip 60 | remediation = @remediation_background.gsub(/<\/?[^>]*>/, "").gsub("\n", '').strip 61 | refs = @references.gsub(/<\/?[^>]*>/, "").gsub("\n", '').strip 62 | 63 | #report description, detail, source, severity, fingerprint 64 | @task.report @issue_name, "#{background}\n\n#{remediation}\n\n#{refs}", @location, @severity, @serial_number 65 | end 66 | end 67 | 68 | def text(text) 69 | @text = text 70 | end 71 | 72 | def cdata(cdata) 73 | @cdata = cdata 74 | end 75 | end 76 | 77 | class Glue::Burp < Glue::BaseTask 78 | Glue::Tasks.add self 79 | include Glue::Util 80 | 81 | def initialize(trigger, tracker) 82 | super(trigger, tracker) 83 | @name = "Burp" 84 | @description = "Burp Suite Pro Issues" 85 | 86 | @stage = :code 87 | @labels << "code" 88 | 89 | @burp_xml_path = @tracker.options[:burp_xml_path] 90 | end 91 | 92 | def run 93 | Glue.notify "#{@name}" 94 | end 95 | 96 | def analyze 97 | begin 98 | Glue.debug "Parsing report #{@burp_xml_path}" 99 | get_warnings(@burp_xml_path) 100 | rescue Exception => e 101 | Glue.notify "Problem running Burp ... skipped." 102 | Glue.notify e.message 103 | raise e 104 | end 105 | end 106 | 107 | def supported? 108 | true 109 | end 110 | 111 | def get_warnings(path) 112 | listener = Glue::BurpListener.new(self) 113 | 114 | xml_stream = get_input_stream 115 | parser = Parsers::StreamParser.new(xml_stream, listener) 116 | parser.parse 117 | end 118 | 119 | def get_input_stream 120 | File.new(@tracker.options[:burp_xml_path]) 121 | end 122 | end 123 | -------------------------------------------------------------------------------- /lib/glue/tasks/checkmarx.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'glue/util' 3 | require 'nokogiri' 4 | 5 | class Glue::Checkmarx < Glue::BaseTask 6 | 7 | Glue::Tasks.add self 8 | include Glue::Util 9 | 10 | def initialize(trigger, tracker) 11 | super(trigger, tracker) 12 | @name = "Checkmarx" 13 | @description = "CxSAST" 14 | @stage = :code 15 | @labels << "code" 16 | @checkmarx_path = tracker.options[:checkmarx_path] || "runCxConsole.sh" 17 | end 18 | 19 | def run 20 | rootpath = @trigger.path 21 | 22 | # source: https://stackoverflow.com/a/2149183/4792970 23 | mandatory = [:checkmarx_user, :checkmarx_password, :checkmarx_server, :checkmarx_project] 24 | missing = mandatory.select{ |param| @tracker.options[param].nil? } 25 | unless missing.empty? 26 | Glue.error "missing one or more required params: #{missing}" 27 | return 28 | end 29 | 30 | params = [@checkmarx_path, "scan", "-v", 31 | "-CxUser", "#{@tracker.options[:checkmarx_user]}", 32 | "-CxPassword", "#{@tracker.options[:checkmarx_password]}", 33 | "-CxServer", "#{@tracker.options[:checkmarx_server]}", 34 | "-LocationType", "folder", 35 | "-LocationPath", "#{rootpath}", 36 | "-ProjectName", "#{@tracker.options[:checkmarx_project]}", 37 | "-ReportXML", "#{Dir.pwd}/checkmarx_results.xml", 38 | "-ReportPDF", "#{Dir.pwd}/checkmarx_results.pdf"] 39 | 40 | if (@tracker.options[:checkmarx_log]) 41 | params << "-Log" 42 | params << "#{@tracker.options[:checkmarx_log]}" 43 | end 44 | 45 | if (@tracker.options[:checkmarx_exclude]) 46 | params << "-LocationExclude" 47 | params << "#{@tracker.options[:checkmarx_exclude]}" 48 | end 49 | 50 | if (@tracker.options[:checkmarx_preset]) 51 | params << "-Preset" 52 | params << "#{@tracker.options[:checkmarx_preset]}" 53 | end 54 | 55 | if (@tracker.options[:checkmarx_incremental]) 56 | params << "-Incremental" 57 | end 58 | 59 | output = runsystem(true, *params) 60 | 61 | #CxConsole does not set exit code on errors, so we need to test if a report file created 62 | if (File.file?("checkmarx_results.xml")) 63 | @results = Nokogiri::XML(File.read("checkmarx_results.xml")).xpath '//Result' 64 | else 65 | Glue.fatal "checkmarx scan failed: #{output}" 66 | end 67 | end 68 | 69 | def analyze 70 | begin 71 | if (@results == nil) 72 | return 73 | end 74 | @results.each do |result| 75 | description = result.parent.attributes['name'].value.gsub('_', ' ') 76 | detail = result.attributes['DeepLink'].value 77 | state = result.attributes['state'].value.to_i 78 | 79 | #state different from zero mean that the result is marked as ignored (not exploitable) in CxSAST web portal 80 | if (state > 0) 81 | return 82 | end 83 | 84 | source = { :scanner => @name, :file => result.attributes['FileName'].value, :line => result.attributes['Line'].value.to_i, :code => result.at_xpath('Path/PathNode/Snippet/Line/Code').text } 85 | sev = severity(result.parent.attributes['Severity'].value) 86 | fprint = fingerprint("#{description}#{source}#{sev}") 87 | 88 | report description, detail, source, sev, fprint 89 | end 90 | rescue Exception => e 91 | Glue.warn e.message 92 | Glue.warn e.backtrace 93 | end 94 | end 95 | 96 | def supported? 97 | supported=runsystem(true, @checkmarx_path, "--help") 98 | if supported =~ /command not found/ 99 | Glue.notify "Install CxConsolePlugin" 100 | return false 101 | else 102 | return true 103 | end 104 | end 105 | 106 | end 107 | -------------------------------------------------------------------------------- /lib/glue/tasks/clamav.rb: -------------------------------------------------------------------------------- 1 | # https://gist.github.com/paulspringett/8802240 2 | 3 | require 'glue/tasks/base_task' 4 | 5 | class Glue::ClamAV < Glue::BaseTask 6 | 7 | Glue::Tasks.add self 8 | 9 | def initialize(trigger, tracker) 10 | super(trigger,tracker) 11 | @name = "ClamAV" 12 | @description = "Test for virus/malware" 13 | @stage = :file 14 | @labels << "filesystem" 15 | end 16 | 17 | def run 18 | # Update AV 19 | `freshclam` 20 | # Run AV 21 | # TODO: Circle back and use runsystem. 22 | Glue.notify "Malware/Virus Check" 23 | rootpath = @trigger.path 24 | @result=`clamscan --no-summary -i -r "#{rootpath}"` 25 | end 26 | 27 | def analyze 28 | list = @result.split(/\n/) 29 | list.each do |v| 30 | # v.slice! installdir 31 | Glue.notify v 32 | report "Malicious file identified.", v, @name, :medium 33 | end 34 | end 35 | 36 | def supported? 37 | # TODO verify. 38 | # In future, verify tool is available. 39 | return true 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /lib/glue/tasks/dawnscanner.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'glue/util' 3 | require 'tempfile' 4 | 5 | class Glue::DawnScanner < Glue::BaseTask 6 | 7 | Glue::Tasks.add self 8 | include Glue::Util 9 | 10 | def initialize(trigger, tracker) 11 | super(trigger, tracker) 12 | @name = "DawnScanner" 13 | @description = "DawnScanner ruby analyzer" 14 | @stage = :code 15 | @labels << "code" 16 | end 17 | 18 | def run 19 | @results_file = Tempfile.new(['dawnresults', 'xml']) 20 | runsystem(true, "dawn", "-F", "#{@results_file.path}", "-j", ".", :chdir => @trigger.path) 21 | @results = JSON.parse(File.read("#{@results_file.path}"))['vulnerabilities'] 22 | end 23 | 24 | def analyze 25 | begin 26 | @results.each do |result| 27 | description = result['name'].gsub('\n',' ') 28 | detail = "#{result['message']}\n#{result['remediation']}\n#{result['cve_link']}" 29 | source = {:scanner => @name, :file => nil, :line => nil, :code => nil} 30 | sev = severity(result['severity']) 31 | fprint = fingerprint("#{description}#{detail}#{source}#{sev}") 32 | 33 | report description, detail, source, sev, fprint 34 | end 35 | rescue Exception => e 36 | Glue.warn e.message 37 | Glue.warn e.backtrace 38 | ensure 39 | File.unlink @results_file 40 | end 41 | end 42 | 43 | def supported? 44 | supported=runsystem(true, "dawn", "--version") 45 | if supported =~ /command not found/ 46 | Glue.notify "Install dawnscanner: 'gem install dawnscanner'" 47 | return false 48 | else 49 | return true 50 | end 51 | end 52 | 53 | end 54 | -------------------------------------------------------------------------------- /lib/glue/tasks/dynamic.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'glue/util' 3 | require 'json' 4 | require 'json-schema' 5 | 6 | class Glue::Dynamic < Glue::BaseTask 7 | Glue::Tasks.add self 8 | include Glue::Util 9 | 10 | MAPPING_NAME_REGEX = /\A\w{0,20}\z/ 11 | MAPPING_FOLDER = File.join(File.dirname(__FILE__), "../mappings") 12 | SCHEMA_FILE_PATH = File.join(MAPPING_FOLDER, "schema.json") 13 | 14 | def initialize(trigger, tracker) 15 | super(trigger, tracker) 16 | @name = "Dynamic Task" 17 | @description = "Dynamic task that parse JSON reports by using a mappings file" 18 | @stage = :code 19 | @labels << "code" 20 | end 21 | 22 | def run 23 | mapping_schema = JSON.parse(File.read(SCHEMA_FILE_PATH)) 24 | report_path = "#{@tracker.options[:target]}" 25 | mapping_file_path = "#{@tracker.options[:mapping_file_path]}" 26 | 27 | if (!!MAPPING_NAME_REGEX.match(mapping_file_path) && 28 | File.exist?(File.join(MAPPING_FOLDER, "#{mapping_file_path}.json"))) 29 | mapping_file_path = File.join(MAPPING_FOLDER, "#{mapping_file_path}.json") 30 | elsif (!File.exist?(mapping_file_path)) 31 | Glue.fatal "Mapping file #{mapping_file_path} not found" 32 | end 33 | 34 | if (!File.exist?(report_path)) 35 | Glue.fatal "Report #{report_path} not found" 36 | end 37 | 38 | report = JSON.parse(File.read(report_path)) 39 | mappings = JSON.parse(File.read(mapping_file_path)) 40 | 41 | errors = JSON::Validator.fully_validate(mapping_schema, mappings, :validate_schema => true) 42 | 43 | if errors.any? 44 | Glue.fatal "Invalid mappings JSON: #{errors.inspect}" 45 | end 46 | 47 | app_name = report[mappings["app_name"]] 48 | task_name = mappings["task_name"] 49 | 50 | mappings["mappings"].each do |map| 51 | key = map["key"] 52 | 53 | if (report[key] == nil) 54 | Glue.fatal "report does not contains key '#{key}''" 55 | end 56 | 57 | report[key].each do |item| 58 | description = item[map["properties"]["description"]] 59 | detail = item[map["properties"]["detail"]] 60 | source = item[map["properties"]["source"]] 61 | severity_raw = item[map["properties"]["severity"]] 62 | fingerprint = item[map["properties"]["fingerprint"]] 63 | finding = Glue::Finding.new( app_name, description, detail, source, severity(severity_raw), fingerprint, task_name ) 64 | @findings << finding 65 | end 66 | end 67 | 68 | end 69 | 70 | def analyze 71 | end 72 | 73 | def supported? 74 | return true 75 | end 76 | 77 | end 78 | -------------------------------------------------------------------------------- /lib/glue/tasks/eslint.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'json' 3 | require 'glue/util' 4 | 5 | class Glue::ESLint < Glue::BaseTask 6 | 7 | Glue::Tasks.add self 8 | include Glue::Util 9 | 10 | def initialize(trigger, tracker) 11 | super(trigger,tracker) 12 | @name = "ESLint/ScanJS" 13 | @description = "Source analysis for JavaScript" 14 | @stage = :code 15 | @labels << "code" << "javascript" 16 | end 17 | 18 | def run 19 | rootpath = @trigger.path 20 | currentpath = File.expand_path File.dirname(__FILE__) 21 | Glue.debug "ESLint Config Path: #{currentpath}" 22 | @result = `eslint -c #{currentpath}/scanjs-eslintrc --no-color --quiet --format json #{rootpath}` 23 | end 24 | 25 | def analyze 26 | # puts @result 27 | begin 28 | parsed = JSON.parse(@result) 29 | parsed.each do |result| 30 | findings = {} 31 | prints = [] 32 | messages = [] 33 | result['messages'].each do |msg| 34 | message = msg['message'] 35 | findings[message] = {} if findings[message].nil? 36 | findings[message][:detail] = msg['ruleId'] 37 | if messages.include?(message) 38 | findings[message][:source] = "#{findings[message][:source]},#{msg['line']}" unless findings[message][:source].include?(",#{msg['line']}") 39 | else 40 | findings[message][:source] = "#{result['filePath']} Line: #{msg['line']}" 41 | messages << message 42 | end 43 | findings[message][:severity] = severity(msg['severity'].to_s) 44 | end 45 | findings.each do |key, value| 46 | print = fingerprint("#{key}#{value[:detail]}#{value[:source]}#{value[:sev]}") 47 | unless prints.include?(print) 48 | prints << print 49 | report key, value[:detail], value[:source], value[:severity], print 50 | end 51 | end 52 | end 53 | rescue Exception => e 54 | Glue.warn e.message 55 | Glue.warn e.backtrace 56 | Glue.warn "Raw result: #{@result}" 57 | end 58 | end 59 | 60 | def supported? 61 | supported=runsystem(true, "eslint", "-c", "~/.scanjs-eslintrc") 62 | if supported =~ /command not found/ 63 | Glue.notify "Install eslint and the scanjs .eslintrc" 64 | return false 65 | else 66 | return true 67 | end 68 | end 69 | 70 | end 71 | -------------------------------------------------------------------------------- /lib/glue/tasks/fim.rb: -------------------------------------------------------------------------------- 1 | # https://github.com/jessek/hashdeep/releases/tag/release-4.4 2 | 3 | require 'glue/tasks/base_task' 4 | require 'open3' 5 | 6 | class Glue::FIM < Glue::BaseTask 7 | 8 | Glue::Tasks.add self 9 | 10 | def initialize(trigger, tracker) 11 | super(trigger,tracker) 12 | @name = "FIM" 13 | @description = "File integrity monitor" 14 | @stage = :file 15 | @result = '' 16 | @labels << "filesystem" 17 | end 18 | 19 | def run 20 | rootpath = @trigger.path 21 | if File.exists?("/area81/tmp/#{rootpath}/filehash") 22 | Glue.notify "File Hashes found, comparing to file system" 23 | cmd="hashdeep -j99 -r -a -vv -k /area81/tmp/#{rootpath}/filehash #{rootpath}" 24 | 25 | # Ugly stdout parsing 26 | r=/(.*): No match/ 27 | Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr| 28 | while line = stdout.gets 29 | if line.match r 30 | @result << line 31 | end 32 | end 33 | end 34 | else 35 | Glue.notify "No existing baseline - generating initial hashes" 36 | cmd="mkdir -p /area81/tmp/#{rootpath}; hashdeep -j99 -r #{rootpath} > /area81/tmp/#{rootpath}/filehash" 37 | Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr| 38 | while line = stdout.gets 39 | puts "." 40 | end 41 | end 42 | @result = '' 43 | end 44 | end 45 | 46 | def analyze 47 | list = @result.split(/\n/) 48 | list.each do |v| 49 | # v.slice! installdir 50 | Glue.notify v 51 | report "File changed.", v, @name, :low 52 | end 53 | end 54 | 55 | def supported? 56 | # In future, verify tool is available. 57 | return true 58 | end 59 | 60 | end 61 | -------------------------------------------------------------------------------- /lib/glue/tasks/findsecbugs.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'glue/util' 3 | require 'nokogiri' 4 | require 'tempfile' 5 | require 'mkmf' 6 | 7 | MakeMakefile::Logging.instance_variable_set(:@logfile, File::NULL) 8 | 9 | class Glue::FindSecurityBugs < Glue::BaseTask 10 | 11 | Glue::Tasks.add self 12 | include Glue::Util 13 | 14 | def initialize(trigger, tracker) 15 | super(trigger, tracker) 16 | @name = "FindSecurityBugs" 17 | @description = "FindSecurityBugs plugin for FindBugs" 18 | @stage = :code 19 | @labels << "code" 20 | end 21 | 22 | def run 23 | @results_file = Tempfile.new(['findsecbugs','xml']) 24 | 25 | unless File.exist?("#{@trigger.path}/.git/config") 26 | runsystem(true, "git", "init", :chdir => @trigger.path) 27 | runsystem(true, "git", "add", "*", :chdir => @trigger.path) 28 | runsystem(true, "git", "commit", "-am", "fake commit for mvn compile", :chdir => @trigger.path) 29 | end 30 | 31 | directories_with?('pom.xml').each do |dir| 32 | runsystem(true, "mvn", "compile", "-fn", :chdir => dir) 33 | end 34 | 35 | runsystem(true, "/bin/sh", "#{@tracker.options[:findsecbugs_path]}/findsecbugs.sh", "-effort:max", "-quiet", "-xml:withMessages", "-output", "#{@results_file.path}", "#{@trigger.path}", :chdir => @tracker.options[:findsecbugs_path] ) 36 | @results = Nokogiri::XML(File.read(@results_file)).xpath '//BugInstance' 37 | end 38 | 39 | def analyze 40 | begin 41 | @results.each do |result| 42 | description = result.xpath('ShortMessage').text 43 | bug_type = result.attributes['type'].value 44 | detail = "Class: #{result.at_xpath('Method').attributes['classname'].value}, Method: #{result.at_xpath('Method').attributes['name'].value}\n#{result.xpath('LongMessage').text}\nhttps://find-sec-bugs.github.io/bugs.htm##{bug_type}" 45 | 46 | file = result.at_xpath('SourceLine').attributes['sourcepath'].value 47 | trigger_path = Pathname.new(@trigger.path) 48 | real_path = nil 49 | trigger_path.find {|path| real_path = path if path.fnmatch "*/#{file}"} 50 | file = real_path.relative_path_from(trigger_path).to_s unless real_path.nil? 51 | 52 | line = result.at_xpath('SourceLine[@primary="true"]').attributes['start'].value 53 | code = "#{result.at_xpath('String').attributes['value'].value}" 54 | source = {:scanner => @name, :file => file, :line => line, :code => code} 55 | sev = result.attributes['priority'].value 56 | fprint = fingerprint("#{description}#{detail}#{source}") 57 | 58 | report description, detail, source, sev, fprint 59 | end 60 | rescue Exception => e 61 | Glue.warn e.message 62 | Glue.warn e.backtrace 63 | ensure 64 | File.unlink @results_file 65 | end 66 | end 67 | 68 | def supported? 69 | unless find_executable0('mvn') and File.exist?("#{@trigger.path}/pom.xml") 70 | Glue.notify "FindSecurityBugs support requires maven and pom.xml" 71 | Glue.notify "Please install maven somewhere in your PATH and include a valid pom.xml in the project root" 72 | return false 73 | end 74 | 75 | unless @tracker.options.has_key?(:findsecbugs_path) and File.exist?("#{@tracker.options[:findsecbugs_path]}/findsecbugs.sh") 76 | Glue.notify "#{@tracker.options[:findsecbugs_path]}" 77 | Glue.notify "Download and unpack the latest findsecbugs-cli release: https://github.com/find-sec-bugs/find-sec-bugs/releases" 78 | return false 79 | else 80 | return true 81 | end 82 | end 83 | 84 | end 85 | -------------------------------------------------------------------------------- /lib/glue/tasks/npm.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'glue/util' 3 | require 'find' 4 | 5 | class Glue::Npm < Glue::BaseTask 6 | 7 | Glue::Tasks.add self 8 | include Glue::Util 9 | 10 | def initialize(trigger, tracker) 11 | super(trigger, tracker) 12 | @name = "NPM" 13 | @description = "Node Package Manager" 14 | @stage = :file 15 | @labels << "file" << "javascript" 16 | @results = [] 17 | end 18 | 19 | def run 20 | exclude_dirs = ['node_modules','bower_components'] 21 | exclude_dirs = exclude_dirs.concat(@tracker.options[:exclude_dirs]).uniq if @tracker.options[:exclude_dirs] 22 | directories_with?('package.json', exclude_dirs).each do |dir| 23 | Glue.notify "#{@name} scanning: #{dir}" 24 | if @tracker.options.has_key?(:npm_registry) 25 | registry = "--registry #{@tracker.options[:npm_registry]}" 26 | else 27 | registry = nil 28 | end 29 | @command = "npm install -q --ignore-scripts #{registry}" 30 | @results << runsystem(true, @command, :chdir => dir) 31 | end 32 | end 33 | 34 | def analyze 35 | begin 36 | if @results.include? false 37 | Glue.warn 'Error installing javascript dependencies with #{@command}' 38 | end 39 | rescue Exception => e 40 | Glue.warn e.message 41 | Glue.warn e.backtrace 42 | end 43 | end 44 | 45 | def supported? 46 | supported = find_executable0('npm') 47 | unless supported 48 | Glue.notify "Install npm: https://nodejs.org/en/download/" 49 | return false 50 | else 51 | return true 52 | end 53 | end 54 | 55 | end 56 | -------------------------------------------------------------------------------- /lib/glue/tasks/nsp.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'glue/util' 3 | 4 | class Glue::NodeSecurityProject < Glue::BaseTask 5 | 6 | Glue::Tasks.add self 7 | include Glue::Util 8 | 9 | def initialize(trigger, tracker) 10 | super(trigger, tracker) 11 | @name = "NodeSecurityProject" 12 | @description = "Node Security Project" 13 | @stage = :code 14 | @labels << "code" << "javascript" << "node" 15 | @results = [] 16 | end 17 | 18 | def run 19 | exclude_dirs = ['node_modules','bower_components'] 20 | exclude_dirs = exclude_dirs.concat(@tracker.options[:exclude_dirs]).uniq if @tracker.options[:exclude_dirs] 21 | directories_with?('package.json', exclude_dirs).each do |dir| 22 | Glue.notify "#{@name} scanning: #{dir}" 23 | res = runsystem(true, "nsp", "check", "--output", "json", :chdir => dir) 24 | @results << JSON.parse(res) 25 | end 26 | end 27 | 28 | def analyze 29 | begin 30 | @results.each do |dir_result| 31 | # This block iterates through each package name found and selects the unique nsp advisories 32 | # regardless of version, and builds a Glue finding hash for each unique package/advisory combo. 33 | dir_result.uniq {|finding| finding['module']}.each do |package| 34 | dir_result.select {|f| f['module'] == package['module']}.uniq {|m| m['advisory']}.each do |unique_finding| 35 | description = "#{unique_finding['module']} - #{unique_finding['title']}" 36 | detail = "Upgrade to versions: #{unique_finding['patched_versions']}\n#{unique_finding['advisory']}" 37 | source = { 38 | :scanner => 'NodeSecurityProject', 39 | :file => "#{unique_finding['module']} - #{unique_finding['vulnerable_versions']}", 40 | :line => nil, 41 | :code => nil 42 | } 43 | report description, detail, source, 'medium', fingerprint("#{description}#{detail}#{source}") 44 | end 45 | end 46 | end 47 | rescue Exception => e 48 | Glue.warn e.message 49 | Glue.warn e.backtrace 50 | end 51 | end 52 | 53 | def supported? 54 | supported=runsystem(true, "nsp", "--version") 55 | if supported =~ /command not found/ 56 | Glue.notify "Install nodesecurity: 'npm install -g nsp'" 57 | return false 58 | else 59 | return true 60 | end 61 | end 62 | 63 | end 64 | -------------------------------------------------------------------------------- /lib/glue/tasks/pmd.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'glue/util' 3 | require 'nokogiri' 4 | require 'pathname' 5 | 6 | class Glue::PMD < Glue::BaseTask 7 | 8 | Glue::Tasks.add self 9 | include Glue::Util 10 | 11 | def initialize(trigger, tracker) 12 | super(trigger, tracker) 13 | @name = "PMD" 14 | @description = "PMD Source Code Analyzer" 15 | @stage = :code 16 | @labels << "code" 17 | end 18 | 19 | def run 20 | @tracker.options[:pmd_checks] ||= "java-basic,java-sunsecure" 21 | results_xml = runsystem(true,'bin/run.sh', 'pmd', '-d', "#{@trigger.path}", '-f', 'xml', '-R', "#{@tracker.options[:pmd_checks]}", :chdir => @tracker.options[:pmd_path]) 22 | @results = Nokogiri::XML(results_xml).xpath('//file') 23 | end 24 | 25 | def analyze 26 | begin 27 | @results.each do |result| 28 | attributes = result.at_xpath('violation').attributes 29 | description = result.children.children.to_s.strip 30 | detail = "Ruleset: #{attributes['ruleset']}" 31 | source = {:scanner => @name, :file => result.attributes['name'].to_s.split(Pathname.new(@trigger.path).cleanpath.to_s)[1][1..-1], :line => attributes['beginline'].to_s, :code => "package: #{attributes['package'].to_s}\nclass: #{attributes['class'].to_s}\nmethod: #{attributes['method'].to_s}" } 32 | case attributes['priority'].value.to_i 33 | when 3 34 | sev = 1 35 | when 2 36 | sev = 2 37 | when 1 38 | sev = 3 39 | else 40 | sev = 0 41 | end 42 | fprint = fingerprint("#{description}#{detail}#{source}#{sev}") 43 | 44 | report description, detail, source, sev, fprint 45 | end 46 | rescue Exception => e 47 | Glue.warn e.message 48 | Glue.warn e.backtrace 49 | end 50 | end 51 | 52 | def supported? 53 | unless @tracker.options.has_key?(:pmd_path) and File.exist?("#{@tracker.options[:pmd_path]}/bin/run.sh") 54 | Glue.notify "#{@tracker.options[:pmd_path]}" 55 | Glue.notify "Install PMD from: https://pmd.github.io/" 56 | return false 57 | else 58 | return true 59 | end 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /lib/glue/tasks/scanjs.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | 3 | class Glue::ScanJS < Glue::BaseTask 4 | 5 | # WIP 6 | # Glue::Tasks.add self 7 | 8 | def initialize(trigger, tracker) 9 | super(trigger) 10 | @name = "ScanJS" 11 | @description = "Source analysis for JavaScript" 12 | @stage = :code 13 | @labels << "code" << "javascript" 14 | end 15 | 16 | def run 17 | Glue.notify "#{@name}" 18 | rootpath = @trigger.path 19 | @result=`scanner.js -t "#{rootpath}"` 20 | end 21 | 22 | def analyze 23 | puts @result 24 | end 25 | 26 | def supported? 27 | # In future, verify tool is available. 28 | return true 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /lib/glue/tasks/scout2.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'json' 3 | require 'glue/util' 4 | require 'securerandom' 5 | 6 | class Glue::Scout < Glue::BaseTask 7 | 8 | Glue::Tasks.add self 9 | include Glue::Util 10 | 11 | def initialize(trigger, tracker) 12 | super(trigger, tracker) 13 | @name = "Scout" 14 | @description = "Security review for your AWS environment" 15 | @stage = :live 16 | @labels << "cloud" << "aws" 17 | end 18 | 19 | # TODO AWS Credentials 20 | # TODO Docker image 21 | # TODO Cleanup issues - release 1.0 22 | 23 | def run 24 | rootpath = @trigger.path 25 | context = SecureRandom.uuid 26 | @tmppath = "/tmp/#{context}/" 27 | runsystem(true, "Scout2", "--no-browser", "--report-dir", "#{@tmppath}") 28 | file = File.open("#{@tmppath}/inc-awsconfig/aws_config.js", "rb") 29 | @result= file.read 30 | end 31 | 32 | def analyze 33 | begin 34 | # Glue.warn @result 35 | start = @result.index('{') # First we need to take out the variable = part which is not proper JSON 36 | json = @result.slice(start, @result.size) 37 | parsed = JSON.parse(json) 38 | count = 0 39 | parsed["services"].each do |servicename, servicesjson| 40 | # This would be a chance to skip a named service... 41 | if servicesjson["findings"] then # Have seen this as empty / nil in practice 42 | servicesjson["findings"].each do |findingname, detail| 43 | count = count + 1 44 | severity = "low" 45 | if detail["level"] === "danger" then severity = "high" end 46 | 47 | source = { :scanner => @name, 48 | :service => servicename, 49 | :findingname => findingname 50 | # TODO Add region? 51 | } 52 | 53 | # This would be a place to only report danger. (If Sev low) 54 | report findingname, 55 | detail["description"], 56 | source, 57 | severity, 58 | fingerprint(source.to_s) 59 | 60 | end 61 | end 62 | end 63 | rescue Exception => e 64 | Glue.warn e.message 65 | Glue.warn e.backtrace 66 | Glue.warn "Raw result: #{@result}" 67 | end 68 | end 69 | 70 | def supported? 71 | supported=runsystem(true, "Scout2", "-h") 72 | if supported =~ /usage: Scout2/ 73 | return true 74 | else 75 | Glue.notify "Install python and pip." 76 | Glue.notify "Run: pip install awsscout" 77 | Glue.notify "See: https://github.com/nccgroup/Scout2" 78 | return false 79 | end 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /lib/glue/tasks/sfl.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'glue/util' 3 | require 'json' 4 | require 'find' 5 | require 'English' 6 | 7 | class Glue::SFL < Glue::BaseTask 8 | Glue::Tasks.add self 9 | include Glue::Util 10 | 11 | PATTERNS_FILE_PATH = File.join(File.dirname(__FILE__), "patterns.json") 12 | 13 | def initialize(trigger, tracker) 14 | super(trigger, tracker) 15 | @name = "SFL" 16 | @description = "Sensitive File Lookup (SFL)" 17 | @stage = :code 18 | @labels << "code" 19 | @results = [] 20 | self 21 | end 22 | 23 | def run 24 | begin 25 | Glue.notify @name 26 | run_sfl! 27 | rescue StandardError => e 28 | log_error(e) 29 | end 30 | 31 | self 32 | end 33 | 34 | def analyze 35 | @results.each do |result| 36 | begin 37 | report_finding! result 38 | rescue StandardError => e 39 | log_error(e) 40 | end 41 | end 42 | 43 | self 44 | end 45 | 46 | def supported? 47 | true 48 | end 49 | 50 | def self.patterns 51 | @patterns ||= read_patterns_file 52 | @patterns.dup 53 | end 54 | 55 | def self.matches?(filepath, pattern) 56 | text = extract_filepart(filepath, pattern) 57 | pattern_matched?(text, pattern) 58 | end 59 | 60 | private 61 | 62 | def run_sfl! 63 | files = Find.find(@trigger.path).select { |path| File.file?(path) } 64 | Glue.debug "Found #{files.count} files" 65 | 66 | files.each do |filepath| 67 | self.class.patterns.each do |pattern| 68 | if self.class.matches?(filepath, pattern) 69 | @results << { filepath: filepath, pattern: pattern } 70 | end 71 | end 72 | end 73 | 74 | nil 75 | end 76 | 77 | def report_finding!(result) 78 | pattern = result[:pattern] 79 | filepath = result[:filepath] 80 | 81 | description = pattern['caption'] 82 | detail = pattern['description'] 83 | source = "#{@name}:#{filepath}" 84 | severity = 1 85 | fprint = fingerprint("SFL-#{pattern['part']}#{pattern['type']}" \ 86 | "#{pattern['pattern']}#{filepath}") 87 | 88 | report description, detail, source, severity, fprint 89 | end 90 | 91 | private_class_method def self.read_patterns_file 92 | JSON.parse(File.read(PATTERNS_FILE_PATH)) 93 | rescue 94 | modified_message = "#{$ERROR_INFO} (problem with SFL patterns file)" 95 | raise $ERROR_INFO, modified_message, $ERROR_INFO.backtrace 96 | end 97 | 98 | private_class_method def self.extract_filepart(filepath, pattern) 99 | case pattern['part'] 100 | when 'filename' then File.basename(filepath) 101 | when 'extension' then File.extname(filepath).gsub(/^\./, '') 102 | when 'path' then filepath 103 | else '' 104 | end 105 | end 106 | 107 | private_class_method def self.pattern_matched?(text, pattern) 108 | case pattern['type'] 109 | when 'match' 110 | text == pattern['pattern'] 111 | when 'regex' 112 | regex = Regexp.new(pattern['pattern'], Regexp::IGNORECASE) 113 | !!regex.match(text) 114 | else 115 | false 116 | end 117 | end 118 | 119 | def log_error(e) 120 | Glue.notify "Problem running SFL" 121 | Glue.warn e.inspect 122 | Glue.warn e.backtrace 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /lib/glue/tasks/snyk.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'glue/util' 3 | require 'redcarpet' 4 | 5 | class Glue::Snyk < Glue::BaseTask 6 | 7 | Glue::Tasks.add self 8 | include Glue::Util 9 | 10 | BASE_EXCLUDE_DIRS = %w(node_modules bower_components).freeze 11 | 12 | def initialize(trigger, tracker) 13 | super(trigger, tracker) 14 | @name = "Snyk" 15 | @description = "Snyk.io JS dependency checker" 16 | @stage = :code 17 | @labels << "code" << "javascript" 18 | @results = [] 19 | end 20 | 21 | def run 22 | directories_with?('package.json', exclude_dirs).each do |dir| 23 | Glue.notify "#{@name} scanning: #{dir}" 24 | raw_output = runsystem(true, "snyk", "test", "--json", :chdir => dir) 25 | parsed_output = parse_snyk(raw_output) 26 | @results << parsed_output unless parsed_output.nil? 27 | end 28 | 29 | self 30 | end 31 | 32 | def analyze 33 | @results.each do |dir_results| 34 | # We build a single finding for each uniq result ID within a given directory, 35 | # adding the unique info (upgrade path and files) as a list. 36 | begin 37 | dir_results.chunk { |r| r['id'] }.each do |_, results| 38 | result = results.first 39 | 40 | description = "#{result['name']}@#{result['version']} - #{result['title']}" 41 | detail = markdown_to_html(result['description']) 42 | source = build_source_hash(results) 43 | sev = severity(result['severity']) 44 | fprint = fingerprint("#{description}#{detail}#{source}#{sev}") 45 | 46 | report description, detail, source, sev, fprint 47 | end 48 | rescue NoMethodError, TypeError => e 49 | log_error(e) 50 | end 51 | end 52 | 53 | self 54 | end 55 | 56 | def supported? 57 | supported = find_executable0('snyk') 58 | 59 | unless supported 60 | Glue.notify "Install Snyk: 'npm install -g snyk'" 61 | false 62 | else 63 | true 64 | end 65 | end 66 | 67 | private 68 | 69 | def exclude_dirs 70 | extra_exclude_dirs = @tracker.options[:exclude_dirs] || [] 71 | BASE_EXCLUDE_DIRS | extra_exclude_dirs 72 | end 73 | 74 | def parse_snyk(raw_output) 75 | JSON.parse(raw_output)["vulnerabilities"] 76 | rescue JSON::ParserError, TypeError => e 77 | log_error(e) 78 | nil 79 | end 80 | 81 | def log_error(e) 82 | Glue.notify "Problem running Snyk" 83 | Glue.warn e.inspect 84 | Glue.warn e.backtrace 85 | end 86 | 87 | def markdown_to_html(markdown) 88 | # Use Redcarpet to render the Markdown details to something pretty for web display 89 | @@markdown_engine ||= Redcarpet::Markdown.new Redcarpet::Render::HTML.new(link_attributes: {target: "_blank"}), autolink: true, tables: true 90 | @@markdown_engine.render(markdown).gsub('h2>','strong>').gsub('h3>', 'strong>') 91 | end 92 | 93 | def build_source_hash(results) 94 | # Consolidate the list of files and upgrade paths for all results with the same 'id' 95 | # in the same directory. 96 | # This uses the same form as the retirejs task so it all looks nice together. 97 | 98 | upgrade_paths = [ "Upgrade Path:\n" ] 99 | files = [] 100 | 101 | results.each do |res| 102 | res['upgradePath'].each_with_index do |upgrade, i| 103 | upgrade_paths << "#{res['from'][i]} -> #{upgrade}" 104 | end 105 | files << res['from'].join('->') 106 | end 107 | 108 | { 109 | :scanner => @name, 110 | :file => files.join('
'), 111 | :line => nil, 112 | :code => upgrade_paths.uniq.join("\n"), 113 | } 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /lib/glue/tasks/test.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'glue/util' 3 | 4 | class Glue::Test < Glue::BaseTask 5 | Glue::Tasks.add self 6 | include Glue::Util 7 | 8 | def initialize(trigger, tracker) 9 | super(trigger, tracker) 10 | @name = "Test" 11 | @description = "Test" 12 | @stage = :code 13 | @labels << "code" << "ruby" 14 | end 15 | 16 | def run 17 | # Glue.notify "#{@name}" 18 | rootpath = @trigger.path 19 | Glue.debug "Rootpath: #{rootpath}" 20 | @result= runsystem(true, "grep", "-R", "secret", :chdir => rootpath) 21 | end 22 | 23 | def analyze 24 | begin 25 | list = @result.split(/\n/) 26 | list.each do |match| 27 | report "Match", match, @name, :low, "fingerprint" 28 | end 29 | rescue Exception => e 30 | Glue.warn e.message 31 | Glue.notify "Error grepping ... " 32 | end 33 | end 34 | 35 | def supported? 36 | supported=runsystem(true, "grep", "-h") 37 | if supported =~ /usage/ 38 | Glue.notify "Install grep." 39 | return false 40 | else 41 | return true 42 | end 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /lib/glue/tasks/trufflehog.rb: -------------------------------------------------------------------------------- 1 | require 'glue/tasks/base_task' 2 | require 'glue/util' 3 | require 'httparty' 4 | 5 | # Runs the TruffleHog scanner. See https://github.com/dxa4481/truffleHog for details. 6 | class Glue::Trufflehog < Glue::BaseTask 7 | Glue::Tasks.add self 8 | include Glue::Util 9 | 10 | ISSUE_SEVERITY = 4 11 | 12 | def initialize(trigger, tracker) 13 | super(trigger, tracker) 14 | @name = "Trufflehog" 15 | @description = "Runs Trufflehog check" 16 | @stage = :code 17 | @labels << "code" << "java" << ".net" 18 | 19 | @trufflehog_path = '/home/glue/tools/truffleHog/truffleHog/truffleHog.py' 20 | end 21 | 22 | def run 23 | Glue.notify "#{@name}" 24 | @result = runsystem(true, '/usr/bin/env', 'python', @trufflehog_path, '--json', @trigger.path) 25 | end 26 | 27 | def analyze 28 | begin 29 | # Glue.debug "Parsing results..." 30 | # puts @result 31 | get_warnings 32 | rescue Exception => e 33 | Glue.notify "Problem running Trufflehog ... skipped." 34 | Glue.notify e.message 35 | raise e 36 | end 37 | end 38 | 39 | def supported? 40 | if runsystem(false, '/usr/bin/env', 'python', @trufflehog_path, '-h').empty? 41 | Glue.notify "Check that TruffleHog is installed at #{@trufflehog_path}." 42 | return false 43 | end 44 | 45 | true 46 | end 47 | 48 | private 49 | 50 | def get_warnings 51 | JSON::parse(@result).each do |title, string| 52 | detail = "Apparent password or other secret: #{string}" 53 | fingerprint = "Trufflehog|#{title}" 54 | self.report "Possible password or other secret in source code.", detail, title, ISSUE_SEVERITY, fingerprint 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/glue/tracker.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | class Glue::Tracker 4 | attr_reader :options 5 | attr_reader :warnings 6 | attr_reader :errors 7 | attr_reader :findings 8 | 9 | # Pass in the options. 10 | # Let the Tracker be the one thing that gets passed around 11 | # with options and collecting output. 12 | def initialize options 13 | @options = options 14 | @warnings = [] 15 | @errors = [] 16 | @findings = [] 17 | end 18 | 19 | #Process events that 20 | def process event 21 | 22 | end 23 | 24 | def error error 25 | @errors << error 26 | end 27 | 28 | def warn warning 29 | @warnings << warning 30 | end 31 | 32 | def report finding 33 | @findings << finding 34 | end 35 | 36 | def get_worst_finding 37 | worst = nil 38 | @findings.each do |finding| 39 | if !worst 40 | worst = finding 41 | elsif finding.severity > worst.severity 42 | worst = finding 43 | end 44 | end 45 | worst 46 | end 47 | 48 | def to_json 49 | s = "{ \"findings\": [ " 50 | @findings.each do |finding| 51 | s << finding.to_json 52 | s << "," 53 | end 54 | s = s.slice(0,s.length-1) # One easy way to remove the last , 55 | s << "] }" 56 | s 57 | 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/glue/util.rb: -------------------------------------------------------------------------------- 1 | require 'open3' 2 | require 'pathname' 3 | require 'digest' 4 | 5 | module Glue::Util 6 | 7 | def runsystem(report, *splat) 8 | Open3.popen3(*splat) do |stdin, stdout, stderr, wait_thr| 9 | 10 | # start a thread consuming the stdout buffer 11 | # if the pipes fill up a deadlock occurs 12 | stdout_consumed = "" 13 | consumer_thread = Thread.new { 14 | while line = stdout.gets do 15 | stdout_consumed += line 16 | end 17 | } 18 | 19 | if $logfile and report 20 | while line = stderr.gets do 21 | $logfile.puts line 22 | end 23 | end 24 | 25 | consumer_thread.join 26 | return stdout_consumed.chomp 27 | #return stdout.read.chomp 28 | end 29 | end 30 | 31 | def fingerprint text 32 | Digest::SHA2.new(256).update(text).to_s 33 | end 34 | 35 | def strip_archive_path path, delimeter 36 | path.split(delimeter).last.split('/')[1..-1].join('/') 37 | end 38 | 39 | def relative_path path, pwd 40 | pathname = Pathname.new(path) 41 | return path if pathname.relative? 42 | pathname.relative_path_from(Pathname.new pwd) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/glue/version.rb: -------------------------------------------------------------------------------- 1 | module Glue 2 | Version = "0.9.4" 3 | end 4 | -------------------------------------------------------------------------------- /spec/awsscout_spec.rb: -------------------------------------------------------------------------------- 1 | # require 'spec_helper' 2 | 3 | # require 'glue/tasks' 4 | # require 'glue/tracker' 5 | # require 'glue/tasks/scout2' 6 | # require 'glue' 7 | 8 | # def get_scouter 9 | # options = {} 10 | # trigger = "abc" 11 | # tracker = Glue::Tracker.new(options) 12 | # scouter = Glue::Scout.new(@trigger, @tracker) 13 | # scouter 14 | # end 15 | 16 | # RSpec.describe "Test AWS Scout Glue Task Supports" do 17 | # scouter = get_scouter 18 | # result = scouter.supported? 19 | # it { 20 | # expect(result).to be == true 21 | # } 22 | # end 23 | 24 | # RSpec.describe "Test analyze on main scout_data.json file" do 25 | # scouter = get_scouter 26 | # scouter.result = File.open("#{File.expand_path(File.dirname(__FILE__))}/scout_data.json", "rb") 27 | # scouter.analyze 28 | # # scouter. 29 | # end 30 | 31 | 32 | -------------------------------------------------------------------------------- /spec/cli_spec.rb: -------------------------------------------------------------------------------- 1 | require 'aruba' 2 | 3 | # RSpec.describe 'CLI HELP', :type => :aruba do 4 | # before(:each) { run('glue -h') } 5 | # it { expect(last_command_started).to be_successfully_executed } 6 | # it { expect(last_command_started).to have_output /Glue is a swiss army knife of security analysis tools/ } 7 | # end 8 | 9 | # RSpec.describe 'CLI Version', :type => :aruba do 10 | # before(:each) { run('glue -v') } 11 | # it { expect(last_command_started).to be_successfully_executed } 12 | # it { expect(last_command_started).to have_output /Glue 0.9.4/ } 13 | # end 14 | -------------------------------------------------------------------------------- /spec/eicar.com: -------------------------------------------------------------------------------- 1 | X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H* -------------------------------------------------------------------------------- /spec/filters/file_filter/targets/finding_ignore.json: -------------------------------------------------------------------------------- 1 | { 2 | "fingerprint1": "ignore", 3 | "fingerprint2": "new" 4 | } -------------------------------------------------------------------------------- /spec/filters/file_filter/targets/finding_partial.json: -------------------------------------------------------------------------------- 1 | { 2 | "fingerprint1": "ignore", 3 | "fingerprint2": "new" 4 | } -------------------------------------------------------------------------------- /spec/filters/file_filter/targets/finding_postpone.json: -------------------------------------------------------------------------------- 1 | { 2 | "fingerprint1": "postpone:1-1-2999", 3 | "fingerprint2": "new" 4 | } -------------------------------------------------------------------------------- /spec/filters/file_filter/targets/finding_postpone_passed.json: -------------------------------------------------------------------------------- 1 | { 2 | "fingerprint1": "postpone:1-1-1", 3 | "fingerprint2": "new" 4 | } -------------------------------------------------------------------------------- /spec/parse_scout_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'json' 3 | 4 | RSpec.describe 'Parse Scout JSON' do 5 | file = File.open("#{File.expand_path(File.dirname(__FILE__))}/scout_data.json", "rb") 6 | result = file.read 7 | start = result.index('{') 8 | result = result.slice(start,result.size) 9 | #puts "Result: #{result}" 10 | it { 11 | expect( JSON.parse(result) ) 12 | json = JSON.parse(result) 13 | count = 0 14 | findingcount = 0 15 | dangercount = 0 16 | json["services"].each do |name, servicesjson| 17 | count = count + 1 18 | # puts "Count: #{count}" 19 | # puts name 20 | if servicesjson["findings"] then 21 | servicesjson["findings"].each do |findingname, detail| 22 | findingcount = findingcount + 1 23 | # puts "\t#{findingname}" 24 | # puts "\t\t#{detail["description"]}" 25 | # puts "\t\t#{detail["level"]}" 26 | if detail["level"] == "danger" then 27 | dangercount = dangercount + 1 28 | end 29 | end 30 | end 31 | end 32 | # puts "Finding count #{dangercount}" 33 | expect(findingcount).to be == 109 34 | expect(dangercount).to be == 75 35 | expect(count).to be == 15 36 | } 37 | end 38 | -------------------------------------------------------------------------------- /spec/reporters/jira_reporter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'glue' 4 | require 'glue/event' 5 | require 'glue/tracker' 6 | require 'glue/finding' 7 | require 'glue/reporters' 8 | require 'glue/reporters/jira_reporter' 9 | 10 | describe Glue::JiraReporter do 11 | 12 | describe "JIRA Reporter" do 13 | subject {Glue::JiraReporter.new()} 14 | 15 | it "should set jira issue type to Bug when no type given" do 16 | expected_output = "Bug" 17 | actual_output_example_1 = subject.send("jira_issue_type", nil) 18 | actual_output_example_2 = subject.send("jira_issue_type", "") 19 | 20 | expect(expected_output).to eq(actual_output_example_1) 21 | expect(expected_output).to eq(actual_output_example_2) 22 | end 23 | 24 | it "should set jira issue to type passed by the user" do 25 | expected_output = "Story" 26 | actual_output = subject.send("jira_issue_type", "Story") 27 | 28 | expect(expected_output).to eq(actual_output) 29 | end 30 | 31 | end 32 | end -------------------------------------------------------------------------------- /spec/reporters/slack_reporter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'glue' 4 | require 'glue/event' 5 | require 'glue/tracker' 6 | require 'glue/finding' 7 | require 'glue/reporters' 8 | require 'glue/reporters/slack_reporter' 9 | 10 | describe Glue::SlackReporter do 11 | 12 | before do 13 | @tracker = Glue::Tracker.new({ 14 | slack_token: "", 15 | slack_channel: "" 16 | }) 17 | 18 | @tracker.report Glue::Finding.new( "finding_appname", 19 | "finding_description", 20 | "finding_detail", 21 | "finding_test", 22 | 1, 23 | "fingerprint_1", 24 | "finding_task" ) 25 | end 26 | 27 | describe "Slack Reporter" do 28 | subject {Glue::SlackReporter.new()} 29 | 30 | it "should report findings as a slack message with an attachment" do 31 | # Stub out requests to Slack API 32 | stub_request(:post, "https://slack.com/api/auth.test") 33 | .to_return(status: 200, body: "", headers: {}) 34 | 35 | stub_request(:post, "https://slack.com/api/chat.postMessage") 36 | .to_return(status: 200, body: "", headers: {}) 37 | 38 | 39 | # Build slack report 40 | subject.run_report(@tracker) 41 | 42 | # Check slack client made request to send message with attachment for findings 43 | WebMock.should have_requested(:post, "https://slack.com/api/chat.postMessage") 44 | .with{|req| 45 | req.body.include?("attachments=%0A%09Description%3A+finding_description") 46 | req.body.include?("text=OWASP+Glue+test+run+completed+-+See+attachment.") 47 | } 48 | end 49 | end 50 | end -------------------------------------------------------------------------------- /spec/reporters/teamcity_reporter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'glue' 4 | require 'glue/event' 5 | require 'glue/tracker' 6 | require 'glue/finding' 7 | require 'glue/reporters' 8 | require 'glue/reporters/teamcity_reporter' 9 | 10 | describe Glue::TeamCityReporter do 11 | 12 | before do 13 | @tracker = Glue::Tracker.new({}) 14 | @tracker.report Glue::Finding.new( "test", "test", "test", "test", 1, "fingerprint1", "some test" ) 15 | end 16 | 17 | describe "TeamCity Reporter" do 18 | subject {Glue::TeamCityReporter.new()} 19 | describe "Report non-high finding as ignored tests" do 20 | 21 | it "should write all finding to file with state 'new'" do 22 | output = subject.run_report(@tracker) 23 | expected = %q(##teamcity[message text='Report failed tests for each finding with severity equal or above High' status='NORMAL'] 24 | ##teamcity[testSuiteStarted name='some test'] 25 | ##teamcity[testIgnored name='fingerprint1' message='Severity Low'] 26 | ##teamcity[testSuiteFinished name='some test'] 27 | ) 28 | expect(output).to eq(expected) 29 | end 30 | end 31 | 32 | describe "Report all finding as failing tests when setting the appropriate level" do 33 | before do 34 | @tracker.options[:teamcity_min_level] = 1 35 | end 36 | 37 | it "should write all finding to file with state 'new'" do 38 | output = subject.run_report(@tracker) 39 | expected = %q(##teamcity[message text='Report failed tests for each finding with severity equal or above Low' status='NORMAL'] 40 | ##teamcity[testSuiteStarted name='some test'] 41 | ##teamcity[testStarted name='fingerprint1' captureStandardOutput='true'] 42 | Source: test 43 | Details: test 44 | ##teamcity[testFailed name='fingerprint1' message='Severity Low' details='test'] 45 | ##teamcity[testFinished name='fingerprint1'] 46 | ##teamcity[testSuiteFinished name='some test'] 47 | ) 48 | expect(output).to eq(expected) 49 | end 50 | end 51 | end 52 | end -------------------------------------------------------------------------------- /spec/support/aruba.rb: -------------------------------------------------------------------------------- 1 | require 'aruba/rspec' 2 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/README.md: -------------------------------------------------------------------------------- 1 | ## bundler-audit spec tests 2 | 3 | ### Overview of bundler-audit 4 | 5 | [bundler-audit](https://github.com/rubysec/bundler-audit) 6 | 7 | Scans a project's vulnerable versions of gems in the `Gemfile.lock` file and checks for gem sources without TLS. 8 | 9 | The names/versions are compared against the [ruby-advisory-db ](https://github.com/rubysec/ruby-advisory-db). 10 | 11 | To install bundler-audit: 12 | ``` 13 | gem install bundler-audit 14 | ``` 15 | 16 | The simplest way to run it from the command line is to `cd` to the folder with the `Gemfile.lock` file and call: 17 | ``` 18 | bundle-audit check 19 | ``` 20 | 21 | In Glue, `bundler-audit` is called with the following argument: 22 | ``` 23 | bundle-audit check 24 | ``` 25 | 26 | Some other command line options when running bundler-audit locally: 27 | * `--update` - Updates the ruby-advisory-db; 28 | * `--ignore [ADVISORY-ID]` - Ignores specific advisories. 29 | 30 | See [bundler-audit documentation](https://www.rubydoc.info/gems/bundler-audit/frames) for more info. 31 | 32 | ### The spec tests 33 | 34 | The specs do not call the bundler-audit tool because this would be too slow (~1 sec per spec test). 35 | Instead the specs rely on stubbing Glue's `runsystem` method (which calls CLI commands). 36 | 37 | In the specs, the return value of `runsystem` is always a canned response. 38 | Either it will be a generic, minimal response, or it will be a snapshot of an actual bundler-audit report. 39 | 40 | The actual reports were generated via the script `generate_reports.sh`. 41 | The targets of the spec tests were set up in a minimal way to produce non-trivial output. 42 | This required a `Gemfile.lock` file per target. 43 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/generate_reports.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Runs bundle-audit on the contents of the 'targets' dir, 3 | # storing the output in 'report.txt' within each target folder. 4 | # Include a 'SKIP.txt' file next to 'package.json' if you don't want snyk to run on that target. 5 | 6 | set -e 7 | 8 | run_bundleaudit_recurs () 9 | { 10 | if [ -f "Gemfile.lock" ] && [ ! -f "SKIP.txt" ]; then 11 | bundle-audit check > report.txt 12 | fi 13 | 14 | for SUBTARGET in * 15 | do 16 | if [ -d ${SUBTARGET} ]; then 17 | cd ${SUBTARGET} 18 | run_bundleaudit_recurs 19 | cd .. 20 | fi 21 | done 22 | } 23 | 24 | DIR=`dirname $0` 25 | cd "${DIR}/targets/" 26 | run_bundleaudit_recurs 27 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/targets/finding_1/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | kafo (0.3.1) 5 | 6 | PLATFORMS 7 | ruby 8 | 9 | RUBY VERSION 10 | ruby 2.3.3 11 | 12 | BUNDLED WITH 13 | 1.14.6 14 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/targets/finding_1/report.txt: -------------------------------------------------------------------------------- 1 | Name: kafo 2 | Version: 0.3.1 3 | Advisory: CVE-2014-0135 4 | Criticality: Low 5 | URL: http://osvdb.org/show/osvdb/106826 6 | Title: Kafo default_values.yaml Insecure Permissions Local Information Disclosure 7 | Solution: upgrade to ~> 0.3.17, >= 0.5.2 8 | 9 | Vulnerabilities found! 10 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/targets/finding_2/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | http (0.7.3) 5 | 6 | PLATFORMS 7 | ruby 8 | 9 | RUBY VERSION 10 | ruby 2.3.3 11 | 12 | BUNDLED WITH 13 | 1.14.6 14 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/targets/finding_2/report.txt: -------------------------------------------------------------------------------- 1 | Name: http 2 | Version: 0.7.1 3 | Advisory: CVE-2015-1828 4 | Criticality: Medium 5 | URL: https://groups.google.com/forum/#!topic/httprb/jkb4oxwZjkU 6 | Title: HTTPS MitM vulnerability in http.rb 7 | Solution: upgrade to >= 0.7.3, ~> 0.6.4 8 | 9 | Vulnerabilities found! 10 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/targets/finding_2_unknown/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | nokogiri (1.8.2) 5 | 6 | PLATFORMS 7 | ruby 8 | 9 | RUBY VERSION 10 | ruby 2.3.3 11 | 12 | BUNDLED WITH 13 | 1.14.6 14 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/targets/finding_2_unknown/report.txt: -------------------------------------------------------------------------------- 1 | Name: nokogiri 2 | Version: 1.8.2 3 | Advisory: CVE-2018-8048 4 | Criticality: Unknown 5 | URL: https://github.com/sparklemotion/nokogiri/pull/1746 6 | Title: Revert libxml2 behavior in Nokogiri gem that could cause XSS 7 | Solution: upgrade to >= 1.8.3 8 | 9 | Vulnerabilities found! 10 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/targets/finding_3/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | curl 5 | 6 | PLATFORMS 7 | ruby 8 | 9 | RUBY VERSION 10 | ruby 2.3.3 11 | 12 | BUNDLED WITH 13 | 1.14.6 14 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/targets/finding_3/report.txt: -------------------------------------------------------------------------------- 1 | Name: curl 2 | Version: 3 | Advisory: CVE-2013-2617 4 | Criticality: High 5 | URL: http://osvdb.org/show/osvdb/91230 6 | Title: Curl Gem for Ruby URI Handling Arbitrary Command Injection 7 | Solution: remove or disable this gem until a patch is available! 8 | 9 | Vulnerabilities found! 10 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/targets/no_findings/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | nokogiri (1.8.4) 5 | 6 | PLATFORMS 7 | ruby 8 | 9 | RUBY VERSION 10 | ruby 2.3.3 11 | 12 | BUNDLED WITH 13 | 1.14.6 14 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/targets/no_findings/report.txt: -------------------------------------------------------------------------------- 1 | No vulnerabilities found 2 | -------------------------------------------------------------------------------- /spec/tasks/bundle-audit/targets/no_findings_no_gemfile_lock/example.txt: -------------------------------------------------------------------------------- 1 | Empty 2 | -------------------------------------------------------------------------------- /spec/tasks/dynamic/targets/dummy/invalid_report.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "items": [ 4 | { 5 | "description": "desc", 6 | "detail": "detail", 7 | "source": "source", 8 | "severity": "high", 9 | "fingerprint": "fingerprint" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /spec/tasks/dynamic/targets/dummy/invalid_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "task_name": "dummy", 3 | "a_name": "name", 4 | "mappings": [ 5 | { 6 | "key": "issues", 7 | "properties": { 8 | "description": "description", 9 | "detail": "detail", 10 | "source": "source", 11 | "severity": "severity", 12 | "fingerprint": "fingerprint" 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /spec/tasks/dynamic/targets/dummy/mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "task_name": "dummy", 3 | "app_name": "name", 4 | "mappings": [ 5 | { 6 | "key": "issues", 7 | "properties": { 8 | "description": "description", 9 | "detail": "detail", 10 | "source": "source", 11 | "severity": "severity", 12 | "fingerprint": "fingerprint" 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /spec/tasks/dynamic/targets/dummy/report.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "issues": [ 4 | { 5 | "description": "desc", 6 | "detail": "detail", 7 | "source": "source", 8 | "severity": "high", 9 | "fingerprint": "fingerprint" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /spec/tasks/dynamic/targets/tools_samples/zaproxy.json: -------------------------------------------------------------------------------- 1 | { 2 | "@name": "http://api:9999", 3 | "alerts": [ 4 | { 5 | "description": "

Base64 encoded data was disclosed by the application/web server

", 6 | "source": "URI: http://api:9999/ Method: POST", 7 | "detail": "Base64 Disclosure \n Evidence: DxyPP_YQ6qdWluCCz93Xs1CeJPvg \n Solution:

Manually confirm that the Base64 data does not leak sensitive information, and that the data cannot be aggregated/used to exploit other vulnerabilities.

\n Other info:

\\x000f\\x001c�?�\\x0010�V���Re\\x000c��9�7C\\x001b \\x0011Ű�\\x0004?a\tP�\\x0017���\u007f@]ۺ�\\x0005\\x0007��7\\x0006\\x000e���\\x0019�,�D[�n���_)��X�w��&^���3l����'�~h?��O\\x0011�H����΅\\x001c��ޕ�Bi|��>\\x0007\u007fŽ:�-QY(\\x0016

��A|��9��E��%&\\x0011�]�j\\x001c!��o�\\x000e�\\x0014԰�L�\\x0000j:\\x0008V:��]L����փԫ�o$\\x0003����KՆn��5�T_P�ͭ�w����l$\\x000fU���+vq\\x001e\\x001b& P\n7+���u9�\\x001e��tN����+\\x0003�X�R$\\,��{5\t�O

\n Reference:

https://www.owasp.org/index.php/Top_10_2013-A6-Sensitive_Data_Exposure

http://projects.webappsec.org/w/page/13246936/Information%20Leakage

", 8 | "severity": "Informational", 9 | "fingerprint": "10094_http://api:9999/_POST" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /spec/tasks/owasp-dep-check/generate_reports.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Runs dependency-check.sh on the contents of the 'targets' dir, 3 | # storing the output in 'dependency-check-report.xml' within each target root folder. 4 | # Include a 'SKIP.txt' in the root folder if you don't want dependency-check to run on that target. 5 | 6 | set -e 7 | 8 | DEP_CHECK_PATH=~/dependency-check/bin/dependency-check.sh 9 | 10 | function usage () { 11 | local name=$(basename "$0") 12 | 13 | echo >&2 "Usage: ${name} [-p PATH_TO_DEP_CHECK]" 14 | echo >&2 "Options:" 15 | echo >&2 " -p Path to dependency-check.sh executable" 16 | exit 1 17 | } 18 | 19 | run_dependency_check () { 20 | FILES=$(find -type f -name "*.jar") 21 | if [ ! -z ${FILES} ] && [ ! -f "SKIP.txt" ]; then 22 | ${DEP_CHECK_PATH} --project Glue -s . -f XML 23 | fi 24 | } 25 | 26 | 27 | while getopts ":p:h" opt; do 28 | case $opt in 29 | p) 30 | DEP_CHECK_PATH=${OPTARG} 31 | ;; 32 | h) 33 | usage 34 | ;; 35 | \?) 36 | echo >&2 "Invalid option: -${OPTARG}" 37 | usage 38 | ;; 39 | esac 40 | done 41 | 42 | echo >&2 "Using dependency-check path: ${DEP_CHECK_PATH}" 43 | 44 | DIR=`dirname $0` 45 | cd "${DIR}/targets/" 46 | 47 | # dependency-check.sh will generate a report in each root directory 48 | # for all dependencies found in its sub directories 49 | for SUBTARGET in * 50 | do 51 | if [ -d ${SUBTARGET} ]; then 52 | cd ${SUBTARGET} 53 | run_dependency_check 54 | cd .. 55 | fi 56 | done 57 | -------------------------------------------------------------------------------- /spec/tasks/owasp-dep-check/targets/findings_1/kibana-2.2.335.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/owasp-dep-check/targets/findings_1/kibana-2.2.335.jar -------------------------------------------------------------------------------- /spec/tasks/owasp-dep-check/targets/findings_1_nested/findings_1/jackson-databind-2.1.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/owasp-dep-check/targets/findings_1_nested/findings_1/jackson-databind-2.1.4.jar -------------------------------------------------------------------------------- /spec/tasks/owasp-dep-check/targets/findings_2/limesurvey-rc-0.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/owasp-dep-check/targets/findings_2/limesurvey-rc-0.6.jar -------------------------------------------------------------------------------- /spec/tasks/owasp-dep-check/targets/no_findings/jinjava-2.4.15.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/owasp-dep-check/targets/no_findings/jinjava-2.4.15.jar -------------------------------------------------------------------------------- /spec/tasks/retirejs/README.md: -------------------------------------------------------------------------------- 1 | ## RetireJS spec tests 2 | 3 | ### Overview of RetireJS 4 | 5 | [Retire](https://github.com/retirejs/retire.js/) 6 | scans a project's Node.js package dependencies 7 | (similar to [Snyk](https://snyk.io/)) 8 | and also scans the content of files looking for dependencies 9 | on JS libraries. 10 | 11 | The names/versions are compared against 12 | [repositories](https://github.com/RetireJS/retire.js/tree/master/repository) 13 | of known npm and JS library vulnerabilities. 14 | 15 | To install RetireJS: 16 | ``` 17 | npm install -g retire 18 | ``` 19 | 20 | The simplest way to run it from the command line 21 | is to `cd` to the root folder of your project and call: 22 | ``` 23 | retire 24 | ``` 25 | 26 | In Glue, `retire` is called with the following arguments: 27 | ``` 28 | retire -c --outputpath /dev/stdout --outputformat json --path 29 | ``` 30 | (By default, `retire` outputs to `STDERR`. Glue expects results to be 31 | output to `STDOUT`, hence the need for the `--outputpath /dev/stdout`.) 32 | 33 | ### The spec tests 34 | 35 | The specs do not call the RetireJS API because this would be too 36 | slow (about 1 sec per spec test). Instead the specs rely on stubbing 37 | Glue's `runsystem` method (which calls CLI commands). 38 | 39 | In the specs, the return value of `runsystem` is always a canned response. 40 | Either it will be a generic, minimal response, or it will be a snapshot of an 41 | actual RetireJS report (generated using 42 | [this commit](https://github.com/RetireJS/retire.js/commit/75d728139eda79aa825d1fe17ad2af6d48120146) 43 | of RetireJS.) 44 | 45 | The actual reports were generated via the script 'generate_reports.sh'. 46 | The targets of the spec tests were set up in a minimal way to produce 47 | non-trivial output. 48 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/generate_reports.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Runs 'retire' on the contents of the 'test_targets' dir, 3 | # storing the output in 'report.json' within each target folder. 4 | # 5 | # After running it, transfer the results to the 'targets' dir. 6 | # 7 | # Include a 'SKIP.txt' file next to 'package.json' 8 | # if you don't want retire to run on that target. 9 | # 10 | # This uses sed to find-replace the absolute file paths 11 | # with truncated relative versions. 12 | # (Some vulnerabilities report an abs file path. 13 | # Glue attempts to parse this to a relative path, using 'relative_path'. 14 | # But this will not work correctly for the canned reports of 15 | # the spec tests, since the abs file path in the canned report 16 | # won't necessarily match the abs file path on the user's machine. 17 | # To get around this for the spec tests, we just convert the 18 | # reported abs file paths to relative file paths.) 19 | # 20 | # Note with sed: on Mac (but not on Linux) the -i (inplace editing) 21 | # will always create a backup, with extension equal to the first arg 22 | # after -i. 23 | 24 | run_retire_recurs () 25 | { 26 | if [ -f package.json ] && [ ! -f SKIP.txt ]; then 27 | # pwd 28 | retire -c --outputformat json --outputpath report.json 29 | sed -i.bak -e "s;$ABS_DIR/;;g" report.json 30 | rm report.json.bak 31 | fi 32 | 33 | for SUBTARGET in * 34 | do 35 | if [ -d $SUBTARGET ] && [ $SUBTARGET != "node_modules" ]; then 36 | cd $SUBTARGET 37 | run_retire_recurs 38 | cd .. 39 | fi 40 | done 41 | } 42 | 43 | DIR=`dirname $0` 44 | # cd "$DIR/targets/" 45 | cd "$DIR/test_targets/" 46 | ABS_DIR="$(pwd)" 47 | 48 | run_retire_recurs 49 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_1/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_1/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_1_nested/finding_1/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_1_nested/finding_1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_1_nested/finding_1/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_2/node_modules/cookie-signature/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-signature", 3 | "version": "1.0.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cookie-signature": "1.0.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_2/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"cookie-signature","version":"1.0.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/134"],"severity":"medium","identifiers":{"advisory":"Timing attack vulnerability"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_3/node_modules/pivottable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pivottable", 3 | "version": "1.4.0" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "pivottable": "1.4.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_3/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"pivottable","version":"1.4.0","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/139"],"severity":"high","identifiers":{"advisory":"XSS"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_f1/file_1.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI Tooltip 1.9.2 3 | */ 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_f1/package.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/retirejs/targets/finding_f1/package.json -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_f1/report.json: -------------------------------------------------------------------------------- 1 | [{"file":"finding_f1/file_1.js","results":[{"version":"1.9.2","component":"jquery-ui-tooltip","detection":"filecontent","vulnerabilities":[{"info":["http://bugs.jqueryui.com/ticket/8859"],"severity":"high","identifiers":{"bug":"8859","summary":"Autocomplete cross-site scripting vulnerability"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_f1_nested/js_files/file_1.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI Tooltip 1.9.2 3 | */ 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_f1_nested/package.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/retirejs/targets/finding_f1_nested/package.json -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/finding_f1_nested/report.json: -------------------------------------------------------------------------------- 1 | [{"file":"finding_f1_nested/js_files/file_1.js","results":[{"version":"1.9.2","component":"jquery-ui-tooltip","detection":"filecontent","vulnerabilities":[{"info":["http://bugs.jqueryui.com/ticket/8859"],"severity":"high","identifiers":{"bug":"8859","summary":"Autocomplete cross-site scripting vulnerability"}}]}]}] 2 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1-2/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3", 4 | "dependencies": { 5 | "cookie-signature": "1.0.3" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1-2/node_modules/cookie-signature/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-signature", 3 | "version": "1.0.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1-2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1-2/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]},{"results":[{"component":"cookie-signature","version":"1.0.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/134"],"severity":"medium","identifiers":{"advisory":"Timing attack vulnerability"}}]}]},{"results":[{"component":"cookie-signature","version":"1.0.3","parent":{"component":"cli","version":"0.11.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]},"level":2,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/134"],"severity":"medium","identifiers":{"advisory":"Timing attack vulnerability"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123/node_modules/cookie-signature/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-signature", 3 | "version": "1.0.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123/node_modules/pivottable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pivottable", 3 | "version": "1.4.0" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cli": "0.11.3", 5 | "cookie-signature": "1.0.3", 6 | "pivottable": "1.4.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]},{"results":[{"component":"cookie-signature","version":"1.0.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/134"],"severity":"medium","identifiers":{"advisory":"Timing attack vulnerability"}}]}]},{"results":[{"component":"pivottable","version":"1.4.0","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/139"],"severity":"high","identifiers":{"advisory":"XSS"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123_2-1_3-1/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123_2-1_3-1/node_modules/cookie-signature/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-signature", 3 | "version": "1.0.3", 4 | "dependencies": { 5 | "cli": "0.11.3" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123_2-1_3-1/node_modules/pivottable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pivottable", 3 | "version": "1.4.0", 4 | "dependencies": { 5 | "cli": "0.11.3" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123_2-1_3-1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cli": "0.11.3", 5 | "cookie-signature": "1.0.3", 6 | "pivottable": "1.4.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123_2-1_3-1/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]},{"results":[{"component":"cookie-signature","version":"1.0.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/134"],"severity":"medium","identifiers":{"advisory":"Timing attack vulnerability"}}]}]},{"results":[{"component":"pivottable","version":"1.4.0","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/139"],"severity":"high","identifiers":{"advisory":"XSS"}}]}]},{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"pivottable","version":"1.4.0","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/139"],"severity":"high","identifiers":{"advisory":"XSS"}}]},"level":2,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]},{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"cookie-signature","version":"1.0.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/134"],"severity":"medium","identifiers":{"advisory":"Timing attack vulnerability"}}]},"level":2,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123_2-1_3-12/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123_2-1_3-12/node_modules/cookie-signature/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-signature", 3 | "version": "1.0.3", 4 | "dependencies": { 5 | "cli": "0.11.3" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123_2-1_3-12/node_modules/pivottable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pivottable", 3 | "version": "1.4.0", 4 | "dependencies": { 5 | "cli": "0.11.3", 6 | "cookie-signature": "1.0.3" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123_2-1_3-12/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cli": "0.11.3", 5 | "cookie-signature": "1.0.3", 6 | "pivottable": "1.4.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_123_2-1_3-12/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]},{"results":[{"component":"cookie-signature","version":"1.0.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/134"],"severity":"medium","identifiers":{"advisory":"Timing attack vulnerability"}}]}]},{"results":[{"component":"pivottable","version":"1.4.0","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/139"],"severity":"high","identifiers":{"advisory":"XSS"}}]}]},{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"pivottable","version":"1.4.0","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/139"],"severity":"high","identifiers":{"advisory":"XSS"}}]},"level":2,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]},{"results":[{"component":"cookie-signature","version":"1.0.3","parent":{"component":"pivottable","version":"1.4.0","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/139"],"severity":"high","identifiers":{"advisory":"XSS"}}]},"level":2,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/134"],"severity":"medium","identifiers":{"advisory":"Timing attack vulnerability"}}]}]},{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"cookie-signature","version":"1.0.3","parent":{"component":"pivottable","version":"1.4.0","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/139"],"severity":"high","identifiers":{"advisory":"XSS"}}]},"level":2,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/134"],"severity":"medium","identifiers":{"advisory":"Timing attack vulnerability"}}]},"level":3,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]},{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"cookie-signature","version":"1.0.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/134"],"severity":"medium","identifiers":{"advisory":"Timing attack vulnerability"}}]},"level":2,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1_2_3/finding_1/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1_2_3/finding_1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1_2_3/finding_1/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1_2_3/finding_2/node_modules/cookie-signature/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-signature", 3 | "version": "1.0.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1_2_3/finding_2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cookie-signature": "1.0.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1_2_3/finding_2/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"cookie-signature","version":"1.0.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/134"],"severity":"medium","identifiers":{"advisory":"Timing attack vulnerability"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1_2_3/finding_3/node_modules/pivottable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pivottable", 3 | "version": "1.4.0" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1_2_3/finding_3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "pivottable": "1.4.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1_2_3/finding_3/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"pivottable","version":"1.4.0","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/139"],"severity":"high","identifiers":{"advisory":"XSS"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1f1/file_1.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI Tooltip 1.9.2 3 | */ 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1f1/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1f1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_1f1/report.json: -------------------------------------------------------------------------------- 1 | [{"file":"finding_1f1/file_1.js","results":[{"version":"1.9.2","component":"jquery-ui-tooltip","detection":"filecontent","vulnerabilities":[{"info":["http://bugs.jqueryui.com/ticket/8859"],"severity":"high","identifiers":{"bug":"8859","summary":"Autocomplete cross-site scripting vulnerability"}}]}]},{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]}] 2 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_4/node_modules/uglify-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uglify-js", 3 | "version": "2.4.0" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_4/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "uglify-js": "2.4.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_4/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"uglify-js","version":"2.4.0","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://github.com/mishoo/UglifyJS2/issues/751","https://github.com/tmcw/mdast-uglify-bug","https://nodesecurity.io/advisories/39"],"severity":"high"},{"info":["https://nodesecurity.io/advisories/48"],"severity":"medium"}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_5f5/file_5.js: -------------------------------------------------------------------------------- 1 | /* jQuery v1.8.0 */ 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_5f5/node_modules/jquery/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "version": "1.8.0" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_5f5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "jquery": "1.8.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_5f5/report.json: -------------------------------------------------------------------------------- 1 | [{"file":"findings_5f5/file_5.js","results":[{"version":"1.8.0","component":"jquery","detection":"filecontent","vulnerabilities":[{"info":["http://bugs.jquery.com/ticket/11290","http://research.insecurelabs.org/jquery/test/"],"severity":"medium","identifiers":{"bug":"11290","summary":"Selector interpreted as HTML"}},{"info":["https://github.com/jquery/jquery/issues/2432","http://blog.jquery.com/2016/01/08/jquery-2-2-and-1-12-released/"],"severity":"medium","identifiers":{"issue":"2432","summary":"3rd party CORS request may execute"}}]}]},{"results":[{"component":"jquery","version":"1.8.0","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/330"],"severity":"medium","identifiers":{"summary":"jquery_exceeding-stack-call-limit-dos"}},{"info":["https://nodesecurity.io/advisories/328"],"severity":"high","identifiers":{"summary":"jquery_xss"}},{"info":["https://nodesecurity.io/advisories/329"],"severity":"high","identifiers":{"summary":"jquery_xss-via-improper-selector-detection"}}]}]}] 2 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f12/file_12.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI Tooltip 1.9.2 3 | * sessvars ver 1.00 4 | */ 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f12/package.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/retirejs/targets/findings_f12/package.json -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f12/report.json: -------------------------------------------------------------------------------- 1 | [{"file":"findings_f12/file_12.js","results":[{"version":"1.9.2","component":"jquery-ui-tooltip","detection":"filecontent","vulnerabilities":[{"info":["http://bugs.jqueryui.com/ticket/8859"],"severity":"high","identifiers":{"bug":"8859","summary":"Autocomplete cross-site scripting vulnerability"}}]},{"version":"1.00","component":"sessvars","detection":"filecontent","vulnerabilities":[{"info":["http://www.thomasfrank.se/sessionvars.html"],"severity":"low","identifiers":{"summary":"Unsanitized data passed to eval()"}}]}]}] 2 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f1_components_wo_vulnerabilities/file_1.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI - v1.9.0 - 2012-10-05 3 | * */ 4 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f1_components_wo_vulnerabilities/node_modules/example.txt: -------------------------------------------------------------------------------- 1 | // No findings 2 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f1_components_wo_vulnerabilities/package.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/retirejs/targets/findings_f1_components_wo_vulnerabilities/package.json -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f1_components_wo_vulnerabilities/report.json: -------------------------------------------------------------------------------- 1 | [{"file":"findings_f1_components_wo_vulnerabilities/file_1.js","results":[{"version":"1.9.0","component":"jquery-ui-dialog","detection":"filecontent","vulnerabilities":[{"info":["http://bugs.jqueryui.com/ticket/6016","https://nvd.nist.gov/vuln/detail/CVE-2010-5312"],"severity":"medium","identifiers":{"CVE":["CVE-2010-5312"],"bug":"6016","summary":"Title cross-site scripting vulnerability"}},{"info":["https://github.com/jquery/api.jqueryui.com/issues/281","https://nvd.nist.gov/vuln/detail/CVE-2016-7103","https://snyk.io/vuln/npm:jquery-ui:20160721"],"severity":"high","identifiers":{"CVE":["CVE-2016-7103"],"bug":"281","summary":"XSS Vulnerability on closeText option"}}]},{"version":"1.9.0","component":"jquery-ui-autocomplete","detection":"filecontent"},{"version":"1.9.0","component":"jquery-ui-tooltip","detection":"filecontent"}]}] 2 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f1f1/file_1.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI Tooltip 1.9.2 3 | */ 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f1f1/file_2.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI Tooltip 1.9.2 3 | */ 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f1f1/package.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/retirejs/targets/findings_f1f1/package.json -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f1f1/report.json: -------------------------------------------------------------------------------- 1 | [{"file":"findings_f1f1/file_1.js","results":[{"version":"1.9.2","component":"jquery-ui-tooltip","detection":"filecontent","vulnerabilities":[{"info":["http://bugs.jqueryui.com/ticket/8859"],"severity":"high","identifiers":{"bug":"8859","summary":"Autocomplete cross-site scripting vulnerability"}}]}]},{"file":"findings_f1f1/file_2.js","results":[{"version":"1.9.2","component":"jquery-ui-tooltip","detection":"filecontent","vulnerabilities":[{"info":["http://bugs.jqueryui.com/ticket/8859"],"severity":"high","identifiers":{"bug":"8859","summary":"Autocomplete cross-site scripting vulnerability"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f1f2/file_1.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI Tooltip 1.9.2 3 | */ 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f1f2/file_2.js: -------------------------------------------------------------------------------- 1 | /* 2 | * sessvars ver 1.00 3 | */ 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f1f2/package.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/retirejs/targets/findings_f1f2/package.json -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f1f2/report.json: -------------------------------------------------------------------------------- 1 | [{"file":"findings_f1f2/file_1.js","results":[{"version":"1.9.2","component":"jquery-ui-tooltip","detection":"filecontent","vulnerabilities":[{"info":["http://bugs.jqueryui.com/ticket/8859"],"severity":"high","identifiers":{"bug":"8859","summary":"Autocomplete cross-site scripting vulnerability"}}]}]},{"file":"findings_f1f2/file_2.js","results":[{"version":"1.00","component":"sessvars","detection":"filecontent","vulnerabilities":[{"info":["http://www.thomasfrank.se/sessionvars.html"],"severity":"low","identifiers":{"summary":"Unsanitized data passed to eval()"}}]}]}] 2 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f3/file_3.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery UI Dialog 1.8.9 3 | */ 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f3/package.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/retirejs/targets/findings_f3/package.json -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/findings_f3/report.json: -------------------------------------------------------------------------------- 1 | [{"file":"findings_f3/file_3.js","results":[{"version":"1.8.9","component":"jquery-ui-dialog","detection":"filecontent","vulnerabilities":[{"info":["http://bugs.jqueryui.com/ticket/6016"],"severity":"medium","identifiers":{"bug":"6016","summary":"Title cross-site scripting vulnerability"}},{"info":["https://github.com/jquery/api.jqueryui.com/issues/281","https://snyk.io/vuln/npm:jquery-ui:20160721"],"severity":"high","identifiers":{"bug":"281","summary":"XSS Vulnerability on closeText option"}}]}]}] 2 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/malformed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/malformed/report.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/malformed_nested/finding_1/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/malformed_nested/finding_1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/malformed_nested/finding_1/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/malformed_nested/malformed/package.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/retirejs/targets/malformed_nested/malformed/package.json -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/malformed_nested/malformed/report.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/malformed_nested/zz_finding_1/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/malformed_nested/zz_finding_1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/malformed_nested/zz_finding_1/report.json: -------------------------------------------------------------------------------- 1 | [{"results":[{"component":"cli","version":"0.11.3","parent":{"component":"retirejs-test","version":""},"level":1,"vulnerabilities":[{"info":["https://nodesecurity.io/advisories/95"],"severity":"low","identifiers":{"advisory":"Arbitrary File Write"}}]}]}] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/no_findings/node_modules/example.txt: -------------------------------------------------------------------------------- 1 | // No findings 2 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/no_findings/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "retirejs-test", 3 | "dependencies": { 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/no_findings/report.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /spec/tasks/retirejs/targets/no_findings_no_package_json/example.txt: -------------------------------------------------------------------------------- 1 | // No findings 2 | -------------------------------------------------------------------------------- /spec/tasks/retirejs/test_targets/README.md: -------------------------------------------------------------------------------- 1 | This is a placeholder directory for generating reports for new 2 | test targets. 3 | -------------------------------------------------------------------------------- /spec/tasks/sfl/malformed_patterns_file.json: -------------------------------------------------------------------------------- 1 | // A non-JSON file 2 | -------------------------------------------------------------------------------- /spec/tasks/sfl/sfl_patterns_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require 'glue/tasks' 4 | require 'glue/tasks/sfl' 5 | 6 | describe "For Glue::SFL.patterns:" do 7 | before(:all) do 8 | @example_pattern = { 9 | "part" => "filename", 10 | "type" => "regex", 11 | "pattern" => "\\A\\.?(bash|zsh)rc\\z", 12 | "caption" => "Shell configuration file", 13 | "description" => "Shell configuration files might contain..." 14 | } 15 | 16 | @example_keys = @example_pattern.keys 17 | @valid_filepath_parts = %w[filename extension path] 18 | @valid_match_types = %w[match regex] 19 | end 20 | 21 | Glue::SFL.patterns.each do |pattern| 22 | context "the pattern #{pattern}" do 23 | it "has valid keys" do 24 | expect(pattern.keys).to eq(@example_keys) 25 | end 26 | 27 | it "has String values for all keys (or 'nil' for 'description')" do 28 | is_valid = pattern.all? do |key, value| 29 | value.is_a?(String) || (key == 'description' && value.nil?) 30 | end 31 | 32 | expect(is_valid).to eq(true) 33 | end 34 | 35 | it "has a valid pattern['part']" do 36 | expect(pattern['part']).to be_included_in(*@valid_filepath_parts) 37 | end 38 | 39 | it "has a valid pattern['type']" do 40 | expect(pattern['type']).to be_included_in(*@valid_match_types) 41 | end 42 | end 43 | end 44 | 45 | def be_included_in(first_value, *rest) 46 | # https://github.com/rspec/rspec-expectations/issues/760 47 | rest.inject(eq(first_value)) do |matcher, value| 48 | matcher.or eq(value) 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/no_findings/example.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/no_findings/example.txt -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/no_findings_password_subdir/password/example.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/no_findings_password_subdir/password/example.txt -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/one_finding_extension_match/test.pkcs12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/one_finding_extension_match/test.pkcs12 -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/one_finding_extension_regex/test.keypair: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/one_finding_extension_regex/test.keypair -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/one_finding_filename_match/secret_token.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/one_finding_filename_match/secret_token.rb -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/one_finding_filename_regex/.id_rsa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/one_finding_filename_regex/.id_rsa -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/one_finding_path_regex/purple/accounts.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/one_finding_path_regex/purple/accounts.xml -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/two_findings/.id_rsa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/two_findings/.id_rsa -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/two_findings/example.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/two_findings/example.txt -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/two_findings/secret_token.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/two_findings/secret_token.rb -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/two_findings_difft_dirs/dir1/secret_token.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/two_findings_difft_dirs/dir1/secret_token.rb -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/two_findings_difft_dirs/dir2/secret_token.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/two_findings_difft_dirs/dir2/secret_token.rb -------------------------------------------------------------------------------- /spec/tasks/sfl/targets/two_findings_one_file/password_backup.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/sfl/targets/two_findings_one_file/password_backup.txt -------------------------------------------------------------------------------- /spec/tasks/snyk/README.md: -------------------------------------------------------------------------------- 1 | ## Snyk spec tests 2 | 3 | ### Overview of Snyk 4 | 5 | [Snyk](https://snyk.io/) scans a project's Node.js package dependencies, 6 | comparing package version numbers against 7 | [Snyk's database of known vulnerabilities](https://snyk.io/vuln). 8 | 9 | Snyk requires sign-up and an authentication token before it can be run. 10 | See their website for details. It can be run on open source projects for 11 | free, up to a set number of times per billing cycle. 12 | 13 | Once installed, to run Snyk locally from within a project's root folder, run: 14 | ``` 15 | snyk test 16 | ``` 17 | 18 | Snyk can also scan Ruby, Java, Scala, and Python dependencies. 19 | By default, if Snyk does not find a 'yarn.lock' or a 'package.json' file, 20 | then it will look for a 'Gemfile'. If none is found, it will look for a 21 | 'pom.xml'. And so on for the different languages. When it finds a 22 | package-management file that it recognizes, it stops searching further. 23 | 24 | In Glue, Snyk is only called on directories that have a 'package.json' file. 25 | Therefore Glue will only return Snyk results for Node.js package vulnerabilities. 26 | 27 | To replicate this behavior when calling Snyk directly from the command line from 28 | within a given project: 29 | ``` 30 | snyk test --file=package.json 31 | ``` 32 | 33 | Some other command line options when running Snyk locally: 34 | * `--json` - Outputs in json, with more detailed information than the default output. 35 | * `--dev` - Include dev dependencies. By default (and in Glue) dev dependencies are excluded. 36 | 37 | See [Snyk's CLI documentation](https://snyk.io/docs/using-snyk) for more info. 38 | 39 | ### The spec tests 40 | 41 | The specs do not call the actual Snyk API, for two reasons. First, because it would be too slow. 42 | Second (and more importantly), because we would quickly breach the limit for the 43 | number of times we can run Snyk for free per billing cycle. 44 | 45 | Instead, the specs rely on stubbing Glue's 'runsystem' method (which is responsible 46 | for calling CLI commands). 47 | 48 | In the specs, the return value of 'runsystem' is always a canned response. 49 | Either it will be a generic, minimal response, or it will be a snapshot of an 50 | actual Snyk report. 51 | 52 | The actual reports were generated via the script 'generate_reports.sh'. 53 | The targets of the spec tests were set up in a minimal way to produce non-trivial output. 54 | This required a 'package.json' file, a 'node_modules' folder with the package sub-folders, 55 | and a 'package.json' file within the package sub-folders. The 'package.json' files only needed 56 | the "dependencies" list. All extraneous information from the 'package.json' files was 57 | deleted, and the code for the packages themselves was not included. 58 | -------------------------------------------------------------------------------- /spec/tasks/snyk/generate_reports.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Runs Snyk on the contents of the 'targets' dir, 3 | # storing the output in 'report.json' within each target folder. 4 | # Filters the output through grep to delete lines with personal info. 5 | # 6 | # Include a 'SKIP.txt' file next to 'package.json' if you don't want snyk to run on that target. 7 | 8 | run_snyk_recurs () 9 | { 10 | if [ -f package.json ] && [ ! -f SKIP.txt ]; then 11 | # pwd 12 | snyk test --json | grep -v "\"org\"\|\"__filename\"" > report.json 13 | fi 14 | 15 | for SUBTARGET in * 16 | do 17 | if [ -d $SUBTARGET ] && [ $SUBTARGET != "node_modules" ]; then 18 | cd $SUBTARGET 19 | run_snyk_recurs 20 | cd .. 21 | fi 22 | done 23 | } 24 | 25 | DIR=`dirname $0` 26 | cd "$DIR/targets/" 27 | run_snyk_recurs 28 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/finding_1/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/finding_1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snyk-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/finding_1_nested/finding_1/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/finding_1_nested/finding_1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snyk-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/finding_2/node_modules/cookie-signature/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-signature", 3 | "version": "1.0.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/finding_2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snyk-test", 3 | "dependencies": { 4 | "cookie-signature": "1.0.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/finding_2/report.json: -------------------------------------------------------------------------------- 1 | { 2 | "ok": false, 3 | "vulnerabilities": [ 4 | { 5 | "title": "Non-Constant Time String Comparison", 6 | "credit": [ 7 | "" 8 | ], 9 | "creationTime": "2016-08-04T03:44:13.904Z", 10 | "modificationTime": "2016-08-04T03:44:13.904Z", 11 | "publicationTime": "2016-08-29T00:00:00.000Z", 12 | "disclosureTime": "2014-01-28T00:00:00.000Z", 13 | "description": "## Overview\n['cookie-signature'](https://www.npmjs.com/package/cookie-signature) is a library for signing cookies.\n\nVersions before `1.0.4` of the library use the built-in string comparison mechanism, `===`, and not a time constant string comparison. As a result, the comparison will fail faster when the first characters in the token are incorrect. \nAn attacker can use this difference to perform a timing attack, essentially allowing them to guess the secret one character at a time.\n\n## Details\nYou can read more about timing attacks in Node.js on the Snyk blog: https://snyk.io/blog/node-js-timing-attack-ccc-ctf/\n\n## Remediation\nUpgrade to `1.0.4` or greater.\n\n## References\n- https://github.com/tj/node-cookie-signature/blob/master/History.md#104--2014-06-25\n- https://github.com/tj/node-cookie-signature/commit/39791081692e9e14aa62855369e1c7f80fbfd50e\n", 14 | "semver": { 15 | "vulnerable": "<=1.0.3", 16 | "unaffected": ">=1.0.4" 17 | }, 18 | "CVSSv3": "CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N", 19 | "severity": "medium", 20 | "identifiers": { 21 | "CWE": [ 22 | "CWE-208" 23 | ], 24 | "CVE": [], 25 | "NSP": 134, 26 | "ALTERNATIVE": [ 27 | "SNYK-JS-COOKIESIGNATURE-10134" 28 | ] 29 | }, 30 | "patches": [], 31 | "moduleName": "cookie-signature", 32 | "language": "js", 33 | "packageManager": "npm", 34 | "id": "npm:cookie-signature:20160804", 35 | "packageName": "cookie-signature", 36 | "cvssScore": 6.3, 37 | "alternativeIds": [ 38 | "SNYK-JS-COOKIESIGNATURE-10134" 39 | ], 40 | "from": [ 41 | "snyk-test@null", 42 | "cookie-signature@1.0.3" 43 | ], 44 | "upgradePath": [ 45 | false, 46 | "cookie-signature@1.0.4" 47 | ], 48 | "version": "1.0.3", 49 | "name": "cookie-signature", 50 | "isUpgradable": true, 51 | "isPatchable": false, 52 | "parentDepType": "prod" 53 | } 54 | ], 55 | "dependencyCount": 1, 56 | "licensesPolicy": null, 57 | "isPrivate": true, 58 | "packageManager": "npm", 59 | "summary": "1 vulnerable dependency path", 60 | "filtered": { 61 | "ignore": [], 62 | "patch": [] 63 | }, 64 | "uniqueCount": 1 65 | } 66 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/finding_3/node_modules/pivottable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pivottable", 3 | "version": "1.4.0" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/finding_3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snyk-test", 3 | "dependencies": { 4 | "pivottable": "1.4.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/finding_3/report.json: -------------------------------------------------------------------------------- 1 | { 2 | "ok": false, 3 | "vulnerabilities": [ 4 | { 5 | "title": "Cross-site Scripting (XSS)", 6 | "credit": [ 7 | "Todd Wolfson" 8 | ], 9 | "creationTime": "2016-08-17T15:13:32.564Z", 10 | "modificationTime": "2016-08-17T15:13:32.564Z", 11 | "publicationTime": "2016-08-17T15:13:32.564Z", 12 | "disclosureTime": "2016-08-17T15:13:32.564Z", 13 | "description": "## Overview\n[`PivotTable.js`](https://www.npmjs.com/package/pivottable) is a Javascript Pivot Table library with drag-and-drop functionality built on top of jQuery/jQueryUI.\n\nDue to a change from text to html functions in how JSON elements are rendered, a Cross-site scripting (XSS) vulnerability was introduced in version 1.4.0. This vulnerability remained in place until version 2.0.0.\n\nSource: _Node Security Project_\n\n## Remediation\nUpgrade to version 2.0.0 or later.\n\n## References\n- https://github.com/nicolaskruchten/pivottable/pull/401\n\n", 14 | "semver": { 15 | "vulnerable": ">=1.4.0 <2.0.0", 16 | "unaffected": ">=2.0.0" 17 | }, 18 | "CVSSv3": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N", 19 | "severity": "high", 20 | "identifiers": { 21 | "CWE": [ 22 | "CWE-80" 23 | ], 24 | "CVE": [], 25 | "NSP": 139, 26 | "ALTERNATIVE": [ 27 | "SNYK-JS-PIVOTTABLE-10132" 28 | ] 29 | }, 30 | "patches": [], 31 | "moduleName": "pivottable", 32 | "language": "js", 33 | "packageManager": "npm", 34 | "id": "npm:pivottable:20160817", 35 | "packageName": "pivottable", 36 | "cvssScore": 7.2, 37 | "alternativeIds": [ 38 | "SNYK-JS-PIVOTTABLE-10132" 39 | ], 40 | "from": [ 41 | "snyk-test@null", 42 | "pivottable@1.4.0" 43 | ], 44 | "upgradePath": [ 45 | false, 46 | "pivottable@2.0.0" 47 | ], 48 | "version": "1.4.0", 49 | "name": "pivottable", 50 | "isUpgradable": true, 51 | "isPatchable": false, 52 | "parentDepType": "prod" 53 | } 54 | ], 55 | "dependencyCount": 1, 56 | "licensesPolicy": null, 57 | "isPrivate": true, 58 | "packageManager": "npm", 59 | "summary": "1 vulnerable dependency path", 60 | "filtered": { 61 | "ignore": [], 62 | "patch": [] 63 | }, 64 | "uniqueCount": 1 65 | } 66 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/findings_123_2-1_3-12/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/findings_123_2-1_3-12/node_modules/cookie-signature/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-signature", 3 | "version": "1.0.3", 4 | "dependencies": { 5 | "cli": "0.11.3" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/findings_123_2-1_3-12/node_modules/pivottable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pivottable", 3 | "version": "1.4.0", 4 | "dependencies": { 5 | "cli": "0.11.3", 6 | "cookie-signature": "1.0.3" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/findings_123_2-1_3-12/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snyk-test", 3 | "dependencies": { 4 | "cli": "0.11.3", 5 | "cookie-signature": "1.0.3", 6 | "pivottable": "1.4.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/findings_1_2_3/finding_1/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/findings_1_2_3/finding_1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snyk-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/findings_1_2_3/finding_2/node_modules/cookie-signature/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cookie-signature", 3 | "version": "1.0.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/findings_1_2_3/finding_2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snyk-test", 3 | "dependencies": { 4 | "cookie-signature": "1.0.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/findings_1_2_3/finding_2/report.json: -------------------------------------------------------------------------------- 1 | { 2 | "ok": false, 3 | "vulnerabilities": [ 4 | { 5 | "title": "Non-Constant Time String Comparison", 6 | "credit": [ 7 | "" 8 | ], 9 | "creationTime": "2016-08-04T03:44:13.904Z", 10 | "modificationTime": "2016-08-04T03:44:13.904Z", 11 | "publicationTime": "2016-08-29T00:00:00.000Z", 12 | "disclosureTime": "2014-01-28T00:00:00.000Z", 13 | "description": "## Overview\n['cookie-signature'](https://www.npmjs.com/package/cookie-signature) is a library for signing cookies.\n\nVersions before `1.0.4` of the library use the built-in string comparison mechanism, `===`, and not a time constant string comparison. As a result, the comparison will fail faster when the first characters in the token are incorrect. \nAn attacker can use this difference to perform a timing attack, essentially allowing them to guess the secret one character at a time.\n\n## Details\nYou can read more about timing attacks in Node.js on the Snyk blog: https://snyk.io/blog/node-js-timing-attack-ccc-ctf/\n\n## Remediation\nUpgrade to `1.0.4` or greater.\n\n## References\n- https://github.com/tj/node-cookie-signature/blob/master/History.md#104--2014-06-25\n- https://github.com/tj/node-cookie-signature/commit/39791081692e9e14aa62855369e1c7f80fbfd50e\n", 14 | "semver": { 15 | "vulnerable": "<=1.0.3", 16 | "unaffected": ">=1.0.4" 17 | }, 18 | "CVSSv3": "CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N", 19 | "severity": "medium", 20 | "identifiers": { 21 | "CWE": [ 22 | "CWE-208" 23 | ], 24 | "CVE": [], 25 | "NSP": 134, 26 | "ALTERNATIVE": [ 27 | "SNYK-JS-COOKIESIGNATURE-10134" 28 | ] 29 | }, 30 | "patches": [], 31 | "moduleName": "cookie-signature", 32 | "language": "js", 33 | "packageManager": "npm", 34 | "id": "npm:cookie-signature:20160804", 35 | "packageName": "cookie-signature", 36 | "cvssScore": 6.3, 37 | "alternativeIds": [ 38 | "SNYK-JS-COOKIESIGNATURE-10134" 39 | ], 40 | "from": [ 41 | "snyk-test@null", 42 | "cookie-signature@1.0.3" 43 | ], 44 | "upgradePath": [ 45 | false, 46 | "cookie-signature@1.0.4" 47 | ], 48 | "version": "1.0.3", 49 | "name": "cookie-signature", 50 | "isUpgradable": true, 51 | "isPatchable": false, 52 | "parentDepType": "prod" 53 | } 54 | ], 55 | "dependencyCount": 1, 56 | "licensesPolicy": null, 57 | "isPrivate": true, 58 | "packageManager": "npm", 59 | "summary": "1 vulnerable dependency path", 60 | "filtered": { 61 | "ignore": [], 62 | "patch": [] 63 | }, 64 | "uniqueCount": 1 65 | } 66 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/findings_1_2_3/finding_3/node_modules/pivottable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pivottable", 3 | "version": "1.4.0" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/findings_1_2_3/finding_3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snyk-test", 3 | "dependencies": { 4 | "pivottable": "1.4.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/findings_1_2_3/finding_3/report.json: -------------------------------------------------------------------------------- 1 | { 2 | "ok": false, 3 | "vulnerabilities": [ 4 | { 5 | "title": "Cross-site Scripting (XSS)", 6 | "credit": [ 7 | "Todd Wolfson" 8 | ], 9 | "creationTime": "2016-08-17T15:13:32.564Z", 10 | "modificationTime": "2016-08-17T15:13:32.564Z", 11 | "publicationTime": "2016-08-17T15:13:32.564Z", 12 | "disclosureTime": "2016-08-17T15:13:32.564Z", 13 | "description": "## Overview\n[`PivotTable.js`](https://www.npmjs.com/package/pivottable) is a Javascript Pivot Table library with drag-and-drop functionality built on top of jQuery/jQueryUI.\n\nDue to a change from text to html functions in how JSON elements are rendered, a Cross-site scripting (XSS) vulnerability was introduced in version 1.4.0. This vulnerability remained in place until version 2.0.0.\n\nSource: _Node Security Project_\n\n## Remediation\nUpgrade to version 2.0.0 or later.\n\n## References\n- https://github.com/nicolaskruchten/pivottable/pull/401\n\n", 14 | "semver": { 15 | "vulnerable": ">=1.4.0 <2.0.0", 16 | "unaffected": ">=2.0.0" 17 | }, 18 | "CVSSv3": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N", 19 | "severity": "high", 20 | "identifiers": { 21 | "CWE": [ 22 | "CWE-80" 23 | ], 24 | "CVE": [], 25 | "NSP": 139, 26 | "ALTERNATIVE": [ 27 | "SNYK-JS-PIVOTTABLE-10132" 28 | ] 29 | }, 30 | "patches": [], 31 | "moduleName": "pivottable", 32 | "language": "js", 33 | "packageManager": "npm", 34 | "id": "npm:pivottable:20160817", 35 | "packageName": "pivottable", 36 | "cvssScore": 7.2, 37 | "alternativeIds": [ 38 | "SNYK-JS-PIVOTTABLE-10132" 39 | ], 40 | "from": [ 41 | "snyk-test@null", 42 | "pivottable@1.4.0" 43 | ], 44 | "upgradePath": [ 45 | false, 46 | "pivottable@2.0.0" 47 | ], 48 | "version": "1.4.0", 49 | "name": "pivottable", 50 | "isUpgradable": true, 51 | "isPatchable": false, 52 | "parentDepType": "prod" 53 | } 54 | ], 55 | "dependencyCount": 1, 56 | "licensesPolicy": null, 57 | "isPrivate": true, 58 | "packageManager": "npm", 59 | "summary": "1 vulnerable dependency path", 60 | "filtered": { 61 | "ignore": [], 62 | "patch": [] 63 | }, 64 | "uniqueCount": 1 65 | } 66 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/malformed/package.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/snyk/targets/malformed/package.json -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/malformed/report.json: -------------------------------------------------------------------------------- 1 | Missing node_modules folder: we can't test without dependencies. 2 | Please run `npm install` first. 3 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/malformed_nested/finding_1/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/malformed_nested/finding_1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snyk-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/malformed_nested/malformed/package.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/snyk/targets/malformed_nested/malformed/package.json -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/malformed_nested/malformed/report.json: -------------------------------------------------------------------------------- 1 | Missing node_modules folder: we can't test without dependencies. 2 | Please run `npm install` first. 3 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/malformed_nested/zz_finding_1/node_modules/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli", 3 | "version": "0.11.3" 4 | } 5 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/malformed_nested/zz_finding_1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snyk-test", 3 | "dependencies": { 4 | "cli": "0.11.3" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/no_findings/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "snyk-test", 3 | "dependencies": { 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/no_findings/report.json: -------------------------------------------------------------------------------- 1 | { 2 | "ok": true, 3 | "vulnerabilities": [], 4 | "dependencyCount": 0, 5 | "licensesPolicy": null, 6 | "isPrivate": true, 7 | "packageManager": "npm", 8 | "summary": "No known vulnerabilities", 9 | "uniqueCount": 0 10 | } 11 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/no_findings_no_package_json/example.txt: -------------------------------------------------------------------------------- 1 | // No findings 2 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/snyk-sample-data/SKIP.txt: -------------------------------------------------------------------------------- 1 | The snyk cli should not be run on this target. 2 | The 'report.json' is a mildly-edited version of: 3 | https://github.com/snyk/snyk-to-html/blob/master/sample-data/test-report.json 4 | 5 | In the spec, Glue::Snyk.analyze will receive this file as input, 6 | and generate a list of findings from it. 7 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/snyk-sample-data/generate_report.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Grabs a set of sample data from Snyk's github. 3 | 4 | curl https://raw.githubusercontent.com/snyk/snyk-to-html/master/sample-data/test-report.json | grep -v "\"org\"\|\"__filename\"" > report.json 5 | -------------------------------------------------------------------------------- /spec/tasks/snyk/targets/snyk-sample-data/package.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/snyk/targets/snyk-sample-data/package.json -------------------------------------------------------------------------------- /spec/tasks/trufflehog/generate_reports.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Runs Trufflehog on the contents of the 'targets' dir and stores the output in 'results'. 3 | DIR=`dirname $0` 4 | TRUFFLEHOG="/home/glue/tools/truffleHog/truffleHog/truffleHog.py" 5 | 6 | for TARGET_PATH in "$DIR/targets/"* 7 | do 8 | TARGET=`basename $TARGET_PATH` 9 | # echo $TARGET 10 | # echo a > "$DIR/reports/$TARGET.json" 11 | python $TRUFFLEHOG --json $TARGET_PATH > "$DIR/reports/$TARGET.json" 12 | done 13 | -------------------------------------------------------------------------------- /spec/tasks/trufflehog/reports/mult_findings.json: -------------------------------------------------------------------------------- 1 | { 2 | "sub_one_finding/example_m1.txt:3": "b8e407aed80782ce12a008a8c", 3 | "sub_two_findings/example_m2.txt:3": "b8e407aed80782ce12a008a8c", 4 | "sub_two_findings/example_m2.txt:8": "e52a81ad69857a8a7578f68b1a23" 5 | } 6 | -------------------------------------------------------------------------------- /spec/tasks/trufflehog/reports/one_finding.json: -------------------------------------------------------------------------------- 1 | { 2 | "example.txt:3": "b8e407aed80782ce12a008a8c" 3 | } 4 | -------------------------------------------------------------------------------- /spec/tasks/trufflehog/reports/zero_findings.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /spec/tasks/trufflehog/targets/mult_findings/sub_one_finding/example_m1.txt: -------------------------------------------------------------------------------- 1 | // This line has no issues 2 | 3 | // The following line will get flagged: 4 | Application.config.secret_token = 'b8e407aed80782ce12a008a8c' 5 | 6 | // This line has no issues either 7 | -------------------------------------------------------------------------------- /spec/tasks/trufflehog/targets/mult_findings/sub_two_findings/example_m2.txt: -------------------------------------------------------------------------------- 1 | // This line has no issues 2 | 3 | // The following line will get flagged: 4 | Application.config.secret_token = 'b8e407aed80782ce12a008a8c' 5 | 6 | // This line has no issues either 7 | 8 | // The following line will get flagged as well: 9 | Application.config.another_secret_token = 'e52a81ad69857a8a7578f68b1a23' 10 | -------------------------------------------------------------------------------- /spec/tasks/trufflehog/targets/mult_findings/sub_zero_findings/example_m0.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/trufflehog/targets/mult_findings/sub_zero_findings/example_m0.txt -------------------------------------------------------------------------------- /spec/tasks/trufflehog/targets/one_finding/example.txt: -------------------------------------------------------------------------------- 1 | // This line has no issues 2 | 3 | // The following line will get flagged: 4 | Application.config.secret_token = 'b8e407aed80782ce12a008a8c' 5 | 6 | // This line has no issues either 7 | -------------------------------------------------------------------------------- /spec/tasks/trufflehog/targets/zero_findings/example.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/glue/87033800b03330fa9b4a6ce3d11dc2a799f60919/spec/tasks/trufflehog/targets/zero_findings/example.txt -------------------------------------------------------------------------------- /spec/tasks/zap/alerts.json: -------------------------------------------------------------------------------- 1 | { 2 | "alerts": [ 3 | { 4 | "sourceid": "3", 5 | "other": "", 6 | "method": "GET", 7 | "evidence": "max-age=0", 8 | "pluginId": "10049", 9 | "cweid": "524", 10 | "confidence": "Medium", 11 | "wascid": "13", 12 | "description": "description", 13 | "messageId": "1", 14 | "url": "http://juiceshop/", 15 | "reference": "https://tools.ietf.org/html/rfc7234\nhttps://tools.ietf.org/html/rfc7231\nhttp://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html (obsoleted by rfc7234)", 16 | "solution": "solution", 17 | "alert": "Storable but Non-Cacheable Content", 18 | "param": "param", 19 | "attack": "", 20 | "name": "Storable but Non-Cacheable Content", 21 | "risk": "Medium", 22 | "id": "0" 23 | }, 24 | { 25 | "sourceid": "3", 26 | "other": "", 27 | "method": "GET", 28 | "evidence": "", 29 | "pluginId": "10038", 30 | "cweid": "16", 31 | "confidence": "Medium", 32 | "wascid": "15", 33 | "description": "Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware. CSP provides a set of standard HTTP headers that allow website owners to declare approved sources of content that browsers should be allowed to load on that page — covered types are JavaScript, CSS, HTML frames, fonts, images and embeddable objects such as Java applets, ActiveX, audio and video files.", 34 | "messageId": "1", 35 | "url": "http://localhost:3000/", 36 | "reference": "https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Introducing_Content_Security_Policy\nhttps://www.owasp.org/index.php/Content_Security_Policy\nhttp://www.w3.org/TR/CSP/\nhttp://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html\nhttp://www.html5rocks.com/en/tutorials/security/content-security-policy/\nhttp://caniuse.com/#feat=contentsecuritypolicy\nhttp://content-security-policy.com/", 37 | "solution": "Ensure that your web server, application server, load balancer, etc. is configured to set the Content-Security-Policy header, to achieve optimal browser support: \"Content-Security-Policy\" for Chrome 25+, Firefox 23+ and Safari 7+, \"X-Content-Security-Policy\" for Firefox 4.0+ and Internet Explorer 10+, and \"X-WebKit-CSP\" for Chrome 14+ and Safari 6+.", 38 | "alert": "Content Security Policy (CSP) Header Not Set", 39 | "param": "", 40 | "attack": "", 41 | "name": "Content Security Policy (CSP) Header Not Set", 42 | "risk": "Low", 43 | "id": "1" 44 | } 45 | ] 46 | } --------------------------------------------------------------------------------