├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── build.gradle ├── config └── checkstyle │ └── checkstyle.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src └── main ├── java ├── com │ └── imperva │ │ ├── apiattacktool │ │ ├── ToolMain.java │ │ ├── activators │ │ │ ├── EndpointModelToValueConverter.java │ │ │ ├── EndpointTestRequestDataConverter.java │ │ │ ├── HttpRequestGenerator.java │ │ │ ├── ModelToValueConverter.java │ │ │ ├── RequestDataConverter.java │ │ │ └── TestHttpRequestGenerator.java │ │ ├── cli │ │ │ ├── ApiAttackTool.java │ │ │ └── TestListener.java │ │ ├── fuzzing │ │ │ ├── Fuzzer.java │ │ │ ├── modelgenerators │ │ │ │ ├── CloningIterativeFuzzedModelsGenerator.java │ │ │ │ ├── FuzzedModelsGenerator.java │ │ │ │ └── SingleValueFuzzedModelsGenerator.java │ │ │ ├── parameters │ │ │ │ ├── AllParametersPolicyEnforcer.java │ │ │ │ ├── PolicyEnforcer.java │ │ │ │ └── RequiredOnlyPolicyEnforcer.java │ │ │ ├── path │ │ │ │ └── PathFuzzer.java │ │ │ └── value │ │ │ │ ├── CommonValueFuzzer.java │ │ │ │ ├── NegativeSingleValueFuzzer.java │ │ │ │ ├── PositiveSingleValueFuzzer.java │ │ │ │ └── ValueFuzzer.java │ │ ├── infra │ │ │ └── Tuple.java │ │ ├── model │ │ │ ├── tests │ │ │ │ ├── BaseHttpResponseValidator.java │ │ │ │ ├── EndpointTestRequestData.java │ │ │ │ ├── HttpMethod.java │ │ │ │ ├── HttpRequestWrapper.java │ │ │ │ ├── HttpResponseValidator.java │ │ │ │ ├── NegativeTestHttpResponseValidator.java │ │ │ │ ├── ParameterLocation.java │ │ │ │ └── PositiveTestHttpResponseValidator.java │ │ │ └── valued │ │ │ │ ├── ArrayPropertyValue.java │ │ │ │ ├── ArrayPropertyValueAdapter.java │ │ │ │ ├── BooleanPropertyValue.java │ │ │ │ ├── EndpointValuedModel.java │ │ │ │ ├── NumericPropertyValue.java │ │ │ │ ├── PropertyPropertyValue.java │ │ │ │ ├── PropertyValue.java │ │ │ │ ├── PropertyValueNode.java │ │ │ │ ├── StringPropertyValue.java │ │ │ │ └── factory │ │ │ │ ├── PropertyValueFactory.java │ │ │ │ └── SimplePropertyValueFactory.java │ │ ├── processors │ │ │ ├── EndpointModelProcessor.java │ │ │ └── FuzzEndpointModelProcessor.java │ │ ├── tests │ │ │ ├── AbstractTestDriver.java │ │ │ ├── MainTest.java │ │ │ ├── NegativeSinglePropertyScenarioTestDriver.java │ │ │ ├── ScenariosDataProvider.java │ │ │ ├── SingleValueScenarioTestDriver.java │ │ │ ├── TestConfiguration.java │ │ │ ├── TestDriver.java │ │ │ └── TestHttpResponse.java │ │ └── utils │ │ │ └── TestReporter.java │ │ └── apispecparser │ │ ├── exceptions │ │ └── ParseException.java │ │ ├── model │ │ ├── ArrayProperty.java │ │ ├── BooleanProperty.java │ │ ├── EndpointModel.java │ │ ├── EnumerableProperty.java │ │ ├── NumericProperty.java │ │ ├── Property.java │ │ ├── PropertyNode.java │ │ ├── PropertyType.java │ │ └── StringProperty.java │ │ ├── normalizer │ │ ├── ApiDefinitions.java │ │ ├── NormalizedApiSpec.java │ │ ├── NormalizedApiSpecImpl.java │ │ ├── NormalizedEndpoint.java │ │ ├── NormalizedEndpointImpl.java │ │ ├── NormalizedParameter.java │ │ ├── NormalizedPath.java │ │ ├── NormalizedPathImpl.java │ │ └── swagger │ │ │ ├── SwaggerDefinitions.java │ │ │ ├── SwaggerDefinitionsNormalizer.java │ │ │ ├── SwaggerNormalizedApiSpec.java │ │ │ ├── SwaggerNormalizedEndpoint.java │ │ │ ├── SwaggerNormalizedParameter.java │ │ │ └── SwaggerNormalizedPath.java │ │ ├── parsers │ │ ├── ApiSpecFileLocation.java │ │ ├── ApiSpecParser.java │ │ └── swagger │ │ │ ├── Swagger2Parser.java │ │ │ ├── property │ │ │ ├── ApiKeyAuthenticationParamType.java │ │ │ ├── ApiKeyAuthenticationProperties.java │ │ │ ├── AuthenticationProperties.java │ │ │ ├── SimpleSwaggerPropertyFactory.java │ │ │ ├── SwaggerAuthenticationToPropertyFactory.java │ │ │ ├── SwaggerParameterProperties.java │ │ │ └── SwaggerPropertyFactory.java │ │ │ └── propertynode │ │ │ ├── SwaggerArrayItemsToPropertyNode.java │ │ │ ├── SwaggerModelImplToPropertyNode.java │ │ │ ├── SwaggerModelToPropertyNodeFactory.java │ │ │ ├── SwaggerModelToPropertyNodeImpl.java │ │ │ ├── SwaggerPropertyNodeConverter.java │ │ │ └── SwaggerRefModelToPropertyNode.java │ │ └── utils │ │ └── FileUtils.java └── testng.xml └── resources ├── logback.xml ├── runnable.sh └── swaggerPetStore.yaml /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build pipeline 2 | 3 | on: [push] 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v1 10 | - name: Build 11 | run: ./gradlew clean build 12 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Creates a Github release and uploads it 2 | # 3 | # Prerequisite: create a secret called GITHUB_API_KEY with an Github token with public_repo scope 4 | 5 | name: Release pipeline 6 | 7 | on: 8 | push: 9 | tags: 10 | - 'v*' # Triggers only on tags like v1.0 (versions) 11 | 12 | jobs: 13 | build: 14 | name: Create a new release 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@master 19 | - name: Get the version 20 | id: get_version 21 | run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3) 22 | - name: Build project 23 | run: | 24 | ./gradlew clean build 25 | - name: Create release 26 | id: create_release 27 | uses: actions/create-release@v1.0.0 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_API_KEY }} 30 | with: 31 | tag_name: ${{ github.ref }} 32 | release_name: Release ${{ github.ref }} 33 | draft: false 34 | prerelease: false 35 | - name: Upload executable jar 36 | id: upload-release-asset 37 | uses: actions/upload-release-asset@v1.0.1 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_API_KEY }} 40 | with: 41 | upload_url: ${{ steps.create_release.outputs.upload_url }} 42 | asset_path: ./build/libs/imperva-api-attack-tool.jar 43 | asset_name: imperva-api-attack-tool-${{ steps.get_version.outputs.VERSION }}.jar 44 | asset_content_type: application/zip -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/java,maven,macos,gradle,python,intellij+all 3 | # Edit at https://www.gitignore.io/?templates=java,maven,macos,gradle,python,intellij+all 4 | 5 | ### Intellij+all ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff 10 | .idea/**/workspace.xml 11 | .idea/**/tasks.xml 12 | .idea/**/usage.statistics.xml 13 | .idea/**/dictionaries 14 | .idea/**/shelf 15 | 16 | # Generated files 17 | .idea/**/contentModel.xml 18 | 19 | # Sensitive or high-churn files 20 | .idea/**/dataSources/ 21 | .idea/**/dataSources.ids 22 | .idea/**/dataSources.local.xml 23 | .idea/**/sqlDataSources.xml 24 | .idea/**/dynamic.xml 25 | .idea/**/uiDesigner.xml 26 | .idea/**/dbnavigator.xml 27 | 28 | # Gradle 29 | .idea/**/gradle.xml 30 | .idea/**/libraries 31 | 32 | # Gradle and Maven with auto-import 33 | # When using Gradle or Maven with auto-import, you should exclude module files, 34 | # since they will be recreated, and may cause churn. Uncomment if using 35 | # auto-import. 36 | # .idea/modules.xml 37 | # .idea/*.iml 38 | # .idea/modules 39 | 40 | # CMake 41 | cmake-build-*/ 42 | 43 | # Mongo Explorer plugin 44 | .idea/**/mongoSettings.xml 45 | 46 | # File-based project format 47 | *.iws 48 | 49 | # IntelliJ 50 | out/ 51 | 52 | # mpeltonen/sbt-idea plugin 53 | .idea_modules/ 54 | 55 | # JIRA plugin 56 | atlassian-ide-plugin.xml 57 | 58 | # Cursive Clojure plugin 59 | .idea/replstate.xml 60 | 61 | # Crashlytics plugin (for Android Studio and IntelliJ) 62 | com_crashlytics_export_strings.xml 63 | crashlytics.properties 64 | crashlytics-build.properties 65 | fabric.properties 66 | 67 | # Editor-based Rest Client 68 | .idea/httpRequests 69 | 70 | # Android studio 3.1+ serialized cache file 71 | .idea/caches/build_file_checksums.ser 72 | 73 | ### Intellij+all Patch ### 74 | # Ignores the whole .idea folder and all .iml files 75 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 76 | 77 | .idea/ 78 | 79 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 80 | 81 | *.iml 82 | modules.xml 83 | .idea/misc.xml 84 | *.ipr 85 | 86 | ### Java ### 87 | # Compiled class file 88 | *.class 89 | 90 | # Log file 91 | *.log 92 | 93 | # BlueJ files 94 | *.ctxt 95 | 96 | # Mobile Tools for Java (J2ME) 97 | .mtj.tmp/ 98 | 99 | # Package Files # 100 | *.jar 101 | *.war 102 | *.nar 103 | *.ear 104 | *.zip 105 | *.tar.gz 106 | *.rar 107 | 108 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 109 | hs_err_pid* 110 | 111 | ### macOS ### 112 | # General 113 | .DS_Store 114 | .AppleDouble 115 | .LSOverride 116 | 117 | # Icon must end with two \r 118 | Icon 119 | 120 | # Thumbnails 121 | ._* 122 | 123 | # Files that might appear in the root of a volume 124 | .DocumentRevisions-V100 125 | .fseventsd 126 | .Spotlight-V100 127 | .TemporaryItems 128 | .Trashes 129 | .VolumeIcon.icns 130 | .com.apple.timemachine.donotpresent 131 | 132 | # Directories potentially created on remote AFP share 133 | .AppleDB 134 | .AppleDesktop 135 | Network Trash Folder 136 | Temporary Items 137 | .apdisk 138 | 139 | ### Maven ### 140 | target/ 141 | pom.xml.tag 142 | pom.xml.releaseBackup 143 | pom.xml.versionsBackup 144 | pom.xml.next 145 | release.properties 146 | dependency-reduced-pom.xml 147 | buildNumber.properties 148 | .mvn/timing.properties 149 | .mvn/wrapper/maven-wrapper.jar 150 | 151 | ### Python ### 152 | # Byte-compiled / optimized / DLL files 153 | __pycache__/ 154 | *.py[cod] 155 | *$py.class 156 | 157 | # C extensions 158 | *.so 159 | 160 | # Distribution / packaging 161 | .Python 162 | build/ 163 | develop-eggs/ 164 | dist/ 165 | downloads/ 166 | eggs/ 167 | .eggs/ 168 | lib/ 169 | lib64/ 170 | parts/ 171 | sdist/ 172 | var/ 173 | wheels/ 174 | share/python-wheels/ 175 | *.egg-info/ 176 | .installed.cfg 177 | *.egg 178 | MANIFEST 179 | 180 | # PyInstaller 181 | # Usually these files are written by a python script from a template 182 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 183 | *.manifest 184 | *.spec 185 | 186 | # Installer logs 187 | pip-log.txt 188 | pip-delete-this-directory.txt 189 | 190 | # Unit test / coverage reports 191 | htmlcov/ 192 | .tox/ 193 | .nox/ 194 | .coverage 195 | .coverage.* 196 | .cache 197 | nosetests.xml 198 | coverage.xml 199 | *.cover 200 | .hypothesis/ 201 | .pytest_cache/ 202 | 203 | # Translations 204 | *.mo 205 | *.pot 206 | 207 | # Django stuff: 208 | local_settings.py 209 | db.sqlite3 210 | 211 | # Flask stuff: 212 | instance/ 213 | .webassets-cache 214 | 215 | # Scrapy stuff: 216 | .scrapy 217 | 218 | # Sphinx documentation 219 | docs/_build/ 220 | 221 | # PyBuilder 222 | 223 | # Jupyter Notebook 224 | .ipynb_checkpoints 225 | 226 | # IPython 227 | profile_default/ 228 | ipython_config.py 229 | 230 | # pyenv 231 | .python-version 232 | 233 | # celery beat schedule file 234 | celerybeat-schedule 235 | 236 | # SageMath parsed files 237 | *.sage.py 238 | 239 | # Environments 240 | .env 241 | .venv 242 | env/ 243 | venv/ 244 | ENV/ 245 | env.bak/ 246 | venv.bak/ 247 | 248 | # Spyder project settings 249 | .spyderproject 250 | .spyproject 251 | 252 | # Rope project settings 253 | .ropeproject 254 | 255 | # mkdocs documentation 256 | /site 257 | 258 | # mypy 259 | .mypy_cache/ 260 | .dmypy.json 261 | dmypy.json 262 | 263 | # Pyre type checker 264 | .pyre/ 265 | 266 | ### Python Patch ### 267 | .venv/ 268 | 269 | ### Gradle ### 270 | .gradle 271 | /build/ 272 | 273 | # Ignore Gradle GUI config 274 | gradle-app.setting 275 | 276 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 277 | !gradle-wrapper.jar 278 | 279 | # Cache of project 280 | .gradletasknamecache 281 | 282 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 283 | # gradle/wrapper/gradle-wrapper.properties 284 | 285 | ### Gradle Patch ### 286 | **/build/ 287 | 288 | # End of https://www.gitignore.io/api/java,maven,macos,gradle,python,intellij+all -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imperva/automatic-api-attack-tool/2e039f2babd62f3cdbb522451b6fea0567396a99/CODE_OF_CONDUCT.md -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imperva/automatic-api-attack-tool/2e039f2babd62f3cdbb522451b6fea0567396a99/CONTRIBUTING.md -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | MIT License 3 | 4 | Copyright (c) 2018 Imperva 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.github.spotbugs" version "3.0.0" 3 | id 'application' 4 | } 5 | 6 | apply plugin: 'com.github.spotbugs' 7 | apply plugin: 'checkstyle' 8 | 9 | sourceCompatibility = 1.8 10 | project.ext.artifactId = "imperva-api-attack-tool" 11 | project.ext.version = '1.0' 12 | project.ext.buildNumber = System.getenv('BUILD_NUMBER') != null ? System.getenv('BUILD_NUMBER') : 0 13 | 14 | repositories { 15 | jcenter() 16 | mavenCentral() 17 | maven { url "https://dl.bintray.com/epam/reportportal" } 18 | } 19 | 20 | sourceSets { 21 | main { 22 | resources { 23 | srcDirs = ["src/main/resources"] 24 | exclude "**/*.java" 25 | } 26 | } 27 | test { 28 | resources { 29 | srcDirs = ["src/test/java", "src/test/resources"] 30 | exclude "**/*.java" 31 | } 32 | } 33 | } 34 | 35 | dependencies { 36 | implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.10.1' 37 | // Apache License 2.0 38 | implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.6' // Apache License 2.0 39 | implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3' // LGPL v2.1 40 | implementation group: 'io.swagger.parser.v3', name: 'swagger-parser', version: '2.0.16' // Apache License 2.0 41 | implementation group: 'info.picocli', name: 'picocli', version: '4.1.0' // Apache License 2.0 42 | implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.10' // Apache License 2.0 43 | implementation group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.5.10' // Apache License 2.0 44 | implementation group: 'org.testng', name: 'testng', version: '7.0.0' // Apache License 2.0 45 | implementation group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.29' 46 | // MIT License (compatible with Apache License) 47 | implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.29' 48 | // MIT License (compatible with Apache License) 49 | } 50 | 51 | configurations { 52 | compile.exclude module: 'commons-logging' 53 | } 54 | 55 | jar { 56 | duplicatesStrategy = DuplicatesStrategy.INCLUDE 57 | archiveBaseName = project.ext.artifactId 58 | manifest { 59 | mainClassName = 'com.imperva.apiattacktool.ToolMain' 60 | attributes 'Implementation-Title': project.ext.artifactId, 61 | 'Implementation-Version': project.ext.version, 62 | 'Build-Number': project.ext.buildNumber, 63 | 'Main-Class': 'com.imperva.apiattacktool.ToolMain' 64 | } 65 | from { 66 | configurations.compileClasspath.collect { 67 | it.isDirectory() ? it : zipTree(it).matching { exclude { it.path.contains('META-INF') } } 68 | } 69 | } 70 | } 71 | 72 | 73 | spotbugsMain.enabled = false 74 | checkstyleMain.enabled = false 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /config/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 73 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 98 | 99 | 100 | 102 | 103 | 104 | 105 | 107 | 108 | 109 | 110 | 112 | 113 | 114 | 115 | 116 | 117 | 119 | 120 | 121 | 122 | 124 | 125 | 126 | 127 | 129 | 130 | 131 | 132 | 134 | 136 | 138 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imperva/automatic-api-attack-tool/2e039f2babd62f3cdbb522451b6fea0567396a99/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 30 16:31:57 CET 2019 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/ToolMain.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool; 2 | 3 | import ch.qos.logback.classic.LoggerContext; 4 | import ch.qos.logback.classic.joran.JoranConfigurator; 5 | import ch.qos.logback.core.joran.spi.JoranException; 6 | import com.imperva.apiattacktool.cli.ApiAttackTool; 7 | import org.slf4j.LoggerFactory; 8 | import picocli.CommandLine; 9 | 10 | import java.net.URL; 11 | 12 | 13 | public class ToolMain { 14 | public static void main(String[] args) { 15 | configureLogging(); 16 | ApiAttackTool apiAttackTool = new ApiAttackTool(); 17 | System.exit(new CommandLine(apiAttackTool).execute(args)); 18 | } 19 | 20 | private static void configureLogging() { 21 | final LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); 22 | try { 23 | JoranConfigurator configurator = new JoranConfigurator(); 24 | configurator.setContext( context ); 25 | context.reset(); 26 | URL path = ClassLoader.getSystemClassLoader().getResource("logback.xml"); 27 | configurator.doConfigure(path); 28 | } 29 | catch ( JoranException ignore ) { 30 | System.out.println("Logging is disabled"); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/activators/EndpointModelToValueConverter.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.activators; 2 | 3 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 4 | import com.imperva.apiattacktool.model.valued.factory.PropertyValueFactory; 5 | import com.imperva.apispecparser.model.EndpointModel; 6 | 7 | import java.util.Collections; 8 | import java.util.List; 9 | import java.util.stream.Collectors; 10 | 11 | public class EndpointModelToValueConverter implements ModelToValueConverter { 12 | 13 | private PropertyValueFactory propertyValueFactory; 14 | 15 | public EndpointModelToValueConverter(PropertyValueFactory propertyValueFactory) { 16 | this.propertyValueFactory = propertyValueFactory; 17 | } 18 | 19 | @Override 20 | public List endpointModelToEndpointValuedModel(List endpointModelList) { 21 | List endpointValuedModelList = Collections.EMPTY_LIST; 22 | if (endpointModelList != null) { 23 | endpointValuedModelList = endpointModelList.stream().map( 24 | endpointModel -> new EndpointValuedModel(endpointModel, propertyValueFactory)) 25 | .collect(Collectors.toList()); 26 | } 27 | return endpointValuedModelList; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/activators/EndpointTestRequestDataConverter.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.activators; 2 | 3 | import com.google.gson.Gson; 4 | import com.imperva.apiattacktool.model.tests.EndpointTestRequestData; 5 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 6 | import com.imperva.apiattacktool.model.valued.ArrayPropertyValue; 7 | import com.imperva.apiattacktool.model.valued.ArrayPropertyValueAdapter; 8 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 9 | import com.imperva.apiattacktool.model.valued.PropertyValue; 10 | import com.imperva.apiattacktool.model.valued.PropertyValueNode; 11 | 12 | import java.util.Collections; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Optional; 16 | import java.util.regex.Matcher; 17 | import java.util.stream.Collectors; 18 | 19 | public class EndpointTestRequestDataConverter implements RequestDataConverter { 20 | 21 | private final Gson gson; 22 | 23 | public EndpointTestRequestDataConverter() { 24 | gson = new Gson(); 25 | } 26 | 27 | @Override 28 | public List processList(List endpointModelList) { 29 | if (endpointModelList == null) { 30 | return Collections.emptyList(); 31 | } 32 | 33 | List endpointTestRequestDataList = endpointModelList.stream() 34 | .map(this::convertFrom) 35 | .collect(Collectors.toList()); 36 | return endpointTestRequestDataList; 37 | } 38 | 39 | private EndpointTestRequestData convertFrom(EndpointValuedModel endpointValuedModel) { 40 | Map> parameterLocationToPropertyValueMap = 41 | endpointValuedModel.getPropertiesMap().values().stream() 42 | .filter(propertyValue -> propertyValue.getParameterLocation() != ParameterLocation.BODY) 43 | .map(propertyValue -> propertyValue instanceof ArrayPropertyValue ? new ArrayPropertyValueAdapter((ArrayPropertyValue) propertyValue) : 44 | propertyValue) 45 | .collect(Collectors.groupingBy(PropertyValue::getParameterLocation)); 46 | 47 | Optional> bodyParamEntry = 48 | endpointValuedModel.getChildrenMap().entrySet().stream() 49 | .filter(stringPropertyValueNodeEntry -> stringPropertyValueNodeEntry.getValue().getParameterLocation() == ParameterLocation.BODY) 50 | .findFirst(); 51 | 52 | String bodyParamString = null; 53 | if (bodyParamEntry.isPresent()) { 54 | Map bodyParamMapRepresentation = bodyParamEntry.get().getValue().bodyParameterJsonRepresentationMap(); 55 | bodyParamString = gson.toJson(bodyParamMapRepresentation); 56 | } 57 | 58 | String urlWithInjectedValues = 59 | getUrlInjectedWithValues(endpointValuedModel.getFullPathWithParamBrackets(), parameterLocationToPropertyValueMap.get(ParameterLocation.PATH)); 60 | EndpointTestRequestData endpointTestRequestData = 61 | new EndpointTestRequestData(endpointValuedModel.getHttpMethod(), endpointValuedModel.getFullPathWithParamBrackets(), urlWithInjectedValues, 62 | parameterLocationToPropertyValueMap, bodyParamString, endpointValuedModel.getConsumes(), endpointValuedModel.getTestComment(), 63 | endpointValuedModel.getHttpResponseCodesCollection()); 64 | return endpointTestRequestData; 65 | } 66 | 67 | private String getUrlInjectedWithValues(String urlWithPathParametersInBrackets, List pathParametersList) { 68 | if (pathParametersList == null) { 69 | return urlWithPathParametersInBrackets; 70 | } 71 | 72 | String injectedString = urlWithPathParametersInBrackets; 73 | for (PropertyValue propertyValue : pathParametersList) { 74 | injectedString = injectedString.replaceAll( 75 | getPathParameterPatternToReplace(propertyValue.getName()), Matcher.quoteReplacement(String.valueOf(propertyValue.getValue()))); 76 | } 77 | return injectedString; 78 | } 79 | 80 | /** 81 | * @param pathParamName Path parameter name 82 | * @return Path parameter pattern 83 | */ 84 | private String getPathParameterPatternToReplace(String pathParamName) { 85 | return "\\{" + pathParamName + "}"; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/activators/HttpRequestGenerator.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.activators; 2 | 3 | import com.imperva.apiattacktool.model.tests.EndpointTestRequestData; 4 | import com.imperva.apiattacktool.model.tests.HttpRequestWrapper; 5 | 6 | import java.util.List; 7 | 8 | public interface HttpRequestGenerator { 9 | List generateFrom(List endpointTestRequestDataList); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/activators/ModelToValueConverter.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.activators; 2 | 3 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 4 | import com.imperva.apispecparser.model.EndpointModel; 5 | 6 | import java.util.List; 7 | 8 | public interface ModelToValueConverter { 9 | 10 | List endpointModelToEndpointValuedModel(List endpointModelList); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/activators/RequestDataConverter.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.activators; 2 | 3 | import com.imperva.apiattacktool.model.tests.EndpointTestRequestData; 4 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 5 | 6 | import java.util.List; 7 | 8 | public interface RequestDataConverter { 9 | List processList(List endpointModelList); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/activators/TestHttpRequestGenerator.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.activators; 2 | 3 | import com.imperva.apiattacktool.model.tests.EndpointTestRequestData; 4 | import com.imperva.apiattacktool.model.tests.HttpMethod; 5 | import com.imperva.apiattacktool.model.tests.HttpRequestWrapper; 6 | import com.imperva.apiattacktool.model.tests.HttpResponseValidator; 7 | import com.imperva.apiattacktool.model.valued.PropertyValue; 8 | import org.apache.http.HttpRequest; 9 | import org.apache.http.MethodNotSupportedException; 10 | import org.apache.http.client.utils.URIBuilder; 11 | import org.apache.http.entity.ContentType; 12 | import org.apache.http.entity.StringEntity; 13 | import org.apache.http.entity.mime.MultipartEntityBuilder; 14 | import org.apache.http.entity.mime.content.ContentBody; 15 | import org.apache.http.entity.mime.content.StringBody; 16 | import org.apache.http.impl.DefaultHttpRequestFactory; 17 | import org.apache.http.message.BasicHeader; 18 | import org.apache.http.message.BasicHttpEntityEnclosingRequest; 19 | import org.apache.http.protocol.HTTP; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import java.net.URISyntaxException; 24 | import java.util.Collection; 25 | import java.util.Collections; 26 | import java.util.List; 27 | import java.util.Objects; 28 | import java.util.function.Function; 29 | import java.util.stream.Collectors; 30 | 31 | public class TestHttpRequestGenerator implements HttpRequestGenerator { 32 | 33 | private static final Logger logger = LoggerFactory.getLogger(TestHttpRequestGenerator.class); 34 | private Function, HttpResponseValidator> httpResponseValidatorGenerator; 35 | 36 | public TestHttpRequestGenerator(Function, HttpResponseValidator> httpResponseValidatorGenerator) { 37 | this.httpResponseValidatorGenerator = httpResponseValidatorGenerator; 38 | } 39 | 40 | @Override 41 | public List generateFrom(List endpointTestRequestDataList) { 42 | if (endpointTestRequestDataList == null) { 43 | return Collections.emptyList(); 44 | } 45 | List httpRequestList = 46 | endpointTestRequestDataList.stream() 47 | .map(endpointTestRequestData -> { 48 | try { 49 | return generateFrom(endpointTestRequestData); 50 | } catch (MethodNotSupportedException methodNotSupportedException) { 51 | logger.error("Could not create create HttpRequest for this endpoint: {}", endpointTestRequestData, methodNotSupportedException); 52 | return null; // swallow and continue 53 | } 54 | }) 55 | .filter(Objects::nonNull) 56 | .collect(Collectors.toList()); 57 | return httpRequestList; 58 | } 59 | 60 | private HttpRequestWrapper generateFrom(EndpointTestRequestData endpointTestRequestData) throws MethodNotSupportedException { 61 | HttpRequest httpRequest; 62 | HttpMethod httpMethod = endpointTestRequestData.getHttpMethod(); 63 | try { 64 | String url = endpointTestRequestData.getEndpointUrl(); 65 | if (endpointTestRequestData.hasQueryParameters()) { 66 | url = attachQueryParametersToUrl(endpointTestRequestData.getEndpointUrl(), endpointTestRequestData.getQueryParameters()); 67 | } 68 | 69 | if (endpointTestRequestData.getHttpMethod() != HttpMethod.PATCH) { 70 | httpRequest = DefaultHttpRequestFactory.INSTANCE.newHttpRequest(httpMethod.toString(), url); 71 | } else { 72 | httpRequest = new BasicHttpEntityEnclosingRequest(httpMethod.toString(), url); 73 | } 74 | if (endpointTestRequestData.hasHeaderParameters()) { 75 | updateRequestWithRequestHeaders(httpRequest, endpointTestRequestData.getHeaderParameters()); 76 | } 77 | } catch (URISyntaxException uriSyntaxException) { 78 | logger.error("Couldn't generate http request for {}", endpointTestRequestData, uriSyntaxException); 79 | return null; 80 | } 81 | 82 | if (httpRequest instanceof BasicHttpEntityEnclosingRequest) { 83 | BasicHttpEntityEnclosingRequest basicHttpEntityEnclosingRequest = (BasicHttpEntityEnclosingRequest) httpRequest; 84 | if (endpointTestRequestData.hasBodyParameters()) { 85 | String bodyString = endpointTestRequestData.getBodyParameter(); 86 | logger.debug("Endpoint: {} [{}], Body: {}", endpointTestRequestData.getEndpointUrl(), httpMethod, bodyString); 87 | if (bodyString != null) { 88 | try { 89 | StringEntity entity = new StringEntity(endpointTestRequestData.getBodyParameter(), "UTF-8"); 90 | basicHttpEntityEnclosingRequest.setEntity(entity); 91 | } catch (Exception anyException) { 92 | logger.error("Encountered an error while setting body string for endpoint url: {}, method: {}", 93 | endpointTestRequestData.getEndpointUrl(), endpointTestRequestData.getHttpMethod(), anyException); 94 | } 95 | } 96 | } else if (endpointTestRequestData.hasFormDataParameters()) { 97 | MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create(); 98 | endpointTestRequestData.getFormDataParameters().forEach(propertyValue -> 99 | multipartEntityBuilder.addPart(propertyValue.getName(), formDataToContentBody(propertyValue, 100 | endpointTestRequestData.getConsumesMimeTypes()))); 101 | basicHttpEntityEnclosingRequest.setEntity(multipartEntityBuilder.build()); 102 | } 103 | } 104 | 105 | HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(httpRequest, endpointTestRequestData.getTestComment(), 106 | httpResponseValidatorGenerator.apply(endpointTestRequestData.getHttpResponseCodesCollection())); 107 | return httpRequestWrapper; 108 | } 109 | 110 | private ContentBody formDataToContentBody(PropertyValue formDataPropertyValue, List consumesMimeTypes) { 111 | String mimeTypeString = (consumesMimeTypes == null || consumesMimeTypes.isEmpty()) 112 | ? ContentType.APPLICATION_FORM_URLENCODED.getMimeType() 113 | : consumesMimeTypes.get(0); 114 | 115 | switch (formDataPropertyValue.getType()) { 116 | case FILE: 117 | case BYTE_ARRAY: 118 | // not implemented right now, but should use ByteArrayBody 119 | case ARRAY: 120 | case FLOAT: 121 | case LONG: 122 | case DECIMAL: 123 | case DOUBLE: 124 | case BASE_INTEGER: 125 | case INTEGER: 126 | case UUID: 127 | case PASSWORD: 128 | case EMAIL: 129 | case DATETIME: 130 | case DATE: 131 | case BINARY: 132 | case STRING: 133 | case BOOLEAN: 134 | return new StringBody(String.valueOf(formDataPropertyValue.getValue()), ContentType.getByMimeType(mimeTypeString)); 135 | case UNTYPED: 136 | default: 137 | return new StringBody(String.valueOf(formDataPropertyValue.getValue()), ContentType.TEXT_PLAIN); 138 | } 139 | } 140 | 141 | private void updateRequestWithRequestHeaders(HttpRequest httpRequest, List headerParameters) { 142 | headerParameters.forEach(propertyValue -> httpRequest.addHeader(propertyValue.getName(), String.valueOf(propertyValue.getValue()))); 143 | } 144 | 145 | private String attachQueryParametersToUrl(String url, List queryParameters) throws URISyntaxException { 146 | URIBuilder builder = new URIBuilder(url); 147 | queryParameters.forEach(queryParam -> builder.addParameter(queryParam.getName(), String.valueOf(queryParam.getValue()))); 148 | return builder.build().toString(); 149 | } 150 | 151 | // Add cookie handling 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/cli/ApiAttackTool.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.cli; 2 | 3 | import com.imperva.apiattacktool.tests.TestConfiguration; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.testng.TestNG; 7 | import picocli.CommandLine.Command; 8 | import picocli.CommandLine.Option; 9 | 10 | import java.util.List; 11 | import java.util.concurrent.Callable; 12 | 13 | @Command(name = "api-attack", mixinStandardHelpOptions = true, requiredOptionMarker = '*') 14 | public class ApiAttackTool implements Callable { 15 | 16 | private static final Logger logger = LoggerFactory.getLogger(ApiAttackTool.class); 17 | 18 | @Option(names = {"-f", "--specFile"}, required = true, description = "The API specification file (Swagger 2.0) to run on. JSON/YAML format. For better results, make sure responses are well defined for each endpoint.") 19 | private String specFilePath; 20 | 21 | @Option(names = {"-s", "--hostScheme"}, required = true, description = "Connection to host will be made using this scheme; e.g: https or http") 22 | private String hostScheme; 23 | 24 | @Option(names = {"-n", "--hostName"}, required = true, description = "The host name to connect to. It can also be an IP") 25 | private String hostName; 26 | 27 | @Option(names = {"-p", "--hostPort"}, description = "The port the host is listening on for API calls") 28 | private Integer hostPort; 29 | 30 | @Option(names = {"-ph", "--proxyHost"}, description = "Specify the proxy host to send the requests via a proxy") 31 | private String proxyHost; 32 | 33 | @Option(names = {"-pp", "--proxyPort"}, description = "The proxy port") 34 | private Integer proxyPort; 35 | 36 | @Option(names = {"-rcp", "--addPositiveRC"}, split = ",", description = "Additional response codes to be accepted in positive checks (legitimate value attacks). Multiple values are supported, separated by commas") 37 | private List userProvidedPositiveResponseCodes; 38 | 39 | @Option(names = {"-rcn", "--addNegativeRC"}, split = ",", description = "Additional response codes to be accepted in negative attacks (e.g. bad value attacks). Multiple values are supported, separated by commas") 40 | private List userProvidedNegativeResponseCodes; 41 | 42 | public String getSpecFilePath() { 43 | return specFilePath; 44 | } 45 | 46 | public String getHostScheme() { 47 | return hostScheme; 48 | } 49 | 50 | public String getHostName() { 51 | return hostName; 52 | } 53 | 54 | public Integer getHostPort() { 55 | return hostPort; 56 | } 57 | 58 | public String getProxyHost() { 59 | return proxyHost; 60 | } 61 | 62 | public Integer getProxyPort() { 63 | return proxyPort; 64 | } 65 | 66 | public List getUserProvidedPositiveResponseCodes() { 67 | return userProvidedPositiveResponseCodes; 68 | } 69 | 70 | public List getUserProvidedNegativeResponseCodes() { 71 | return userProvidedNegativeResponseCodes; 72 | } 73 | 74 | @Override 75 | public Integer call() { 76 | TestConfiguration.initFrom(this); 77 | logger.info("Running with this configuration:\n{}", TestConfiguration.getWorkingConfigurationString()); 78 | 79 | TestNG testNG = new TestNG(); 80 | testNG.setOutputDirectory("build/testng-results"); 81 | testNG.addListener(new TestListener()); 82 | testNG.setTestClasses(new Class[] { 83 | com.imperva.apiattacktool.tests.MainTest.class}); 84 | testNG.setDefaultSuiteName("API Attacks"); 85 | testNG.setDefaultTestName("Full Suite"); 86 | testNG.setVerbose(1); 87 | testNG.run(); 88 | 89 | return testNG.getStatus(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/cli/TestListener.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.cli; 2 | 3 | import org.testng.ISuite; 4 | import org.testng.ISuiteListener; 5 | 6 | public class TestListener implements ISuiteListener { 7 | 8 | @Override 9 | public void onFinish(ISuite suite) { 10 | System.out.println("Tool run results output directory = " + suite.getOutputDirectory()); 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/fuzzing/Fuzzer.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.fuzzing; 2 | 3 | import com.imperva.apiattacktool.model.valued.PropertyValueNode; 4 | import com.imperva.apispecparser.model.ArrayProperty; 5 | import com.imperva.apispecparser.model.BooleanProperty; 6 | import com.imperva.apispecparser.model.NumericProperty; 7 | import com.imperva.apispecparser.model.Property; 8 | import com.imperva.apispecparser.model.StringProperty; 9 | 10 | import java.util.List; 11 | 12 | public interface Fuzzer { 13 | List fuzz(Property property); 14 | 15 | List fuzz(BooleanProperty booleanProperty); 16 | 17 | List fuzz(StringProperty stringProperty); 18 | 19 | List fuzz(NumericProperty numericProperty); 20 | 21 | List> fuzz(ArrayProperty arrayProperty); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/fuzzing/modelgenerators/CloningIterativeFuzzedModelsGenerator.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.fuzzing.modelgenerators; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 5 | import com.imperva.apiattacktool.model.valued.PropertyValue; 6 | import com.imperva.apiattacktool.model.valued.PropertyValueNode; 7 | import com.imperva.apiattacktool.model.valued.factory.PropertyValueFactory; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.io.UnsupportedEncodingException; 12 | import java.net.URLEncoder; 13 | import java.nio.charset.StandardCharsets; 14 | import java.util.Collections; 15 | import java.util.LinkedList; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | public class CloningIterativeFuzzedModelsGenerator implements FuzzedModelsGenerator { 20 | private static final Logger logger = LoggerFactory.getLogger(CloningIterativeFuzzedModelsGenerator.class); 21 | 22 | private PropertyValueFactory propertyValueFactory; 23 | 24 | public CloningIterativeFuzzedModelsGenerator(PropertyValueFactory propertyValueFactory) { 25 | this.propertyValueFactory = propertyValueFactory; 26 | } 27 | 28 | /** 29 | * Requires an already fuzzed endpointValueModel, since assumes all other values in iteration are already set 30 | * 31 | * @param endpointValuedModel 32 | * @param fuzzer 33 | * @return 34 | */ 35 | @Override 36 | public List fuzzModelValues(EndpointValuedModel endpointValuedModel, Fuzzer fuzzer) { 37 | if (endpointValuedModel == null) { 38 | return Collections.emptyList(); 39 | } 40 | 41 | List fuzzedEndpointValuedModelList = fuzzModelValue(endpointValuedModel, fuzzer); 42 | return fuzzedEndpointValuedModelList; 43 | } 44 | 45 | @Override 46 | public PropertyValueFactory getPropertyValueFactory() { 47 | return propertyValueFactory; 48 | } 49 | 50 | 51 | private List fuzzModelValue(EndpointValuedModel endpointValuedModel, Fuzzer fuzzer) { 52 | EndpointValuedModel clonedEndpointValueModel; 53 | try { 54 | clonedEndpointValueModel = (EndpointValuedModel) endpointValuedModel.clone(); 55 | } catch (CloneNotSupportedException cloneException) { 56 | logger.error("Clone of endpointValuedModel failed: {} {}", 57 | endpointValuedModel.getHttpMethod(), endpointValuedModel.getFullPathWithParamBrackets()); 58 | clonedEndpointValueModel = endpointValuedModel; 59 | } 60 | 61 | List fuzzedEndpointModelValuesList = 62 | traverseFuzzAndInjectPropertyValueNode(clonedEndpointValueModel, clonedEndpointValueModel.getPropertyValueNode(), fuzzer, null); 63 | return fuzzedEndpointModelValuesList; 64 | } 65 | 66 | private List fuzzAndInjectPropertyValues(EndpointValuedModel referenceModel, Map nameToPropertyValueMap, 67 | Fuzzer fuzzer, String relativePathForPropertyName) { 68 | List resultList = new LinkedList<>(); 69 | 70 | nameToPropertyValueMap.entrySet().forEach( 71 | stringToPropertyValueEntry -> { 72 | PropertyValue propertyValue = stringToPropertyValueEntry.getValue(); 73 | List valuesList = propertyValue.fuzz(fuzzer); 74 | if (valuesList != null && !valuesList.isEmpty()) { 75 | // Store current value 76 | Object currentValue = stringToPropertyValueEntry.getValue().getValue(); 77 | // Set fuzzed value and clone 78 | stringToPropertyValueEntry.getValue().setValue(valuesList.get(0)); 79 | 80 | EndpointValuedModel clonedReferenceModel; 81 | try { 82 | clonedReferenceModel = (EndpointValuedModel) referenceModel.clone(); 83 | clonedReferenceModel.setTestComment( 84 | "Property: " + buildRelativePath(relativePathForPropertyName, stringToPropertyValueEntry.getKey()) 85 | + " (" + stringToPropertyValueEntry.getValue().getType() + "), value: " + stringToPropertyValueEntry.getValue().getValue() 86 | + ", URL encoded: " 87 | + URLEncoder.encode(String.valueOf(stringToPropertyValueEntry.getValue().getValue()), StandardCharsets.UTF_8.toString())); 88 | resultList.add(clonedReferenceModel); 89 | } catch (CloneNotSupportedException | UnsupportedEncodingException e) { 90 | logger.error("Error cloning reference model: {}", referenceModel); 91 | return; 92 | } 93 | // Put current value back 94 | stringToPropertyValueEntry.getValue().setValue(currentValue); 95 | } 96 | }); 97 | return resultList; 98 | } 99 | 100 | private List traverseFuzzAndInjectPropertyValueNode(EndpointValuedModel referenceModel, PropertyValueNode propertyValueNode, 101 | Fuzzer fuzzer, String relativePathForPropertyName) { 102 | List resultList = new LinkedList<>(); 103 | if (relativePathForPropertyName == null) { 104 | relativePathForPropertyName = ""; 105 | } 106 | 107 | if (propertyValueNode.getPropertiesMap().size() > 0) { 108 | resultList.addAll(fuzzAndInjectPropertyValues(referenceModel, propertyValueNode.getPropertiesMap(), fuzzer, relativePathForPropertyName)); 109 | } 110 | 111 | if (propertyValueNode.getChildrenMap().size() > 0) { 112 | for (Map.Entry stringToPropertyNodeMap : propertyValueNode.getChildrenMap().entrySet()) { 113 | resultList.addAll(traverseFuzzAndInjectPropertyValueNode(referenceModel, stringToPropertyNodeMap.getValue(), fuzzer, 114 | buildRelativePath(relativePathForPropertyName, stringToPropertyNodeMap.getKey()))); 115 | } 116 | } 117 | return resultList; 118 | } 119 | 120 | private String buildRelativePath(String parent, String son) { 121 | return parent + "/" + son; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/fuzzing/modelgenerators/FuzzedModelsGenerator.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.fuzzing.modelgenerators; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 5 | import com.imperva.apiattacktool.model.valued.factory.PropertyValueFactory; 6 | 7 | import java.util.List; 8 | 9 | public interface FuzzedModelsGenerator { 10 | List fuzzModelValues(EndpointValuedModel endpointValuedModel, Fuzzer fuzzer); 11 | 12 | PropertyValueFactory getPropertyValueFactory(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/fuzzing/modelgenerators/SingleValueFuzzedModelsGenerator.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.fuzzing.modelgenerators; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 5 | import com.imperva.apiattacktool.model.valued.PropertyValue; 6 | import com.imperva.apiattacktool.model.valued.PropertyValueNode; 7 | import com.imperva.apiattacktool.model.valued.factory.PropertyValueFactory; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.Collections; 12 | import java.util.LinkedList; 13 | import java.util.List; 14 | import java.util.Map; 15 | // TODO: Fuzzer should take MIME type (and it's corresponding charset) into account, when generating values for formData params 16 | public class SingleValueFuzzedModelsGenerator implements FuzzedModelsGenerator { 17 | private static final Logger logger = LoggerFactory.getLogger(SingleValueFuzzedModelsGenerator.class); 18 | 19 | private PropertyValueFactory propertyValueFactory; 20 | 21 | public SingleValueFuzzedModelsGenerator(PropertyValueFactory propertyValueFactory) { 22 | this.propertyValueFactory = propertyValueFactory; 23 | } 24 | 25 | @Override 26 | public List fuzzModelValues(EndpointValuedModel endpointValuedModel, Fuzzer fuzzer) { 27 | if (endpointValuedModel == null) { 28 | return Collections.emptyList(); 29 | } 30 | 31 | List endpointValuedModelList = new LinkedList<>(); 32 | endpointValuedModelList.add(fuzzModelValue(endpointValuedModel, fuzzer)); 33 | return endpointValuedModelList; 34 | } 35 | 36 | @Override 37 | public PropertyValueFactory getPropertyValueFactory() { 38 | return propertyValueFactory; 39 | } 40 | 41 | 42 | private EndpointValuedModel fuzzModelValue(EndpointValuedModel endpointValuedModel, Fuzzer fuzzer) { 43 | EndpointValuedModel clonedEndpointValueModel; 44 | try { 45 | clonedEndpointValueModel = (EndpointValuedModel) endpointValuedModel.clone(); 46 | } catch (CloneNotSupportedException cloneException) { 47 | logger.error("Clone of endpointValuedModel failed: {} {}", 48 | endpointValuedModel.getHttpMethod(), endpointValuedModel.getFullPathWithParamBrackets()); 49 | clonedEndpointValueModel = endpointValuedModel; 50 | } 51 | clonedEndpointValueModel.setTestComment(endpointValuedModel.getFullPathWithParamBrackets()); 52 | fuzzAndInjectPropertyValues(clonedEndpointValueModel.getPropertiesMap(), fuzzer); 53 | clonedEndpointValueModel.getChildrenMap().entrySet().forEach( 54 | nameToPropertyValueNodeEntry -> { 55 | traverseFuzzAndInjectPropertyValueNode(nameToPropertyValueNodeEntry.getValue(), fuzzer); 56 | }); 57 | return clonedEndpointValueModel; 58 | } 59 | 60 | private void fuzzAndInjectPropertyValues(Map nameToPropertyValueMap, Fuzzer fuzzer) { 61 | List propertiesToDeleteList = new LinkedList<>(); 62 | nameToPropertyValueMap.entrySet().forEach( 63 | stringToPropertyValueEntry -> { 64 | PropertyValue propertyValue = stringToPropertyValueEntry.getValue(); 65 | List valuesList = propertyValue.fuzz(fuzzer); 66 | if (valuesList != null && !valuesList.isEmpty()) { 67 | stringToPropertyValueEntry.getValue().setValue(valuesList.get(0)); 68 | } else { 69 | propertiesToDeleteList.add(stringToPropertyValueEntry.getKey()); 70 | } 71 | }); 72 | propertiesToDeleteList.forEach(name -> nameToPropertyValueMap.remove(name)); 73 | } 74 | 75 | private void traverseFuzzAndInjectPropertyValueNode(PropertyValueNode propertyValueNode, Fuzzer fuzzer) { 76 | 77 | if (propertyValueNode.getPropertiesMap().size() > 0) { 78 | fuzzAndInjectPropertyValues(propertyValueNode.getPropertiesMap(), fuzzer); 79 | } 80 | 81 | if (propertyValueNode.getChildrenMap().size() > 0) { 82 | propertyValueNode.getChildrenMap().entrySet().forEach( 83 | stringToPropertyNodeMap -> 84 | traverseFuzzAndInjectPropertyValueNode(stringToPropertyNodeMap.getValue(), fuzzer) 85 | ); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/fuzzing/parameters/AllParametersPolicyEnforcer.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.fuzzing.parameters; 2 | 3 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 4 | 5 | import java.util.List; 6 | 7 | public class AllParametersPolicyEnforcer implements PolicyEnforcer { 8 | @Override 9 | public List enforcePolicyOn(List endpointValuedModelList) { 10 | return endpointValuedModelList; // NO-OP 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/fuzzing/parameters/PolicyEnforcer.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.fuzzing.parameters; 2 | 3 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 4 | 5 | import java.util.List; 6 | 7 | public interface PolicyEnforcer { 8 | List enforcePolicyOn(List endpointValuedModelList); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/fuzzing/parameters/RequiredOnlyPolicyEnforcer.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.fuzzing.parameters; 2 | 3 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 4 | import com.imperva.apiattacktool.model.valued.PropertyValue; 5 | import com.imperva.apiattacktool.model.valued.PropertyValueNode; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.Collections; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.stream.Collectors; 13 | 14 | /** 15 | * Makes sure the supplied endpointValueModels only have required parameters and properties. 16 | */ 17 | public class RequiredOnlyPolicyEnforcer implements PolicyEnforcer { 18 | private static final Logger logger = LoggerFactory.getLogger(RequiredOnlyPolicyEnforcer.class); 19 | 20 | @Override 21 | public List enforcePolicyOn(List endpointValuedModelList) { 22 | if (endpointValuedModelList == null) { 23 | return Collections.emptyList(); 24 | } 25 | 26 | List processedEndpointValueModelList = 27 | endpointValuedModelList.stream() 28 | .map(endpointValuedModel -> processModel(endpointValuedModel)) 29 | .collect(Collectors.toList()); 30 | return processedEndpointValueModelList; 31 | } 32 | 33 | private EndpointValuedModel processModel(EndpointValuedModel endpointValuedModel) { 34 | EndpointValuedModel clonedEndpointValueModel; 35 | try { 36 | clonedEndpointValueModel = (EndpointValuedModel) endpointValuedModel.clone(); 37 | } catch (CloneNotSupportedException cloneException) { 38 | logger.error("Clone of endpointValuedModel failed: {} {}", 39 | endpointValuedModel.getHttpMethod(), endpointValuedModel.getFullPathWithParamBrackets()); 40 | clonedEndpointValueModel = endpointValuedModel; 41 | } 42 | 43 | PropertyValueNode processedPropertyValueNode = processPropertyValueNode(clonedEndpointValueModel.getPropertyValueNode()); 44 | clonedEndpointValueModel.setPropertyValueNode(processedPropertyValueNode); 45 | return clonedEndpointValueModel; 46 | } 47 | 48 | private Map processPropertyValues(Map nameToPropertyValueMap) { 49 | Map processedPropertiesMap = nameToPropertyValueMap.entrySet().stream() 50 | .filter(nameStringToPropertyValue -> nameStringToPropertyValue.getValue().isRequired()) 51 | .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); 52 | return processedPropertiesMap; 53 | } 54 | 55 | private PropertyValueNode processPropertyValueNode(PropertyValueNode propertyValueNode) { 56 | PropertyValueNode newPropertyValueNode = new PropertyValueNode(propertyValueNode.getParameterLocation(), propertyValueNode.isRequired()); 57 | newPropertyValueNode.setPropertiesMap(propertyValueNode.getPropertiesMap()); 58 | newPropertyValueNode.setChildrenMap(propertyValueNode.getChildrenMap()); 59 | 60 | if (propertyValueNode.getPropertiesMap().size() > 0) { 61 | Map processedPropertiesMap = processPropertyValues(propertyValueNode.getPropertiesMap()); 62 | newPropertyValueNode.setPropertiesMap(processedPropertiesMap); 63 | } 64 | 65 | if (propertyValueNode.getChildrenMap().size() > 0) { 66 | Map processedChildrenMap = propertyValueNode.getChildrenMap().entrySet().stream() 67 | .filter(entry -> entry.getValue().isRequired()) 68 | .collect(Collectors.toMap(Map.Entry::getKey, entry -> processPropertyValueNode(entry.getValue()))); 69 | newPropertyValueNode.setChildrenMap(processedChildrenMap); 70 | } 71 | return newPropertyValueNode; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/fuzzing/path/PathFuzzer.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.fuzzing.path; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | 5 | public interface PathFuzzer extends Fuzzer { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/fuzzing/value/CommonValueFuzzer.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.fuzzing.value; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apiattacktool.model.valued.PropertyValue; 5 | import com.imperva.apiattacktool.model.valued.PropertyValueNode; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.LinkedList; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public class CommonValueFuzzer { 14 | private static final Logger logger = LoggerFactory.getLogger(CommonValueFuzzer.class); 15 | 16 | /** 17 | * @param propertyValueNode 18 | * @param fuzzer 19 | * @return a cloned instance with values filled in 20 | */ 21 | protected PropertyValueNode traverseFuzzAndInjectPropertyValueNode( 22 | PropertyValueNode propertyValueNode, Fuzzer fuzzer) { 23 | PropertyValueNode clonedPropertyValueNode; 24 | try { 25 | clonedPropertyValueNode = (PropertyValueNode) propertyValueNode.clone(); 26 | } catch (CloneNotSupportedException cloneNotSupportedException) { 27 | logger.error("Could not clone propertyValueNode: {} %n NOT GENERATING VALUE", propertyValueNode); 28 | return null; 29 | } 30 | 31 | if (clonedPropertyValueNode.getPropertiesMap().size() > 0) { 32 | fuzzAndInjectPropertyValues(clonedPropertyValueNode.getPropertiesMap(), fuzzer); 33 | } 34 | 35 | if (clonedPropertyValueNode.hasChildren()) { 36 | clonedPropertyValueNode.getChildrenMap().entrySet().forEach( 37 | stringToPropertyNodeMap -> 38 | traverseFuzzAndInjectPropertyValueNode(stringToPropertyNodeMap.getValue(), fuzzer) 39 | ); 40 | } 41 | return clonedPropertyValueNode; 42 | } 43 | 44 | // Make this something that is taken from a fuzzedModelGenerator 45 | // Take the generator in c'tor of the fuzzer and use it here to traverse the arrays' propertyValueNode 46 | protected void fuzzAndInjectPropertyValues(Map nameToPropertyValueMap, Fuzzer fuzzer) { 47 | List propertiesToDeleteList = new LinkedList<>(); 48 | nameToPropertyValueMap.entrySet().forEach( 49 | stringToPropertyValueEntry -> { 50 | PropertyValue propertyValue = stringToPropertyValueEntry.getValue(); 51 | List valuesList = propertyValue.fuzz(fuzzer); 52 | if (valuesList != null && !valuesList.isEmpty()) { 53 | propertyValue.setValue(valuesList.get(0)); 54 | } else { 55 | propertiesToDeleteList.add(stringToPropertyValueEntry.getKey()); 56 | } 57 | }); 58 | propertiesToDeleteList.forEach(nameToPropertyValueMap::remove); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/fuzzing/value/NegativeSingleValueFuzzer.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.fuzzing.value; 2 | 3 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 4 | import com.imperva.apiattacktool.model.valued.PropertyValueNode; 5 | import com.imperva.apiattacktool.model.valued.factory.PropertyValueFactory; 6 | import com.imperva.apispecparser.model.ArrayProperty; 7 | import com.imperva.apispecparser.model.BooleanProperty; 8 | import com.imperva.apispecparser.model.NumericProperty; 9 | import com.imperva.apispecparser.model.Property; 10 | import com.imperva.apispecparser.model.StringProperty; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.util.ArrayList; 15 | import java.util.LinkedList; 16 | import java.util.List; 17 | import java.util.concurrent.ThreadLocalRandom; 18 | 19 | /** 20 | * Fills trivial values 21 | */ 22 | public class NegativeSingleValueFuzzer extends CommonValueFuzzer implements ValueFuzzer { 23 | 24 | private static final Logger logger = LoggerFactory.getLogger(NegativeSingleValueFuzzer.class); 25 | private PropertyValueFactory propertyValueFactory; 26 | 27 | public NegativeSingleValueFuzzer(PropertyValueFactory propertyValueFactory) { 28 | this.propertyValueFactory = propertyValueFactory; 29 | } 30 | 31 | @Override 32 | public List fuzz(Property property) { 33 | /*List resultList = new ArrayList<>(); 34 | resultList.add("negative untyped"); 35 | return resultList;*/ 36 | return null; // Disabling fuzzing, until we support file type 37 | } 38 | 39 | @Override 40 | public List fuzz(BooleanProperty booleanProperty) { 41 | String[] invalidValues = new String[] 42 | {"troo", "phalse", "tru", "Fals", "falsee", "ttrue", "tTrue", "128", "-256", "4True", "rue", "alse"}; 43 | int randomValueIndex = ThreadLocalRandom.current().nextInt(invalidValues.length); 44 | 45 | List resultList = new ArrayList<>(); 46 | resultList.add(invalidValues[randomValueIndex]); 47 | return resultList; 48 | } 49 | 50 | @Override 51 | public List fuzz(StringProperty stringProperty) { 52 | ThreadLocalRandom random = ThreadLocalRandom.current(); 53 | List resultList = new LinkedList<>(); 54 | /*if (stringProperty.getParameterLocation() == ParameterLocation.PATH) { 55 | String pathParamString = "#screwedPathParam"; 56 | resultList.add(pathParamString); 57 | return resultList; 58 | } 59 | 60 | int generatedLength = 0; 61 | int minimumLength = stringProperty.getMinLength(); 62 | if (minimumLength > 1) { 63 | generatedLength = minimumLength - 1; 64 | } 65 | 66 | int maximumLength = ThreadLocalRandom.current().nextInt(10) + 1; 67 | if (stringProperty.getMaxLength() != null) { 68 | maximumLength = stringProperty.getMaxLength(); 69 | } 70 | if (minimumLength == 0) { 71 | generatedLength = maximumLength + 1; 72 | } 73 | 74 | int randomLengthFactor = random.nextInt(1, generatedLength); 75 | byte[] array = new byte[randomLengthFactor]; 76 | random.nextBytes(array); 77 | String generatedString = new String(array, Charset.forName("UTF-16"));*/ 78 | if (stringProperty.getParameterLocation() == ParameterLocation.PATH) { // Temporary hack, until we resolve how we check strings 79 | // This string is sent as is, and is not being encoded. So to fail Path param string, it is pretty straightforward (don't comply to the allowed character set in the URL). 80 | String generatedString = "{"; 81 | resultList.add(generatedString); 82 | return resultList; 83 | } else { 84 | // We currently don't have a way to inject a negative value in a string. Next phase: generate negative values if it has length limits 85 | return null; // This would cause the negative fuzz to fail and be skipped. 86 | } 87 | } 88 | 89 | @Override 90 | public List fuzz(NumericProperty numericProperty) { 91 | List resultList = new ArrayList(); 92 | Number randomNumber = getIncompatibleRandomNumberByType(numericProperty); 93 | resultList.add(randomNumber); 94 | return resultList; 95 | } 96 | 97 | 98 | @Override 99 | public List> fuzz(ArrayProperty arrayProperty) { 100 | return null; // Until next phase, disabling the negative fuzzing 101 | /*if (arrayProperty.getItems() == null) { 102 | return Collections.emptyList(); 103 | } 104 | 105 | int generatedLength = 10; 106 | if (arrayProperty.getMaximumItems() != null) { 107 | generatedLength = arrayProperty.getMaximumItems() + 1; 108 | } 109 | 110 | if (arrayProperty.getMinimumItems() > 1) { 111 | generatedLength = arrayProperty.getMinimumItems() - 1; 112 | } 113 | 114 | PropertyNode arrayItemsPropertyNode = arrayProperty.getItems(); 115 | PropertyValueNode propertyValueNode = new PropertyValueNode(arrayItemsPropertyNode, propertyValueFactory); 116 | List arrayValuesList = new LinkedList<>(); 117 | for (int i = 0; i < generatedLength; i++) { 118 | PropertyValueNode fuzzedPropertyValueNode = traverseFuzzAndInjectPropertyValueNode(propertyValueNode, this); 119 | if (fuzzedPropertyValueNode == null) { 120 | logger.error("Could not clone PropertyValueNode: {}, while fuzzing. Skipping.", propertyValueNode); 121 | continue; 122 | } 123 | arrayValuesList.add(fuzzedPropertyValueNode); 124 | } 125 | 126 | List> resultList = new LinkedList<>(); 127 | resultList.add(arrayValuesList); 128 | return resultList;*/ 129 | } 130 | 131 | private Number getIncompatibleRandomNumberByType(NumericProperty numericProperty) { 132 | // TODO: add multipleOf handling 133 | switch (numericProperty.getType()) { 134 | case DECIMAL: 135 | case DOUBLE: 136 | case FLOAT: 137 | return ThreadLocalRandom.current().nextInt(); 138 | case BASE_INTEGER: 139 | case INTEGER: 140 | default: 141 | /*float probabilityFactor = ThreadLocalRandom.current().nextFloat(); 142 | int intMinimum = numericProperty.getMinimum() == null 143 | ? Integer.MIN_VALUE 144 | : numericProperty.getMinimum().intValue(); 145 | int intMaximum = numericProperty.getMaximum() == null 146 | ? Integer.MAX_VALUE 147 | : numericProperty.getMaximum().intValue(); 148 | if (numericProperty.getExclusiveMinimum()) { 149 | intMinimum++; // So that random boundary catches it 150 | } 151 | if (!numericProperty.getExclusiveMaximum() && intMaximum != Integer.MAX_VALUE) { 152 | intMaximum++; // So that we accidentally don't supply a valid value 153 | } 154 | if (probabilityFactor <= 0.33 && (intMinimum != Integer.MIN_VALUE || intMaximum != Integer.MAX_VALUE)) { 155 | int randomInt; 156 | if (intMinimum != Integer.MIN_VALUE) { 157 | randomInt = ThreadLocalRandom.current().nextInt(Integer.MIN_VALUE, intMinimum); 158 | } else { 159 | randomInt = ThreadLocalRandom.current().nextInt(intMaximum, Integer.MAX_VALUE); 160 | } 161 | return randomInt; 162 | } 163 | if (probabilityFactor <= 0.66) { 164 | long randomLong; 165 | if (intMinimum != Integer.MIN_VALUE) { 166 | randomLong = ThreadLocalRandom.current().nextLong(Long.MIN_VALUE, intMinimum); 167 | } else { 168 | randomLong = ThreadLocalRandom.current().nextLong(intMaximum, Long.MAX_VALUE); 169 | } 170 | return randomLong; 171 | }*/ 172 | case LONG: 173 | float randomFloat = ThreadLocalRandom.current().nextFloat(); 174 | return randomFloat; 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/fuzzing/value/ValueFuzzer.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.fuzzing.value; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | 5 | public interface ValueFuzzer extends Fuzzer { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/infra/Tuple.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.infra; 2 | 3 | public class Tuple { 4 | 5 | public L left; 6 | public R right; 7 | 8 | public Tuple(L left, R right) { 9 | this.left = left; 10 | this.right = right; 11 | } 12 | 13 | @Override 14 | public String toString() { 15 | return "(" + String.valueOf(left) + ", " + String.valueOf(right) + ")"; 16 | } 17 | 18 | @Override 19 | public boolean equals(Object obj) { 20 | if (obj == this) { 21 | return true; 22 | } 23 | if (obj instanceof Tuple) { 24 | Tuple other = (Tuple) obj; 25 | return left.equals(other.left) && right.equals(other.right); 26 | } 27 | return false; 28 | } 29 | 30 | @Override 31 | public int hashCode() { 32 | int result = 0; 33 | if (left != null) { 34 | result += (31 * result + left.hashCode()); 35 | } 36 | if (right != null) { 37 | result += (31 * result + right.hashCode()); 38 | } 39 | return result; 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/tests/BaseHttpResponseValidator.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.tests; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import java.util.Collection; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | import java.util.stream.Collectors; 9 | 10 | public abstract class BaseHttpResponseValidator implements HttpResponseValidator { 11 | 12 | private Set httpResponseCodes; 13 | 14 | public BaseHttpResponseValidator(Collection httpResponseCodesList) { 15 | this.httpResponseCodes = new HashSet<>(httpResponseCodesList); 16 | } 17 | 18 | @Override 19 | public abstract boolean isValidHttpCode(int httpResponseCode); 20 | 21 | protected Set getHttpResponseCodes() { 22 | return httpResponseCodes; 23 | } 24 | 25 | protected String getStringFromResponseCodeCollection(Collection responseCodeCollection) { 26 | String commaSeparatedString = 27 | responseCodeCollection.stream() 28 | .filter(responseCode -> responseCode != 0) 29 | .map(responseCode -> String.valueOf(responseCode)) 30 | .collect(Collectors.joining(",")); 31 | if(responseCodeCollection.contains(0)) { 32 | commaSeparatedString = commaSeparatedString + (responseCodeCollection.size() > 1 ? ", " : "") + "any"; 33 | } 34 | return commaSeparatedString; 35 | } 36 | 37 | protected String buildToString(Collection userProvidedResponseCodeCollection, String scenarioOverride) { 38 | StringBuilder responseStringBuilder = new StringBuilder("Valid responses: "); 39 | String userProvidedString = getStringFromResponseCodeCollection(userProvidedResponseCodeCollection); 40 | if (!StringUtils.isBlank(userProvidedString)) { 41 | responseStringBuilder.append("(user override:"); 42 | responseStringBuilder.append(userProvidedString); 43 | responseStringBuilder.append("), "); 44 | } 45 | if (!StringUtils.isBlank(responseStringBuilder)) { 46 | responseStringBuilder.append("(scenario override, not: "); 47 | responseStringBuilder.append(scenarioOverride); 48 | responseStringBuilder.append("), "); 49 | } 50 | responseStringBuilder.append(getStringFromResponseCodeCollection(getHttpResponseCodes())); 51 | 52 | return responseStringBuilder.toString(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/tests/EndpointTestRequestData.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.tests; 2 | 3 | import com.imperva.apiattacktool.model.valued.PropertyValue; 4 | 5 | import java.util.Collection; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class EndpointTestRequestData { 10 | private HttpMethod httpMethod; 11 | private String fullPathWithParamBrackets; // This is for debug purposes 12 | private String endpointUrl; 13 | private Map> parameterLocationToParameterList; 14 | private String bodyString; 15 | private List consumesMimeTypes; 16 | private String testComment; 17 | private Collection httpResponseCodesCollection; 18 | 19 | public EndpointTestRequestData(HttpMethod httpMethod, String fullPathWithParamBrackets, String endpointUrl, Map> parameterLocationToParameterList, String bodyString, List consumesMimeTypes, String testComment, 21 | Collection httpResponseCodeList) { 22 | this.httpMethod = httpMethod; 23 | this.fullPathWithParamBrackets = fullPathWithParamBrackets; 24 | this.endpointUrl = endpointUrl; 25 | this.parameterLocationToParameterList = parameterLocationToParameterList; 26 | this.bodyString = bodyString; 27 | this.consumesMimeTypes = consumesMimeTypes; 28 | this.testComment = testComment; 29 | this.httpResponseCodesCollection = httpResponseCodeList; 30 | } 31 | 32 | public HttpMethod getHttpMethod() { 33 | return httpMethod; 34 | } 35 | 36 | public String getFullPathWithParamBrackets() { 37 | return fullPathWithParamBrackets; 38 | } 39 | 40 | public String getEndpointUrl() { 41 | return endpointUrl; 42 | } 43 | 44 | public boolean hasBodyParameters() { 45 | return bodyString != null && bodyString.length() > 0; 46 | } 47 | 48 | public boolean hasHeaderParameters() { 49 | return hasParameterInLocation(ParameterLocation.HEADER); 50 | } 51 | 52 | public boolean hasQueryParameters() { 53 | return hasParameterInLocation(ParameterLocation.QUERY); 54 | } 55 | 56 | public boolean hasCookieParameters() { 57 | return hasParameterInLocation(ParameterLocation.COOKIE); 58 | } 59 | 60 | public boolean hasFormDataParameters() { 61 | return hasParameterInLocation(ParameterLocation.FORMDATA); 62 | } 63 | 64 | public String getBodyParameter() { 65 | return bodyString; 66 | } 67 | 68 | public List getHeaderParameters() { 69 | return parameterLocationToParameterList.get(ParameterLocation.HEADER); 70 | } 71 | 72 | public List getQueryParameters() { 73 | return parameterLocationToParameterList.get(ParameterLocation.QUERY); 74 | } 75 | 76 | public List getFormDataParameters() { 77 | return parameterLocationToParameterList.get(ParameterLocation.FORMDATA); 78 | } 79 | 80 | public List getConsumesMimeTypes() { 81 | return consumesMimeTypes; 82 | } 83 | 84 | public Collection getHttpResponseCodesCollection() { 85 | return httpResponseCodesCollection; 86 | } 87 | 88 | private boolean hasParameterInLocation(ParameterLocation location) { 89 | return parameterLocationToParameterList.get(location) != null && parameterLocationToParameterList.get(location).size() > 0; 90 | } 91 | 92 | public String getTestComment() { 93 | return testComment; 94 | } 95 | 96 | @Override 97 | public String toString() { 98 | return "EndpointTestRequestData{" 99 | + httpMethod + " " + endpointUrl 100 | + ", parametersByLocation=" + parameterLocationToParameterList 101 | + ", bodyString='" + bodyString + '\'' 102 | + '}'; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/tests/HttpMethod.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.tests; 2 | 3 | public enum HttpMethod { 4 | POST, 5 | GET, 6 | PUT, 7 | PATCH, 8 | DELETE, 9 | HEAD, 10 | OPTIONS 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/tests/HttpRequestWrapper.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.tests; 2 | 3 | import org.apache.http.HttpRequest; 4 | 5 | public class HttpRequestWrapper { 6 | 7 | private HttpRequest httpRequest; 8 | private String testElementComment; 9 | private HttpResponseValidator httpResponseValidator; 10 | 11 | public HttpRequestWrapper(HttpRequest httpRequest, String testElementComment, HttpResponseValidator httpResponseValidator) { 12 | this.httpRequest = httpRequest; 13 | this.testElementComment = testElementComment; 14 | this.httpResponseValidator = httpResponseValidator; 15 | } 16 | 17 | public HttpRequest getHttpRequest() { 18 | return httpRequest; 19 | } 20 | 21 | public String getTestElementComment() { 22 | return testElementComment; 23 | } 24 | 25 | public HttpResponseValidator getHttpResponseValidator() { 26 | return httpResponseValidator; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/tests/HttpResponseValidator.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.tests; 2 | 3 | public interface HttpResponseValidator { 4 | boolean isValidHttpCode(int httpResponseCode); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/tests/NegativeTestHttpResponseValidator.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.tests; 2 | 3 | import com.imperva.apiattacktool.tests.TestConfiguration; 4 | 5 | import java.util.Collection; 6 | 7 | public class NegativeTestHttpResponseValidator extends BaseHttpResponseValidator { 8 | 9 | public NegativeTestHttpResponseValidator(Collection httpResponseCodesList) { 10 | super(httpResponseCodesList); 11 | } 12 | 13 | @Override 14 | public boolean isValidHttpCode(int httpResponseCode) { 15 | if (TestConfiguration.getUserProvidedNegativeResponseCodes().contains(httpResponseCode)) { 16 | return true; 17 | } 18 | if (httpResponseCode < 300 || httpResponseCode >= 500) { 19 | return false; 20 | } 21 | if (getHttpResponseCodes().contains(0)) { 22 | return true; 23 | } 24 | 25 | return getHttpResponseCodes().contains(httpResponseCode); 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return buildToString(TestConfiguration.getUserProvidedNegativeResponseCodes(), "1xx,2xx,5xx"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/tests/ParameterLocation.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.tests; 2 | 3 | public enum ParameterLocation { 4 | HEADER, 5 | COOKIE, 6 | PATH, 7 | QUERY, 8 | BODY, 9 | FORMDATA 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/tests/PositiveTestHttpResponseValidator.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.tests; 2 | 3 | import com.imperva.apiattacktool.tests.TestConfiguration; 4 | 5 | import java.util.Collection; 6 | 7 | public class PositiveTestHttpResponseValidator extends BaseHttpResponseValidator { 8 | 9 | public PositiveTestHttpResponseValidator(Collection httpResponseCodesList) { 10 | super(httpResponseCodesList); 11 | } 12 | 13 | @Override 14 | public boolean isValidHttpCode(int httpResponseCode) { 15 | if (TestConfiguration.getUserProvidedPositiveResponseCodes().contains(httpResponseCode)) { 16 | return true; 17 | } 18 | if (httpResponseCode >= 500) { 19 | return false; 20 | } 21 | if (getHttpResponseCodes().contains(0)) { 22 | return true; 23 | } 24 | 25 | return getHttpResponseCodes().contains(httpResponseCode); 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return buildToString(TestConfiguration.getUserProvidedPositiveResponseCodes(), "5xx"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/valued/ArrayPropertyValue.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.valued; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apispecparser.model.ArrayProperty; 5 | import com.imperva.apispecparser.model.Property; 6 | 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.stream.Collectors; 11 | 12 | public class ArrayPropertyValue extends ArrayProperty implements PropertyValue> { 13 | 14 | private List value; 15 | 16 | 17 | public ArrayPropertyValue(ArrayProperty arrayProperty, List items) { 18 | super(new Property(arrayProperty.getParameterLocation(), arrayProperty.getType(), arrayProperty.getName(), arrayProperty.isRequired(), 19 | arrayProperty.getReadOnly(), arrayProperty.getAllowEmptyValue()), 20 | arrayProperty.getAreItemsUnique(), arrayProperty.getItems(), arrayProperty.getMinimumItems(), arrayProperty.getMaximumItems(), 21 | arrayProperty.getCollectionFormat().getFormatString()); 22 | this.value = items; 23 | } 24 | 25 | @Override 26 | public Object clone() throws CloneNotSupportedException { 27 | return super.clone(); 28 | } 29 | 30 | @Override 31 | public void setValue(List value) { 32 | this.value = value; 33 | } 34 | 35 | @Override 36 | public List getValue() { 37 | return value; 38 | } 39 | 40 | @Override 41 | public List> fuzz(Fuzzer fuzzer) { 42 | return fuzzer.fuzz(this); 43 | } 44 | 45 | @Override 46 | public Map bodyParameterJsonRepresentationMap() { 47 | HashMap representationMap = new HashMap<>(1); 48 | representationMap.put(this.getName(), value.stream().map(PropertyValueNode::bodyParameterJsonRepresentationMap).collect(Collectors.toList())); 49 | return representationMap; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "ArrayPropertyValue{" 55 | + "value=" + value 56 | + ", " + super.toString() + '}'; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/valued/ArrayPropertyValueAdapter.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.valued; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 5 | import com.imperva.apispecparser.model.PropertyType; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.stream.Collectors; 10 | 11 | /** 12 | * An adapter to adapt getValue to return a string, whereas the array items property is represented by PropertyValueNode 13 | * It should be used in the values translation phase 14 | */ 15 | public class ArrayPropertyValueAdapter implements PropertyValue { 16 | 17 | private ArrayPropertyValue arrayPropertyValue; 18 | 19 | public ArrayPropertyValueAdapter(ArrayPropertyValue arrayPropertyValue) { 20 | this.arrayPropertyValue = arrayPropertyValue; 21 | } 22 | 23 | @Override 24 | public String getName() { 25 | return arrayPropertyValue.getName(); 26 | } 27 | 28 | @Override 29 | public ParameterLocation getParameterLocation() { 30 | return arrayPropertyValue.getParameterLocation(); 31 | } 32 | 33 | @Override 34 | public PropertyType getType() { 35 | return arrayPropertyValue.getType(); 36 | } 37 | 38 | @Override 39 | public Object clone() throws CloneNotSupportedException { 40 | return arrayPropertyValue.clone(); 41 | } 42 | 43 | @Override 44 | public String getValue() { 45 | List propertyValueNodeList = arrayPropertyValue.getValue(); 46 | if (propertyValueNodeList != null && !propertyValueNodeList.isEmpty()) { 47 | String delimiterSeparatedArrayValues = 48 | propertyValueNodeList.stream() 49 | .map(propertyValueNode -> 50 | String.valueOf( 51 | propertyValueNode.getPropertiesMap().entrySet().stream().findFirst().get().getValue().getValue())) 52 | .collect(Collectors.joining(arrayPropertyValue.getCollectionFormat().getOutputString())); 53 | return delimiterSeparatedArrayValues; 54 | } 55 | return ""; 56 | } 57 | 58 | @Override 59 | public void setValue(String value) { 60 | throw new IllegalStateException(); 61 | } 62 | 63 | @Override 64 | public boolean isRequired() { 65 | return arrayPropertyValue.isRequired(); 66 | } 67 | 68 | @Override 69 | public List fuzz(Fuzzer fuzzer) { 70 | throw new IllegalStateException(); 71 | } 72 | 73 | @Override 74 | public Map bodyParameterJsonRepresentationMap() { 75 | return arrayPropertyValue.bodyParameterJsonRepresentationMap(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/valued/BooleanPropertyValue.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.valued; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apispecparser.model.BooleanProperty; 5 | import com.imperva.apispecparser.model.Property; 6 | 7 | import java.util.List; 8 | 9 | public class BooleanPropertyValue extends BooleanProperty implements PropertyValue { 10 | 11 | String value; 12 | 13 | public BooleanPropertyValue(BooleanProperty booleanProperty, String value) { 14 | super(new Property(booleanProperty.getParameterLocation(), booleanProperty.getType(), booleanProperty.getName(), 15 | booleanProperty.isRequired(), booleanProperty.getReadOnly(), booleanProperty.getAllowEmptyValue()), 16 | booleanProperty.getEnumList()); 17 | this.value = value; 18 | } 19 | 20 | @Override 21 | public void setValue(String value) { 22 | this.value = value; 23 | } 24 | 25 | @Override 26 | public String getValue() { 27 | return value; 28 | } 29 | 30 | @Override 31 | public List fuzz(Fuzzer fuzzer) { 32 | return fuzzer.fuzz(this); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "BooleanPropertyValue{" 38 | + "value='" + value + '\'' 39 | + ", " + super.toString() + '}'; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/valued/EndpointValuedModel.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.valued; 2 | 3 | import com.imperva.apiattacktool.model.tests.HttpMethod; 4 | import com.imperva.apiattacktool.model.valued.factory.PropertyValueFactory; 5 | import com.imperva.apispecparser.model.EndpointModel; 6 | import com.imperva.apispecparser.model.Property; 7 | 8 | import java.util.Collection; 9 | import java.util.LinkedList; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.stream.Collectors; 13 | 14 | public class EndpointValuedModel implements Cloneable { 15 | 16 | private String fullPathWithParamBrackets; 17 | private HttpMethod httpMethod; 18 | private List consumes; 19 | private PropertyValueNode propertyValueNode; 20 | private String testComment = ""; 21 | private Collection httpResponseCodesCollection; 22 | 23 | public EndpointValuedModel(EndpointModel endpointModel, PropertyValueFactory propertyValueFactory) { 24 | this.fullPathWithParamBrackets = endpointModel.getFullPathWithParamBrackets(); 25 | this.httpMethod = endpointModel.getHttpMethod(); 26 | this.consumes = endpointModel.getConsumes(); 27 | this.propertyValueNode = new PropertyValueNode(null, true); 28 | 29 | endpointModel.getSimpleParametersMap().entrySet().forEach( 30 | nameToPropertyMapEntry -> { 31 | this.propertyValueNode.getPropertiesMap().put(nameToPropertyMapEntry.getKey(), 32 | convertPropertyToPropertyValue(nameToPropertyMapEntry.getValue(), propertyValueFactory)); 33 | }); 34 | 35 | endpointModel.getChildrenMap().entrySet().forEach(nameToPropertyNodeMap -> { 36 | this.propertyValueNode.getChildrenMap().put( 37 | nameToPropertyNodeMap.getKey(), new PropertyValueNode(nameToPropertyNodeMap.getValue(), propertyValueFactory)); 38 | }); 39 | 40 | this.httpResponseCodesCollection = endpointModel.getHttpResponseCodesList().stream() 41 | .map(httpResponseCode -> new Integer(httpResponseCode)) 42 | .collect(Collectors.toList()); 43 | } 44 | 45 | private EndpointValuedModel() { 46 | this.propertyValueNode = new PropertyValueNode(null, true); 47 | } 48 | 49 | public String getFullPathWithParamBrackets() { 50 | return fullPathWithParamBrackets; 51 | } 52 | 53 | public HttpMethod getHttpMethod() { 54 | return httpMethod; 55 | } 56 | 57 | public List getConsumes() { 58 | return consumes; 59 | } 60 | 61 | public PropertyValueNode getPropertyValueNode() { 62 | return propertyValueNode; 63 | } 64 | 65 | public void setPropertyValueNode(PropertyValueNode propertyValueNode) { 66 | this.propertyValueNode = propertyValueNode; 67 | } 68 | 69 | public Map getPropertiesMap() { 70 | return propertyValueNode.getPropertiesMap(); 71 | } 72 | 73 | public Map getChildrenMap() { 74 | return propertyValueNode.getChildrenMap(); 75 | } 76 | 77 | public String getTestComment() { 78 | return testComment; 79 | } 80 | 81 | public void setTestComment(String testComment) { 82 | this.testComment = testComment; 83 | } 84 | 85 | public Collection getHttpResponseCodesCollection() { 86 | return httpResponseCodesCollection; 87 | } 88 | 89 | @Override 90 | public Object clone() throws CloneNotSupportedException { 91 | EndpointValuedModel newEndpointValuedModel = new EndpointValuedModel(); 92 | newEndpointValuedModel.fullPathWithParamBrackets = this.fullPathWithParamBrackets; 93 | newEndpointValuedModel.httpMethod = this.httpMethod; 94 | newEndpointValuedModel.consumes = new LinkedList<>(this.consumes); 95 | newEndpointValuedModel.propertyValueNode = new PropertyValueNode(this.propertyValueNode.getParameterLocation(), this.propertyValueNode.isRequired()); 96 | 97 | // Keys are not cloned, as they are not meant to be changed 98 | for (Map.Entry entry : this.getPropertiesMap().entrySet()) { 99 | newEndpointValuedModel.propertyValueNode.getPropertiesMap().put(entry.getKey(), (PropertyValue) entry.getValue().clone()); 100 | } 101 | 102 | for (Map.Entry entry : this.getChildrenMap().entrySet()) { 103 | newEndpointValuedModel.propertyValueNode.getChildrenMap().put(entry.getKey(), (PropertyValueNode) entry.getValue().clone()); 104 | } 105 | 106 | newEndpointValuedModel.httpResponseCodesCollection = new LinkedList<>(this.httpResponseCodesCollection); 107 | return newEndpointValuedModel; 108 | } 109 | 110 | private PropertyValue convertPropertyToPropertyValue(Property property, PropertyValueFactory propertyValueFactory) { 111 | return propertyValueFactory.getPropertyValueFromProperty(property); 112 | } 113 | 114 | @Override 115 | public String toString() { 116 | return "EndpointValuedModel{" 117 | + "fullPathWithParamBrackets='" + fullPathWithParamBrackets + '\'' 118 | + ", httpMethod=" + httpMethod 119 | + ", propertyValueNode=" + propertyValueNode 120 | + '}'; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/valued/NumericPropertyValue.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.valued; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apispecparser.model.NumericProperty; 5 | import com.imperva.apispecparser.model.Property; 6 | 7 | import java.util.List; 8 | 9 | public class NumericPropertyValue extends NumericProperty implements PropertyValue { 10 | 11 | private Number value; 12 | 13 | public NumericPropertyValue(NumericProperty numericProperty, Number value) { 14 | super(new Property(numericProperty.getParameterLocation(), numericProperty.getType(), numericProperty.getName(), 15 | numericProperty.isRequired(), numericProperty.getReadOnly(), numericProperty.getAllowEmptyValue()), 16 | numericProperty.getEnumList(), numericProperty.getMinimum(), numericProperty.getMaximum(), numericProperty.getMultipleOf(), 17 | numericProperty.getExclusiveMinimum(), numericProperty.getExclusiveMaximum()); 18 | this.value = value; 19 | } 20 | 21 | @Override 22 | public Object clone() throws CloneNotSupportedException { 23 | return super.clone(); 24 | } 25 | 26 | @Override 27 | public void setValue(Number value) { 28 | this.value = value; 29 | } 30 | 31 | @Override 32 | public Number getValue() { 33 | return value; 34 | } 35 | 36 | @Override 37 | public List fuzz(Fuzzer fuzzer) { 38 | return fuzzer.fuzz(this); 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "NumericPropertyValue{" 44 | + "value=" + value 45 | + ", " + super.toString() + '}'; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/valued/PropertyPropertyValue.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.valued; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apispecparser.model.Property; 5 | 6 | import java.util.List; 7 | 8 | public class PropertyPropertyValue extends Property implements PropertyValue { 9 | 10 | private String value; 11 | 12 | public PropertyPropertyValue(Property property, String value) { 13 | super(property); 14 | this.value = value; 15 | } 16 | 17 | @Override 18 | public Object clone() throws CloneNotSupportedException { 19 | return super.clone(); 20 | } 21 | 22 | @Override 23 | public void setValue(String value) { 24 | this.value = value; 25 | } 26 | 27 | @Override 28 | public String getValue() { 29 | return value; 30 | } 31 | 32 | @Override 33 | public List fuzz(Fuzzer fuzzer) { 34 | return fuzzer.fuzz(this); 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "PropertyPropertyValue{" 40 | + "value='" + value + '\'' 41 | + ", Property=" + super.toString() + '}'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/valued/PropertyValue.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.valued; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 5 | import com.imperva.apispecparser.model.PropertyType; 6 | 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public interface PropertyValue extends Cloneable { 12 | String getName(); 13 | 14 | ParameterLocation getParameterLocation(); 15 | 16 | PropertyType getType(); 17 | 18 | Object clone() throws CloneNotSupportedException; 19 | 20 | T getValue(); 21 | 22 | void setValue(T value); 23 | 24 | boolean isRequired(); 25 | 26 | List fuzz(Fuzzer fuzzer); // Visitor DP, accept method 27 | 28 | // https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html 29 | // You want to specify the behavior of a particular data type, but not concerned about who implements its behavior 30 | // You want to take advantage of multiple inheritance of type 31 | default Map bodyParameterJsonRepresentationMap() { 32 | HashMap representationMap = new HashMap<>(1); 33 | representationMap.put(this.getName(), this.getValue()); 34 | return representationMap; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/valued/PropertyValueNode.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.valued; 2 | 3 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 4 | import com.imperva.apiattacktool.model.valued.factory.PropertyValueFactory; 5 | import com.imperva.apispecparser.model.Property; 6 | import com.imperva.apispecparser.model.PropertyNode; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class PropertyValueNode implements Cloneable { 12 | 13 | private ParameterLocation parameterLocation; 14 | private boolean isRequired; 15 | private Map propertyNameToPropertyValueMap; 16 | private Map propertyNameToPropertyValueNodeMap; 17 | 18 | public PropertyValueNode(ParameterLocation parameterLocation, boolean isRequired) { 19 | this.parameterLocation = parameterLocation; 20 | this.isRequired = isRequired; 21 | this.propertyNameToPropertyValueNodeMap = new HashMap<>(); 22 | this.propertyNameToPropertyValueMap = new HashMap<>(); 23 | } 24 | 25 | public PropertyValueNode(PropertyNode propertyNode, PropertyValueFactory propertyValueFactory) { 26 | PropertyValueNode propertyValueNode = traverseNodeConvertToPropertyValue(propertyNode, propertyValueFactory); 27 | this.parameterLocation = propertyValueNode.getParameterLocation(); 28 | this.isRequired = propertyValueNode.isRequired(); 29 | this.propertyNameToPropertyValueMap = propertyValueNode.propertyNameToPropertyValueMap; 30 | this.propertyNameToPropertyValueNodeMap = propertyValueNode.propertyNameToPropertyValueNodeMap; 31 | } 32 | 33 | public void addPropertyValueType(String propertyName, PropertyValue property) { 34 | propertyNameToPropertyValueMap.put(propertyName, property); 35 | } 36 | 37 | public void addPropertyNode(String propertyName, PropertyValueNode propertyNode) { 38 | propertyNameToPropertyValueNodeMap.put(propertyName, propertyNode); 39 | } 40 | 41 | public ParameterLocation getParameterLocation() { 42 | return parameterLocation; 43 | } 44 | 45 | public boolean isRequired() { 46 | return isRequired; 47 | } 48 | 49 | public Map getPropertiesMap() { 50 | return propertyNameToPropertyValueMap; 51 | } 52 | 53 | public void setPropertiesMap(Map propertyNameToPropertyValueMap) { 54 | this.propertyNameToPropertyValueMap = propertyNameToPropertyValueMap; 55 | } 56 | 57 | public boolean hasChildren() { 58 | return propertyNameToPropertyValueNodeMap.size() > 0; 59 | } 60 | 61 | public Map getChildrenMap() { 62 | return propertyNameToPropertyValueNodeMap; 63 | } 64 | 65 | public void setChildrenMap(Map propertyNameToPropertyValueNodeMap) { 66 | this.propertyNameToPropertyValueNodeMap = propertyNameToPropertyValueNodeMap; 67 | } 68 | 69 | @Override 70 | public Object clone() throws CloneNotSupportedException { 71 | PropertyValueNode propertyValueNode = new PropertyValueNode(this.parameterLocation, this.isRequired); 72 | 73 | for (Map.Entry entry : propertyNameToPropertyValueMap.entrySet()) { 74 | propertyValueNode.propertyNameToPropertyValueMap.put(entry.getKey(), (PropertyValue) entry.getValue().clone()); 75 | } 76 | 77 | for (Map.Entry entry : propertyNameToPropertyValueNodeMap.entrySet()) { 78 | propertyValueNode.propertyNameToPropertyValueNodeMap.put(entry.getKey(), (PropertyValueNode) entry.getValue().clone()); 79 | } 80 | 81 | return propertyValueNode; 82 | } 83 | 84 | public Map bodyParameterJsonRepresentationMap() { 85 | Map jsonRepresentationMap = 86 | new HashMap<>(propertyNameToPropertyValueMap.size() + propertyNameToPropertyValueNodeMap.size()); 87 | propertyNameToPropertyValueMap.entrySet().stream() 88 | .filter(stringPropertyValueEntry -> stringPropertyValueEntry.getValue().getParameterLocation() == ParameterLocation.BODY) 89 | .forEach(stringToPropertyValueEntry -> 90 | jsonRepresentationMap.putAll(stringToPropertyValueEntry.getValue().bodyParameterJsonRepresentationMap())); 91 | 92 | propertyNameToPropertyValueNodeMap.entrySet().stream() 93 | .filter(stringPropertyValueEntry -> stringPropertyValueEntry.getValue().getParameterLocation() == ParameterLocation.BODY) 94 | .forEach( 95 | stringPropertyToValueNodeEntry -> 96 | jsonRepresentationMap.put(stringPropertyToValueNodeEntry.getKey(), 97 | stringPropertyToValueNodeEntry.getValue().bodyParameterJsonRepresentationMap())); 98 | return jsonRepresentationMap; 99 | } 100 | 101 | @Override 102 | public String toString() { 103 | return "PropertyValueNode{" 104 | + "propertyNameToPropertyValueMap=" + propertyNameToPropertyValueMap 105 | + ", propertyNameToPropertyValueNodeMap=" + propertyNameToPropertyValueNodeMap 106 | + '}'; 107 | } 108 | 109 | private PropertyValue convertPropertyToPropertyValue(Property property, PropertyValueFactory propertyValueFactory) { 110 | return propertyValueFactory.getPropertyValueFromProperty(property); 111 | } 112 | 113 | private PropertyValueNode traverseNodeConvertToPropertyValue(PropertyNode propertyNode, PropertyValueFactory propertyValueFactory) { 114 | PropertyValueNode propertyValueNode = new PropertyValueNode(propertyNode.getParameterLocation(), propertyNode.isRequired()); 115 | if (propertyNode.getPropertiesMap().size() > 0) { 116 | propertyNode.getPropertiesMap().entrySet().forEach( 117 | locatorToPropertyMapEntry -> { 118 | propertyValueNode.addPropertyValueType(locatorToPropertyMapEntry.getKey(), 119 | convertPropertyToPropertyValue(locatorToPropertyMapEntry.getValue(), propertyValueFactory)); 120 | }); 121 | } 122 | if (propertyNode.hasChildren()) { 123 | propertyNode.getChildrenMap().entrySet().forEach( 124 | locatorToPropertyNodeMap -> { 125 | PropertyValueNode convertedPropertyValueNode = traverseNodeConvertToPropertyValue(locatorToPropertyNodeMap.getValue(), 126 | propertyValueFactory); 127 | propertyValueNode.addPropertyNode(locatorToPropertyNodeMap.getKey(), convertedPropertyValueNode); 128 | } 129 | ); 130 | } 131 | return propertyValueNode; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/valued/StringPropertyValue.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.valued; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apispecparser.model.Property; 5 | import com.imperva.apispecparser.model.StringProperty; 6 | 7 | import java.util.List; 8 | 9 | public class StringPropertyValue extends StringProperty implements PropertyValue { 10 | 11 | private String value; 12 | 13 | public StringPropertyValue(StringProperty stringProperty, String value) { 14 | super(new Property(stringProperty.getParameterLocation(), stringProperty.getType(), stringProperty.getName(), stringProperty.isRequired(), 15 | stringProperty.getReadOnly(), stringProperty.getAllowEmptyValue()), 16 | stringProperty.getEnumList(), stringProperty.getMinLength(), stringProperty.getMaxLength(), stringProperty.getPattern()); 17 | this.value = value; 18 | } 19 | 20 | @Override 21 | public Object clone() throws CloneNotSupportedException { 22 | return super.clone(); 23 | } 24 | 25 | @Override 26 | public void setValue(String value) { 27 | this.value = value; 28 | } 29 | 30 | @Override 31 | public String getValue() { 32 | return value; 33 | } 34 | 35 | @Override 36 | public List fuzz(Fuzzer fuzzer) { 37 | return fuzzer.fuzz(this); 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "StringPropertyValue{" 43 | + "value='" + value + '\'' 44 | + ", " + super.toString() + '}'; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/valued/factory/PropertyValueFactory.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.valued.factory; 2 | 3 | import com.imperva.apiattacktool.model.valued.PropertyValue; 4 | import com.imperva.apispecparser.model.Property; 5 | 6 | public interface PropertyValueFactory { 7 | PropertyValue getPropertyValueFromProperty(Property property); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/model/valued/factory/SimplePropertyValueFactory.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.model.valued.factory; 2 | 3 | import com.imperva.apiattacktool.model.valued.ArrayPropertyValue; 4 | import com.imperva.apiattacktool.model.valued.BooleanPropertyValue; 5 | import com.imperva.apiattacktool.model.valued.NumericPropertyValue; 6 | import com.imperva.apiattacktool.model.valued.PropertyPropertyValue; 7 | import com.imperva.apiattacktool.model.valued.PropertyValue; 8 | import com.imperva.apiattacktool.model.valued.StringPropertyValue; 9 | import com.imperva.apispecparser.model.ArrayProperty; 10 | import com.imperva.apispecparser.model.BooleanProperty; 11 | import com.imperva.apispecparser.model.NumericProperty; 12 | import com.imperva.apispecparser.model.Property; 13 | import com.imperva.apispecparser.model.StringProperty; 14 | 15 | public class SimplePropertyValueFactory implements PropertyValueFactory { 16 | 17 | @Override 18 | public PropertyValue getPropertyValueFromProperty(Property property) { 19 | if (property == null) { 20 | return null; 21 | } 22 | 23 | switch (property.getType()) { 24 | case ARRAY: 25 | ArrayProperty arrayProperty = (ArrayProperty) property; 26 | return new ArrayPropertyValue(arrayProperty, null); 27 | case FLOAT: 28 | case LONG: 29 | case DECIMAL: 30 | case DOUBLE: 31 | case BASE_INTEGER: 32 | case INTEGER: 33 | NumericProperty numericProperty = (NumericProperty) property; 34 | return new NumericPropertyValue(numericProperty, null); 35 | case UUID: 36 | case PASSWORD: 37 | case EMAIL: 38 | case DATETIME: 39 | case DATE: 40 | // As awkward this may sound. We should convert the (enum values) to Date using a dedicated active propertynode, instead of this circus 41 | case BINARY: 42 | case STRING: 43 | case BYTE_ARRAY: 44 | StringProperty stringProperty = (StringProperty) property; 45 | return new StringPropertyValue(stringProperty, null); 46 | case BOOLEAN: 47 | BooleanProperty booleanProperty = (BooleanProperty) property; 48 | return new BooleanPropertyValue(booleanProperty, null); 49 | case UNTYPED: 50 | case FILE: 51 | default: 52 | return new PropertyPropertyValue(property, null); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/processors/EndpointModelProcessor.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.processors; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 5 | 6 | import java.util.List; 7 | 8 | public interface EndpointModelProcessor { 9 | List process(List endpointModelList, Fuzzer fuzzer); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/processors/FuzzEndpointModelProcessor.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.processors; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apiattacktool.fuzzing.modelgenerators.FuzzedModelsGenerator; 5 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 6 | import com.imperva.apiattacktool.model.valued.factory.PropertyValueFactory; 7 | 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | public class FuzzEndpointModelProcessor implements EndpointModelProcessor { 13 | 14 | private FuzzedModelsGenerator fuzzedModelsGenerator; 15 | private PropertyValueFactory propertyValueFactory; 16 | 17 | public FuzzEndpointModelProcessor(FuzzedModelsGenerator fuzzedModelsGenerator) { 18 | this.fuzzedModelsGenerator = fuzzedModelsGenerator; 19 | this.propertyValueFactory = fuzzedModelsGenerator.getPropertyValueFactory(); 20 | } 21 | 22 | @Override 23 | public List process(List endpointModelList, Fuzzer fuzzer) { 24 | if (endpointModelList == null) { 25 | return Collections.EMPTY_LIST; 26 | } 27 | 28 | return endpointModelList.stream() 29 | .flatMap(endpointValuedModel -> fuzzedModelsGenerator.fuzzModelValues( 30 | endpointValuedModel, fuzzer).stream()) 31 | .collect(Collectors.toList()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/tests/AbstractTestDriver.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.tests; 2 | 3 | import com.imperva.apiattacktool.activators.HttpRequestGenerator; 4 | import com.imperva.apiattacktool.activators.ModelToValueConverter; 5 | import com.imperva.apiattacktool.activators.RequestDataConverter; 6 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 7 | import com.imperva.apiattacktool.fuzzing.parameters.PolicyEnforcer; 8 | import com.imperva.apiattacktool.processors.EndpointModelProcessor; 9 | import com.imperva.apispecparser.exceptions.ParseException; 10 | import com.imperva.apispecparser.model.EndpointModel; 11 | import com.imperva.apispecparser.parsers.ApiSpecFileLocation; 12 | import com.imperva.apispecparser.parsers.swagger.Swagger2Parser; 13 | import com.imperva.apispecparser.utils.FileUtils; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.io.IOException; 18 | import java.util.Collections; 19 | import java.util.List; 20 | 21 | public class AbstractTestDriver { 22 | private static final Logger logger = LoggerFactory.getLogger(AbstractTestDriver.class); 23 | 24 | private ModelToValueConverter modelToValueConverter; 25 | private PolicyEnforcer policyEnforcer; 26 | private EndpointModelProcessor beforeMainEndpointModelProcessor; 27 | private EndpointModelProcessor mainEndpointModelProcessor; 28 | private RequestDataConverter testRequestDataConverter; 29 | private HttpRequestGenerator httpRequestGenerator; 30 | private Fuzzer fuzzer; 31 | 32 | public AbstractTestDriver(ModelToValueConverter modelToValueConverter, 33 | PolicyEnforcer policyEnforcer, 34 | EndpointModelProcessor beforeMainEndpointModelProcessor, 35 | EndpointModelProcessor mainEndpointModelProcessor, 36 | RequestDataConverter testRequestDataConverter, 37 | HttpRequestGenerator httpRequestGenerator, 38 | Fuzzer fuzzer) { 39 | this.modelToValueConverter = modelToValueConverter; 40 | this.policyEnforcer = policyEnforcer; 41 | this.beforeMainEndpointModelProcessor = beforeMainEndpointModelProcessor; 42 | this.mainEndpointModelProcessor = mainEndpointModelProcessor; 43 | this.testRequestDataConverter = testRequestDataConverter; 44 | this.httpRequestGenerator = httpRequestGenerator; 45 | this.fuzzer = fuzzer; 46 | } 47 | 48 | public ModelToValueConverter getModelToValueConverter() { 49 | return modelToValueConverter; 50 | } 51 | 52 | public PolicyEnforcer getPolicyEnforcer() { 53 | return policyEnforcer; 54 | } 55 | 56 | public EndpointModelProcessor getBeforeMainEndpointModelProcessor() { 57 | return beforeMainEndpointModelProcessor; 58 | } 59 | 60 | public EndpointModelProcessor getMainEndpointModelProcessor() { 61 | return mainEndpointModelProcessor; 62 | } 63 | 64 | public RequestDataConverter getTestRequestDataConverter() { 65 | return testRequestDataConverter; 66 | } 67 | 68 | public HttpRequestGenerator getHttpRequestGenerator() { 69 | return httpRequestGenerator; 70 | } 71 | 72 | public Fuzzer getFuzzer() { 73 | return fuzzer; 74 | } 75 | 76 | protected String loadResourceFileAsString(String resourceFileName) throws IOException { 77 | return FileUtils.readResource(resourceFileName); 78 | } 79 | 80 | protected List parseSwagger(String resourceFileName) { 81 | List endpointModelList; 82 | Swagger2Parser swagger2Parser = new Swagger2Parser(); 83 | 84 | try { 85 | endpointModelList = swagger2Parser.getEndpointModelList(resourceFileName, ApiSpecFileLocation.EXTERNAL); 86 | } catch (ParseException parseException) { 87 | logger.error("Error parsing swagger: {}", parseException.getMessage()); 88 | return Collections.emptyList(); 89 | } 90 | return endpointModelList; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/tests/NegativeSinglePropertyScenarioTestDriver.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.tests; 2 | 3 | import com.imperva.apiattacktool.activators.EndpointModelToValueConverter; 4 | import com.imperva.apiattacktool.activators.EndpointTestRequestDataConverter; 5 | import com.imperva.apiattacktool.activators.TestHttpRequestGenerator; 6 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 7 | import com.imperva.apiattacktool.fuzzing.modelgenerators.CloningIterativeFuzzedModelsGenerator; 8 | import com.imperva.apiattacktool.fuzzing.modelgenerators.SingleValueFuzzedModelsGenerator; 9 | import com.imperva.apiattacktool.fuzzing.parameters.PolicyEnforcer; 10 | import com.imperva.apiattacktool.model.tests.EndpointTestRequestData; 11 | import com.imperva.apiattacktool.model.tests.HttpRequestWrapper; 12 | import com.imperva.apiattacktool.model.tests.NegativeTestHttpResponseValidator; 13 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 14 | import com.imperva.apiattacktool.model.valued.factory.PropertyValueFactory; 15 | import com.imperva.apiattacktool.processors.FuzzEndpointModelProcessor; 16 | import com.imperva.apispecparser.model.EndpointModel; 17 | 18 | import java.util.Collections; 19 | import java.util.List; 20 | 21 | public class NegativeSinglePropertyScenarioTestDriver extends AbstractTestDriver implements TestDriver { 22 | private Fuzzer firstStepFuzzer; 23 | 24 | public NegativeSinglePropertyScenarioTestDriver(Fuzzer firstStepFuzzer, 25 | Fuzzer secondStepFuzzer, 26 | PolicyEnforcer policyEnforcer, 27 | PropertyValueFactory propertyValueFactory) { 28 | super(new EndpointModelToValueConverter(propertyValueFactory), 29 | policyEnforcer, 30 | new FuzzEndpointModelProcessor(new SingleValueFuzzedModelsGenerator(propertyValueFactory)), 31 | new FuzzEndpointModelProcessor(new CloningIterativeFuzzedModelsGenerator(propertyValueFactory)), 32 | new EndpointTestRequestDataConverter(), 33 | new TestHttpRequestGenerator(NegativeTestHttpResponseValidator::new), 34 | secondStepFuzzer); 35 | this.firstStepFuzzer = firstStepFuzzer; 36 | } 37 | 38 | @Override 39 | public List getHttpRequestList(String resourceFileName) { 40 | List endpointModelList = parseSwagger(resourceFileName); 41 | if (endpointModelList.isEmpty()) { 42 | return Collections.emptyList(); 43 | } 44 | 45 | List endpointValuedModelList = getModelToValueConverter().endpointModelToEndpointValuedModel(endpointModelList); 46 | List modelsWithPolicyEnforced = getPolicyEnforcer().enforcePolicyOn(endpointValuedModelList); 47 | List fuzzedModelsWithPositiveValues = getBeforeMainEndpointModelProcessor().process(modelsWithPolicyEnforced, firstStepFuzzer); 48 | List fuzzedEndpointValuedModelList = getMainEndpointModelProcessor().process(fuzzedModelsWithPositiveValues, getFuzzer()); 49 | List endpointTestRequestDataList = getTestRequestDataConverter().processList(fuzzedEndpointValuedModelList); 50 | List httpRequestWrapperList = getHttpRequestGenerator().generateFrom(endpointTestRequestDataList); 51 | return httpRequestWrapperList; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/tests/ScenariosDataProvider.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.tests; 2 | 3 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 4 | import com.imperva.apiattacktool.fuzzing.parameters.AllParametersPolicyEnforcer; 5 | import com.imperva.apiattacktool.fuzzing.parameters.PolicyEnforcer; 6 | import com.imperva.apiattacktool.fuzzing.parameters.RequiredOnlyPolicyEnforcer; 7 | import com.imperva.apiattacktool.fuzzing.value.NegativeSingleValueFuzzer; 8 | import com.imperva.apiattacktool.fuzzing.value.PositiveSingleValueFuzzer; 9 | import com.imperva.apiattacktool.model.tests.HttpRequestWrapper; 10 | import com.imperva.apiattacktool.model.valued.factory.PropertyValueFactory; 11 | import com.imperva.apiattacktool.model.valued.factory.SimplePropertyValueFactory; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | import org.testng.ITestContext; 15 | import org.testng.annotations.DataProvider; 16 | 17 | import java.util.List; 18 | import java.util.concurrent.ThreadLocalRandom; 19 | 20 | public class ScenariosDataProvider { 21 | private static final Logger logger = LoggerFactory.getLogger(ScenariosDataProvider.class); 22 | 23 | @DataProvider() 24 | public static Object[][] positiveScenarioDataProvider() { 25 | PropertyValueFactory propertyValueFactory = new SimplePropertyValueFactory(); 26 | Fuzzer fuzzer = new PositiveSingleValueFuzzer(propertyValueFactory); 27 | PolicyEnforcer policyEnforcer = new AllParametersPolicyEnforcer(); 28 | SingleValueScenarioTestDriver singleValueScenarioTestDriver = 29 | new SingleValueScenarioTestDriver(fuzzer, policyEnforcer, propertyValueFactory); 30 | return getEndpointTestRequestData(singleValueScenarioTestDriver); 31 | } 32 | 33 | @DataProvider() 34 | public static Object[][] positiveRequiredPropertiesOnlyScenarioDataProvider() { 35 | PropertyValueFactory propertyValueFactory = new SimplePropertyValueFactory(); 36 | Fuzzer fuzzer = new PositiveSingleValueFuzzer(propertyValueFactory); 37 | PolicyEnforcer policyEnforcer = new RequiredOnlyPolicyEnforcer(); 38 | SingleValueScenarioTestDriver singleValueScenarioTestDriver = 39 | new SingleValueScenarioTestDriver(fuzzer, policyEnforcer, propertyValueFactory); 40 | return getEndpointTestRequestData(singleValueScenarioTestDriver); 41 | } 42 | 43 | @DataProvider() 44 | public static Object[][] negativeBadPropertyScenarioDataProvider(ITestContext context) { 45 | PropertyValueFactory propertyValueFactory = new SimplePropertyValueFactory(); 46 | PolicyEnforcer policyEnforcer = new AllParametersPolicyEnforcer(); 47 | Fuzzer firstStepFuzzer = new PositiveSingleValueFuzzer(propertyValueFactory); 48 | Fuzzer mainStepFuzzer = new NegativeSingleValueFuzzer(propertyValueFactory); 49 | NegativeSinglePropertyScenarioTestDriver negativeSinglePropertyScenarioTestDriver = 50 | new NegativeSinglePropertyScenarioTestDriver(firstStepFuzzer, mainStepFuzzer, policyEnforcer, propertyValueFactory); 51 | return getEndpointTestRequestData(negativeSinglePropertyScenarioTestDriver); 52 | } 53 | 54 | private static Object[][] getEndpointTestRequestData(TestDriver testDriver) { 55 | List httpRequestWrapperList; 56 | try { 57 | httpRequestWrapperList = testDriver.getHttpRequestList(TestConfiguration.getSpecFilePath()); 58 | } catch (Exception anyException) { 59 | logger.error("Failed to get httpRequestList, for file: {}", TestConfiguration.getSpecFilePath(), anyException); 60 | return null; 61 | } 62 | 63 | Object[][] result = new Object[httpRequestWrapperList.size()][4]; 64 | for (int i = 0; i < httpRequestWrapperList.size(); i++) { 65 | try { 66 | result[i][0] = httpRequestWrapperList.get(i).getHttpRequest(); 67 | result[i][1] = httpRequestWrapperList.get(i).getHttpResponseValidator(); 68 | result[i][2] = httpRequestWrapperList.get(i).getTestElementComment(); 69 | result[i][3] = getTestId(); 70 | } catch (Exception anyException) { 71 | logger.error("Error injecting data for test.", anyException); 72 | } 73 | } 74 | 75 | return result; 76 | } 77 | 78 | private static String getTestId() { 79 | return System.currentTimeMillis() + "-" + ThreadLocalRandom.current().nextInt(100000); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/tests/SingleValueScenarioTestDriver.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.tests; 2 | 3 | import com.imperva.apiattacktool.activators.EndpointModelToValueConverter; 4 | import com.imperva.apiattacktool.activators.EndpointTestRequestDataConverter; 5 | import com.imperva.apiattacktool.activators.TestHttpRequestGenerator; 6 | import com.imperva.apiattacktool.fuzzing.Fuzzer; 7 | import com.imperva.apiattacktool.fuzzing.modelgenerators.SingleValueFuzzedModelsGenerator; 8 | import com.imperva.apiattacktool.fuzzing.parameters.PolicyEnforcer; 9 | import com.imperva.apiattacktool.model.tests.EndpointTestRequestData; 10 | import com.imperva.apiattacktool.model.tests.HttpRequestWrapper; 11 | import com.imperva.apiattacktool.model.tests.PositiveTestHttpResponseValidator; 12 | import com.imperva.apiattacktool.model.valued.EndpointValuedModel; 13 | import com.imperva.apiattacktool.model.valued.factory.PropertyValueFactory; 14 | import com.imperva.apiattacktool.processors.FuzzEndpointModelProcessor; 15 | import com.imperva.apispecparser.model.EndpointModel; 16 | 17 | import java.util.Collections; 18 | import java.util.List; 19 | 20 | public class SingleValueScenarioTestDriver extends AbstractTestDriver implements TestDriver { 21 | 22 | public SingleValueScenarioTestDriver(Fuzzer fuzzer, 23 | PolicyEnforcer policyEnforcer, 24 | PropertyValueFactory propertyValueFactory) { 25 | super(new EndpointModelToValueConverter(propertyValueFactory), 26 | policyEnforcer, 27 | null, 28 | new FuzzEndpointModelProcessor(new SingleValueFuzzedModelsGenerator(propertyValueFactory)), 29 | new EndpointTestRequestDataConverter(), 30 | new TestHttpRequestGenerator(PositiveTestHttpResponseValidator::new), 31 | fuzzer); 32 | } 33 | 34 | @Override 35 | public List getHttpRequestList(String resourceFileName) { 36 | List endpointModelList = parseSwagger(resourceFileName); 37 | if (endpointModelList.isEmpty()) { 38 | return Collections.emptyList(); 39 | } 40 | 41 | List endpointValuedModelList = getModelToValueConverter().endpointModelToEndpointValuedModel(endpointModelList); 42 | List modelsWithPolicyEnforced = getPolicyEnforcer().enforcePolicyOn(endpointValuedModelList); 43 | List fuzzedEndpointValuedModelList = getMainEndpointModelProcessor().process(modelsWithPolicyEnforced, getFuzzer()); 44 | List endpointTestRequestDataList = getTestRequestDataConverter().processList(fuzzedEndpointValuedModelList); 45 | List httpRequestWrapperList = getHttpRequestGenerator().generateFrom(endpointTestRequestDataList); 46 | return httpRequestWrapperList; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/tests/TestConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.tests; 2 | 3 | import com.imperva.apiattacktool.cli.ApiAttackTool; 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | import java.util.Collection; 7 | import java.util.Collections; 8 | import java.util.HashSet; 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | import java.util.stream.Stream; 12 | 13 | public class TestConfiguration { 14 | 15 | private static final int DEFAULT_PROXY_PORT = 80; 16 | private static final int DEFAULT_HOST_PORT = 443; 17 | 18 | private static String specFilePath = System.getProperty("specFile", null); 19 | 20 | private static String hostScheme = System.getProperty("hostScheme", null); 21 | 22 | private static String hostName = System.getProperty("hostName", null); 23 | 24 | private static Integer hostPort = getIntegerFieldFromProperty("hostPort", DEFAULT_HOST_PORT); 25 | 26 | private static String proxyHost = System.getProperty("proxyHost", null); 27 | 28 | private static Integer proxyPort = getIntegerFieldFromProperty("proxyPort", DEFAULT_PROXY_PORT); 29 | 30 | private static Collection userProvidedPositiveResponseCodes = getIntegerListFromProperty("addPositiveRC", Collections.emptyList()); 31 | 32 | private static Collection userProvidedNegativeResponseCodes = getIntegerListFromProperty("addNegativeRC", Collections.emptyList()); 33 | 34 | public static void initFrom(ApiAttackTool apiAttackToolOptions) { 35 | specFilePath = apiAttackToolOptions.getSpecFilePath(); 36 | hostScheme = apiAttackToolOptions.getHostScheme(); 37 | hostName = apiAttackToolOptions.getHostName(); 38 | hostPort = apiAttackToolOptions.getHostPort(); 39 | proxyHost = apiAttackToolOptions.getProxyHost(); 40 | proxyPort = apiAttackToolOptions.getProxyPort(); 41 | userProvidedPositiveResponseCodes = 42 | apiAttackToolOptions.getUserProvidedPositiveResponseCodes() == null 43 | ? Collections.emptyList() 44 | : new HashSet<>(apiAttackToolOptions.getUserProvidedPositiveResponseCodes()); 45 | userProvidedNegativeResponseCodes = 46 | apiAttackToolOptions.getUserProvidedNegativeResponseCodes() == null 47 | ? Collections.emptyList() 48 | : new HashSet<>(apiAttackToolOptions.getUserProvidedNegativeResponseCodes()); 49 | } 50 | 51 | public static String getSpecFilePath() { 52 | return specFilePath; 53 | } 54 | 55 | public static String getHostScheme() { 56 | return hostScheme; 57 | } 58 | 59 | public static String getHostName() { 60 | return hostName; 61 | } 62 | 63 | public static int getHostPort() { 64 | return hostPort == null ? DEFAULT_HOST_PORT : hostPort; 65 | } 66 | 67 | public static String getProxyHost() { 68 | return proxyHost; 69 | } 70 | 71 | public static int getProxyPort() { 72 | return proxyPort == null ? DEFAULT_PROXY_PORT : proxyPort; 73 | } 74 | 75 | public static boolean isProxyDefined() { 76 | return !StringUtils.isBlank(proxyHost); 77 | } 78 | 79 | public static Collection getUserProvidedPositiveResponseCodes() { 80 | return userProvidedPositiveResponseCodes; 81 | } 82 | 83 | public static Collection getUserProvidedNegativeResponseCodes() { 84 | return userProvidedNegativeResponseCodes; 85 | } 86 | 87 | public static String getWorkingConfigurationString() { 88 | return "API Spec file path: " + specFilePath + "\n" 89 | + "Host: (" + hostScheme + ") " + hostName + " : " + getHostPort() + "\n" 90 | + (isProxyDefined() 91 | ? "Proxy Host: " + proxyHost + " : " + getProxyPort() + "\n" 92 | : ""); 93 | 94 | } 95 | 96 | private static Integer getIntegerFieldFromProperty(String propertyName, Integer defaultValue) { 97 | String propertyValue = System.getProperty(propertyName); 98 | if (StringUtils.isBlank(propertyValue)) { 99 | return defaultValue; 100 | } 101 | 102 | return Integer.parseInt(propertyValue); 103 | } 104 | 105 | private static Collection getIntegerListFromProperty(String propertyName, List defaultValue) { 106 | String propertyValue = System.getProperty(propertyName); 107 | if (StringUtils.isBlank(propertyValue)) { 108 | return defaultValue; 109 | } 110 | 111 | String[] listValues = propertyValue.split("[,]"); 112 | Collection integerCollection = 113 | Stream.of(listValues).map(Integer::parseInt).collect(Collectors.toCollection(HashSet::new)); 114 | return integerCollection; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/tests/TestDriver.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.tests; 2 | 3 | import com.imperva.apiattacktool.model.tests.HttpRequestWrapper; 4 | 5 | import java.util.List; 6 | 7 | public interface TestDriver { 8 | List getHttpRequestList(String resourceFileName); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/tests/TestHttpResponse.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.tests; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public class TestHttpResponse { 6 | private String incidentId; 7 | @JsonProperty("hostName") 8 | private String hostname; 9 | private String errorCode; 10 | @JsonProperty("description") 11 | private String responseDescription; 12 | private String timeUtc; 13 | private String clientIp; 14 | private String proxyId; 15 | private String proxyIp; 16 | 17 | public String getIncidentId() { 18 | return incidentId; 19 | } 20 | 21 | public void setIncidentId(String incidentId) { 22 | this.incidentId = incidentId; 23 | } 24 | 25 | public String getHostname() { 26 | return hostname; 27 | } 28 | 29 | public void setHostname(String hostname) { 30 | this.hostname = hostname; 31 | } 32 | 33 | public String getErrorCode() { 34 | return errorCode; 35 | } 36 | 37 | public void setErrorCode(String errorCode) { 38 | this.errorCode = errorCode; 39 | } 40 | 41 | public String getResponseDescription() { 42 | return responseDescription; 43 | } 44 | 45 | public void setResponseDescription(String responseDescription) { 46 | this.responseDescription = responseDescription; 47 | } 48 | 49 | public String getTimeUtc() { 50 | return timeUtc; 51 | } 52 | 53 | public void setTimeUtc(String timeUtc) { 54 | this.timeUtc = timeUtc; 55 | } 56 | 57 | public String getClientIp() { 58 | return clientIp; 59 | } 60 | 61 | public void setClientIp(String clientIp) { 62 | this.clientIp = clientIp; 63 | } 64 | 65 | public String getProxyId() { 66 | return proxyId; 67 | } 68 | 69 | public void setProxyId(String proxyId) { 70 | this.proxyId = proxyId; 71 | } 72 | 73 | public String getProxyIp() { 74 | return proxyIp; 75 | } 76 | 77 | public void setProxyIp(String proxyIp) { 78 | this.proxyIp = proxyIp; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return "TestHttpResponse{\n" 84 | + "\tincidentId :'" + incidentId + "\'\n" 85 | + "\thostname :'" + hostname + "\'\n" 86 | + "\terrorCode :'" + errorCode + "\'\n" 87 | + "\tresponseDescription :'" + responseDescription + "\'\n" 88 | + "\ttimeUtc :'" + timeUtc + "\'\n" 89 | + "\tclientIp :'" + clientIp + "\'\n" 90 | + "\tproxyId :'" + proxyId + "'\'\n" 91 | + "\tproxyIp :'" + proxyIp + "\'\n" 92 | + '}'; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apiattacktool/utils/TestReporter.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apiattacktool.utils; 2 | 3 | import org.testng.Reporter; 4 | 5 | public class TestReporter { 6 | 7 | public static void log(Object message) { 8 | log(String.valueOf(message)); 9 | } 10 | 11 | public static void log(String message) { 12 | Reporter.log(message, true); 13 | } 14 | 15 | public static void log(Exception exception) { 16 | Throwable currentException = exception; 17 | int i = 0; 18 | do { 19 | Reporter.log((i > 0 ? "at " : "") + currentException.getMessage(), true); 20 | currentException = currentException.getCause(); 21 | i++; 22 | } while (currentException != null); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/exceptions/ParseException.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.exceptions; 2 | 3 | public class ParseException extends Exception { 4 | public ParseException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/model/ArrayProperty.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.model; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class ArrayProperty extends Property { 7 | private Boolean areItemsUnique; 8 | private PropertyNode items; 9 | private int minimumItems; 10 | private Integer maximumItems; 11 | private CollectionFormat collectionFormat; 12 | 13 | public ArrayProperty(Property property, Boolean areItemsUnique, PropertyNode items, Integer minItems, Integer maxItems, String collectionFormat) { 14 | super(property); 15 | this.areItemsUnique = areItemsUnique; 16 | this.items = items; 17 | this.minimumItems = minItems == null ? 0 : minItems; 18 | this.maximumItems = maxItems; 19 | this.collectionFormat = CollectionFormat.getBySpecCollectionFormat(collectionFormat); // Sets CSV as default 20 | } 21 | 22 | public Boolean getAreItemsUnique() { 23 | return areItemsUnique; 24 | } 25 | 26 | public PropertyNode getItems() { 27 | return items; 28 | } 29 | 30 | public int getMinimumItems() { 31 | return minimumItems; 32 | } 33 | 34 | public Integer getMaximumItems() { 35 | return maximumItems; 36 | } 37 | 38 | public CollectionFormat getCollectionFormat() { 39 | return collectionFormat; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "ArrayProperty{" 45 | + "areItemsUnique=" + areItemsUnique 46 | + ", items=" + items 47 | + ", minimumItems=" + minimumItems 48 | + ", maximumItems=" + maximumItems 49 | + ", Property=" + super.toString() 50 | + '}'; 51 | } 52 | 53 | public enum CollectionFormat { 54 | COMMA_SEPARATED_VALUES("csv", ","), 55 | SPACE_SEPARATED_VALUES("ssv", "\\s"), 56 | TAB_SEPARATED_VALUES("tsv", "\\t"), 57 | PIPE_SEPARATED_VALUES("pipes", "\\|"), 58 | AMPERSAND_CONCATENATED_VALUES("multi", "&"); 59 | 60 | private static final Map enumValuesMap = initializeMapping(); 61 | private String formatString; 62 | private String outputString; 63 | 64 | CollectionFormat(String formatString, String outputString) { 65 | this.formatString = formatString; 66 | this.outputString = outputString; 67 | } 68 | 69 | private static Map initializeMapping() { 70 | Map enumValuesMap = new HashMap<>(); 71 | for (CollectionFormat collectionFormat : CollectionFormat.values()) { 72 | enumValuesMap.put(collectionFormat.formatString, collectionFormat); 73 | } 74 | return enumValuesMap; 75 | } 76 | 77 | public static CollectionFormat getBySpecCollectionFormat(String specFormat) { 78 | CollectionFormat collectionFormat = enumValuesMap.get(specFormat); 79 | if (collectionFormat == null) { 80 | collectionFormat = COMMA_SEPARATED_VALUES; 81 | } 82 | return collectionFormat; 83 | } 84 | 85 | public String getFormatString() { 86 | return formatString; 87 | } 88 | 89 | public String getOutputString() { 90 | return outputString; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/model/BooleanProperty.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.model; 2 | 3 | import java.util.List; 4 | 5 | public class BooleanProperty extends EnumerableProperty { 6 | public BooleanProperty(Property property, List enumList) { 7 | super(property, enumList); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/model/EndpointModel.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.model; 2 | 3 | import com.imperva.apiattacktool.model.tests.HttpMethod; 4 | 5 | import java.util.Collections; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class EndpointModel { 11 | 12 | private String fullPathWithParamBrackets; 13 | private HttpMethod httpMethod; 14 | private List consumes; 15 | private PropertyNode propertiesNode; 16 | private List httpResponseCodesList; 17 | 18 | public EndpointModel(String fullPathWithParamBrackets, HttpMethod httpMethod, List consumes) { 19 | this.fullPathWithParamBrackets = fullPathWithParamBrackets; 20 | this.httpMethod = httpMethod; 21 | this.consumes = consumes == null ? Collections.emptyList() : consumes; 22 | this.propertiesNode = new PropertyNode(null, false, null); 23 | this.httpResponseCodesList = new LinkedList<>(); 24 | } 25 | 26 | public void addParameter(String name, Property property) { 27 | propertiesNode.getPropertiesMap().put(name, property); 28 | } 29 | 30 | public void addNode(String name, PropertyNode propertyNode) { 31 | propertiesNode.getChildrenMap().put(name, propertyNode); 32 | } 33 | 34 | public void addResponseCode(int responseCode) { 35 | this.httpResponseCodesList.add(responseCode); 36 | } 37 | 38 | public Map getSimpleParametersMap() { 39 | return propertiesNode.getPropertiesMap(); 40 | } 41 | 42 | public String getFullPathWithParamBrackets() { 43 | return fullPathWithParamBrackets; 44 | } 45 | 46 | public HttpMethod getHttpMethod() { 47 | return httpMethod; 48 | } 49 | 50 | public List getConsumes() { 51 | return consumes; 52 | } 53 | 54 | public Map getChildrenMap() { 55 | return propertiesNode.getChildrenMap(); 56 | } 57 | 58 | public List getHttpResponseCodesList() { 59 | return httpResponseCodesList; 60 | } 61 | 62 | public String toString() { 63 | return String.format("Endpoint: %s %s%nNodes:%n%s%nValues:%n%s", 64 | httpMethod, fullPathWithParamBrackets, propertiesNode.getChildrenMap(), propertiesNode.getPropertiesMap()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/model/EnumerableProperty.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.model; 2 | 3 | import java.util.List; 4 | 5 | public class EnumerableProperty extends Property { 6 | 7 | private List enumList; 8 | 9 | public EnumerableProperty(Property property, List enumList) { 10 | super(property); 11 | this.enumList = enumList; 12 | } 13 | 14 | public List getEnumList() { 15 | return enumList; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return "{" 21 | + "enumList=" + enumList 22 | + ", Property=" + super.toString() + '}'; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/model/NumericProperty.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.model; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.List; 5 | 6 | public class NumericProperty extends EnumerableProperty { 7 | private BigDecimal minimum; 8 | private BigDecimal maximum; 9 | private Number multipleOf; 10 | private boolean exclusiveMinimum; 11 | private boolean exclusiveMaximum; 12 | 13 | public NumericProperty(Property property, List enumList, 14 | BigDecimal minimum, BigDecimal maximum, Number multipleOf, Boolean exclusiveMinimum, 15 | Boolean exclusiveMaximum) { 16 | super(property, enumList); 17 | this.minimum = minimum; 18 | this.maximum = maximum; 19 | this.multipleOf = multipleOf; 20 | this.exclusiveMinimum = exclusiveMinimum != null && exclusiveMinimum; 21 | this.exclusiveMaximum = exclusiveMaximum != null && exclusiveMaximum; 22 | } 23 | 24 | public BigDecimal getMinimum() { 25 | return minimum; 26 | } 27 | 28 | public BigDecimal getMaximum() { 29 | return maximum; 30 | } 31 | 32 | public Number getMultipleOf() { 33 | return multipleOf; 34 | } 35 | 36 | public boolean getExclusiveMinimum() { 37 | return exclusiveMinimum; 38 | } 39 | 40 | public boolean getExclusiveMaximum() { 41 | return exclusiveMaximum; 42 | } 43 | 44 | @Override 45 | public Object clone() throws CloneNotSupportedException { 46 | return super.clone(); 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "NumericProperty{" 52 | + "minimum=" + minimum 53 | + ", maximum=" + maximum 54 | + ", multipleOf=" + multipleOf 55 | + ", exclusiveMinimum=" + exclusiveMinimum 56 | + ", exclusiveMaximum=" + exclusiveMaximum 57 | + ", EnumerableProperty=" + super.toString() + '}'; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/model/Property.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.model; 2 | 3 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 4 | 5 | public class Property implements Cloneable { 6 | private ParameterLocation location; 7 | private PropertyType type; 8 | private String name; 9 | private boolean isRequired; 10 | 11 | /** 12 | * Relevant only for Schema "properties" definitions. Declares the property as "read only". 13 | * This means that it MAY be sent as part of a response but MUST NOT be sent as part of the request. 14 | */ 15 | private boolean isReadOnly; 16 | 17 | /** 18 | * Valid only for either query or formData parameters and allows you to send a parameter with a name only or an empty value. 19 | * Default value is false 20 | */ 21 | private boolean allowEmptyValue; 22 | 23 | public Property(ParameterLocation location, PropertyType type, String name, boolean isRequired, Boolean isReadOnly, Boolean allowEmptyValue) { 24 | this.location = location; 25 | this.type = type; 26 | this.name = name; 27 | this.isRequired = isRequired; 28 | this.isReadOnly = isReadOnly != null && isReadOnly; 29 | this.allowEmptyValue = allowEmptyValue != null && allowEmptyValue; 30 | } 31 | 32 | public Property(Property otherProperty) { 33 | this.location = otherProperty.getParameterLocation(); 34 | this.type = otherProperty.getType(); 35 | this.name = otherProperty.getName(); 36 | this.isRequired = otherProperty.isRequired(); 37 | this.isReadOnly = otherProperty.getReadOnly(); 38 | this.allowEmptyValue = otherProperty.getAllowEmptyValue(); 39 | } 40 | 41 | @Override 42 | public Object clone() throws CloneNotSupportedException { 43 | return super.clone(); 44 | } 45 | 46 | public ParameterLocation getParameterLocation() { 47 | return location; 48 | } 49 | 50 | public PropertyType getType() { 51 | return type; 52 | } 53 | 54 | public String getName() { 55 | return name; 56 | } 57 | 58 | public boolean isRequired() { 59 | return isRequired; 60 | } 61 | 62 | public boolean getReadOnly() { 63 | return isReadOnly; 64 | } 65 | 66 | public boolean getAllowEmptyValue() { 67 | return allowEmptyValue; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "{" 73 | + "location=" + location 74 | + ",type='" + type + '\'' 75 | + ",name='" + name + '\'' 76 | + ", isRequired='" + isRequired + '\'' 77 | + '}'; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/model/PropertyNode.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.model; 2 | 3 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class PropertyNode { 9 | 10 | private String name; 11 | private boolean isRequired; 12 | 13 | private ParameterLocation parameterLocation; 14 | private Map propertyNameToPropertyValueMap; 15 | private Map propertyNameToPropertyNodeMap; 16 | 17 | public PropertyNode(String name, boolean isRequired, ParameterLocation parameterLocation) { 18 | this.name = name; 19 | this.isRequired = isRequired; 20 | this.parameterLocation = parameterLocation; 21 | this.propertyNameToPropertyNodeMap = new HashMap<>(); 22 | this.propertyNameToPropertyValueMap = new HashMap<>(); 23 | } 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | 29 | public boolean isRequired() { 30 | return isRequired; 31 | } 32 | 33 | public void addPropertyValueType(String propertyName, Property property) { 34 | propertyNameToPropertyValueMap.put(propertyName, property); 35 | } 36 | 37 | public void addPropertyNode(String propertyName, PropertyNode propertyNode) { 38 | propertyNameToPropertyNodeMap.put(propertyName, propertyNode); 39 | } 40 | 41 | public ParameterLocation getParameterLocation() { 42 | return parameterLocation; 43 | } 44 | 45 | public Map getPropertiesMap() { 46 | return propertyNameToPropertyValueMap; 47 | } 48 | 49 | public boolean hasChildren() { 50 | return propertyNameToPropertyNodeMap.size() > 0; 51 | } 52 | 53 | public Map getChildrenMap() { 54 | return propertyNameToPropertyNodeMap; 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return "{location='" + parameterLocation + '\'' 60 | + ", properties='" + propertyNameToPropertyValueMap.toString() + '\'' 61 | + ", children='" + propertyNameToPropertyNodeMap + '\'' + "}"; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/model/PropertyType.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.model; 2 | 3 | import com.imperva.apiattacktool.infra.Tuple; 4 | 5 | import java.util.Collections; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public enum PropertyType { 10 | ARRAY("array", "null", null), 11 | BOOLEAN("boolean", "null", Boolean.class), 12 | BASE_INTEGER("integer", "null", Integer.class), 13 | INTEGER("integer", "int32", Integer.class), 14 | LONG("integer", "int64", Long.class), 15 | DECIMAL("number", "null", Double.class), // This also accepts custom formats 16 | DOUBLE("number", "double", Double.class), 17 | FLOAT("number", "float", Float.class), 18 | BINARY("string", "binary", String.class), 19 | BYTE_ARRAY("string", "byte", String.class), 20 | DATE("string", "date", String.class), 21 | DATETIME("string", "date-time", String.class), 22 | EMAIL("string", "email", String.class), 23 | PASSWORD("string", "password", String.class), 24 | UUID("string", "uuid", String.class), 25 | STRING("string", "null", String.class), 26 | FILE("file", "null", null), 27 | UNTYPED("null", "null", null); 28 | 29 | private String type; 30 | private String format; 31 | private Class castingClass; 32 | private static final Map, PropertyType> enumValuesMap = Collections.unmodifiableMap(initializeMapping()); 33 | 34 | PropertyType(String type, String format, Class clazz) { 35 | this.type = type; 36 | this.format = format; 37 | this.castingClass = clazz; 38 | } 39 | 40 | public static PropertyType getValueByTypeAndFormat(String type, String format) { 41 | if (type == null) { 42 | type = "null"; 43 | } 44 | if (format == null) { 45 | format = "null"; 46 | } 47 | 48 | if ("number".equals(type) && !"double".equals(format) && !"float".equals(format)) { 49 | return DECIMAL; 50 | } 51 | return enumValuesMap.get(new Tuple<>(type, format)); 52 | } 53 | 54 | private static Map, PropertyType> initializeMapping() { 55 | Map, PropertyType> enumValuesMap = new HashMap<>(); 56 | for (PropertyType propertyType : PropertyType.values()) { 57 | enumValuesMap.put(new Tuple<>(propertyType.type, propertyType.format), propertyType); 58 | } 59 | return enumValuesMap; 60 | } 61 | 62 | public Class getCastingClass() { 63 | return castingClass; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/model/StringProperty.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.model; 2 | 3 | import java.util.List; 4 | 5 | public class StringProperty extends EnumerableProperty { 6 | private int minLength; 7 | private Integer maxLength; 8 | private String pattern; 9 | 10 | public StringProperty(Property property, List enumList, 11 | Integer minLength, Integer maxLength, String pattern) { 12 | super(property, enumList); 13 | this.minLength = minLength == null ? 0 : minLength; 14 | this.maxLength = maxLength; 15 | this.pattern = pattern; 16 | } 17 | 18 | public int getMinLength() { 19 | return minLength; 20 | } 21 | 22 | public Integer getMaxLength() { 23 | return maxLength; 24 | } 25 | 26 | public String getPattern() { 27 | return pattern; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return "StringProperty{" 33 | + "minLength=" + minLength 34 | + ", maxLength=" + maxLength 35 | + ", pattern='" + pattern + '\'' 36 | + ", EnumerableProperty= " + super.toString() + '}'; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/ApiDefinitions.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer; 2 | 3 | import java.util.Map; 4 | 5 | public interface ApiDefinitions { 6 | 7 | Map getDefinitions(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/NormalizedApiSpec.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer; 2 | 3 | import java.util.Map; 4 | 5 | public interface NormalizedApiSpec { 6 | 7 | Map getPathHashCodeToNormalizedPathMap(); 8 | 9 | void addNormalizedPath(Integer pathHashCode, NormalizedPath normalizedPath); 10 | 11 | NormalizedPath getNormalizedPath(Integer pathHashCode); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/NormalizedApiSpecImpl.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public abstract class NormalizedApiSpecImpl implements NormalizedApiSpec { 7 | 8 | private Map pathHashCodeToNormalizedPathMap = new HashMap<>(); 9 | 10 | @Override 11 | public Map getPathHashCodeToNormalizedPathMap() { 12 | return pathHashCodeToNormalizedPathMap; 13 | } 14 | 15 | @Override 16 | public void addNormalizedPath(Integer pathHashCode, NormalizedPath normalizedPath) { 17 | pathHashCodeToNormalizedPathMap.put(pathHashCode, normalizedPath); 18 | } 19 | 20 | @Override 21 | public NormalizedPath getNormalizedPath(Integer pathHashCode) { 22 | return pathHashCodeToNormalizedPathMap.get(pathHashCode); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/NormalizedEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer; 2 | 3 | import java.util.Map; 4 | 5 | public interface NormalizedEndpoint { 6 | 7 | String getName(); 8 | 9 | Map getNormalizedParametersMap(); 10 | 11 | void addNormalizedParameter(Integer parameterHashCode, NormalizedParameter normalizedParameter); 12 | 13 | NormalizedParameter getNormalizedParameter(Integer parameterHashCode); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/NormalizedEndpointImpl.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public abstract class NormalizedEndpointImpl implements NormalizedEndpoint { 7 | 8 | private final String name; 9 | private Map parameterHashCodeToNormalizedParameterMap = new HashMap<>(); 10 | 11 | public NormalizedEndpointImpl(String name) { 12 | this.name = name; 13 | } 14 | 15 | @Override 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | @Override 21 | public Map getNormalizedParametersMap() { 22 | return parameterHashCodeToNormalizedParameterMap; 23 | } 24 | 25 | @Override 26 | public void addNormalizedParameter(Integer parameterHashCode, NormalizedParameter normalizedParameter) { 27 | parameterHashCodeToNormalizedParameterMap.put(parameterHashCode, normalizedParameter); 28 | } 29 | 30 | @Override 31 | public NormalizedParameter getNormalizedParameter(Integer parameterHashCode) { 32 | if (parameterHashCodeToNormalizedParameterMap == null) { 33 | return null; 34 | } 35 | 36 | return parameterHashCodeToNormalizedParameterMap.get(parameterHashCode); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/NormalizedParameter.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer; 2 | 3 | public interface NormalizedParameter { 4 | 5 | ApiDefinitions getApiDefinitions(); 6 | 7 | int getTotalPropertiesCount(); 8 | 9 | T getParameter(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/NormalizedPath.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer; 2 | 3 | import java.util.Map; 4 | 5 | public interface NormalizedPath { 6 | 7 | Map getNormalizedEndpointsMap(); 8 | 9 | void addNormalizedEndpoint(Integer endpointHashCode, NormalizedEndpoint normalizedEndpoint); 10 | 11 | NormalizedEndpoint getNormalizedEndpoint(Integer endpointHashCode); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/NormalizedPathImpl.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public abstract class NormalizedPathImpl implements NormalizedPath { 7 | 8 | private Map endpointHashCodeToNormalizedParameterMap = new HashMap<>(); 9 | 10 | @Override 11 | public Map getNormalizedEndpointsMap() { 12 | return endpointHashCodeToNormalizedParameterMap; 13 | } 14 | 15 | @Override 16 | public void addNormalizedEndpoint(Integer endpointHashCode, NormalizedEndpoint normalizedEndpoint) { 17 | endpointHashCodeToNormalizedParameterMap.put(endpointHashCode, normalizedEndpoint); 18 | } 19 | 20 | @Override 21 | public NormalizedEndpoint getNormalizedEndpoint(Integer endpointHashCode) { 22 | return endpointHashCodeToNormalizedParameterMap == null ? null : endpointHashCodeToNormalizedParameterMap.get(endpointHashCode); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/swagger/SwaggerDefinitions.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer.swagger; 2 | 3 | import com.imperva.apispecparser.normalizer.ApiDefinitions; 4 | import io.swagger.models.Model; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class SwaggerDefinitions implements ApiDefinitions { 10 | 11 | private Map definitions; 12 | 13 | public SwaggerDefinitions(Map definitions) { 14 | this.definitions = definitions; 15 | } 16 | 17 | @Override 18 | public Map getDefinitions() { 19 | return definitions; 20 | } 21 | 22 | public void addDefinition(String modelName, Model model) { 23 | if (definitions == null) { 24 | definitions = new HashMap<>(); 25 | } 26 | 27 | definitions.put(getSimpleModelName(modelName), model); 28 | } 29 | 30 | public void removeDefinition(String modelName) { 31 | if (definitions != null) { 32 | definitions.remove(modelName); 33 | } 34 | } 35 | 36 | public Model getDefinition(String modelName) { 37 | if (definitions == null || modelName == null) { 38 | return null; 39 | } 40 | 41 | return definitions.get(getSimpleModelName(modelName)); 42 | } 43 | 44 | public boolean containsDefinition(String modelName) { 45 | return definitions != null && definitions.get(modelName) != null; 46 | } 47 | 48 | private String getSimpleModelName(String modelName) { 49 | return modelName == null ? null : modelName.substring(modelName.lastIndexOf("/") + 1); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/swagger/SwaggerDefinitionsNormalizer.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer.swagger; 2 | 3 | import io.swagger.models.Model; 4 | import io.swagger.models.ModelImpl; 5 | import io.swagger.models.RefModel; 6 | import io.swagger.models.parameters.BodyParameter; 7 | import io.swagger.models.parameters.Parameter; 8 | import io.swagger.models.properties.ArrayProperty; 9 | import io.swagger.models.properties.Property; 10 | import io.swagger.models.properties.RefProperty; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.util.HashSet; 15 | import java.util.Map; 16 | import java.util.Set; 17 | 18 | public class SwaggerDefinitionsNormalizer { 19 | 20 | private static Logger logger = LoggerFactory.getLogger(SwaggerDefinitionsNormalizer.class); 21 | 22 | public static SwaggerNormalizedParameter normalizeParameterDefinitions(Parameter parameter, 23 | Map swaggerFullDefinitions) { 24 | SwaggerNormalizedParameter swaggerDefinitionNormalizerResult; 25 | 26 | if (parameter instanceof BodyParameter) { 27 | BodyParameter bodyParameter = (BodyParameter) parameter; 28 | Model model = bodyParameter.getSchema(); 29 | swaggerDefinitionNormalizerResult = SwaggerDefinitionsNormalizer.normalizeModelDefinitions(null, model, null, "", 30 | swaggerFullDefinitions, new HashSet<>(), new SwaggerNormalizedParameter<>(parameter)); 31 | } else { 32 | swaggerDefinitionNormalizerResult = new SwaggerNormalizedParameter<>(parameter, swaggerFullDefinitions, 1); 33 | } 34 | 35 | return swaggerDefinitionNormalizerResult; 36 | } 37 | 38 | private static SwaggerNormalizedParameter normalizeModelDefinitions(String currentModelName, Model currentModel, Model parentModel, String ref, 39 | Map swaggerFullDefinitions, Set scannedModels, 40 | SwaggerNormalizedParameter swaggerDefinitionNormalizerResult) { 41 | 42 | Model filteredModel = null; 43 | 44 | if (swaggerDefinitionNormalizerResult.containsDefinition(currentModelName)) { 45 | filteredModel = swaggerDefinitionNormalizerResult.getDefinition(currentModelName); 46 | } else if (currentModel != null) { 47 | filteredModel = (Model) currentModel.clone(); 48 | 49 | if (currentModelName != null) { 50 | swaggerDefinitionNormalizerResult.addDefinition(currentModelName, filteredModel); 51 | } 52 | } 53 | 54 | if (filteredModel instanceof RefModel) { 55 | RefModel refModel = (RefModel) currentModel; 56 | String definitionName = refModel.getSimpleRef(); 57 | Model definitionModel = swaggerFullDefinitions.get(definitionName); 58 | Model newParentModel = parentModel == null ? null : currentModel; //parentModel will be null only on first iteration 59 | 60 | //Remove Ref model (definition) that point to itself 61 | if (definitionModel == currentModel) { 62 | swaggerDefinitionNormalizerResult.removeDefinition(currentModelName); 63 | return swaggerDefinitionNormalizerResult; 64 | } 65 | 66 | normalizeModelDefinitions(definitionName, definitionModel, newParentModel, ref + "/" + definitionName, swaggerFullDefinitions, 67 | scannedModels, swaggerDefinitionNormalizerResult); 68 | } else if (filteredModel instanceof ModelImpl) { 69 | ModelImpl currentModelImpl = (ModelImpl) currentModel; 70 | ModelImpl filteredModelImpl = (ModelImpl) filteredModel; 71 | 72 | //Workaround to bug in swagger model clone which ignore some fields 73 | filteredModelImpl._enum(currentModelImpl.getEnum()); 74 | filteredModelImpl.format(currentModelImpl.getFormat()); 75 | filteredModelImpl.allowEmptyValue(currentModelImpl.getAllowEmptyValue()); 76 | filteredModelImpl.uniqueItems(currentModelImpl.getUniqueItems()); 77 | 78 | 79 | Map propertyMap = currentModelImpl.getProperties(); 80 | 81 | if (propertyMap != null) { 82 | for (String propertyName : currentModelImpl.getProperties().keySet()) { 83 | Property property = currentModelImpl.getProperties().get(propertyName); 84 | 85 | Set propertyScannedModels; 86 | 87 | //If parent model is null this means we're dealing with a new property (not part of the recursion) 88 | //In this case, the 'scannedModels' should be initiated (every single property should calculate loop in its own tree) 89 | if (parentModel == null) { 90 | propertyScannedModels = new HashSet<>(); 91 | } else { 92 | propertyScannedModels = scannedModels; 93 | } 94 | 95 | propertyScannedModels.add(currentModelName); 96 | 97 | filterProperty(currentModelName, filteredModel, property, propertyName, ref, swaggerFullDefinitions, propertyScannedModels, 98 | swaggerDefinitionNormalizerResult); 99 | } 100 | } 101 | } 102 | 103 | return swaggerDefinitionNormalizerResult; 104 | } 105 | 106 | private static void filterProperty(String parentModelName, Model parentFilteredModel, Property property, String propertyName, String ref, 107 | Map swaggerFullDefinitions, Set scannedModels, 108 | SwaggerNormalizedParameter swaggerDefinitionNormalizerResult) { 109 | 110 | if (property instanceof RefProperty) { 111 | RefProperty refProperty = (RefProperty) property; 112 | String refPropertyName = refProperty.getSimpleRef(); 113 | Model definitionModel = swaggerFullDefinitions.get(refPropertyName); 114 | 115 | String propertyRef = ref + " -> " + refPropertyName + " (" + propertyName + ")"; 116 | 117 | if (scannedModels.contains(refPropertyName)) { 118 | if (parentFilteredModel.getProperties().containsKey(propertyName)) { 119 | logger.info("Removing circular property [{}] from Model [{}], Ref: {}", propertyName, parentModelName, propertyRef); 120 | parentFilteredModel.getProperties().remove(propertyName); 121 | } 122 | } else { 123 | normalizeModelDefinitions(refPropertyName, definitionModel, parentFilteredModel, propertyRef, swaggerFullDefinitions, scannedModels, 124 | swaggerDefinitionNormalizerResult); 125 | } 126 | 127 | } else if (property instanceof ArrayProperty) { 128 | ArrayProperty arrayProperty = (ArrayProperty) property; 129 | filterProperty(parentModelName, parentFilteredModel, arrayProperty.getItems(), propertyName, ref, swaggerFullDefinitions, scannedModels, 130 | swaggerDefinitionNormalizerResult); 131 | } else { 132 | swaggerDefinitionNormalizerResult.incrementTotalPropertiesCount(); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/swagger/SwaggerNormalizedApiSpec.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer.swagger; 2 | 3 | import com.imperva.apispecparser.normalizer.NormalizedApiSpecImpl; 4 | import com.imperva.apispecparser.normalizer.NormalizedPath; 5 | import io.swagger.models.Path; 6 | 7 | public class SwaggerNormalizedApiSpec extends NormalizedApiSpecImpl { 8 | 9 | public void addNormalizedPath(Path path, SwaggerNormalizedPath swaggerNormalizedPath) { 10 | if (path != null) { 11 | addNormalizedPath(path.hashCode(), swaggerNormalizedPath); 12 | } 13 | } 14 | 15 | public SwaggerNormalizedPath getNormalizedPath(Path path) { 16 | if (path != null) { 17 | return getNormalizedPath(path.hashCode()); 18 | } 19 | 20 | return null; 21 | } 22 | 23 | @Override 24 | public SwaggerNormalizedPath getNormalizedPath(Integer pathHashCode) { 25 | NormalizedPath normalizedPath = super.getNormalizedPath(pathHashCode); 26 | return normalizedPath == null ? null : (SwaggerNormalizedPath) normalizedPath; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/swagger/SwaggerNormalizedEndpoint.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer.swagger; 2 | 3 | import com.imperva.apispecparser.normalizer.NormalizedEndpointImpl; 4 | import com.imperva.apispecparser.normalizer.NormalizedParameter; 5 | import io.swagger.models.Operation; 6 | import io.swagger.models.Path; 7 | import io.swagger.models.parameters.Parameter; 8 | 9 | public class SwaggerNormalizedEndpoint extends NormalizedEndpointImpl { 10 | 11 | private final Path path; 12 | private final Operation operation; 13 | 14 | public SwaggerNormalizedEndpoint(String endpointName, Path path, Operation operation) { 15 | super(endpointName); 16 | this.path = path; 17 | this.operation = operation; 18 | } 19 | 20 | public void addNormalizedParameter(Parameter parameter, SwaggerNormalizedParameter normalizedParameter) { 21 | if (path != null && operation != null && parameter != null) { 22 | addNormalizedParameter(parameter.hashCode(), normalizedParameter); 23 | } 24 | } 25 | 26 | public SwaggerNormalizedParameter getNormalizedParameter(Parameter parameter) { 27 | if (parameter == null) { 28 | return null; 29 | } 30 | 31 | NormalizedParameter normalizedParameter = super.getNormalizedParameter(parameter.hashCode()); 32 | return normalizedParameter == null ? null : (SwaggerNormalizedParameter) normalizedParameter; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/swagger/SwaggerNormalizedParameter.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer.swagger; 2 | 3 | import com.imperva.apispecparser.normalizer.NormalizedParameter; 4 | import io.swagger.models.Model; 5 | import io.swagger.models.parameters.Parameter; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class SwaggerNormalizedParameter implements NormalizedParameter { 11 | 12 | private final T parameter; 13 | 14 | private SwaggerDefinitions swaggerDefinitions; 15 | 16 | private int totalPropertiesCount; 17 | 18 | public SwaggerNormalizedParameter(T parameter) { 19 | this(parameter, new HashMap<>(), 0); 20 | } 21 | 22 | public SwaggerNormalizedParameter(T parameter, Map normalizedDefinitions, int totalPropertiesCount) { 23 | this.parameter = parameter; 24 | this.swaggerDefinitions = new SwaggerDefinitions(normalizedDefinitions); 25 | this.totalPropertiesCount = totalPropertiesCount; 26 | } 27 | 28 | void addDefinition(String modelName, Model model) { 29 | swaggerDefinitions.addDefinition(modelName, model); 30 | } 31 | 32 | void removeDefinition(String modelName) { 33 | swaggerDefinitions.removeDefinition(modelName); 34 | } 35 | 36 | Model getDefinition(String modelName) { 37 | return swaggerDefinitions.getDefinition(modelName); 38 | } 39 | 40 | boolean containsDefinition(String modelName) { 41 | return swaggerDefinitions.containsDefinition(modelName); 42 | } 43 | 44 | void incrementTotalPropertiesCount() { 45 | totalPropertiesCount += 1; 46 | } 47 | 48 | @Override 49 | public SwaggerDefinitions getApiDefinitions() { 50 | return swaggerDefinitions; 51 | } 52 | 53 | public int getTotalPropertiesCount() { 54 | return totalPropertiesCount; 55 | } 56 | 57 | public T getParameter() { 58 | return parameter; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/normalizer/swagger/SwaggerNormalizedPath.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.normalizer.swagger; 2 | 3 | import com.imperva.apispecparser.normalizer.NormalizedEndpoint; 4 | import com.imperva.apispecparser.normalizer.NormalizedPathImpl; 5 | import io.swagger.models.Operation; 6 | import io.swagger.models.Path; 7 | 8 | public class SwaggerNormalizedPath extends NormalizedPathImpl { 9 | 10 | private final Path path; 11 | 12 | public SwaggerNormalizedPath(Path path) { 13 | this.path = path; 14 | } 15 | 16 | public void addNormalizedEndpoint(Operation operation, SwaggerNormalizedEndpoint swaggerNormalizedEndpoint) { 17 | if (path != null && operation != null) { 18 | addNormalizedEndpoint(operation.hashCode(), swaggerNormalizedEndpoint); 19 | } 20 | } 21 | 22 | public SwaggerNormalizedEndpoint getNormalizedEndpoint(Operation operation) { 23 | if (operation == null) { 24 | return null; 25 | } 26 | 27 | NormalizedEndpoint normalizedEndpoint = super.getNormalizedEndpoint((operation.hashCode())); 28 | return normalizedEndpoint == null ? null : (SwaggerNormalizedEndpoint) normalizedEndpoint; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/ApiSpecFileLocation.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers; 2 | 3 | public enum ApiSpecFileLocation { 4 | RESOURCE, 5 | EXTERNAL 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/ApiSpecParser.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers; 2 | 3 | import com.imperva.apispecparser.exceptions.ParseException; 4 | import com.imperva.apispecparser.model.EndpointModel; 5 | 6 | import java.util.List; 7 | 8 | public interface ApiSpecParser { 9 | 10 | List getEndpointModelList(String filePath, ApiSpecFileLocation apiSpecFileLocation) throws ParseException; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/Swagger2Parser.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger; 2 | 3 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 4 | import com.imperva.apispecparser.exceptions.ParseException; 5 | import com.imperva.apispecparser.model.EndpointModel; 6 | import com.imperva.apispecparser.model.Property; 7 | import com.imperva.apispecparser.normalizer.swagger.SwaggerDefinitionsNormalizer; 8 | import com.imperva.apispecparser.normalizer.swagger.SwaggerNormalizedParameter; 9 | import com.imperva.apispecparser.parsers.ApiSpecFileLocation; 10 | import com.imperva.apispecparser.parsers.ApiSpecParser; 11 | import com.imperva.apispecparser.parsers.swagger.property.SimpleSwaggerPropertyFactory; 12 | import com.imperva.apispecparser.parsers.swagger.property.SwaggerAuthenticationToPropertyFactory; 13 | import com.imperva.apispecparser.parsers.swagger.propertynode.SwaggerModelToPropertyNodeFactory; 14 | import com.imperva.apispecparser.parsers.swagger.propertynode.SwaggerPropertyNodeConverter; 15 | import com.imperva.apispecparser.utils.FileUtils; 16 | import io.swagger.models.HttpMethod; 17 | import io.swagger.models.Model; 18 | import io.swagger.models.Operation; 19 | import io.swagger.models.Path; 20 | import io.swagger.models.Response; 21 | import io.swagger.models.SecurityRequirement; 22 | import io.swagger.models.Swagger; 23 | import io.swagger.models.auth.SecuritySchemeDefinition; 24 | import io.swagger.models.parameters.AbstractSerializableParameter; 25 | import io.swagger.models.parameters.BodyParameter; 26 | import io.swagger.models.parameters.Parameter; 27 | import io.swagger.parser.SwaggerParser; 28 | import org.apache.commons.lang3.StringUtils; 29 | import org.slf4j.Logger; 30 | import org.slf4j.LoggerFactory; 31 | 32 | import java.util.ArrayList; 33 | import java.util.List; 34 | import java.util.Map; 35 | 36 | public class Swagger2Parser implements ApiSpecParser { 37 | 38 | private static final Logger logger = LoggerFactory.getLogger(Swagger2Parser.class); 39 | 40 | @Override 41 | public List getEndpointModelList(String filePath, ApiSpecFileLocation apiSpecFileLocation) throws ParseException { 42 | List endpointModelList = new ArrayList<>(); 43 | 44 | String fileContent = FileUtils.readFile(filePath, apiSpecFileLocation); 45 | 46 | if (fileContent == null) { 47 | return endpointModelList; 48 | } 49 | 50 | Swagger swagger = new SwaggerParser().parse(fileContent); 51 | 52 | for (Map.Entry pathEntry : swagger.getPaths().entrySet()) { 53 | for (Map.Entry httpMethodEntry : pathEntry.getValue().getOperationMap().entrySet()) { 54 | 55 | String fullPathWithParamBrackets = getNormalizedFullPath(swagger.getBasePath(), pathEntry.getKey()); 56 | 57 | EndpointModel endpointModel = new EndpointModel(fullPathWithParamBrackets, 58 | com.imperva.apiattacktool.model.tests.HttpMethod.valueOf(httpMethodEntry.getKey().toString()), 59 | httpMethodEntry.getValue().getConsumes()); 60 | 61 | for (Parameter parameter : httpMethodEntry.getValue().getParameters()) { 62 | Map normalizedDefinitions; 63 | 64 | if (parameter instanceof BodyParameter) { 65 | BodyParameter bodyParameter = (BodyParameter) parameter; 66 | SwaggerNormalizedParameter swaggerDefinitionNormalizerResult = 67 | SwaggerDefinitionsNormalizer.normalizeParameterDefinitions(parameter, swagger.getDefinitions()); 68 | normalizedDefinitions = swaggerDefinitionNormalizerResult.getApiDefinitions().getDefinitions(); 69 | SwaggerPropertyNodeConverter swaggerPropertyNodeConverter = SwaggerModelToPropertyNodeFactory.get(bodyParameter.getSchema(), 70 | bodyParameter.getRequired(), "", ParameterLocation.BODY, normalizedDefinitions); 71 | 72 | if (swaggerPropertyNodeConverter != null) { 73 | endpointModel.addNode(parameter.getName(), swaggerPropertyNodeConverter.getPropertyNode()); 74 | } 75 | } else if (parameter instanceof AbstractSerializableParameter) { 76 | AbstractSerializableParameter abstractSerializableParameter = (AbstractSerializableParameter) parameter; 77 | Property property = SimpleSwaggerPropertyFactory.getPropertyFromAbstractSerializableParameter(abstractSerializableParameter, 78 | parameter.getName(), swagger.getDefinitions()); 79 | endpointModel.addParameter(parameter.getName(), property); 80 | } 81 | } 82 | 83 | addResponseCodesToEndpointModel(endpointModel, httpMethodEntry.getValue().getResponses()); 84 | 85 | addSecurityConfiguration(endpointModel, swagger.getSecurityDefinitions(), swagger.getSecurity(), httpMethodEntry.getValue().getSecurity()); 86 | 87 | endpointModelList.add(endpointModel); 88 | } 89 | } 90 | 91 | return endpointModelList; 92 | } 93 | 94 | private static void addSecurityConfiguration(EndpointModel endpointModel, Map securityDefinitions, 95 | List apiLevelSecurity, 96 | List>> endpointLevelSecurityConfiguration) { 97 | 98 | List>> securityRequirementsList = new ArrayList<>(); 99 | 100 | if (endpointLevelSecurityConfiguration != null) { 101 | securityRequirementsList = endpointLevelSecurityConfiguration; 102 | 103 | } else if (apiLevelSecurity != null && apiLevelSecurity.size() > 0) { 104 | apiLevelSecurity.stream().map(SecurityRequirement::getRequirements).forEach(securityRequirementsList::add); 105 | } 106 | 107 | securityRequirementsList.forEach(securityRequirementMap -> { 108 | securityRequirementMap.forEach((key, value) -> { 109 | SecuritySchemeDefinition securitySchemeDefinition = securityDefinitions.get(key); 110 | Property property = SwaggerAuthenticationToPropertyFactory.get(securitySchemeDefinition); 111 | 112 | if (property != null) { 113 | endpointModel.addParameter(property.getName(), property); 114 | } 115 | }); 116 | }); 117 | } 118 | 119 | private static void addResponseCodesToEndpointModel(EndpointModel endpointModel, Map responseStringToDefinitionMap) { 120 | responseStringToDefinitionMap.keySet().stream().forEach(httpResponseString -> { 121 | try { 122 | int httpReponseCode = Integer.parseInt(httpResponseString); 123 | endpointModel.addResponseCode(httpReponseCode); 124 | } catch (NumberFormatException badNumberException) { 125 | if (httpResponseString.equals("default")) { 126 | endpointModel.addResponseCode(0); 127 | } else { 128 | logger.error("Endpoint definition for: {} ({}), contained unparsable response code: {}. Skipping", 129 | endpointModel.getFullPathWithParamBrackets(), endpointModel.getHttpMethod(), httpResponseString); 130 | } 131 | } 132 | }); 133 | } 134 | 135 | public static String getNormalizedFullPath(String basePath, String path) { 136 | basePath = basePath == null ? "" : StringUtils.removeEnd(basePath.trim(), "/"); 137 | path = path == null ? "" : StringUtils.removeEnd(StringUtils.removeStart(path.trim(), "/"), "/"); 138 | return String.format("%s/%s", basePath, path); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/property/ApiKeyAuthenticationParamType.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger.property; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | public enum ApiKeyAuthenticationParamType { 7 | HEADER, 8 | QUERY; 9 | 10 | private static Map names = new LinkedHashMap<>(); 11 | 12 | public static ApiKeyAuthenticationParamType forValue(String value) { 13 | if (value == null) { 14 | return null; 15 | } 16 | 17 | return names.get(value.toLowerCase()); 18 | } 19 | 20 | static { 21 | names.put("header", HEADER); 22 | names.put("query", QUERY); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/property/ApiKeyAuthenticationProperties.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger.property; 2 | 3 | public class ApiKeyAuthenticationProperties implements AuthenticationProperties { 4 | 5 | private final String name; 6 | 7 | private final ApiKeyAuthenticationParamType in; 8 | 9 | public ApiKeyAuthenticationProperties(String name, ApiKeyAuthenticationParamType in) { 10 | this.name = name; 11 | this.in = in; 12 | } 13 | 14 | @Override 15 | public String getName() { 16 | return name; 17 | } 18 | 19 | @Override 20 | public ApiKeyAuthenticationParamType getIn() { 21 | return in; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/property/AuthenticationProperties.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger.property; 2 | 3 | public interface AuthenticationProperties { 4 | 5 | String getName(); 6 | 7 | ApiKeyAuthenticationParamType getIn(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/property/SimpleSwaggerPropertyFactory.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger.property; 2 | 3 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 4 | import com.imperva.apispecparser.model.ArrayProperty; 5 | import com.imperva.apispecparser.model.BooleanProperty; 6 | import com.imperva.apispecparser.model.NumericProperty; 7 | import com.imperva.apispecparser.model.Property; 8 | import com.imperva.apispecparser.model.PropertyNode; 9 | import com.imperva.apispecparser.model.StringProperty; 10 | import com.imperva.apispecparser.parsers.swagger.propertynode.SwaggerArrayItemsToPropertyNode; 11 | import com.imperva.apispecparser.parsers.swagger.propertynode.SwaggerPropertyNodeConverter; 12 | import io.swagger.models.Model; 13 | import io.swagger.models.parameters.AbstractSerializableParameter; 14 | 15 | import java.util.Collections; 16 | import java.util.List; 17 | import java.util.Map; 18 | import java.util.stream.Collectors; 19 | 20 | public class SimpleSwaggerPropertyFactory { 21 | 22 | public static Property getPropertyFromAbstractSerializableParameter(AbstractSerializableParameter abstractSerializableParameter, String parameterName, 23 | Map definitions) { 24 | SwaggerParameterProperties swaggerParameterProperties = new SwaggerParameterProperties(abstractSerializableParameter, parameterName); 25 | return getPropertyFromSwaggerParameterProperties(swaggerParameterProperties, definitions); 26 | } 27 | 28 | public static Property getPropertyFromSwaggerProperty(io.swagger.models.properties.Property swaggerProperty, String propertyName, 29 | ParameterLocation parameterLocation, boolean isRequired, Map definitions) { 30 | SwaggerParameterProperties swaggerParameterProperties = new SwaggerParameterProperties(swaggerProperty, propertyName, parameterLocation, isRequired); 31 | return getPropertyFromSwaggerParameterProperties(swaggerParameterProperties, definitions); 32 | } 33 | 34 | private static Property getPropertyFromSwaggerParameterProperties(SwaggerParameterProperties swaggerParameterProperties, Map definitions) { 35 | Property property = swaggerParameterProperties.getProperty(); 36 | 37 | switch (property.getType()) { 38 | case ARRAY: 39 | // The propertyNode is created here, as it may come from both a parameter and a property. 40 | // We need to traverse the tree in both cases (and current logic doesn't support traversal for parameters) 41 | SwaggerPropertyNodeConverter swaggerPropertyNodeConverter = new SwaggerArrayItemsToPropertyNode(swaggerParameterProperties.getItems(), 42 | property.isRequired(), property.getName(), property.getParameterLocation(), definitions); 43 | PropertyNode propertyNode = swaggerPropertyNodeConverter.getPropertyNode(); 44 | return new ArrayProperty(property, swaggerParameterProperties.getUniqueItems(), propertyNode, 45 | swaggerParameterProperties.getMaxItems(), swaggerParameterProperties.getMinItems(), swaggerParameterProperties.getCollectionFormat()); 46 | case FLOAT: 47 | return new NumericProperty<>(property, getEnumAsType(Float.class, swaggerParameterProperties.getEnumList()), 48 | swaggerParameterProperties.getMinimum(), swaggerParameterProperties.getMaximum(), swaggerParameterProperties.getMultipleOf(), 49 | swaggerParameterProperties.getExclusiveMinimum(), swaggerParameterProperties.getExclusiveMaximum()); 50 | case LONG: 51 | return new NumericProperty<>(property, getEnumAsType(Long.class, swaggerParameterProperties.getEnumList()), 52 | swaggerParameterProperties.getMinimum(), swaggerParameterProperties.getMaximum(), swaggerParameterProperties.getMultipleOf(), 53 | swaggerParameterProperties.getExclusiveMinimum(), swaggerParameterProperties.getExclusiveMaximum()); 54 | case DECIMAL: 55 | case DOUBLE: 56 | return new NumericProperty<>(property, getEnumAsType(Double.class, swaggerParameterProperties.getEnumList()), 57 | swaggerParameterProperties.getMinimum(), swaggerParameterProperties.getMaximum(), swaggerParameterProperties.getMultipleOf(), 58 | swaggerParameterProperties.getExclusiveMinimum(), swaggerParameterProperties.getExclusiveMaximum()); 59 | case BASE_INTEGER: 60 | case INTEGER: 61 | return new NumericProperty<>(property, getEnumAsType(Integer.class, swaggerParameterProperties.getEnumList()), 62 | swaggerParameterProperties.getMinimum(), swaggerParameterProperties.getMaximum(), swaggerParameterProperties.getMultipleOf(), 63 | swaggerParameterProperties.getExclusiveMinimum(), swaggerParameterProperties.getExclusiveMaximum()); 64 | case UUID: 65 | case PASSWORD: 66 | case EMAIL: 67 | case DATETIME: 68 | case DATE: 69 | // As awkward this may sound. We should convert the (enum values) to Date using a dedicated active converter, instead of this circus 70 | case BINARY: 71 | case STRING: 72 | return new StringProperty(property, getEnumAsType(String.class, swaggerParameterProperties.getEnumList()), 73 | swaggerParameterProperties.getMinLength(), swaggerParameterProperties.getMaxLength(), swaggerParameterProperties.getPattern()); 74 | case BYTE_ARRAY: 75 | return new StringProperty(property, null, 76 | swaggerParameterProperties.getMinLength(), swaggerParameterProperties.getMaxLength(), swaggerParameterProperties.getPattern()); 77 | case BOOLEAN: 78 | return new BooleanProperty(property, getEnumAsType(Boolean.class, swaggerParameterProperties.getEnumList())); 79 | case UNTYPED: 80 | case FILE: 81 | default: 82 | return new Property(property); 83 | } 84 | } 85 | 86 | private static List getEnumAsType(Class clazz, List enumList) { 87 | if (enumList != null) { 88 | return enumList.stream().map(clazz::cast).collect(Collectors.toList()); 89 | } else { 90 | return Collections.emptyList(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/property/SwaggerAuthenticationToPropertyFactory.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger.property; 2 | 3 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 4 | import com.imperva.apispecparser.model.Property; 5 | import com.imperva.apispecparser.model.PropertyType; 6 | import com.imperva.apispecparser.model.StringProperty; 7 | import io.swagger.models.auth.ApiKeyAuthDefinition; 8 | import io.swagger.models.auth.BasicAuthDefinition; 9 | import io.swagger.models.auth.In; 10 | import io.swagger.models.auth.SecuritySchemeDefinition; 11 | 12 | public class SwaggerAuthenticationToPropertyFactory { 13 | 14 | public static Property get(SecuritySchemeDefinition securitySchemeDefinition) { 15 | Property property = null; 16 | 17 | if (securitySchemeDefinition instanceof BasicAuthDefinition) { 18 | property = new Property(ParameterLocation.HEADER, PropertyType.STRING, "Authorization", true, false, false); 19 | } else if (securitySchemeDefinition instanceof ApiKeyAuthDefinition) { 20 | ApiKeyAuthDefinition apiKeyAuthDefinition = (ApiKeyAuthDefinition) securitySchemeDefinition; 21 | 22 | In in = apiKeyAuthDefinition.getIn(); 23 | AuthenticationProperties authenticationProperties = null; 24 | 25 | if (in != null && in.toValue() != null) { 26 | authenticationProperties = new ApiKeyAuthenticationProperties(apiKeyAuthDefinition.getName(), 27 | ApiKeyAuthenticationParamType.forValue(in.toValue())); 28 | } 29 | 30 | if (in != null && authenticationProperties != null) { 31 | switch (in) { 32 | case QUERY: 33 | property = new Property(ParameterLocation.QUERY, PropertyType.STRING, authenticationProperties.getName(), true, false, false); 34 | break; 35 | case HEADER: 36 | property = new Property(ParameterLocation.HEADER, PropertyType.STRING, authenticationProperties.getName(), true, false, false); 37 | break; 38 | default: 39 | property = null; 40 | break; 41 | } 42 | } 43 | } 44 | 45 | return property == null ? null : new StringProperty(property, null, null, null, null); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/property/SwaggerPropertyFactory.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger.property; 2 | 3 | import com.imperva.apispecparser.model.Property; 4 | import io.swagger.models.parameters.AbstractSerializableParameter; 5 | 6 | public interface SwaggerPropertyFactory { 7 | Property getPropertyFromAbstractSerializableParameter(AbstractSerializableParameter abstractSerializableParameter, String parameterName); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/propertynode/SwaggerArrayItemsToPropertyNode.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger.propertynode; 2 | 3 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 4 | import com.imperva.apispecparser.model.PropertyNode; 5 | import com.imperva.apispecparser.parsers.swagger.property.SimpleSwaggerPropertyFactory; 6 | import io.swagger.models.Model; 7 | import io.swagger.models.properties.ObjectProperty; 8 | import io.swagger.models.properties.Property; 9 | import io.swagger.models.properties.RefProperty; 10 | 11 | import java.util.Map; 12 | 13 | public class SwaggerArrayItemsToPropertyNode implements SwaggerPropertyNodeConverter { 14 | 15 | private Property property; 16 | private boolean isRequired; 17 | private final String parentName; 18 | private ParameterLocation parameterLocation; 19 | private Map definitions; 20 | 21 | 22 | public SwaggerArrayItemsToPropertyNode(Property property, boolean isRequired, String arrayPropertyName, ParameterLocation parameterLocation, 23 | Map definitions) { 24 | this.property = property; 25 | this.isRequired = isRequired; 26 | this.parentName = arrayPropertyName; 27 | this.parameterLocation = parameterLocation; 28 | this.definitions = definitions; 29 | } 30 | 31 | @Override 32 | public PropertyNode getPropertyNode() { 33 | if (property instanceof RefProperty) { 34 | RefProperty refProperty = (RefProperty) property; 35 | String ref = refProperty.getSimpleRef(); 36 | Model model = definitions.get(ref); 37 | SwaggerPropertyNodeConverter swaggerPropertyNodeConverter = 38 | SwaggerModelToPropertyNodeFactory.get(model, property.getRequired(), parentName, parameterLocation, definitions); 39 | if (swaggerPropertyNodeConverter == null) { 40 | return null; 41 | } 42 | return swaggerPropertyNodeConverter.getPropertyNode(); 43 | } else if (property instanceof ObjectProperty) { 44 | ObjectProperty objectProperty = (ObjectProperty) property; 45 | PropertyNode propertyNode = new PropertyNode(parentName, isRequired, parameterLocation); 46 | if (objectProperty.getProperties() != null) { 47 | objectProperty.getProperties().entrySet().forEach(propertyNameToProperty -> { 48 | propertyNode.addPropertyValueType(propertyNameToProperty.getKey(), 49 | SimpleSwaggerPropertyFactory.getPropertyFromSwaggerProperty(propertyNameToProperty.getValue(), parentName, parameterLocation, 50 | isRequired, definitions)); 51 | }); 52 | } 53 | } else { 54 | PropertyNode propertyNode = new PropertyNode(parentName, isRequired, parameterLocation); 55 | propertyNode.addPropertyValueType(parentName, SimpleSwaggerPropertyFactory.getPropertyFromSwaggerProperty(property, parentName, parameterLocation, 56 | isRequired, definitions)); 57 | return propertyNode; 58 | } 59 | 60 | return null; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/propertynode/SwaggerModelImplToPropertyNode.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger.propertynode; 2 | 3 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 4 | import com.imperva.apispecparser.model.PropertyNode; 5 | import com.imperva.apispecparser.parsers.swagger.property.SimpleSwaggerPropertyFactory; 6 | import io.swagger.models.Model; 7 | import io.swagger.models.ModelImpl; 8 | import io.swagger.models.properties.Property; 9 | import io.swagger.models.properties.RefProperty; 10 | 11 | import java.util.Map; 12 | 13 | public class SwaggerModelImplToPropertyNode extends SwaggerModelToPropertyNodeImpl { 14 | 15 | public SwaggerModelImplToPropertyNode(ModelImpl model, boolean isRequired, String parentName, ParameterLocation parameterLocation, 16 | Map definitions) { 17 | super(model, isRequired, parentName, parameterLocation, definitions); 18 | } 19 | 20 | 21 | @Override 22 | public PropertyNode getPropertyNode() { 23 | PropertyNode propertyNode = new PropertyNode(parentName, isRequired, parameterLocation); 24 | 25 | Map propertyMap = model.getProperties(); 26 | 27 | if (propertyMap == null) { 28 | return propertyNode; 29 | } 30 | 31 | for (Map.Entry entry : propertyMap.entrySet()) { 32 | Property property = entry.getValue(); 33 | 34 | if (property instanceof RefProperty) { 35 | RefProperty refProperty = (RefProperty) property; 36 | String ref = refProperty.getSimpleRef(); 37 | Model model = definitions.get(ref); 38 | 39 | SwaggerPropertyNodeConverter swaggerPropertyNodeConverter = SwaggerModelToPropertyNodeFactory.get(model, property.getRequired(), parentName, 40 | parameterLocation, definitions); 41 | 42 | if (swaggerPropertyNodeConverter == null) { 43 | return null; 44 | } 45 | 46 | propertyNode.addPropertyNode(entry.getKey(), swaggerPropertyNodeConverter.getPropertyNode()); 47 | } else { 48 | propertyNode.addPropertyValueType(entry.getKey(), SimpleSwaggerPropertyFactory.getPropertyFromSwaggerProperty(property, entry.getKey(), 49 | parameterLocation, isPropertyRequired(entry.getKey()), definitions)); 50 | } 51 | } 52 | 53 | return propertyNode; 54 | } 55 | 56 | private boolean isPropertyRequired(String propertyName) { 57 | return model.getRequired() != null && model.getRequired().contains(propertyName); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/propertynode/SwaggerModelToPropertyNodeFactory.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger.propertynode; 2 | 3 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 4 | import io.swagger.models.Model; 5 | import io.swagger.models.ModelImpl; 6 | import io.swagger.models.RefModel; 7 | 8 | import java.util.Map; 9 | 10 | public class SwaggerModelToPropertyNodeFactory { 11 | 12 | public static SwaggerPropertyNodeConverter get(Model model, boolean isRequired, String parentName, ParameterLocation parameterLocation, 13 | Map definitions) { 14 | if (model == null) { 15 | return null; 16 | } 17 | 18 | if (model instanceof ModelImpl) { 19 | return new SwaggerModelImplToPropertyNode((ModelImpl) model, isRequired, parentName, parameterLocation, definitions); 20 | } else if (model instanceof RefModel) { 21 | return new SwaggerRefModelToPropertyNode((RefModel) model, isRequired, parentName, parameterLocation, definitions); 22 | } else { 23 | return null; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/propertynode/SwaggerModelToPropertyNodeImpl.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger.propertynode; 2 | 3 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 4 | import io.swagger.models.Model; 5 | 6 | import java.util.Map; 7 | 8 | public abstract class SwaggerModelToPropertyNodeImpl implements SwaggerPropertyNodeConverter { 9 | 10 | final T model; 11 | 12 | boolean isRequired; 13 | 14 | final String parentName; 15 | 16 | ParameterLocation parameterLocation; 17 | 18 | Map definitions; 19 | 20 | public SwaggerModelToPropertyNodeImpl(T model, boolean isRequired, String parentName, ParameterLocation parameterLocation, Map definitions) { 21 | this.model = model; 22 | this.isRequired = isRequired; 23 | this.parentName = parentName; 24 | this.parameterLocation = parameterLocation; 25 | this.definitions = definitions; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/propertynode/SwaggerPropertyNodeConverter.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger.propertynode; 2 | 3 | import com.imperva.apispecparser.model.PropertyNode; 4 | 5 | public interface SwaggerPropertyNodeConverter { 6 | 7 | PropertyNode getPropertyNode(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/parsers/swagger/propertynode/SwaggerRefModelToPropertyNode.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.parsers.swagger.propertynode; 2 | 3 | import com.imperva.apiattacktool.model.tests.ParameterLocation; 4 | import com.imperva.apispecparser.model.PropertyNode; 5 | import io.swagger.models.Model; 6 | import io.swagger.models.RefModel; 7 | 8 | import java.util.Map; 9 | 10 | public class SwaggerRefModelToPropertyNode extends SwaggerModelToPropertyNodeImpl { 11 | 12 | public SwaggerRefModelToPropertyNode(RefModel model, boolean isRequired, String parentName, ParameterLocation parameterLocation, 13 | Map definitions) { 14 | super(model, isRequired, parentName, parameterLocation, definitions); 15 | } 16 | 17 | @Override 18 | public PropertyNode getPropertyNode() { 19 | Model model = definitions.get(this.model.getSimpleRef()); 20 | 21 | //Prevent endless loop when Ref model points to itself 22 | if (model == this.model) { 23 | return null; 24 | } 25 | 26 | SwaggerPropertyNodeConverter swaggerPropertyNodeConverter = SwaggerModelToPropertyNodeFactory.get(model, isRequired, parentName, parameterLocation, 27 | definitions); 28 | return swaggerPropertyNodeConverter == null ? null : swaggerPropertyNodeConverter.getPropertyNode(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/imperva/apispecparser/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.imperva.apispecparser.utils; 2 | 3 | import com.imperva.apiattacktool.utils.TestReporter; 4 | import com.imperva.apispecparser.parsers.ApiSpecFileLocation; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.InputStreamReader; 11 | import java.nio.charset.StandardCharsets; 12 | import java.util.stream.Collectors; 13 | 14 | public class FileUtils { 15 | 16 | public static String readFile(String filePath, ApiSpecFileLocation apiSpecFileLocation) { 17 | if (filePath == null) { 18 | return null; 19 | } 20 | 21 | String fileContent = null; 22 | 23 | try { 24 | fileContent = apiSpecFileLocation == ApiSpecFileLocation.EXTERNAL ? readFile(filePath) : readResource(filePath); 25 | } catch (IOException e) { 26 | TestReporter.log("Failed to read file [" + filePath + "]"); 27 | } 28 | 29 | return fileContent; 30 | } 31 | 32 | public static String readFile(String filePath) throws IOException { 33 | File file = new File(filePath); 34 | return org.apache.commons.io.FileUtils.readFileToString(file, StandardCharsets.UTF_8); 35 | } 36 | 37 | public static String readResource(String resourcePath) { 38 | ClassLoader classLoader = ClassLoader.getSystemClassLoader(); 39 | InputStream is = classLoader.getResourceAsStream(resourcePath); 40 | if (is != null) { 41 | BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 42 | return reader.lines().collect(Collectors.joining(System.lineSeparator())); 43 | } 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UTF-8 6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/resources/runnable.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | MYSELF=`which "$0" 2>/dev/null` 3 | [ $? -gt 0 -a -f "$0" ] && MYSELF="./$0" 4 | java=java 5 | if test -n "$JAVA_HOME"; then 6 | java="$JAVA_HOME/bin/java" 7 | fi 8 | exec "$java" $java_args -jar $MYSELF "$@" 9 | exit 1 --------------------------------------------------------------------------------