├── .gitignore ├── LICENSE ├── README.md ├── bun.lockb ├── data.json ├── docker-compose.yml ├── package-lock.json ├── package.json ├── pocman.ts ├── tests ├── test-1 │ ├── v1 │ │ ├── Dockerfile │ │ ├── index.php │ │ └── readme.txt │ └── v2 │ │ ├── Dockerfile │ │ ├── app.py │ │ ├── readme.txt │ │ └── requirements.txt ├── test-10 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-11 │ └── v1 │ │ ├── .env │ │ ├── Dockerfile │ │ ├── access.log │ │ └── data.txt ├── test-12 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-13 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-14 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-15 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-16 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-17 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-19 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-2 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-20 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-21 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-22 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-23 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-24 │ └── v1 │ │ ├── Dockerfile │ │ ├── index.php │ │ └── user_log.txt ├── test-25 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-26 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-27 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-28 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-29 │ └── v1 │ │ ├── Dockerfile │ │ └── index.html ├── test-3 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-30 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-31 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-32 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-33 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-34 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-35 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-36 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-37 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-38 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-39 │ └── v1 │ │ ├── Dockerfile │ │ ├── index.php │ │ └── ssl.conf ├── test-4 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-40 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-41 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-42 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-44 │ ├── v1 │ │ ├── Dockerfile │ │ └── index.php │ └── v2 │ │ ├── Dockerfile │ │ ├── package.json │ │ ├── public │ │ └── index.html │ │ └── server.js ├── test-45 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-46 │ └── v1 │ │ ├── Controllers │ │ └── UserController.cs │ │ ├── Dockerfile │ │ ├── Models │ │ └── User.cs │ │ ├── Program.cs │ │ ├── Test46.csproj │ │ ├── Tests │ │ └── UserControllerTests.cs │ │ └── wwwroot │ │ └── index.html ├── test-47 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-48 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-49 │ └── v1 │ │ └── Dockerfile ├── test-5 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-50 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-51 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-52 │ └── v1 │ │ ├── Dockerfile │ │ ├── index.html │ │ ├── package.json │ │ └── server.js ├── test-53 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-54 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-55 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-56 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-6 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-7 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php ├── test-8 │ └── v1 │ │ ├── Dockerfile │ │ └── index.php └── test-9 │ └── v1 │ ├── Dockerfile │ ├── index.php │ └── readme.txt ├── utils ├── checkProfileConsistency.ts ├── findMissingTests.ts ├── findUncoveredCwes.ts └── types.ts └── visualizer ├── .editorconfig ├── .eslintrc-auto-import.json ├── .gitattributes ├── .gitignore ├── .prettierrc.json ├── auto-imports.d.ts ├── bun.lockb ├── components.d.ts ├── env.d.ts ├── eslint.config.ts ├── index.html ├── package-lock.json ├── package.json ├── public └── favicon.ico ├── src ├── App.vue ├── assets │ ├── base.css │ ├── logo.svg │ └── main.css ├── components │ ├── BarChart.vue │ ├── ChartControls.vue │ ├── DashboardPage.vue │ ├── DataTable.vue │ ├── HeatmapChart.vue │ ├── RadarChart.vue │ ├── ToolCoverageGap.vue │ ├── data.ts │ ├── icons │ │ ├── IconCommunity.vue │ │ ├── IconDocumentation.vue │ │ ├── IconEcosystem.vue │ │ ├── IconSupport.vue │ │ └── IconTooling.vue │ └── types.ts ├── main.ts ├── router │ └── index.ts ├── stores │ └── counter.ts ├── views │ └── HomeView.vue └── vite-env.d.ts ├── tsconfig.json └── vite.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AppSec Detection Framework (ASDF) 2 | 3 | A framework for understanding the capabilities of automated detection methods at identifying classes of application security vulnerabilities. 4 | 5 | ## Table of Contents 6 | 7 | - [Overview](#overview) 8 | - [Requirements](#requirements) 9 | - [Getting Started](#getting-started) 10 | - [Running Tests](#running-tests) 11 | - [Management Interface](#management-interface) 12 | - [Recorded Tests Generator](#recorded-tests-generator) 13 | - [Project Structure](#project-structure) 14 | - [Tests](#tests) 15 | - [Data Management](#data-management) 16 | - [data.json](#datajson) 17 | - [Analysis Utilities](#analysis-utilities) 18 | - [Docker Configuration](#docker-configuration) 19 | - [docker-compose.yml](#docker-composeyml) 20 | - [Dockerfile](#dockerfile) 21 | - [Vulnerable Code](#vulnerable-code) 22 | - [Data Visualization](#data-visualization) 23 | - [Setup](#setup) 24 | - [Features](#features) 25 | - [Linting](#linting) 26 | - [Contributing](#contributing) 27 | - [License](#license) 28 | 29 | ## Overview 30 | 31 | ASDF is designed to evaluate and compare the effectiveness of various security scanners in detecting common web application vulnerabilities. It provides a standardized set of vulnerable applications and a framework for testing security tools against them. 32 | 33 | ## Requirements 34 | 35 | - [Bun](https://bun.sh) - JavaScript runtime and package manager 36 | - [Docker](https://www.docker.com) - Container platform 37 | - [Docker Compose](https://docs.docker.com/compose/) - Multi-container Docker application tool 38 | 39 | ## Getting Started 40 | 41 | ### Running Tests 42 | 43 | To test a new scanner or set of exploits, you'll use Pocman, which orchestrates the proof of concept applications in this repository. 44 | 45 | 1. Install dependencies: 46 | ```sh 47 | bun install 48 | ``` 49 | 50 | 2. Run Pocman: 51 | ```sh 52 | ./pocman.ts 53 | ``` 54 | 55 | 3. Navigate through test batches: 56 | - Pocman deploys proofs of concept in batches (default: 15 images) to avoid resource constraints 57 | - Enter 'next' in the command prompt to navigate to the next batch 58 | - The index of proof of concepts will be hosted on `localhost:3000` 59 | - Point your scanner to this URL to crawl all available PoCs 60 | 61 | #### Management Interface 62 | 63 | Pocman now includes a web-based management interface that allows you to control the test batches through a browser: 64 | 65 | 1. Access the management interface at `http://localhost:3001` while Pocman is running 66 | 2. Use the control buttons to: 67 | - Navigate to the next batch 68 | - Return to the previous batch 69 | - Stop the current batch 70 | - Start the current batch 71 | - Restart the current batch 72 | 3. View the current batch status 73 | 4. Generate recordedTests output for your scanner 74 | 75 | ##### Recorded Tests Generator 76 | 77 | The management interface now includes a utility to help generate the `recordedTests` output for your scanner: 78 | 79 | 1. Enter your scanner name (e.g., "zap_v2.16.0") 80 | 2. Provide a description of your scanner's configuration in the scan profile field 81 | 3. For each test in the current batch: 82 | - Select which CWEs were detected by checking the boxes in the "Detected CWEs" column 83 | - Select which CWEs were not detected by checking the boxes in the "Undetected CWEs" column 84 | - A CWE can only be in one category at a time (detected or undetected) 85 | 4. Click "Generate Recorded Tests" to create the JSON output 86 | 5. Copy the output and add it to your `data.json` file 87 | 88 | This utility makes it easy to record your scanner's test results in the correct format for the ASDF framework. 89 | 90 | For more information, run: 91 | ```sh 92 | bun install && ./pocman.ts --help 93 | ``` 94 | 95 | ## Project Structure 96 | 97 | ### Tests 98 | 99 | The `tests` folder contains all of the definitions for each of the vulnerabilities. The structure of this folder should be: 100 | 101 | ```bash 102 | tests/ 103 | ├── test-1/ # The test ID of the vulnerability (increments) 104 | │ └── v1/ # The version of the specific test 105 | │ ├── Dockerfile # The dockerfile for building the test environment 106 | │ └── index.php # The vulnerable code (can be any language) 107 | ├── test-2/ 108 | │ └── v1/ 109 | │ ├── Dockerfile 110 | │ └── index.js 111 | └── test-3/ 112 | └── v1/ 113 | ├── Dockerfile 114 | └── index.py 115 | ``` 116 | 117 | Each test folder follows the pattern `test-{id}` where `id` is a sequential number. Within each test folder, there can be multiple versions (v1, v2, etc.) of the same vulnerability test. 118 | 119 | ### Data Management 120 | 121 | #### data.json 122 | 123 | This is the file that contains our test data and the OWASP top 10 CWEs. The file has two main sections: 124 | 125 | 1. `vulnerabilities`: An array of OWASP Top 10 2021 categories and their associated CWEs 126 | 2. `recordedTests`: An object where each key is a scanner name and the value contains the scanner's profile and test results 127 | 128 | Example structure: 129 | 130 | ```json 131 | { 132 | "vulnerabilities": [ 133 | { 134 | "OWASP": "A01:2021", 135 | "CWEDetails": [ 136 | { 137 | "id": 22, 138 | "title": "Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')", 139 | "tests": ["test_1_v1", "test_1_v2"] 140 | } 141 | ], 142 | "group": "Broken Access Control" 143 | } 144 | ], 145 | "recordedTests": { 146 | "scanner_name": { 147 | "scanProfile": "Description of the scanner's capabilities and purpose", 148 | "tests": [ 149 | { 150 | "test": "test_1_v1", 151 | "detectedCWEs": [22, 693], 152 | "undetectedCWEs": [23], 153 | "updatedAt": 1740999692 154 | } 155 | ] 156 | } 157 | } 158 | } 159 | ``` 160 | 161 | When adding new test results: 162 | 163 | 1. The scanner name should include the version number (e.g., "zap_v2.16.0") 164 | 2. The `scanProfile` should describe the scanner's configuration 165 | 3. Each test result should include: 166 | - `test`: The name of the docker container of the test 167 | - `detectedCWEs`: Array of CWE IDs that were detected 168 | - `undetectedCWEs`: Array of CWE IDs that were not detected 169 | - `updatedAt`: Unix timestamp of when the test occurred 170 | 171 | > [!TIP] 172 | > You can use the Recorded Tests Generator in the management interface to easily create this output in the correct format. 173 | 174 | #### Analysis Utilities 175 | 176 | The framework includes utilities to help analyze the data.json file and identify gaps in test coverage: 177 | 178 | ##### Find Missing Tests 179 | 180 | ```bash 181 | bun run utils/findMissingTests.ts [options] 182 | ``` 183 | 184 | This utility analyzes scanner results to identify: 185 | - Missing tests that haven't been run by a scanner 186 | - Missing CWEs that should be detected but aren't 187 | - Incorrect CWE associations (CWEs that are reported but shouldn't be) 188 | 189 | Options: 190 | - `--file `: Path to the data.json file (default: 'data.json') 191 | - `-v, --verbose`: Enable verbose output 192 | 193 | ##### Find Uncovered CWEs 194 | 195 | ```bash 196 | bun run utils/findUncoveredCwes.ts [options] 197 | ``` 198 | 199 | This utility identifies CWEs that lack test coverage in the framework: 200 | - CWEs with no associated tests 201 | - CWEs with limited coverage (only one test) 202 | 203 | Options: 204 | - `--file `: Path to the data.json file (default: 'data.json') 205 | - `-v, --verbose`: Enable verbose output 206 | 207 | ##### Check Profile Consistency 208 | 209 | ```bash 210 | bun run utils/checkProfileConsistency.ts 211 | ``` 212 | 213 | This utility verifies that the profiles in docker-compose.yml match the CWEs and OWASP categories associated with tests in data.json. It performs case-insensitive matching and identifies: 214 | 215 | - Tests in docker-compose.yml that don't exist in data.json 216 | - CWEs in docker-compose.yml profiles that don't exist in data.json 217 | - OWASP categories in docker-compose.yml profiles that don't exist in data.json 218 | - Tests in data.json that don't exist in docker-compose.yml 219 | - CWEs in data.json that aren't referenced in docker-compose.yml profiles 220 | - OWASP categories in data.json that aren't referenced in docker-compose.yml profiles 221 | - Tests that are missing the "all" profile in docker-compose.yml 222 | 223 | The utility will exit with code 1 if any inconsistencies are found, making it suitable for CI/CD pipelines. 224 | 225 | > [!TIP] 226 | > Use these utilities regularly to identify gaps in test coverage and ensure your scanner results are accurate. 227 | 228 | ### Docker Configuration 229 | 230 | #### docker-compose.yml 231 | 232 | The `docker-compose.yml` file manages the deployment of groups of containers. 233 | 234 | ##### Port Configuration 235 | 236 | Each container should port forward from a local port on the host using an unreserved port (above 1024), following the convention `8 {test ID} {version number}`: 237 | - test 1 v1 would use port `8011` 238 | - test 2 v1 would use port `8021` 239 | 240 | ##### Service Profiles 241 | 242 | The `profiles` should be defined for each service to include: 243 | 244 | - The language the vulnerability was written in (e.g., php, js, python) 245 | - The webserver technology in use (e.g., apache, nginx) 246 | - CWE IDs associated with the vulnerability (e.g., cwe-23) 247 | - The OWASP Top 10 2021 category code (e.g., a01:2021) 248 | - The profile of "all" to ensure these are run by default 249 | 250 | Example entry: 251 | 252 | ```yaml 253 | services: 254 | test_1_v1: 255 | image: test_1_v1:latest 256 | build: 257 | context: tests/test-1/v1/ 258 | dockerfile: Dockerfile 259 | ports: 260 | - "8011:80" 261 | profiles: 262 | - a01:2021 263 | - php 264 | - apache 265 | - cwe-23 266 | - cwe-22 267 | - all 268 | ``` 269 | 270 | #### Dockerfile 271 | 272 | The `Dockerfile` is responsible for deploying the vulnerable code. It should: 273 | 274 | 1. Set up the appropriate runtime environment 275 | 2. Install necessary dependencies 276 | 3. Copy the vulnerable code into the container 277 | 4. Configure the web server to serve the application 278 | 279 | #### Vulnerable Code 280 | 281 | The vulnerable code (typically named `index.php`, `index.js`, etc.) should: 282 | 283 | - Be brief and easily readable 284 | - Focus solely on demonstrating the vulnerability 285 | - Avoid unnecessary styling or details that don't contribute to the vulnerability 286 | - Be properly commented to explain the vulnerability 287 | 288 | ## Data Visualization 289 | 290 | The `visualizer` directory contains ASDFviz, a Vue-based visualization tool for analyzing the test results. 291 | 292 | ### Setup 293 | 294 | 1. Navigate to the visualizer directory: 295 | ```sh 296 | cd visualizer 297 | ``` 298 | 299 | 2. Install dependencies: 300 | ```sh 301 | bun install -D 302 | ``` 303 | 304 | 3. Start the development server: 305 | ```sh 306 | bun dev 307 | ``` 308 | 309 | 4. Access the visualization at `http://localhost:5173` 310 | 311 | ### Features 312 | 313 | - Coverage gap analysis 314 | - Detection rate comparison 315 | - OWASP category analysis 316 | - CWE-specific analysis 317 | - Export functionality for test results 318 | 319 | ### Linting 320 | 321 | To lint the code with ESLint: 322 | ```sh 323 | bun lint 324 | ``` 325 | 326 | ## Contributing 327 | 328 | Contributions are welcome! There are two main ways to contribute to ASDF: 329 | 330 | ### Adding a New Test 331 | 332 | You can identify CWEs that we don't currently have tests for by running `bun run utils/findUncoveredCwes.ts` 333 | 334 | To add a test into the collection: 335 | 336 | 1. Create a new test directory in `tests/` following the naming convention `test-{id}/v1/` 337 | 2. Create a `Dockerfile` that: 338 | - Sets up the appropriate runtime environment 339 | - Installs necessary dependencies 340 | - Copies the vulnerable code 341 | - Configures the web server 342 | 3. Create the vulnerable code file (e.g., `index.php`, `index.js`) that: 343 | - Demonstrates a specific vulnerability 344 | - Is brief and easily readable 345 | - Includes clear comments explaining the vulnerability 346 | 4. Update `docker-compose.yml` to include your test with appropriate: 347 | - Port mapping (following the `8{test_id}{version}` convention) 348 | - Service profiles (language, webserver, CWE IDs, OWASP category) 349 | 5. Add your test to `data.json` under the appropriate OWASP category and CWE 350 | 6. Validate your changes using the analysis utilities: 351 | ```sh 352 | # Ensure profile consistency across data.json and docker-compose.yml 353 | bun run utils/checkProfileConsistency.ts 354 | ``` 355 | 356 | ### Adding Scan Results 357 | 358 | 1. Run your security scanner against the test suite using Pocman 359 | 2. Use the Recorded Tests Generator in the management interface (`http://localhost:3001`) to: 360 | - Enter your scanner name and version 361 | - Provide a detailed scan profile 362 | - Record which CWEs were detected/undetected for each test 363 | 3. Add the generated JSON to `data.json` under the `recordedTests` section 364 | 4. Ensure your scanner name includes the version number (e.g., "zap_v2.16.0") 365 | 5. Include a detailed `scanProfile` that describes your scanner's configuration 366 | 6. Validate your scan results: 367 | ```sh 368 | # Check for missing tests in your scan results 369 | bun run utils/findMissingTests.ts 370 | ``` 371 | 372 | > [!IMPORTANT] 373 | > Always run the validation utilities before submitting your contribution. They help ensure: 374 | > - All tests are properly documented in data.json 375 | > - CWEs are correctly associated with tests 376 | > - Docker profiles match the documented vulnerabilities 377 | > - No tests or CWEs are missing from your scan results 378 | 379 | For both types of contributions: 380 | 1. Fork the repository 381 | 2. Create a feature branch (`git checkout -b feature/your-contribution`) 382 | 3. Commit your changes (`git commit -m 'Add test/scan results for [description]'`) 383 | 4. Push to the branch (`git push origin feature/your-contribution`) 384 | 5. Open a Pull Request 385 | 386 | ## License 387 | 388 | This project is licensed under the MIT License - see the LICENSE file for details. 389 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cytix-software/AppSec-Detection-Framework/882e5a4cf4eb04cf563644bbf36c126cbeeb639c/bun.lockb -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | test_1_v1: 3 | image: test_1_v1:latest 4 | build: 5 | context: tests/test-1/v1/ 6 | dockerfile: Dockerfile 7 | ports: 8 | - "8011:80" 9 | profiles: 10 | - test-1 11 | - a01:2021 12 | - php 13 | - apache 14 | - cwe-23 15 | - cwe-22 16 | - all 17 | test_1_v2: 18 | image: test_1_v2:latest 19 | build: 20 | context: tests/test-1/v2/ 21 | dockerfile: Dockerfile 22 | ports: 23 | - "8012:80" 24 | profiles: 25 | - test-1 26 | - a01:2021 27 | - python 28 | - flask 29 | - cwe-23 30 | - cwe-22 31 | - all 32 | test_2_v1: 33 | image: test_2_v1:latest 34 | build: 35 | context: tests/test-2/v1/ 36 | dockerfile: Dockerfile 37 | ports: 38 | - "8021:80" 39 | profiles: 40 | - test-2 41 | - php 42 | - apache 43 | - cwe-78 44 | - a03:2021 45 | - all 46 | 47 | test_3_v1: 48 | image: test_3_v1:latest 49 | build: 50 | context: tests/test-3/v1/ 51 | dockerfile: Dockerfile 52 | ports: 53 | - "8031:80" 54 | profiles: 55 | - test-3 56 | - php 57 | - apache 58 | - cwe-352 59 | - a01:2021 60 | - all 61 | 62 | test_4_v1: 63 | image: test_4_v1:latest 64 | build: 65 | context: tests/test-4/v1/ 66 | dockerfile: Dockerfile 67 | ports: 68 | - "8041:80" 69 | profiles: 70 | - test-4 71 | - php 72 | - apache 73 | - cwe-80 74 | - a03:2021 75 | - all 76 | 77 | test_5_v1: 78 | image: test_5_v1:latest 79 | build: 80 | context: tests/test-5/v1/ 81 | dockerfile: Dockerfile 82 | ports: 83 | - "8051:80" 84 | profiles: 85 | - test-5 86 | - php 87 | - apache 88 | - cwe-83 89 | - a03:2021 90 | - all 91 | 92 | test_6_v1: 93 | image: test_6_v1:latest 94 | build: 95 | context: tests/test-6/v1/ 96 | dockerfile: Dockerfile 97 | ports: 98 | - "8061:80" 99 | profiles: 100 | - test-6 101 | - php 102 | - apache 103 | - cwe-434 104 | - a04:2021 105 | - all 106 | 107 | test_7_v1: 108 | image: test_7_v1:latest 109 | build: 110 | context: tests/test-7/v1/ 111 | dockerfile: Dockerfile 112 | ports: 113 | - "8071:80" 114 | profiles: 115 | - test-7 116 | - php 117 | - apache 118 | - cwe-502 119 | - a08:2021 120 | - all 121 | 122 | test_8_v1: 123 | image: test_8_v1:latest 124 | build: 125 | context: tests/test-8/v1/ 126 | dockerfile: Dockerfile 127 | ports: 128 | - "8081:80" 129 | profiles: 130 | - test-8 131 | - php 132 | - apache 133 | - cwe-1004 134 | - a05:2021 135 | - all 136 | 137 | test_9_v1: 138 | image: test_9_v1:latest 139 | build: 140 | context: tests/test-9/v1/ 141 | dockerfile: Dockerfile 142 | ports: 143 | - "8091:80" 144 | profiles: 145 | - test-9 146 | - php 147 | - apache 148 | - cwe-35 149 | - cwe-22 150 | - a01:2021 151 | - all 152 | 153 | test_10_v1: 154 | image: test_10_v1:latest 155 | build: 156 | context: tests/test-10/v1/ 157 | dockerfile: Dockerfile 158 | ports: 159 | - "8101:80" 160 | profiles: 161 | - test-10 162 | - php 163 | - apache 164 | - cwe-276 165 | - a01:2021 166 | - all 167 | 168 | test_11_v1: 169 | image: test_11_v1:latest 170 | build: 171 | context: tests/test-11/v1/ 172 | dockerfile: Dockerfile 173 | ports: 174 | - "8111:80" 175 | profiles: 176 | - test-11 177 | - php 178 | - apache 179 | - cwe-219 180 | - cwe-552 181 | - cwe-256 182 | - a01:2021 183 | - a04:2021 184 | - all 185 | 186 | test_12_v1: 187 | image: test_12_v1:latest 188 | build: 189 | context: tests/test-12/v1/ 190 | dockerfile: Dockerfile 191 | ports: 192 | - "8121:80" 193 | profiles: 194 | - test-12 195 | - php 196 | - apache 197 | - cwe-377 198 | - a01:2021 199 | - all 200 | 201 | test_13_v1: 202 | image: test_13_v1:latest 203 | build: 204 | context: tests/test-13/v1/ 205 | dockerfile: Dockerfile 206 | ports: 207 | - "8131:80" 208 | profiles: 209 | - test-13 210 | - php 211 | - apache 212 | - cwe-862 213 | - a01:2021 214 | - all 215 | 216 | test_14_v1: 217 | image: test_14_v1:latest 218 | build: 219 | context: tests/test-14/v1/ 220 | dockerfile: Dockerfile 221 | ports: 222 | - "8141:80" 223 | profiles: 224 | - test-14 225 | - php 226 | - apache 227 | - cwe-863 228 | - cwe-287 229 | - cwe-784 230 | - cwe-565 231 | - a01:2021 232 | - a07:2021 233 | - a08:2021 234 | - all 235 | 236 | test_15_v1: 237 | image: test_15_v1:latest 238 | build: 239 | context: tests/test-15/v1/ 240 | dockerfile: Dockerfile 241 | ports: 242 | - "8151:80" 243 | profiles: 244 | - test-15 245 | - php 246 | - apache 247 | - cwe-328 248 | - cwe-327 249 | - cwe-759 250 | - cwe-916 251 | - a02:2021 252 | - all 253 | 254 | test_16_v1: 255 | image: test_16_v1:latest 256 | build: 257 | context: tests/test-16/v1/ 258 | dockerfile: Dockerfile 259 | ports: 260 | - "8161:80" 261 | profiles: 262 | - test-16 263 | - php 264 | - apache 265 | - a10:2021 266 | - cwe-918 267 | - a03:2021 268 | - cwe-78 269 | - all 270 | 271 | test_17_v1: 272 | image: test_17_v1:latest 273 | build: 274 | context: tests/test-17/v1/ 275 | dockerfile: Dockerfile 276 | ports: 277 | - "8171:80" 278 | profiles: 279 | - test-17 280 | - php 281 | - apache 282 | - cwe-760 283 | - cwe-327 284 | - cwe-328 285 | - cwe-916 286 | - cwe-321 287 | - cwe-798 288 | - a02:2021 289 | - a07:2021 290 | - all 291 | 292 | test_19_v1: 293 | image: test_19_v1:latest 294 | build: 295 | context: tests/test-19/v1/ 296 | dockerfile: Dockerfile 297 | ports: 298 | - "8191:80" 299 | profiles: 300 | - test-19 301 | - php 302 | - apache 303 | - cwe-501 304 | - cwe-287 305 | - A04:2021 306 | - A07:2021 307 | - all 308 | 309 | test_20_v1: 310 | image: test_20_v1:latest 311 | build: 312 | context: tests/test-20/v1/ 313 | dockerfile: Dockerfile 314 | ports: 315 | - "8201:80" 316 | profiles: 317 | - test-20 318 | - php 319 | - apache 320 | - cwe-778 321 | - cwe-799 322 | - cwe-223 323 | - cwe-307 324 | - a09:2021 325 | - a07:2021 326 | - a04:2021 327 | - all 328 | 329 | test_21_v1: 330 | image: test_21_v1:latest 331 | build: 332 | context: tests/test-21/v1/ 333 | dockerfile: Dockerfile 334 | ports: 335 | - "8211:80" 336 | profiles: 337 | - test-21 338 | - php 339 | - apache 340 | - cwe-117 341 | - A09:2021 342 | - all 343 | 344 | test_22_v1: 345 | image: test_22_v1:latest 346 | build: 347 | context: tests/test-22/v1/ 348 | dockerfile: Dockerfile 349 | ports: 350 | - "8221:80" 351 | profiles: 352 | - test-22 353 | - php 354 | - apache 355 | - cwe-613 356 | - a07:2021 357 | - all 358 | 359 | test_23_v1: 360 | image: test_23_v1:latest 361 | build: 362 | context: tests/test-23/v1/ 363 | dockerfile: Dockerfile 364 | ports: 365 | - "8231:80" 366 | profiles: 367 | - test-23 368 | - php 369 | - apache 370 | - cwe-384 371 | - a07:2021 372 | - all 373 | 374 | test_24_v1: 375 | image: test_24_v1:latest 376 | build: 377 | context: tests/test-24/v1/ 378 | dockerfile: Dockerfile 379 | ports: 380 | - "8241:80" 381 | profiles: 382 | - test-24 383 | - php 384 | - apache 385 | - cwe-359 386 | - cwe-522 387 | - cwe-532 388 | - a01:2021 389 | - a04:2021 390 | - a09:2021 391 | - all 392 | 393 | test_25_v1: 394 | image: test_25_v1:latest 395 | build: 396 | context: tests/test-25/v1/ 397 | dockerfile: Dockerfile 398 | ports: 399 | - "8251:80" 400 | profiles: 401 | - test-25 402 | - php 403 | - apache 404 | - cwe-98 405 | - cwe-706 406 | - cwe-829 407 | - cwe-426 408 | - a01:2021 409 | - a03:2021 410 | - A08:2021 411 | - all 412 | 413 | test_26_v1: 414 | image: test_26_v1:latest 415 | build: 416 | context: tests/test-26/v1/ 417 | dockerfile: Dockerfile 418 | ports: 419 | - "8261:80" 420 | profiles: 421 | - test-26 422 | - php 423 | - apache 424 | - cwe-646 425 | - cwe-345 426 | - a04:2021 427 | - a08:2021 428 | - all 429 | 430 | test_27_v1: 431 | image: test_27_v1:latest 432 | build: 433 | context: tests/test-27/v1/ 434 | dockerfile: Dockerfile 435 | ports: 436 | - "8271:80" 437 | profiles: 438 | - test-27 439 | - php 440 | - apache 441 | - cwe-353 442 | - cwe-345 443 | - a08:2021 444 | - all 445 | 446 | test_28_v1: 447 | image: test_28_v1:latest 448 | build: 449 | context: tests/test-28/v1/ 450 | dockerfile: Dockerfile 451 | ports: 452 | - "8281:80" 453 | profiles: 454 | - test-28 455 | - php 456 | - apache 457 | - cwe-494 458 | - cwe-345 459 | - a08:2021 460 | - all 461 | 462 | test_29_v1: 463 | image: test_29_v1:latest 464 | build: 465 | context: tests/test-29/v1/ 466 | dockerfile: Dockerfile 467 | ports: 468 | - "8291:80" 469 | profiles: 470 | - test-29 471 | - php 472 | - apache 473 | - cwe-830 474 | - cwe-829 475 | - a08:2021 476 | - all 477 | 478 | test_30_v1: 479 | image: test_30_v1:latest 480 | build: 481 | context: tests/test-30/v1/ 482 | dockerfile: Dockerfile 483 | ports: 484 | - "8301:80" 485 | profiles: 486 | - test-30 487 | - php 488 | - apache 489 | - cwe-915 490 | - cwe-913 491 | - a08:2021 492 | - a01:2021 493 | - all 494 | 495 | test_31_v1: 496 | image: test_31_v1:latest 497 | build: 498 | context: tests/test-31/v1/ 499 | dockerfile: Dockerfile 500 | ports: 501 | - "8311:80" 502 | profiles: 503 | - test-31 504 | - php 505 | - apache 506 | - cwe-940 507 | - cwe-346 508 | - cwe-352 509 | - a07:2021 510 | - a01:2021 511 | - all 512 | 513 | test_32_v1: 514 | image: test_32_v1:latest 515 | build: 516 | context: tests/test-32/v1/ 517 | dockerfile: Dockerfile 518 | ports: 519 | - "8321:80" 520 | profiles: 521 | - test-32 522 | - php 523 | - apache 524 | - cwe-640 525 | - cwe-257 526 | - cwe-256 527 | - cwe-798 528 | - cwe-307 529 | - a07:2021 530 | - a04:2021 531 | - all 532 | 533 | test_33_v1: 534 | image: test_33_v1:latest 535 | build: 536 | context: tests/test-33/v1/ 537 | dockerfile: Dockerfile 538 | ports: 539 | - "8331:80" 540 | profiles: 541 | - test-33 542 | - apache 543 | - php 544 | - cwe-620 545 | - cwe-306 546 | - cwe-352 547 | - cwe-598 548 | - a07:2021 549 | - a04:2021 550 | - a01:2021 551 | - all 552 | 553 | test_34_v1: 554 | image: test_34_v1:latest 555 | build: 556 | context: tests/test-34/v1/ 557 | dockerfile: Dockerfile 558 | ports: 559 | - "8341:80" 560 | profiles: 561 | - test-34 562 | - apache 563 | - php 564 | - cwe-521 565 | - a07:2021 566 | - all 567 | 568 | test_35_v1: 569 | image: test_35_v1:latest 570 | build: 571 | context: tests/test-35/v1/ 572 | dockerfile: Dockerfile 573 | ports: 574 | - "8351:80" 575 | profiles: 576 | - test-35 577 | - apache 578 | - php 579 | - cwe-302 580 | - cwe-807 581 | - cwe-287 582 | - cwe-784 583 | - cwe-565 584 | - a07:2021 585 | - a04:2021 586 | - a08:2021 587 | - all 588 | 589 | test_36_v1: 590 | image: test_36_v1:latest 591 | build: 592 | context: tests/test-36/v1/ 593 | dockerfile: Dockerfile 594 | ports: 595 | - "8361:80" 596 | profiles: 597 | - test-36 598 | - apache 599 | - php 600 | - cwe-297 601 | - cwe-295 602 | - a07:2021 603 | - all 604 | 605 | test_37_v1: 606 | image: test_37_v1:latest 607 | build: 608 | context: tests/test-37/v1/ 609 | dockerfile: Dockerfile 610 | ports: 611 | - "8371:80" 612 | profiles: 613 | - test-37 614 | - apache 615 | - php 616 | - cwe-294 617 | - cwe-798 618 | - a07:2021 619 | - all 620 | 621 | test_38_v1: 622 | image: test_38_v1:latest 623 | build: 624 | context: tests/test-38/v1/ 625 | dockerfile: Dockerfile 626 | ports: 627 | - "8381:80" 628 | profiles: 629 | - test-38 630 | - apache 631 | - php 632 | - cwe-290 633 | - cwe-287 634 | - cwe-602 635 | - a04:2021 636 | - a07:2021 637 | - all 638 | 639 | test_39_v1: 640 | image: test_39_v1:latest 641 | build: 642 | context: tests/test-39/v1/ 643 | dockerfile: Dockerfile 644 | ports: 645 | - "8391:443" 646 | profiles: 647 | - test-39 648 | - apache 649 | - php 650 | - cwe-614 651 | - a05:2021 652 | - all 653 | 654 | test_40_v1: 655 | image: test_40_v1:latest 656 | build: 657 | context: tests/test-40/v1/ 658 | dockerfile: Dockerfile 659 | ports: 660 | - "8401:80" 661 | profiles: 662 | - test-40 663 | - apache 664 | - php 665 | - cwe-776 666 | - a01:2021 667 | - cwe-610 668 | - cwe-611 669 | - a03:2021 670 | - a05:2021 671 | - all 672 | 673 | test_41_v1: 674 | image: test_41_v1:latest 675 | build: 676 | context: tests/test-41/v1/ 677 | dockerfile: Dockerfile 678 | ports: 679 | - "8411:80" 680 | profiles: 681 | - test-41 682 | - apache 683 | - php 684 | - cwe-315 685 | - a05:2021 686 | - all 687 | 688 | test_42_v1: 689 | image: test_42_v1:latest 690 | build: 691 | context: tests/test-42/v1/ 692 | dockerfile: Dockerfile 693 | ports: 694 | - "8421:80" 695 | profiles: 696 | - test-42 697 | - apache 698 | - php 699 | - cwe-526 700 | - cwe-312 701 | - a05:2021 702 | - a04:2021 703 | - all 704 | 705 | test_44_v1: 706 | image: test_44_v1:latest 707 | build: 708 | context: tests/test-44/v1/ 709 | dockerfile: Dockerfile 710 | ports: 711 | - "8441:80" 712 | profiles: 713 | - test-44 714 | - apache 715 | - php 716 | - cwe-1104 717 | - cwe-1035 718 | - cwe-79 719 | - a06:2021 720 | - a03:2021 721 | - all 722 | 723 | test_44_v2: 724 | image: test_44_v2:latest 725 | build: 726 | context: tests/test-44/v2/ 727 | dockerfile: Dockerfile 728 | ports: 729 | - "8442:80" 730 | profiles: 731 | - test-44 732 | - node 733 | - express 734 | - cwe-1104 735 | - cwe-79 736 | - a06:2021 737 | - a03:2021 738 | - all 739 | 740 | test_45_v1: 741 | image: test_45_v1:latest 742 | build: 743 | context: tests/test-45/v1/ 744 | dockerfile: Dockerfile 745 | ports: 746 | - "8451:80" 747 | profiles: 748 | - test-45 749 | - php 750 | - apache 751 | - cwe-1275 752 | - cwe-1004 753 | - a01:2021 754 | - a05:2021 755 | - all 756 | 757 | test_46_v1: 758 | image: test_46_v1:latest 759 | build: 760 | context: tests/test-46/v1/ 761 | dockerfile: Dockerfile 762 | ports: 763 | - "8461:80" 764 | profiles: 765 | - test-46 766 | - dotnet 767 | - aspnet 768 | - cwe-1174 769 | - cwe-1173 770 | - a05:2021 771 | - a04:2021 772 | - all 773 | 774 | test_47_v1: 775 | image: test_47_v1:latest 776 | build: 777 | context: tests/test-47/v1/ 778 | dockerfile: Dockerfile 779 | ports: 780 | - "8471:80" 781 | profiles: 782 | - test-47 783 | - apache 784 | - php 785 | - cwe-840 786 | - a04:2021 787 | - all 788 | 789 | test_48_v1: 790 | image: test_48_v1:latest 791 | build: 792 | context: tests/test-48/v1/ 793 | dockerfile: Dockerfile 794 | ports: 795 | - "8481:80" 796 | profiles: 797 | - test-48 798 | - apache 799 | - php 800 | - cwe-1021 801 | - a04:2021 802 | - cwe-451 803 | - all 804 | 805 | test_49_v1: 806 | image: test_49_v1:latest 807 | build: 808 | context: tests/test-49/v1/ 809 | dockerfile: Dockerfile 810 | ports: 811 | - "8491:80" 812 | profiles: 813 | - test-49 814 | - apache 815 | - cwe-756 816 | - a05:2021 817 | - all 818 | 819 | test_50_v1: 820 | image: test_50_v1:latest 821 | build: 822 | context: tests/test-50/v1/ 823 | dockerfile: Dockerfile 824 | ports: 825 | - "8501:80" 826 | profiles: 827 | - test-50 828 | - apache 829 | - php 830 | - cwe-942 831 | - a05:2021 832 | - all 833 | 834 | test_51_v1: 835 | image: test_51_v1:latest 836 | build: 837 | context: tests/test-51/v1/ 838 | dockerfile: Dockerfile 839 | ports: 840 | - "8511:80" 841 | profiles: 842 | - test-51 843 | - apache 844 | - php 845 | - cwe-651 846 | - a01:2021 847 | - all 848 | 849 | test_52_v1: 850 | image: test_52_v1:latest 851 | build: 852 | context: tests/test-52/v1/ 853 | dockerfile: Dockerfile 854 | ports: 855 | - "8521:80" 856 | profiles: 857 | - test-52 858 | - node 859 | - cwe-523 860 | - a02:2021 861 | - all 862 | 863 | test_53_v1: 864 | image: test_53_v1:latest 865 | build: 866 | context: tests/test-53/v1/ 867 | dockerfile: Dockerfile 868 | ports: 869 | - "8531:80" 870 | profiles: 871 | - test-53 872 | - apache 873 | - php 874 | - cwe-525 875 | - a04:2021 876 | - all 877 | 878 | test_54_v1: 879 | image: test_54_v1:latest 880 | build: 881 | context: tests/test-54/v1/ 882 | dockerfile: Dockerfile 883 | ports: 884 | - "8541:80" 885 | profiles: 886 | - test-54 887 | - apache 888 | - php 889 | - cwe-601 890 | - a01:2021 891 | - all 892 | 893 | test_55_v1: 894 | image: test_55_v1:latest 895 | build: 896 | context: tests/test-55/v1/ 897 | dockerfile: Dockerfile 898 | ports: 899 | - "8551:80" 900 | profiles: 901 | - test-55 902 | - apache 903 | - php 904 | - cwe-538 905 | - a01:2021 906 | - all 907 | 908 | test_56_v1: 909 | image: test_56_v1:latest 910 | build: 911 | context: ./tests/test-56/v1 912 | ports: 913 | - "8561:80" 914 | profiles: 915 | - test-56 916 | - apache 917 | - php 918 | - cwe-284 919 | - a01:2021 920 | - all -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "appsec-detection-framework", 3 | "version": "1.0.0", 4 | "description": "Framework for testing and comparing application security scanners", 5 | "module": "index.ts", 6 | "type": "module", 7 | "scripts": { 8 | "find-uncovered": "bun run utils/findUncoveredCwes.ts", 9 | "find-missing": "bun run utils/findMissingTests.ts", 10 | "check-profiles": "bun run utils/checkProfileConsistency.ts" 11 | }, 12 | "dependencies": { 13 | "@koa/router": "^13.1.0", 14 | "@types/koa-bodyparser": "^4.3.12", 15 | "@types/lodash": "^4.17.15", 16 | "@types/minimist": "^1.2.5", 17 | "common-tags": "^1.8.2", 18 | "js-yaml": "^4.1.0", 19 | "koa": "^2.15.4", 20 | "koa-bodyparser": "^4.4.1", 21 | "lodash": "^4.17.21", 22 | "minimist": "^1.2.8", 23 | "yaml": "^2.7.0", 24 | "commander": "^11.1.0" 25 | }, 26 | "devDependencies": { 27 | "@types/bun": "^1.0.0", 28 | "@types/js-yaml": "^4.0.9" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/test-1/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php readme.txt ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-1/v1/index.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | -------------------------------------------------------------------------------- /tests/test-1/v1/readme.txt: -------------------------------------------------------------------------------- 1 | Hello, world! -------------------------------------------------------------------------------- /tests/test-1/v2/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use Python 3.11 slim image as base 2 | FROM python:3.11-slim 3 | 4 | # Set working directory 5 | WORKDIR /app 6 | 7 | # Copy requirements first to leverage Docker cache 8 | COPY requirements.txt . 9 | 10 | # Install dependencies 11 | RUN pip install --no-cache-dir -r requirements.txt 12 | 13 | # Copy the application code 14 | COPY app.py readme.txt . 15 | 16 | # Create a non-root user for security 17 | RUN useradd -m appuser && chown -R appuser:appuser /app 18 | USER appuser 19 | 20 | # Expose the port the app runs on 21 | EXPOSE 80 22 | 23 | # Command to run the application 24 | CMD ["python", "app.py"] -------------------------------------------------------------------------------- /tests/test-1/v2/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, render_template_string 2 | import os 3 | 4 | app = Flask(__name__) 5 | 6 | # HTML template with the form 7 | HTML_TEMPLATE = ''' 8 |
9 | 10 | 11 |
12 | ''' 13 | 14 | @app.route('/', methods=['GET', 'POST']) 15 | def index(): 16 | if request.method == 'POST': 17 | filename = "./" 18 | filename += request.form['input'] 19 | try: 20 | with open(filename, 'r') as f: 21 | output = f.read() 22 | return HTML_TEMPLATE + output 23 | except Exception as e: 24 | return HTML_TEMPLATE + str(e) 25 | return HTML_TEMPLATE 26 | 27 | if __name__ == '__main__': 28 | app.run(host='0.0.0.0', port=80) -------------------------------------------------------------------------------- /tests/test-1/v2/readme.txt: -------------------------------------------------------------------------------- 1 | Hello, world! -------------------------------------------------------------------------------- /tests/test-1/v2/requirements.txt: -------------------------------------------------------------------------------- 1 | flask==3.0.2 -------------------------------------------------------------------------------- /tests/test-10/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | RUN chmod -R 777 /var/www/html 5 | EXPOSE 80 6 | 7 | -------------------------------------------------------------------------------- /tests/test-10/v1/index.php: -------------------------------------------------------------------------------- 1 | 14 | 15 |
16 | 17 | 18 | 19 |
20 | -------------------------------------------------------------------------------- /tests/test-11/v1/.env: -------------------------------------------------------------------------------- 1 | # Mock .env File 2 | 3 | # User Information (Plaintext) 4 | USER_1_USERNAME=Desmond 5 | USER_1_PASSWORD=bearsuperiority 6 | USER_2_USERNAME=Ryan 7 | USER_2_PASSWORD=iliketrains 8 | USER_3_USERNAME=Mr.Muffin 9 | USER_3_PASSWORD=ItsMuffinTime! 10 | 11 | # Admin User (Plaintext) 12 | ADMIN_USERNAME=Mr.T 13 | ADMIN_PASSWORD=meow 14 | 15 | # API Keys (Plaintext) 16 | GOOGLE_API_KEY=AIzaSyD1m0cK3y-FakeKey12345 17 | AWS_SECRET_KEY=AKIAIOSFODNN7EXAMPLE 18 | STRIPE_SECRET_KEY=sk_test_4eC39HqLyjWDarjtT1zdp7dc 19 | 20 | DEBUG=true 21 | 22 | # Database Credentials (Plaintext) 23 | DB_HOST=localhost 24 | DB_PORT=3306 25 | DB_USER=root 26 | DB_PASSWORD= password123 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/test-11/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY access.log data.txt .env ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-11/v1/access.log: -------------------------------------------------------------------------------- 1 | 127.0.0.1 - - [21/Jan/2025:10:15:23 +0000] "GET / HTTP/1.1" 200 1127 2 | 192.168.1.100 - - [21/Jan/2025:10:15:00 +0000] "POST /index.php HTTP/1.1" 200 384 3 | 192.168.1.104 - - [21/Jan/2025:10:17:00 +0000] "GET /data.txt HTTP/1.1" 200 512 4 | 192.168.1.105 - - [21/Jan/2025:10:17:00 +0000] "GET /access.log HTTP/1.1" 200 512 -------------------------------------------------------------------------------- /tests/test-11/v1/data.txt: -------------------------------------------------------------------------------- 1 | OO O o o o... ______________________ _________________ 2 | O ____ | | | | 3 | ][_n_i_| ( ooo___ | | | | 4 | (__________|_[______]_|____________________|_|_______________| 5 | 0--0--0 0 0 0 0 0 0 0 -------------------------------------------------------------------------------- /tests/test-12/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 5 | 6 | -------------------------------------------------------------------------------- /tests/test-12/v1/index.php: -------------------------------------------------------------------------------- 1 |
"; 7 | chmod($temp_filename, 0644); // Insecure Permissions. 8 | 9 | // Write Data to file. 10 | $file = fopen($temp_filename, 'wb'); 11 | if ($file) { 12 | $data = "Sensitive data flavour."; 13 | fwrite($file, $data); 14 | fclose($file); 15 | 16 | // Reads file. 17 | echo "File contents: "; 18 | include($temp_filename); 19 | echo "

"; 20 | 21 | // Delete file. 22 | if (file_exists($temp_filename)) { 23 | unlink($temp_filename); 24 | echo "Temporary file deleted."; 25 | } else { 26 | echo "Temporary file does not exist when attempting to delete.\n"; 27 | } 28 | } else { 29 | echo "Failed to open temporary file for writing.\n"; 30 | } 31 | } else { 32 | echo "Failed to create temporary file.\n"; 33 | } 34 | ?> 35 | -------------------------------------------------------------------------------- /tests/test-13/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 5 | -------------------------------------------------------------------------------- /tests/test-13/v1/index.php: -------------------------------------------------------------------------------- 1 | ['name' => 'Desmond Bear', 'position' => 'Moon Bear', 'email' => 'Desmond.Bear@example.com'], 5 | 'DougalF' => ['name' => 'Dougal Flop', 'position' => 'Flop Guy', 'email' => 'Dougal.Flop@example.com'] 6 | ]; 7 | 8 | // Function to fetch employee data 9 | function runEmployeeQuery($employees, $name){ 10 | // Vulnerability: No authorization check before returning data 11 | if (isset($employees[$name])) { 12 | return $employees[$name]; 13 | } 14 | return null; 15 | } 16 | 17 | // Check for employee in GET request. 18 | if (isset($_GET['EmployeeName'])) { 19 | $employeeName = $_GET['EmployeeName']; 20 | $employeeData = runEmployeeQuery($employees, $employeeName); 21 | 22 | // Display the employee data. 23 | if ($employeeData) { 24 | echo "

Employee Information:

"; 25 | echo "

Name: " . $employeeData['name'] . "

"; 26 | echo "

Position: " . $employeeData['position'] . "

"; 27 | echo "

Email: " . $employeeData['email'] . "

"; 28 | } else { 29 | echo "

No employee found with the name $employeeName.

"; 30 | } 31 | } else { 32 | echo "

Please provide an employee name to query.

"; 33 | } 34 | ?> 35 | 36 |
37 | 38 | 39 | 40 |
41 | -------------------------------------------------------------------------------- /tests/test-14/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-14/v1/index.php: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /tests/test-15/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-15/v1/index.php: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Vulnerable Login System 34 | 35 | 36 |

Login

37 |
38 | 39 | 40 | 41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /tests/test-16/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-16/v1/index.php: -------------------------------------------------------------------------------- 1 |
2 | Enter URL to download HTML:
3 | 4 |
5 | 6 | -------------------------------------------------------------------------------- /tests/test-17/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-17/v1/index.php: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Vulnerable Login System 33 | 34 | 35 |

Login

36 |
37 | 38 | 39 | 40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /tests/test-19/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-19/v1/index.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | Improper Authentication 17 | 18 | 19 | 20 |

Logged in as:

21 | 22 |

Please provide your username:

23 |
24 | 25 | 26 |
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tests/test-2/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-2/v1/index.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | -------------------------------------------------------------------------------- /tests/test-20/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-20/v1/index.php: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Login Page 33 | 34 | 35 |
36 | 37 | 38 |
39 | 40 | 41 |
42 | 43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/test-21/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-21/v1/index.php: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Test 21 27 | 28 | 29 |

Enter a Value

30 |
31 | 32 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/test-22/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-22/v1/index.php: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | Test 22 14 | 15 | 16 |

Test 22

17 |

Die potato!

18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/test-23/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-23/v1/index.php: -------------------------------------------------------------------------------- 1 | Logged in as: " . htmlspecialchars($_SESSION["user"]) . "

"; 8 | } 9 | ?> 10 | 11 | 12 | 13 | 14 | Vulnerable Login - Session Fixation 15 | 16 | 17 |

Login

18 |
19 |
20 |
21 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test-24/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-24/v1/index.php: -------------------------------------------------------------------------------- 1 | 2 | "password123", 11 | "user1" => "mypassword", 12 | "user2" => "securepass" 13 | ]; 14 | 15 | $records = [ 16 | "admin" => "10 Downing Street, London", 17 | "user1" => "The white house, Washington DC", 18 | "user2" => "Kremlin, Moscow" 19 | ]; 20 | 21 | if ($_SERVER["REQUEST_METHOD"] == "POST") { 22 | $username = $_POST["username"]; 23 | $password = $_POST["password"]; 24 | 25 | if (isset($users[$username]) && $users[$username] === $password) { 26 | file_put_contents($logFile, "User: $username | Password: $password | Record Data: $records[$username] | IP: " . $_SERVER["REMOTE_ADDR"] . "\n", FILE_APPEND); 27 | echo "

Welcome, $username!

"; 28 | } else { 29 | echo "

Invalid credentials

"; 30 | } 31 | } 32 | ?> 33 | 34 | 35 | 36 | 37 | Test 24 38 | 39 | 40 |

Login

41 |
42 | Username:
43 | Password:
44 | 45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /tests/test-24/v1/user_log.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cytix-software/AppSec-Detection-Framework/882e5a4cf4eb04cf563644bbf36c126cbeeb639c/tests/test-24/v1/user_log.txt -------------------------------------------------------------------------------- /tests/test-25/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-25/v1/index.php: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /tests/test-26/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | RUN mkdir -p /var/www/html/uploads && \ 5 | chown -R www-data:www-data /var/www/html/uploads && \ 6 | chmod -R 755 /var/www/html/uploads 7 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-26/v1/index.php: -------------------------------------------------------------------------------- 1 | $fileName"; 14 | } else { 15 | echo "Failed to upload file."; 16 | } 17 | } else { 18 | echo "Invalid file type."; 19 | } 20 | } 21 | ?> 22 | 23 | 24 | 25 | 26 | Vulnerable File Upload 27 | 28 | 29 |

Upload a File

30 |
31 | 32 | 33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /tests/test-27/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-27/v1/index.php: -------------------------------------------------------------------------------- 1 | Received Message:"; 7 | echo "

" . htmlspecialchars($message) . "

"; 8 | } 9 | ?> 10 | 11 | 12 | 13 | 14 | Test 27 15 | 16 | 17 |

Send a Message

18 |
19 | 20 | 21 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/test-28/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-28/v1/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vulnerable JavaScript Loading 5 | 6 | 7 | 8 | 9 |

jQuery Library Test

10 |
11 | 12 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/test-29/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.html ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-29/v1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Weather Widget 7 | 8 | 9 |
10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/test-3/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-3/v1/index.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | -------------------------------------------------------------------------------- /tests/test-30/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-30/v1/index.php: -------------------------------------------------------------------------------- 1 | $value) { 10 | $this->$key = $value; // Mass assignment vulnerability 11 | } 12 | } 13 | } 14 | 15 | if ($_SERVER['REQUEST_METHOD'] === 'POST') { 16 | $userData = $_POST; // Accepts user input directly 17 | $user = new User($userData); // Creates a user object with dynamic attributes 18 | 19 | echo "User Created:
"; 20 | echo "Username: " . htmlspecialchars($user->username) . "
"; 21 | echo "Email: " . htmlspecialchars($user->email) . "
"; 22 | echo "Admin Status: " . ($user->isAdmin ? "Yes" : "No") . "
"; 23 | } 24 | ?> 25 | 26 | 27 | 28 | 29 | Vulnerable Mass Assignment Demo 30 | 31 | 32 |

Create User

33 |
34 |
35 |
36 |
37 | 38 |
39 | 40 | -------------------------------------------------------------------------------- /tests/test-31/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-31/v1/index.php: -------------------------------------------------------------------------------- 1 | "Alice", 7 | 2 => "Bob", 8 | 3 => "Charlie" 9 | ]; 10 | } 11 | 12 | function destroyUserData($userID) { 13 | if (isset($_SESSION['users'][$userID])) { 14 | unset($_SESSION['users'][$userID]); 15 | echo "

User with ID $userID has been deleted.

"; 16 | } else { 17 | echo "

No user found with ID $userID.

"; 18 | } 19 | } 20 | 21 | // Handle request 22 | if (isset($_GET['userID'])) { 23 | $userID = intval($_GET['userID']); 24 | destroyUserData($userID); 25 | } 26 | ?> 27 | 28 | 29 | 30 | 31 | 32 | 33 | User Deletion 34 | 35 | 36 |

Delete User

37 |
38 | 39 | 40 | 41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /tests/test-32/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-32/v1/index.php: -------------------------------------------------------------------------------- 1 | [ 4 | "email" => "desmond@themoon.com", 5 | "password" => "password123", // Insecure: password stored in plain text 6 | "security_question" => "What is your favorite color?", 7 | "security_answer" => "blue" // Weak answer (easily guessed or socially discoverable) 8 | ] 9 | ]; 10 | 11 | if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['recover'])) { 12 | $username = isset($_POST['username']) ? trim($_POST['username']) : ''; 13 | $email = isset($_POST['email']) ? trim($_POST['email']) : ''; 14 | $security_answer = isset($_POST['security_answer']) ? strtolower(trim($_POST['security_answer'])) : ''; 15 | 16 | if (isset($users[$username])) { 17 | $user = $users[$username]; 18 | if ($user['email'] === $email && strtolower($user['security_answer']) === $security_answer) { 19 | echo "

Password recovery successful! Your password is: " . htmlspecialchars($user['password']) . "

"; 20 | } else { 21 | echo "

Error: Invalid email or security answer provided.

"; 22 | } 23 | } else { 24 | echo "

Error: User not found.

"; 25 | } 26 | } 27 | ?> 28 | 29 | 30 | 31 | 32 | 33 | Password Recovery 34 | 35 | 36 |

Password Recovery

37 |
38 |
39 |

40 | 41 |
42 |

43 | 44 |
45 |

46 | 47 | 48 |
49 | 50 | 51 | -------------------------------------------------------------------------------- /tests/test-33/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-33/v1/index.php: -------------------------------------------------------------------------------- 1 | Password for user " . htmlspecialchars($user) . " has been changed to " . htmlspecialchars($pass) . "."; 10 | } else { 11 | echo "

Passwords do not match!

"; 12 | } 13 | } 14 | ?> 15 | 16 | 17 | 18 | 19 | 20 | 21 | Password Change 22 | 23 | 24 |

Change Password Form

25 |
26 | 27 |

28 | 29 | 30 |

31 | 32 | 33 |

34 | 35 | 36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/test-34/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-34/v1/index.php: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | Test 34 15 | 16 | 17 |

Register New User

18 |
19 |
20 |

21 | 22 |
23 |

24 | 25 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/test-35/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-35/v1/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test 35 5 | 6 | 7 | Welcome, authenticated user!"; 10 | echo "

Super secret data: I AM A STEGOSAURUS!

"; 11 | } else { 12 | echo "

Access Denied!

"; 13 | echo "

You are not authorized to view this page.

"; 14 | } 15 | ?> 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/test-36/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-36/v1/index.php: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /tests/test-37/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-37/v1/index.php: -------------------------------------------------------------------------------- 1 | 19 |
20 | 21 | 22 |
23 | 26 | -------------------------------------------------------------------------------- /tests/test-38/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-38/v1/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test-39/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | 3 | # Enable SSL module 4 | RUN a2enmod ssl 5 | 6 | # Create directory for SSL certificates 7 | RUN mkdir -p /etc/apache2/ssl 8 | 9 | # Generate self-signed certificate 10 | RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ 11 | -keyout /etc/apache2/ssl/server.key \ 12 | -out /etc/apache2/ssl/server.crt \ 13 | -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" 14 | 15 | # Copy SSL configuration 16 | COPY ssl.conf /etc/apache2/sites-available/default-ssl.conf 17 | RUN a2ensite default-ssl 18 | 19 | WORKDIR /var/www/html 20 | COPY index.php ./ 21 | 22 | # Expose HTTPS port 23 | EXPOSE 443 -------------------------------------------------------------------------------- /tests/test-39/v1/index.php: -------------------------------------------------------------------------------- 1 | true, // Protects against JavaScript access (but still vulnerable) 8 | "samesite" => "Strict" // Helps prevent CSRF attacks 9 | ]); 10 | 11 | ?> 12 | 13 | 14 | 15 | 16 | Test 39 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/test-39/v1/ssl.conf: -------------------------------------------------------------------------------- 1 | 2 | ServerAdmin webmaster@localhost 3 | DocumentRoot /var/www/html 4 | 5 | ErrorLog ${APACHE_LOG_DIR}/error.log 6 | CustomLog ${APACHE_LOG_DIR}/access.log combined 7 | 8 | SSLEngine on 9 | SSLCertificateFile /etc/apache2/ssl/server.crt 10 | SSLCertificateKeyFile /etc/apache2/ssl/server.key 11 | 12 | 13 | SSLOptions +StdEnvVars 14 | 15 | 16 | SSLOptions +StdEnvVars 17 | 18 | -------------------------------------------------------------------------------- /tests/test-4/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-4/v1/index.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | -------------------------------------------------------------------------------- /tests/test-40/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-40/v1/index.php: -------------------------------------------------------------------------------- 1 | children() as $child) { 15 | $result[$child->getName()] = (string)$child; 16 | } 17 | 18 | // Display the processed data 19 | echo '

Processed XML Data

'; 20 | echo '
';
21 |         print_r($result);
22 |         echo '
'; 23 | 24 | // Also display the raw XML for debugging 25 | echo '

Raw XML

'; 26 | echo '
';
27 |         echo htmlspecialchars($xml->asXML());
28 |         echo '
'; 29 | } else { 30 | echo '

Error: Failed to parse XML file.

'; 31 | $errors = libxml_get_errors(); 32 | foreach ($errors as $error) { 33 | echo '

XML Error: ' . $error->message . '

'; 34 | } 35 | libxml_clear_errors(); 36 | } 37 | } 38 | ?> 39 | 40 | 41 | 42 | 43 | 44 | XXE Vulnerability Example 45 | 46 | 47 |

Upload an XML file to demonstrate XXE vulnerability

48 |
49 | 50 | 51 |
52 | 53 | 54 | -------------------------------------------------------------------------------- /tests/test-41/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-41/v1/index.php: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Test 41 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/test-42/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-42/v1/index.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Test 42 16 | 17 | 18 |

Stored Environment Variable:

19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/test-44/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-44/v1/index.php: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | Test 44 9 | 10 | 11 | 12 |
13 | 14 | 15 |
16 |
17 | 18 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/test-44/v2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine 2 | WORKDIR /app 3 | COPY package.json ./ 4 | RUN npm install 5 | COPY . . 6 | EXPOSE 80 7 | CMD ["node", "server.js"] -------------------------------------------------------------------------------- /tests/test-44/v2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-44-v2", 3 | "version": "1.0.0", 4 | "description": "Test 44 v2 - DOM XSS with AngularJS 1.2.32", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "express": "^4.18.2", 11 | "angular": "1.2.32" 12 | } 13 | } -------------------------------------------------------------------------------- /tests/test-44/v2/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DOM XSS Test 5 | 6 | 7 | 8 |
9 | 10 | 11 |
12 |
13 | 14 | 27 | 28 | -------------------------------------------------------------------------------- /tests/test-44/v2/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | const app = express(); 4 | const port = 80; 5 | 6 | // Serve static files 7 | app.use(express.static(path.join(__dirname, 'public'))); 8 | 9 | // API endpoint that returns user input (vulnerable to XSS) 10 | app.get('/api/search', (req, res) => { 11 | const query = req.query.q || ''; 12 | // Intentionally not sanitizing the input to demonstrate the vulnerability 13 | res.json({ results: query }); 14 | }); 15 | 16 | app.listen(port, () => { 17 | console.log(`Test 44 v2 server running on port ${port}`); 18 | }); -------------------------------------------------------------------------------- /tests/test-45/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.4-apache 2 | COPY index.php /var/www/html/ -------------------------------------------------------------------------------- /tests/test-45/v1/index.php: -------------------------------------------------------------------------------- 1 | time() + 3600, 4 | 'path' => '/', 5 | 'secure' => false, 6 | 'httponly' => false 7 | ]); 8 | ?> 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/test-46/v1/Controllers/UserController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Test46.Models; 3 | 4 | namespace Test46.Controllers 5 | { 6 | [ApiController] 7 | [Route("api/[controller]")] 8 | public class UserController : ControllerBase 9 | { 10 | private static List _users = new(); 11 | 12 | [HttpPost("register")] 13 | public IActionResult Register(User user) 14 | { 15 | // No manual validation - relying on automatic model validation 16 | // which is disabled in Program.cs 17 | _users.Add(user); 18 | return Ok(); 19 | } 20 | 21 | [HttpGet] 22 | public IActionResult GetUsers() 23 | { 24 | return Ok(_users); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /tests/test-46/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build 2 | WORKDIR /app 3 | COPY *.csproj ./ 4 | RUN dotnet restore 5 | COPY . . 6 | RUN dotnet publish -c Release -o out 7 | 8 | FROM mcr.microsoft.com/dotnet/aspnet:7.0 9 | WORKDIR /app 10 | COPY --from=build /app/out . 11 | EXPOSE 80 12 | ENTRYPOINT ["dotnet", "Test46.dll"] -------------------------------------------------------------------------------- /tests/test-46/v1/Models/User.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Test46.Models 4 | { 5 | public class User 6 | { 7 | [Required] 8 | [MinLength(3)] 9 | public string Username { get; set; } 10 | 11 | [Required] 12 | [EmailAddress] 13 | public string Email { get; set; } 14 | 15 | [Required] 16 | [MinLength(8)] 17 | public string Password { get; set; } 18 | 19 | [Required] 20 | [Range(0, 150)] 21 | public int Age { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /tests/test-46/v1/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace Test46 8 | { 9 | public class Program 10 | { 11 | public static void Main(string[] args) 12 | { 13 | var builder = WebApplication.CreateBuilder(args); 14 | 15 | // Add services to the container. 16 | builder.Services.AddControllers() 17 | .ConfigureApiBehaviorOptions(options => 18 | { 19 | // Disable automatic model validation 20 | options.SuppressModelStateInvalidFilter = true; 21 | }); 22 | 23 | var app = builder.Build(); 24 | 25 | // Configure the HTTP request pipeline. 26 | if (app.Environment.IsDevelopment()) 27 | { 28 | app.UseDeveloperExceptionPage(); 29 | } 30 | 31 | app.UseDefaultFiles(); 32 | app.UseStaticFiles(); 33 | app.UseHttpsRedirection(); 34 | app.UseAuthorization(); 35 | app.MapControllers(); 36 | 37 | app.Run(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /tests/test-46/v1/Test46.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/test-46/v1/Tests/UserControllerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Net.Http; 3 | using System.Text; 4 | using System.Text.Json; 5 | using Microsoft.AspNetCore.Mvc.Testing; 6 | using Test46.Models; 7 | using Xunit; 8 | 9 | namespace Test46.Tests 10 | { 11 | public class UserControllerTests : IClassFixture> 12 | { 13 | private readonly WebApplicationFactory _factory; 14 | private readonly HttpClient _client; 15 | 16 | public UserControllerTests(WebApplicationFactory factory) 17 | { 18 | _factory = factory; 19 | _client = _factory.CreateClient(); 20 | } 21 | 22 | [Fact] 23 | public async Task Register_WithInvalidModel_StillSucceeds() 24 | { 25 | // Arrange 26 | var invalidUser = new User 27 | { 28 | // Missing required fields 29 | Username = "", // Empty username 30 | Email = "not-an-email", // Invalid email format 31 | Password = "123" // Too short password 32 | }; 33 | 34 | var content = new StringContent( 35 | JsonSerializer.Serialize(invalidUser), 36 | Encoding.UTF8, 37 | "application/json"); 38 | 39 | // Act 40 | var response = await _client.PostAsync("/api/user/register", content); 41 | 42 | // Assert 43 | Assert.Equal(HttpStatusCode.OK, response.StatusCode); 44 | 45 | // Verify the invalid user was actually added 46 | var getResponse = await _client.GetAsync("/api/user"); 47 | var users = await JsonSerializer.DeserializeAsync>( 48 | await getResponse.Content.ReadAsStreamAsync()); 49 | 50 | Assert.Contains(users, u => u.Email == invalidUser.Email); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /tests/test-46/v1/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CWE-1174 Demo - Model Validation Bypass 7 | 90 | 91 | 92 |
93 |

User Registration Form (CWE-1174 Demo)

94 | 95 |
96 |

Expected Validation Rules:

97 |
    98 |
  • Username: Required, minimum 3 characters
  • 99 |
  • Email: Required, must be valid email format
  • 100 |
  • Password: Required, minimum 8 characters
  • 101 |
  • Age: Required, between 0 and 150
  • 102 |
103 |

Note: Due to disabled model validation (CWE-1174), these rules are not enforced!

104 |
105 | 106 |
107 |
108 | 109 | 110 |
111 |
112 | 113 | 114 |
115 |
116 | 117 | 118 |
119 |
120 | 121 | 122 |
123 | 124 |
125 | 126 |
127 | 128 |
129 |

Registered Users

130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 |
UsernameEmailPasswordAge
142 |
143 |
144 | 145 | 220 | 221 | -------------------------------------------------------------------------------- /tests/test-47/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-47/v1/index.php: -------------------------------------------------------------------------------- 1 | true, 'total' => $total]); 15 | exit; 16 | } 17 | ?> 18 | 19 |
20 |
21 |
22 | Premium Widget 23 |

Price: $100.00

24 |
25 | 26 | 27 |
28 |

Total: $100.00

29 | 30 |
31 |
32 |
33 |
34 |
35 |
36 | 37 | Shopping Cart 38 |
39 |
40 |

Cart Total: $0.00

41 | 42 |
43 |
44 |
45 | 46 | -------------------------------------------------------------------------------- /tests/test-48/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ -------------------------------------------------------------------------------- /tests/test-48/v1/index.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | Secure Banking 18 | 19 | 20 |

Secure Banking

21 |

Please enter your transfer details below:

22 | 23 |
24 | 25 | 26 |
27 | 28 | 29 |
30 | 31 |
32 | 33 | 34 |
35 | 36 | 37 |
38 | 39 | -------------------------------------------------------------------------------- /tests/test-49/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | 3 | # Expose port 80 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-5/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-5/v1/index.php: -------------------------------------------------------------------------------- 1 | 1 2 | 3 | "; 7 | } 8 | ?> -------------------------------------------------------------------------------- /tests/test-50/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | 3 | # Copy the vulnerable application 4 | COPY index.php /var/www/html/ 5 | 6 | # Expose port 80 7 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-50/v1/index.php: -------------------------------------------------------------------------------- 1 | '12345', 14 | 'email' => 'user@example.com', 15 | 'role' => 'admin' 16 | ]; 17 | 18 | // Return the sensitive data as JSON 19 | header('Content-Type: application/json'); 20 | echo json_encode($sensitiveData); 21 | ?> -------------------------------------------------------------------------------- /tests/test-51/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | 3 | # Copy the vulnerable application 4 | COPY index.php /var/www/html/ 5 | 6 | # Expose port 80 7 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-51/v1/index.php: -------------------------------------------------------------------------------- 1 | 11 | 17 | 18 | 19 | 20 | 21 | 22 | 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 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | XML; 82 | 83 | // Output the WSDL file 84 | echo $wsdl; 85 | ?> -------------------------------------------------------------------------------- /tests/test-52/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json ./ 6 | RUN npm install 7 | 8 | COPY . . 9 | 10 | EXPOSE 80 11 | 12 | CMD ["node", "server.js"] -------------------------------------------------------------------------------- /tests/test-52/v1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CWE-523 Test 5 | 6 | 7 |

Login Form (Vulnerable to CWE-523)

8 |
9 |

10 |

11 | 12 |
13 |
14 | 15 | 36 | 37 | -------------------------------------------------------------------------------- /tests/test-52/v1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cwe-523-test", 3 | "version": "1.0.0", 4 | "description": "Test for CWE-523: Unprotected Transport of Credentials", 5 | "main": "server.js", 6 | "dependencies": { 7 | "express": "^4.18.2" 8 | } 9 | } -------------------------------------------------------------------------------- /tests/test-52/v1/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | const app = express(); 4 | 5 | app.use(express.json()); 6 | app.use(express.static(path.join(__dirname))); 7 | 8 | // Vulnerable to CWE-523: Unprotected Transport of Credentials 9 | // This server sends credentials over HTTP instead of HTTPS 10 | app.post('/login', (req, res) => { 11 | const { username, password } = req.body; 12 | 13 | // Simulate authentication 14 | if (username === 'admin' && password === 'password123') { 15 | res.json({ 16 | status: 'success', 17 | message: 'Login successful', 18 | // Sensitive data sent over HTTP 19 | userData: { 20 | id: 1, 21 | role: 'admin', 22 | apiKey: 'FAKE_API_KEY_1234567890', 23 | sessionToken: 'FAKE_SESSION_TOKEN_1234567890' 24 | } 25 | }); 26 | } else { 27 | res.status(401).json({ 28 | status: 'error', 29 | message: 'Invalid credentials' 30 | }); 31 | } 32 | }); 33 | 34 | app.listen(80, () => { 35 | console.log('Server running on http://localhost:80'); 36 | }); -------------------------------------------------------------------------------- /tests/test-53/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | 3 | COPY . /var/www/html/ 4 | 5 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-53/v1/index.php: -------------------------------------------------------------------------------- 1 | 1, 10 | 'username' => 'admin', 11 | 'role' => 'administrator', 12 | 'apiKey' => 'FAKE_API_KEY_1234567890' 13 | ]; 14 | } 15 | 16 | // Set cache control headers to cache everything 17 | header('Cache-Control: public, max-age=31536000'); // Cache for 1 year 18 | header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + 31536000)); // Expires in 1 year 19 | header('Pragma: public'); // Explicitly allow caching 20 | header('Content-Type: application/json'); 21 | 22 | // Return sensitive user data that will be cached by the browser 23 | echo json_encode([ 24 | 'status' => 'success', 25 | 'user' => $_SESSION['user'], 26 | 'sensitiveData' => [ 27 | 'accountNumber' => '1234567890', 28 | 'balance' => 10000.00, 29 | 'lastTransaction' => '2024-03-20T10:00:00Z', 30 | 'creditCard' => [ 31 | 'number' => '4111111111111111', 32 | 'expiry' => '12/25', 33 | 'cvv' => '123' 34 | ] 35 | ] 36 | ]); 37 | ?> -------------------------------------------------------------------------------- /tests/test-54/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | 3 | COPY . /var/www/html/ 4 | 5 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-54/v1/index.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | Open Redirect Test 15 | 16 | 17 |

Open Redirect Vulnerability Test

18 |

Enter any URL to redirect to:

19 |
20 |

21 | 22 |
23 |

Example: Try redirecting to https://example.com

24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/test-55/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | 3 | COPY . /var/www/html/ 4 | 5 | # Ensure the debug log file is writable 6 | RUN touch /var/www/html/debug.log && \ 7 | chmod 666 /var/www/html/debug.log 8 | 9 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-55/v1/index.php: -------------------------------------------------------------------------------- 1 | 1, 8 | 'username' => 'admin', 9 | 'apiKey' => 'FAKE_API_KEY_1234567890', 10 | 'creditCard' => [ 11 | 'number' => '4111111111111111', 12 | 'expiry' => '12/25', 13 | 'cvv' => '123' 14 | ] 15 | ]; 16 | 17 | // Add random transaction data 18 | $userData['transaction'] = [ 19 | 'id' => 'TX' . rand(100000, 999999), 20 | 'amount' => rand(10, 1000), 21 | 'timestamp' => date('Y-m-d H:i:s'), 22 | 'status' => 'completed' 23 | ]; 24 | 25 | // Vulnerable: Store sensitive data in a publicly accessible log file 26 | $logEntry = json_encode($userData, JSON_PRETTY_PRINT) . "\n---\n"; 27 | file_put_contents('debug.log', $logEntry, FILE_APPEND); 28 | 29 | // Read the last few entries to display 30 | $logContent = file_exists('debug.log') ? file_get_contents('debug.log') : ''; 31 | $entries = array_filter(explode('---', $logContent)); 32 | $lastEntries = array_slice($entries, -5); // Show last 5 entries 33 | ?> 34 | 35 | 36 | 37 | CWE-538 Test 38 | 41 | 42 | 43 |

Sensitive Data Storage Test

44 |

New transaction data has been automatically stored in the log file.

45 | 46 |

Recent Log Entries:

47 | 48 |
49 | 50 | 51 |

Note: This test stores sensitive data in a publicly accessible file at debug.log

52 | 53 | -------------------------------------------------------------------------------- /tests/test-56/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | 3 | # Copy application files 4 | COPY . /var/www/html/ 5 | 6 | # Expose port 80 7 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-56/v1/index.php: -------------------------------------------------------------------------------- 1 | 'user' // Regular user role 11 | ]; 12 | } 13 | 14 | // Vulnerable function: No proper access control check 15 | function getAdminData() { 16 | // Missing proper role check 17 | return [ 18 | 'admin_users' => [ 19 | ['username' => 'admin1', 'email' => 'admin1@example.com'], 20 | ['username' => 'admin2', 'email' => 'admin2@example.com'] 21 | ], 22 | 'system_settings' => [ 23 | 'maintenance_mode' => false, 24 | 'debug_mode' => true 25 | ] 26 | ]; 27 | } 28 | 29 | // Handle admin data request 30 | if (isset($_GET['action']) && $_GET['action'] === 'get_admin_data') { 31 | header('Content-Type: application/json'); 32 | echo json_encode(getAdminData()); 33 | exit; 34 | } 35 | ?> 36 | 37 | 38 | 39 | 40 | CWE-284 41 | 45 | 46 | 47 |

CWE-284: Improper Access Control

48 |

Current role:

49 | 50 |
51 | 52 | 62 | 63 | -------------------------------------------------------------------------------- /tests/test-6/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-6/v1/index.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | -------------------------------------------------------------------------------- /tests/test-7/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-7/v1/index.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | inject)){ 15 | eval($this->inject); 16 | } 17 | } 18 | } 19 | $output=unserialize($input); 20 | echo $output[0]; 21 | } 22 | ?> -------------------------------------------------------------------------------- /tests/test-8/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-8/v1/index.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /tests/test-9/v1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | WORKDIR /var/www/html 3 | COPY index.php ./ 4 | EXPOSE 80 -------------------------------------------------------------------------------- /tests/test-9/v1/index.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | "; 13 | 14 | if (is_file($filename)) { 15 | $handle = fopen($filename, 'r'); 16 | $output = fread($handle, filesize($filename)); 17 | fclose($handle); 18 | echo nl2br(htmlspecialchars($output)); 19 | } else { 20 | echo "File not found or invalid file."; 21 | } 22 | } 23 | ?> -------------------------------------------------------------------------------- /tests/test-9/v1/readme.txt: -------------------------------------------------------------------------------- 1 | Hello world! -------------------------------------------------------------------------------- /utils/checkProfileConsistency.ts: -------------------------------------------------------------------------------- 1 | import { Data } from './types'; 2 | import fs from 'fs'; 3 | import yaml from 'js-yaml'; 4 | 5 | interface DockerService { 6 | profiles?: string[]; 7 | } 8 | 9 | interface DockerCompose { 10 | services: Record; 11 | } 12 | 13 | function loadData(): Data { 14 | const data = fs.readFileSync('data.json', 'utf8'); 15 | return JSON.parse(data); 16 | } 17 | 18 | function loadDockerCompose(): DockerCompose { 19 | const dockerCompose = fs.readFileSync('docker-compose.yml', 'utf8'); 20 | return yaml.load(dockerCompose) as DockerCompose; 21 | } 22 | 23 | function extractCweFromProfile(profile: string): number | null { 24 | const match = profile.match(/^cwe-(\d+)$/i); 25 | return match ? parseInt(match[1], 10) : null; 26 | } 27 | 28 | function extractOwaspFromProfile(profile: string): string | null { 29 | const match = profile.match(/^a\d{2}:\d{4}$/i); 30 | return match ? match[0].toLowerCase() : null; 31 | } 32 | 33 | function findCweInData(cweId: number, data: Data): boolean { 34 | return data.vulnerabilities.some(vuln => 35 | vuln.CWEDetails.some(cwe => cwe.id === cweId) 36 | ); 37 | } 38 | 39 | function findTestInData(testName: string, data: Data): boolean { 40 | return data.vulnerabilities.some(vuln => 41 | vuln.CWEDetails.some(cwe => cwe.tests.includes(testName)) 42 | ); 43 | } 44 | 45 | function getOwaspForTest(testName: string, data: Data): string[] { 46 | const owaspCategories = new Set(); 47 | 48 | for (const vuln of data.vulnerabilities) { 49 | for (const cwe of vuln.CWEDetails) { 50 | if (cwe.tests.includes(testName)) { 51 | owaspCategories.add(vuln.OWASP.toLowerCase()); 52 | } 53 | } 54 | } 55 | 56 | return Array.from(owaspCategories); 57 | } 58 | 59 | function getCwesForTest(testName: string, data: Data): number[] { 60 | const cweIds = new Set(); 61 | 62 | for (const vuln of data.vulnerabilities) { 63 | for (const cwe of vuln.CWEDetails) { 64 | if (cwe.tests.includes(testName)) { 65 | cweIds.add(cwe.id); 66 | } 67 | } 68 | } 69 | 70 | return Array.from(cweIds); 71 | } 72 | 73 | function main() { 74 | const data = loadData(); 75 | const dockerCompose = loadDockerCompose(); 76 | 77 | const inconsistencies: string[] = []; 78 | 79 | // Check each service in docker-compose.yml 80 | for (const [serviceName, service] of Object.entries(dockerCompose.services)) { 81 | if (!service.profiles) continue; 82 | 83 | // Check if test exists in data.json 84 | if (!findTestInData(serviceName, data)) { 85 | inconsistencies.push(`Test "${serviceName}" exists in docker-compose.yml but not in data.json`); 86 | continue; 87 | } 88 | 89 | // Check if test has the "all" profile 90 | if (!service.profiles.some(p => p.toLowerCase() === 'all')) { 91 | inconsistencies.push(`Test "${serviceName}" is missing the "all" profile in docker-compose.yml`); 92 | } 93 | 94 | // Get expected CWEs and OWASP categories from data.json 95 | const expectedCwes = getCwesForTest(serviceName, data); 96 | const expectedOwasp = getOwaspForTest(serviceName, data); 97 | 98 | // Check CWE profiles 99 | const cweProfiles = service.profiles 100 | .filter(profile => profile.toLowerCase().startsWith('cwe-')) 101 | .map(profile => extractCweFromProfile(profile)) 102 | .filter((cweId): cweId is number => cweId !== null); 103 | 104 | // Check for missing CWEs in docker-compose.yml 105 | for (const cweId of expectedCwes) { 106 | const cweProfile = `cwe-${cweId}`; 107 | if (!service.profiles?.some(p => p.toLowerCase() === cweProfile.toLowerCase())) { 108 | inconsistencies.push(`CWE-${cweId} is referenced in data.json for "${serviceName}" but not in docker-compose.yml profiles`); 109 | } 110 | } 111 | 112 | // Check for extra CWEs in docker-compose.yml 113 | for (const cweId of cweProfiles) { 114 | if (!expectedCwes.includes(cweId)) { 115 | inconsistencies.push(`CWE-${cweId} is referenced in docker-compose.yml for "${serviceName}" but not found in data.json`); 116 | } 117 | } 118 | 119 | // Check OWASP profiles 120 | const owaspProfiles = service.profiles 121 | .filter(profile => profile.toLowerCase().match(/^a\d{2}:\d{4}$/)) 122 | .map(profile => extractOwaspFromProfile(profile)) 123 | .filter((owasp): owasp is string => owasp !== null); 124 | 125 | // Check for missing OWASP categories in docker-compose.yml 126 | for (const owasp of expectedOwasp) { 127 | if (!owaspProfiles.includes(owasp)) { 128 | inconsistencies.push(`OWASP category "${owasp}" is referenced in data.json for "${serviceName}" but not in docker-compose.yml profiles`); 129 | } 130 | } 131 | 132 | // Check for extra OWASP categories in docker-compose.yml 133 | for (const owasp of owaspProfiles) { 134 | if (!expectedOwasp.includes(owasp)) { 135 | inconsistencies.push(`OWASP category "${owasp}" is referenced in docker-compose.yml for "${serviceName}" but not found in data.json`); 136 | } 137 | } 138 | } 139 | 140 | // Check each test in data.json 141 | for (const vuln of data.vulnerabilities) { 142 | for (const cwe of vuln.CWEDetails) { 143 | for (const test of cwe.tests) { 144 | const service = dockerCompose.services[test]; 145 | if (!service) { 146 | inconsistencies.push(`Test "${test}" exists in data.json but not in docker-compose.yml`); 147 | } 148 | } 149 | } 150 | } 151 | 152 | if (inconsistencies.length === 0) { 153 | console.log('✅ All profiles and vulnerabilities are consistent between docker-compose.yml and data.json'); 154 | } else { 155 | console.log('❌ Found inconsistencies between docker-compose.yml and data.json:'); 156 | inconsistencies.forEach(msg => console.log(` - ${msg}`)); 157 | process.exit(1); 158 | } 159 | } 160 | 161 | if (require.main === module) { 162 | main(); 163 | } -------------------------------------------------------------------------------- /utils/findMissingTests.ts: -------------------------------------------------------------------------------- 1 | import { program } from 'commander'; 2 | import { Data } from './types'; 3 | 4 | interface CWEDetail { 5 | id: number; 6 | title: string; 7 | tests: string[]; 8 | } 9 | 10 | interface Vulnerability { 11 | OWASP: string; 12 | CWEDetails: CWEDetail[]; 13 | } 14 | 15 | interface TestResult { 16 | test: string; 17 | detectedCWEs: number[]; 18 | undetectedCWEs: number[]; 19 | } 20 | 21 | interface ScannerResult { 22 | scanProfile: string; 23 | tests: TestResult[]; 24 | } 25 | 26 | interface TestCWEMap { 27 | [key: string]: Set; 28 | } 29 | 30 | interface IncorrectCWEs { 31 | detected: Set; 32 | undetected: Set; 33 | } 34 | 35 | interface IncorrectCWEMap { 36 | [key: string]: IncorrectCWEs; 37 | } 38 | 39 | export async function loadData(): Promise { 40 | try { 41 | const file = Bun.file('data.json'); 42 | return await file.json(); 43 | } catch (error) { 44 | console.error(`Error loading data file: ${error}`); 45 | process.exit(1); 46 | } 47 | } 48 | 49 | function getAllTestsAndCWEs(data: Data): [Set, TestCWEMap] { 50 | const allTests = new Set(); 51 | const testCWEs: TestCWEMap = {}; 52 | 53 | // Get tests and CWEs from vulnerabilities section 54 | for (const vuln of data.vulnerabilities) { 55 | for (const cweDetail of vuln.CWEDetails) { 56 | const cweId = cweDetail.id.toString(); 57 | for (const test of cweDetail.tests) { 58 | allTests.add(test); 59 | if (!testCWEs[test]) { 60 | testCWEs[test] = new Set(); 61 | } 62 | testCWEs[test].add(cweId); 63 | } 64 | } 65 | } 66 | 67 | return [allTests, testCWEs]; 68 | } 69 | 70 | function analyzeScannerResults( 71 | scannerName: string, 72 | scanner: ScannerResult, 73 | allTests: Set, 74 | testCWEs: TestCWEMap 75 | ): [Set, TestCWEMap, IncorrectCWEMap] { 76 | const missingTests = new Set(); 77 | const missingCWEs: TestCWEMap = {}; 78 | const incorrectCWEs: IncorrectCWEMap = {}; 79 | 80 | // Get tests that the scanner has run 81 | const scannerTests = new Set(scanner.tests.map(test => test.test)); 82 | 83 | // Find missing tests 84 | for (const test of allTests) { 85 | if (!scannerTests.has(test)) { 86 | missingTests.add(test); 87 | } 88 | } 89 | 90 | // Find missing and incorrect CWEs for each test 91 | for (const test of scanner.tests) { 92 | const testName = test.test; 93 | if (!testName) continue; 94 | 95 | // Get CWEs that the scanner detected and undetected 96 | const detectedCWEs = new Set(test.detectedCWEs.map(cwe => cwe.toString())); 97 | const undetectedCWEs = new Set(test.undetectedCWEs.map(cwe => cwe.toString())); 98 | const scannerCWEs = new Set([...detectedCWEs, ...undetectedCWEs]); 99 | 100 | // Get expected CWEs for this test 101 | const expectedCWEs = testCWEs[testName] || new Set(); 102 | 103 | // Find missing CWEs (expected but not in scanner results) 104 | const missing = new Set([...expectedCWEs].filter(cwe => !scannerCWEs.has(cwe))); 105 | if (missing.size > 0) { 106 | missingCWEs[testName] = missing; 107 | } 108 | 109 | // Find incorrect CWEs (in scanner results but not expected) 110 | const incorrectDetected = new Set([...detectedCWEs].filter(cwe => !expectedCWEs.has(cwe))); 111 | const incorrectUndetected = new Set([...undetectedCWEs].filter(cwe => !expectedCWEs.has(cwe))); 112 | 113 | if (incorrectDetected.size > 0 || incorrectUndetected.size > 0) { 114 | incorrectCWEs[testName] = { 115 | detected: incorrectDetected, 116 | undetected: incorrectUndetected 117 | }; 118 | } 119 | } 120 | 121 | return [missingTests, missingCWEs, incorrectCWEs]; 122 | } 123 | 124 | function printResults( 125 | scannerName: string, 126 | missingTests: Set, 127 | missingCWEs: TestCWEMap, 128 | incorrectCWEs: IncorrectCWEMap, 129 | verbose: boolean = false, 130 | simplified: boolean = false 131 | ): void { 132 | if (simplified) { 133 | console.log(`\n=== ${scannerName} ===`); 134 | 135 | // Print missing tests count 136 | console.log(`Missing Tests: ${missingTests.size}`); 137 | if (missingTests.size > 0) { 138 | [...missingTests].sort().forEach(test => { 139 | console.log(` - ${test}`); 140 | }); 141 | } 142 | 143 | // Print missing CWEs count 144 | const totalMissingCWEs = Object.values(missingCWEs) 145 | .reduce((sum, cwes) => sum + cwes.size, 0); 146 | console.log(`\nMissing CWEs: ${totalMissingCWEs}`); 147 | 148 | // Print incorrect CWEs count 149 | const totalIncorrectCWEs = Object.values(incorrectCWEs) 150 | .reduce((sum, details) => sum + details.detected.size + details.undetected.size, 0); 151 | console.log(`Incorrect CWEs: ${totalIncorrectCWEs}`); 152 | 153 | return; 154 | } 155 | 156 | console.log(`\n=== Analysis for ${scannerName} ===`); 157 | 158 | if (missingTests.size > 0) { 159 | console.log(`\nMissing Tests (${missingTests.size}):`); 160 | [...missingTests].sort().forEach(test => { 161 | console.log(` - ${test}`); 162 | }); 163 | } else { 164 | console.log('\nNo missing tests!'); 165 | } 166 | 167 | if (Object.keys(missingCWEs).length > 0) { 168 | const totalMissing = Object.values(missingCWEs) 169 | .reduce((sum, cwes) => sum + cwes.size, 0); 170 | console.log(`\nMissing CWEs (${totalMissing} total):`); 171 | Object.entries(missingCWEs).sort().forEach(([test, cwes]) => { 172 | console.log(`\n ${test}:`); 173 | [...cwes].sort().forEach(cwe => { 174 | console.log(` - CWE-${cwe}`); 175 | }); 176 | }); 177 | } else { 178 | console.log('\nNo missing CWEs!'); 179 | } 180 | 181 | if (Object.keys(incorrectCWEs).length > 0) { 182 | const totalIncorrect = Object.values(incorrectCWEs) 183 | .reduce((sum, details) => sum + details.detected.size + details.undetected.size, 0); 184 | console.log(`\nIncorrect CWE Associations (${totalIncorrect} total):`); 185 | Object.entries(incorrectCWEs).sort().forEach(([test, details]) => { 186 | if (details.detected.size > 0 || details.undetected.size > 0) { 187 | console.log(`\n ${test}:`); 188 | if (details.detected.size > 0) { 189 | console.log(' Incorrectly Detected:'); 190 | [...details.detected].sort().forEach(cwe => { 191 | console.log(` - CWE-${cwe}`); 192 | }); 193 | } 194 | if (details.undetected.size > 0) { 195 | console.log(' Incorrectly Undetected:'); 196 | [...details.undetected].sort().forEach(cwe => { 197 | console.log(` - CWE-${cwe}`); 198 | }); 199 | } 200 | } 201 | }); 202 | } else { 203 | console.log('\nNo incorrect CWE associations!'); 204 | } 205 | } 206 | 207 | async function main(): Promise { 208 | program 209 | .option('--file ', 'Path to the data.json file', 'data.json') 210 | .option('-v, --verbose', 'Enable verbose output') 211 | .option('-s, --simplified', 'Enable simplified output') 212 | .parse(process.argv); 213 | 214 | const options = program.opts(); 215 | 216 | // Load data 217 | const data = await loadData(); 218 | 219 | // Get all tests and their CWEs 220 | const [allTests, testCWEs] = getAllTestsAndCWEs(data); 221 | 222 | // Analyze each scanner 223 | Object.entries(data.recordedTests).forEach(([scannerName, scanner]) => { 224 | const [missingTests, missingCWEs, incorrectCWEs] = analyzeScannerResults( 225 | scannerName, 226 | scanner, 227 | allTests, 228 | testCWEs 229 | ); 230 | printResults(scannerName, missingTests, missingCWEs, incorrectCWEs, options.verbose, options.simplified); 231 | }); 232 | } 233 | 234 | if (import.meta.main) { 235 | main(); 236 | } -------------------------------------------------------------------------------- /utils/findUncoveredCwes.ts: -------------------------------------------------------------------------------- 1 | import { program } from 'commander'; 2 | import { loadData } from './findMissingTests'; 3 | import { Data, CweDetails, Vulnerability } from './types'; 4 | 5 | function getAllCwes(data: Data): Map { 6 | const cweMap = new Map(); 7 | 8 | for (const vuln of data.vulnerabilities) { 9 | for (const cwe of vuln.CWEDetails) { 10 | // Skip CWEs that are OWASP Top Ten categories 11 | if (!cwe.title.includes('OWASP Top Ten')) { 12 | cweMap.set(cwe.id, cwe.title); 13 | } 14 | } 15 | } 16 | 17 | return cweMap; 18 | } 19 | 20 | function findUncoveredCwes(data: Data, verbose = false, simplified = false): void { 21 | const cweMap = getAllCwes(data); 22 | 23 | // Get all CWEs that have tests 24 | const cwesWithTests = new Set(); 25 | for (const vuln of data.vulnerabilities) { 26 | for (const cwe of vuln.CWEDetails) { 27 | // Skip OWASP Top Ten categories when counting tests 28 | if (!cwe.title.includes('OWASP Top Ten') && cwe.tests.length > 0) { 29 | cwesWithTests.add(cwe.id); 30 | } 31 | } 32 | } 33 | 34 | // Find CWEs without tests 35 | const uncoveredCwes = new Set(); 36 | for (const [cweId] of cweMap) { 37 | if (!cwesWithTests.has(cweId)) { 38 | uncoveredCwes.add(cweId); 39 | } 40 | } 41 | 42 | if (simplified) { 43 | console.log('\nCWEs without tests:'); 44 | console.log('----------------------------------------'); 45 | for (const cwe of Array.from(uncoveredCwes).sort((a, b) => a - b)) { 46 | console.log(`${cwe}: ${cweMap.get(cwe)}`); 47 | } 48 | return; 49 | } 50 | 51 | if (verbose) { 52 | console.log('----------------------------------------'); 53 | console.log('Note: CWEs with titles containing "OWASP Top Ten" are excluded as these are category references rather than specific CWEs.'); 54 | console.log('----------------------------------------'); 55 | console.log('\nAnalyzing CWEs without tests'); 56 | console.log('----------------------------------------'); 57 | console.log(`Total CWEs (excluding OWASP Top Ten categories): ${cweMap.size}`); 58 | console.log(`CWEs with tests: ${cwesWithTests.size}`); 59 | console.log(`CWEs without tests: ${uncoveredCwes.size}`); 60 | console.log('\nCWEs without tests by OWASP Category:'); 61 | console.log('----------------------------------------'); 62 | 63 | // Group uncovered CWEs by OWASP category 64 | const cwesByCategory = new Map>(); 65 | const coveredCwesByCategory = new Map(); 66 | const totalCwesByCategory = new Map(); 67 | 68 | // Initialize counts for all OWASP categories 69 | for (const vuln of data.vulnerabilities) { 70 | coveredCwesByCategory.set(vuln.OWASP, 0); 71 | totalCwesByCategory.set(vuln.OWASP, 0); 72 | } 73 | 74 | // Count total and covered CWEs for each category 75 | for (const vuln of data.vulnerabilities) { 76 | for (const cwe of vuln.CWEDetails) { 77 | if (!cwe.title.includes('OWASP Top Ten')) { 78 | totalCwesByCategory.set( 79 | vuln.OWASP, 80 | (totalCwesByCategory.get(vuln.OWASP) || 0) + 1 81 | ); 82 | if (cwe.tests.length > 0) { 83 | coveredCwesByCategory.set( 84 | vuln.OWASP, 85 | (coveredCwesByCategory.get(vuln.OWASP) || 0) + 1 86 | ); 87 | } 88 | } 89 | } 90 | } 91 | 92 | for (const cwe of Array.from(uncoveredCwes).sort((a, b) => a - b)) { 93 | // Find which vulnerabilities this CWE is associated with 94 | for (const vuln of data.vulnerabilities) { 95 | for (const cweDetail of vuln.CWEDetails) { 96 | if (cweDetail.id === cwe) { 97 | if (!cwesByCategory.has(vuln.OWASP)) { 98 | cwesByCategory.set(vuln.OWASP, []); 99 | } 100 | cwesByCategory.get(vuln.OWASP)?.push({ 101 | id: cwe, 102 | title: cweMap.get(cwe) || '' 103 | }); 104 | } 105 | } 106 | } 107 | } 108 | 109 | // Find categories with complete coverage 110 | const completeCategories = Array.from(totalCwesByCategory.entries()) 111 | .filter(([category, total]) => { 112 | const covered = coveredCwesByCategory.get(category) || 0; 113 | return total > 0 && covered === total; 114 | }) 115 | .map(([category]) => category) 116 | .sort(); 117 | 118 | if (completeCategories.length > 0) { 119 | console.log('\nOWASP Categories with Complete Coverage:'); 120 | console.log('----------------------------------------'); 121 | for (const category of completeCategories) { 122 | const total = totalCwesByCategory.get(category) || 0; 123 | console.log(`${category} (${total} CWEs covered)`); 124 | } 125 | } 126 | 127 | // Print CWEs grouped by OWASP category 128 | console.log('\nOWASP Categories with Missing Coverage:'); 129 | console.log('----------------------------------------'); 130 | for (const [category, cwes] of Array.from(cwesByCategory.entries()).sort()) { 131 | const coveredCount = coveredCwesByCategory.get(category) || 0; 132 | const totalCount = totalCwesByCategory.get(category) || 0; 133 | console.log(`\n${category} (${coveredCount}/${totalCount} covered):`); 134 | console.log(' Missing tests for:'); 135 | for (const cwe of cwes.sort((a, b) => a.id - b.id)) { 136 | console.log(` - CWE-${cwe.id}: ${cwe.title}`); 137 | } 138 | } 139 | } else { 140 | console.log(`\nCWEs without tests: ${uncoveredCwes.size}`); 141 | console.log('----------------------------------------'); 142 | for (const cwe of Array.from(uncoveredCwes).sort((a, b) => a - b)) { 143 | console.log(`CWE-${cwe}: ${cweMap.get(cwe)}`); 144 | } 145 | } 146 | } 147 | 148 | async function main() { 149 | const args = process.argv.slice(2); 150 | const verbose = args.includes('-v') || args.includes('--verbose'); 151 | const simplified = args.includes('-s') || args.includes('--simplified'); 152 | 153 | const data = await loadData(); 154 | findUncoveredCwes(data, verbose, simplified); 155 | } 156 | 157 | if (require.main === module) { 158 | main().catch(error => { 159 | console.error('Error:', error); 160 | process.exit(1); 161 | }); 162 | } -------------------------------------------------------------------------------- /utils/types.ts: -------------------------------------------------------------------------------- 1 | export interface CweDetails { 2 | id: number; 3 | title: string; 4 | tests: string[]; 5 | } 6 | 7 | export interface Vulnerability { 8 | OWASP: string; 9 | CWEDetails: CweDetails[]; 10 | group: string; 11 | } 12 | 13 | export interface Data { 14 | vulnerabilities: Vulnerability[]; 15 | recordedTests: Record; 23 | }>; 24 | } -------------------------------------------------------------------------------- /visualizer/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}] 2 | charset = utf-8 3 | indent_size = 2 4 | indent_style = space 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | 8 | end_of_line = lf 9 | max_line_length = 100 10 | -------------------------------------------------------------------------------- /visualizer/.eslintrc-auto-import.json: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "Component": true, 4 | "ComponentPublicInstance": true, 5 | "ComputedRef": true, 6 | "DirectiveBinding": true, 7 | "EffectScope": true, 8 | "ExtractDefaultPropTypes": true, 9 | "ExtractPropTypes": true, 10 | "ExtractPublicPropTypes": true, 11 | "InjectionKey": true, 12 | "MaybeRef": true, 13 | "MaybeRefOrGetter": true, 14 | "PropType": true, 15 | "Ref": true, 16 | "VNode": true, 17 | "WritableComputedRef": true, 18 | "computed": true, 19 | "createApp": true, 20 | "customRef": true, 21 | "defineAsyncComponent": true, 22 | "defineComponent": true, 23 | "effectScope": true, 24 | "getCurrentInstance": true, 25 | "getCurrentScope": true, 26 | "h": true, 27 | "inject": true, 28 | "isProxy": true, 29 | "isReactive": true, 30 | "isReadonly": true, 31 | "isRef": true, 32 | "markRaw": true, 33 | "nextTick": true, 34 | "onActivated": true, 35 | "onBeforeMount": true, 36 | "onBeforeRouteLeave": true, 37 | "onBeforeRouteUpdate": true, 38 | "onBeforeUnmount": true, 39 | "onBeforeUpdate": true, 40 | "onDeactivated": true, 41 | "onErrorCaptured": true, 42 | "onMounted": true, 43 | "onRenderTracked": true, 44 | "onRenderTriggered": true, 45 | "onScopeDispose": true, 46 | "onServerPrefetch": true, 47 | "onUnmounted": true, 48 | "onUpdated": true, 49 | "onWatcherCleanup": true, 50 | "provide": true, 51 | "reactive": true, 52 | "readonly": true, 53 | "ref": true, 54 | "resolveComponent": true, 55 | "shallowReactive": true, 56 | "shallowReadonly": true, 57 | "shallowRef": true, 58 | "toRaw": true, 59 | "toRef": true, 60 | "toRefs": true, 61 | "toValue": true, 62 | "triggerRef": true, 63 | "unref": true, 64 | "useAttrs": true, 65 | "useCssModule": true, 66 | "useCssVars": true, 67 | "useId": true, 68 | "useLink": true, 69 | "useModel": true, 70 | "useRoute": true, 71 | "useRouter": true, 72 | "useSlots": true, 73 | "useTemplateRef": true, 74 | "watch": true, 75 | "watchEffect": true, 76 | "watchPostEffect": true, 77 | "watchSyncEffect": true 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /visualizer/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /visualizer/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | 30 | *.tsbuildinfo 31 | -------------------------------------------------------------------------------- /visualizer/.prettierrc.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "$schema": "https://json.schemastore.org/prettierrc", 4 | "semi": false, 5 | "singleQuote": true, 6 | "printWidth": 100 7 | } 8 | -------------------------------------------------------------------------------- /visualizer/auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // noinspection JSUnusedGlobalSymbols 5 | // Generated by unplugin-auto-import 6 | export {} 7 | declare global { 8 | const EffectScope: typeof import('vue')['EffectScope'] 9 | const computed: typeof import('vue')['computed'] 10 | const createApp: typeof import('vue')['createApp'] 11 | const customRef: typeof import('vue')['customRef'] 12 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] 13 | const defineComponent: typeof import('vue')['defineComponent'] 14 | const effectScope: typeof import('vue')['effectScope'] 15 | const getCurrentInstance: typeof import('vue')['getCurrentInstance'] 16 | const getCurrentScope: typeof import('vue')['getCurrentScope'] 17 | const h: typeof import('vue')['h'] 18 | const inject: typeof import('vue')['inject'] 19 | const isProxy: typeof import('vue')['isProxy'] 20 | const isReactive: typeof import('vue')['isReactive'] 21 | const isReadonly: typeof import('vue')['isReadonly'] 22 | const isRef: typeof import('vue')['isRef'] 23 | const markRaw: typeof import('vue')['markRaw'] 24 | const nextTick: typeof import('vue')['nextTick'] 25 | const onActivated: typeof import('vue')['onActivated'] 26 | const onBeforeMount: typeof import('vue')['onBeforeMount'] 27 | const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] 28 | const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] 29 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] 30 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] 31 | const onDeactivated: typeof import('vue')['onDeactivated'] 32 | const onErrorCaptured: typeof import('vue')['onErrorCaptured'] 33 | const onMounted: typeof import('vue')['onMounted'] 34 | const onRenderTracked: typeof import('vue')['onRenderTracked'] 35 | const onRenderTriggered: typeof import('vue')['onRenderTriggered'] 36 | const onScopeDispose: typeof import('vue')['onScopeDispose'] 37 | const onServerPrefetch: typeof import('vue')['onServerPrefetch'] 38 | const onUnmounted: typeof import('vue')['onUnmounted'] 39 | const onUpdated: typeof import('vue')['onUpdated'] 40 | const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] 41 | const provide: typeof import('vue')['provide'] 42 | const reactive: typeof import('vue')['reactive'] 43 | const readonly: typeof import('vue')['readonly'] 44 | const ref: typeof import('vue')['ref'] 45 | const resolveComponent: typeof import('vue')['resolveComponent'] 46 | const shallowReactive: typeof import('vue')['shallowReactive'] 47 | const shallowReadonly: typeof import('vue')['shallowReadonly'] 48 | const shallowRef: typeof import('vue')['shallowRef'] 49 | const toRaw: typeof import('vue')['toRaw'] 50 | const toRef: typeof import('vue')['toRef'] 51 | const toRefs: typeof import('vue')['toRefs'] 52 | const toValue: typeof import('vue')['toValue'] 53 | const triggerRef: typeof import('vue')['triggerRef'] 54 | const unref: typeof import('vue')['unref'] 55 | const useAttrs: typeof import('vue')['useAttrs'] 56 | const useCssModule: typeof import('vue')['useCssModule'] 57 | const useCssVars: typeof import('vue')['useCssVars'] 58 | const useId: typeof import('vue')['useId'] 59 | const useLink: typeof import('vue-router')['useLink'] 60 | const useModel: typeof import('vue')['useModel'] 61 | const useRoute: typeof import('vue-router')['useRoute'] 62 | const useRouter: typeof import('vue-router')['useRouter'] 63 | const useSlots: typeof import('vue')['useSlots'] 64 | const useTemplateRef: typeof import('vue')['useTemplateRef'] 65 | const watch: typeof import('vue')['watch'] 66 | const watchEffect: typeof import('vue')['watchEffect'] 67 | const watchPostEffect: typeof import('vue')['watchPostEffect'] 68 | const watchSyncEffect: typeof import('vue')['watchSyncEffect'] 69 | } 70 | // for type re-export 71 | declare global { 72 | // @ts-ignore 73 | export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' 74 | import('vue') 75 | } 76 | -------------------------------------------------------------------------------- /visualizer/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cytix-software/AppSec-Detection-Framework/882e5a4cf4eb04cf563644bbf36c126cbeeb639c/visualizer/bun.lockb -------------------------------------------------------------------------------- /visualizer/components.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-vue-components 5 | // Read more: https://github.com/vuejs/core/pull/3399 6 | export {} 7 | 8 | declare module 'vue' { 9 | export interface GlobalComponents { 10 | BarChart: typeof import('./src/components/BarChart.vue')['default'] 11 | ChartControls: typeof import('./src/components/ChartControls.vue')['default'] 12 | DashboardPage: typeof import('./src/components/DashboardPage.vue')['default'] 13 | DataTable: typeof import('./src/components/DataTable.vue')['default'] 14 | HeatmapChart: typeof import('./src/components/HeatmapChart.vue')['default'] 15 | IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default'] 16 | IconDocumentation: typeof import('./src/components/icons/IconDocumentation.vue')['default'] 17 | IconEcosystem: typeof import('./src/components/icons/IconEcosystem.vue')['default'] 18 | IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default'] 19 | IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default'] 20 | RadarChart: typeof import('./src/components/RadarChart.vue')['default'] 21 | RouterLink: typeof import('vue-router')['RouterLink'] 22 | RouterView: typeof import('vue-router')['RouterView'] 23 | ToolCoverageGap: typeof import('./src/components/ToolCoverageGap.vue')['default'] 24 | TreemapChart: typeof import('./src/components/TreemapChart.vue')['default'] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /visualizer/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /visualizer/eslint.config.ts: -------------------------------------------------------------------------------- 1 | import pluginVue from 'eslint-plugin-vue' 2 | import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' 3 | import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' 4 | 5 | // To allow more languages other than `ts` in `.vue` files, uncomment the following lines: 6 | // import { configureVueProject } from '@vue/eslint-config-typescript' 7 | // configureVueProject({ scriptLangs: ['ts', 'tsx'] }) 8 | // More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup 9 | 10 | export default defineConfigWithVueTs( 11 | { 12 | name: 'app/files-to-lint', 13 | files: ['**/*.{ts,mts,tsx,vue}'], 14 | }, 15 | 16 | { 17 | name: 'app/files-to-ignore', 18 | ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'], 19 | }, 20 | 21 | pluginVue.configs['flat/essential'], 22 | vueTsConfigs.recommended, 23 | skipFormatting, 24 | ) 25 | -------------------------------------------------------------------------------- /visualizer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ASDFviz 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /visualizer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "visualizer", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "run-p type-check \"build-only {@}\" --", 9 | "preview": "vite preview", 10 | "build-only": "vite build", 11 | "type-check": "vue-tsc --build", 12 | "lint": "eslint . --fix", 13 | "format": "prettier --write src/" 14 | }, 15 | "dependencies": { 16 | "@vicons/antd": "^0.13.0", 17 | "apexcharts": "^4.5.0", 18 | "autoprefixer": "^10.4.20", 19 | "chart.js": "^4.4.8", 20 | "lodash": "^4.17.21", 21 | "lodash-es": "^4.17.21", 22 | "pinia": "^3.0.1", 23 | "vue": "^3.5.13", 24 | "vue-chartjs": "^5.3.2", 25 | "vue-router": "^4.5.0", 26 | "vue3-apexcharts": "^1.8.0", 27 | "yaml": "^2.7.0" 28 | }, 29 | "devDependencies": { 30 | "@modyfi/vite-plugin-yaml": "^1.1.0", 31 | "@types/chart.js": "^2.9.41", 32 | "@types/lodash": "^4.17.15", 33 | "@types/lodash-es": "^4.17.12", 34 | "@types/node": "^22.13.4", 35 | "@vitejs/plugin-legacy": "^5.3.2", 36 | "@vitejs/plugin-vue": "^5.2.1", 37 | "@vue/eslint-config-prettier": "^10.2.0", 38 | "@vue/eslint-config-typescript": "^14.4.0", 39 | "eslint": "^8.57.0", 40 | "eslint-plugin-vue": "^9.24.1", 41 | "jiti": "^2.4.2", 42 | "naive-ui": "^2.31.0", 43 | "prettier": "^3.5.1", 44 | "typescript": "~5.0.0", 45 | "unplugin-auto-import": "^0.17.3", 46 | "unplugin-vue-components": "^0.26.0", 47 | "vite": "^5.2.8", 48 | "vue-tsc": "^2.2.2" 49 | }, 50 | "module": "index.ts" 51 | } 52 | -------------------------------------------------------------------------------- /visualizer/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cytix-software/AppSec-Detection-Framework/882e5a4cf4eb04cf563644bbf36c126cbeeb639c/visualizer/public/favicon.ico -------------------------------------------------------------------------------- /visualizer/src/App.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 23 | 24 | 84 | -------------------------------------------------------------------------------- /visualizer/src/assets/base.css: -------------------------------------------------------------------------------- 1 | /* color palette from */ 2 | :root { 3 | --vt-c-white: #ffffff; 4 | --vt-c-white-soft: #f8f8f8; 5 | --vt-c-white-mute: #f2f2f2; 6 | 7 | --vt-c-black: #181818; 8 | --vt-c-black-soft: #222222; 9 | --vt-c-black-mute: #282828; 10 | 11 | --vt-c-indigo: #2c3e50; 12 | 13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); 14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); 15 | --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); 16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); 17 | 18 | --vt-c-text-light-1: var(--vt-c-indigo); 19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66); 20 | --vt-c-text-dark-1: var(--vt-c-white); 21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); 22 | } 23 | 24 | /* semantic color variables for this project */ 25 | :root { 26 | --color-background: var(--vt-c-white); 27 | --color-background-soft: var(--vt-c-white-soft); 28 | --color-background-mute: var(--vt-c-white-mute); 29 | 30 | --color-border: var(--vt-c-divider-light-2); 31 | --color-border-hover: var(--vt-c-divider-light-1); 32 | 33 | --color-heading: var(--vt-c-text-light-1); 34 | --color-text: var(--vt-c-text-light-1); 35 | 36 | --section-gap: 160px; 37 | } 38 | 39 | @media (prefers-color-scheme: dark) { 40 | :root { 41 | --color-background: var(--vt-c-black); 42 | --color-background-soft: var(--vt-c-black-soft); 43 | --color-background-mute: var(--vt-c-black-mute); 44 | 45 | --color-border: var(--vt-c-divider-dark-2); 46 | --color-border-hover: var(--vt-c-divider-dark-1); 47 | 48 | --color-heading: var(--vt-c-text-dark-1); 49 | --color-text: var(--vt-c-text-dark-2); 50 | } 51 | } 52 | 53 | *, 54 | *::before, 55 | *::after { 56 | box-sizing: border-box; 57 | margin: 0; 58 | font-weight: normal; 59 | } 60 | 61 | body { 62 | min-height: 100vh; 63 | color: var(--color-text); 64 | background: var(--color-background); 65 | transition: 66 | color 0.5s, 67 | background-color 0.5s; 68 | line-height: 1.6; 69 | font-family: 70 | Inter, 71 | -apple-system, 72 | BlinkMacSystemFont, 73 | 'Segoe UI', 74 | Roboto, 75 | Oxygen, 76 | Ubuntu, 77 | Cantarell, 78 | 'Fira Sans', 79 | 'Droid Sans', 80 | 'Helvetica Neue', 81 | sans-serif; 82 | font-size: 15px; 83 | text-rendering: optimizeLegibility; 84 | -webkit-font-smoothing: antialiased; 85 | -moz-osx-font-smoothing: grayscale; 86 | } 87 | -------------------------------------------------------------------------------- /visualizer/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /visualizer/src/assets/main.css: -------------------------------------------------------------------------------- 1 | @import './base.css'; 2 | -------------------------------------------------------------------------------- /visualizer/src/components/BarChart.vue: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 31 | 32 | 39 | -------------------------------------------------------------------------------- /visualizer/src/components/ChartControls.vue: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 39 | 40 | 54 | -------------------------------------------------------------------------------- /visualizer/src/components/DashboardPage.vue: -------------------------------------------------------------------------------- 1 | 2 | 42 | 43 | 398 | 399 | 465 | -------------------------------------------------------------------------------- /visualizer/src/components/DataTable.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 204 | 205 | 257 | -------------------------------------------------------------------------------- /visualizer/src/components/HeatmapChart.vue: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 31 | 32 | 39 | -------------------------------------------------------------------------------- /visualizer/src/components/RadarChart.vue: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /visualizer/src/components/data.ts: -------------------------------------------------------------------------------- 1 | import type { DockerCompose, VulnerabilitiesData, HydratedTest, HydratedHeatmapTest, CWEDetail } from './types' 2 | 3 | const dockerCompose: DockerCompose = (await import('../../../docker-compose.yml')).default 4 | const dataJson: VulnerabilitiesData = (await import('../../../data.json')).default 5 | 6 | export const loadData = () => { 7 | // Flatten the recordedTests object into an array with scanner information 8 | const flattenedTests = Object.entries(dataJson.recordedTests).flatMap(([scanner, scannerData]) => 9 | scannerData.tests.map(test => ({ ...test, scanner })) 10 | ) 11 | 12 | const hydratedHeatmapTests: HydratedHeatmapTest[] = flattenedTests.map((test) => ({ 13 | ...test, 14 | profiles: dockerCompose.services[test.test]?.profiles || [], 15 | })) 16 | 17 | const testMap = new Map() 18 | 19 | dataJson.vulnerabilities.forEach((vul) => { 20 | vul.CWEDetails.forEach((cweDetail) => { 21 | const cwe = cweDetail.id 22 | flattenedTests.forEach((rt) => { 23 | if (rt.detectedCWEs.includes(cwe) || rt.undetectedCWEs?.includes(cwe)) { 24 | // Build "A01:2021 Broken Access Control" using the group from the vulnerability level 25 | const owaspWithGroup = `${vul.OWASP} ${vul.group}` 26 | 27 | const key = `${owaspWithGroup}|${cwe}|${rt.test}` 28 | 29 | if (!testMap.has(key)) { 30 | testMap.set(key, { 31 | owasp: owaspWithGroup, 32 | cwe, 33 | test: rt.test, 34 | detections: [], 35 | }) 36 | } 37 | 38 | testMap.get(key).detections.push({ 39 | scanner: rt.scanner, 40 | detected: rt.detectedCWEs.includes(cwe), 41 | profiles: dockerCompose.services[rt.test]?.profiles || [], 42 | }) 43 | } 44 | }) 45 | }) 46 | }) 47 | 48 | const hydratedTests = Array.from(testMap.values()) 49 | 50 | return { hydratedHeatmapTests, hydratedTests, vulnerabilities: dataJson.vulnerabilities } 51 | } 52 | 53 | // Return all vulnerabilities matching a given OWASP code 54 | export function getDetailsByOwasp(owaspCode: string) { 55 | return dataJson.vulnerabilities 56 | .filter(vuln => vuln.OWASP === owaspCode) 57 | .flatMap(vuln => vuln.CWEDetails) 58 | } 59 | 60 | // Return the single vulnerability object matching a given CWE (assuming unique) 61 | export function getDetailsByCwe(cweId: number): CWEDetail | undefined { 62 | for (const vuln of dataJson.vulnerabilities) { 63 | const cweDetail = vuln.CWEDetails.find(detail => detail.id === cweId) 64 | if (cweDetail) { 65 | return cweDetail 66 | } 67 | } 68 | return undefined 69 | } 70 | -------------------------------------------------------------------------------- /visualizer/src/components/icons/IconCommunity.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /visualizer/src/components/icons/IconDocumentation.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /visualizer/src/components/icons/IconEcosystem.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /visualizer/src/components/icons/IconSupport.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /visualizer/src/components/icons/IconTooling.vue: -------------------------------------------------------------------------------- 1 | 2 | 20 | -------------------------------------------------------------------------------- /visualizer/src/components/types.ts: -------------------------------------------------------------------------------- 1 | export interface DockerService { 2 | image: string 3 | build: { 4 | context: string 5 | dockerfile: string 6 | } 7 | ports: string[] 8 | profiles: string[] 9 | } 10 | 11 | export interface CWEDetail { 12 | id: number 13 | title: string 14 | } 15 | 16 | export interface Vulnerability { 17 | OWASP: string 18 | CWEDetails: CWEDetail[] 19 | group: string 20 | } 21 | 22 | export interface RecordedTest { 23 | test: string 24 | detectedCWEs: number[] 25 | undetectedCWEs: number[] 26 | updatedAt: number 27 | } 28 | 29 | export interface HydratedHeatmapTest extends RecordedTest { 30 | scanner: string 31 | profiles: string[] 32 | } 33 | 34 | export interface HydratedTest { 35 | owasp: string 36 | cwe: number 37 | test: string 38 | detections: { 39 | scanner: string 40 | detected: boolean 41 | profiles: string[] 42 | }[] 43 | } 44 | 45 | export interface DockerCompose { 46 | services: { 47 | [key: string]: DockerService 48 | } 49 | } 50 | 51 | export interface VulnerabilitiesData { 52 | vulnerabilities: Vulnerability[] 53 | recordedTests: { 54 | [scanner: string]: { 55 | scanProfile: string 56 | tests: RecordedTest[] 57 | } 58 | } 59 | } 60 | 61 | export interface WeightedScore { 62 | scanner: string 63 | score: number 64 | } 65 | -------------------------------------------------------------------------------- /visualizer/src/main.ts: -------------------------------------------------------------------------------- 1 | import './assets/main.css' 2 | import { createApp } from 'vue' 3 | import App from './App.vue' 4 | import router from './router' 5 | import VueApexCharts from 'vue3-apexcharts' 6 | 7 | const app = createApp(App) 8 | app.use(router) 9 | app.use(VueApexCharts) 10 | app.mount('#app') 11 | -------------------------------------------------------------------------------- /visualizer/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | import HomeView from '../views/HomeView.vue' 3 | 4 | const router = createRouter({ 5 | history: createWebHistory(import.meta.env.BASE_URL), 6 | routes: [ 7 | { 8 | path: '/', 9 | name: 'home', 10 | component: HomeView, 11 | } 12 | ], 13 | }) 14 | 15 | export default router 16 | -------------------------------------------------------------------------------- /visualizer/src/stores/counter.ts: -------------------------------------------------------------------------------- 1 | import { ref, computed } from 'vue' 2 | import { defineStore } from 'pinia' 3 | 4 | export const useCounterStore = defineStore('counter', () => { 5 | const count = ref(0) 6 | const doubleCount = computed(() => count.value * 2) 7 | function increment() { 8 | count.value++ 9 | } 10 | 11 | return { count, doubleCount, increment } 12 | }) 13 | -------------------------------------------------------------------------------- /visualizer/src/views/HomeView.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /visualizer/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.yml' { 4 | const content: any; 5 | export default content; 6 | } 7 | 8 | declare module '*.json' { 9 | const content: any; 10 | export default content; 11 | } 12 | -------------------------------------------------------------------------------- /visualizer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "allowJs": true, 6 | "strict": true, 7 | "jsx": "preserve", 8 | "jsxImportSource": "vue", 9 | "moduleResolution": "Node", 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "useDefineForClassFields": true, 15 | "resolveJsonModule": true, 16 | "experimentalDecorators": true, 17 | "sourceMap": true, 18 | "baseUrl": ".", 19 | "noImplicitAny": false, 20 | "strictNullChecks": false, 21 | "noEmit": true, 22 | "paths": { 23 | "@/*": ["src/*"] 24 | }, 25 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 26 | "outDir": "dist" 27 | }, 28 | "include": [ 29 | "src/**/*.ts", 30 | "src/**/*.d.ts", 31 | "src/**/*.tsx", 32 | "src/**/*.vue", 33 | "interfaces/src/*.ts", 34 | "components.d.ts", 35 | "auto-imports.d.ts", 36 | "vite.config.ts" 37 | ], 38 | "exclude": ["node_modules"] 39 | } 40 | -------------------------------------------------------------------------------- /visualizer/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import legacy from '@vitejs/plugin-legacy' 4 | import yaml from '@modyfi/vite-plugin-yaml' 5 | import path from 'path' 6 | import AutoImport from 'unplugin-auto-import/vite' 7 | import AutoComponent from 'unplugin-vue-components/vite' 8 | 9 | export default defineConfig({ 10 | plugins: [ 11 | vue(), 12 | legacy(), 13 | AutoImport({ 14 | imports: ['vue', 'vue-router'], 15 | dts: true, 16 | eslintrc: { enabled: true }, 17 | }), 18 | AutoComponent({ dts: true }), 19 | yaml(), 20 | ], 21 | server: { 22 | fs: { 23 | allow: [path.resolve('..')], 24 | }, 25 | }, 26 | resolve: { 27 | alias: { 28 | '@data': path.resolve(__dirname, '../'), 29 | }, 30 | }, 31 | }) 32 | --------------------------------------------------------------------------------