Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b73a367

Browse files
committedMar 4, 2025
Update README.md, Docs, and added function tests for proxy detection, collection path resolution, and proxy verification with requests.
1 parent 5e93d75 commit b73a367

31 files changed

+4226
-197
lines changed
 

‎README.md

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,14 @@ Postman2Burp bridges the gap between API development and security testing by aut
1616

1717
## 📋 Table of Contents
1818

19-
- [Postman2Burp](#postman2burp)
20-
- [📋 Table of Contents](#-table-of-contents)
21-
- [🎯 Purpose](#-purpose)
22-
- [🔮 Assumptions](#-assumptions)
23-
- [📦 Requirements](#-requirements)
24-
- [🚀 Quick Start](#-quick-start)
25-
- [✨ Features](#-features)
26-
- [🎯 Use Cases](#-use-cases)
27-
- [⚠️ Limitations](#️-limitations)
28-
- [📚 Documentation](#-documentation)
29-
- [📜 License](#-license)
30-
- [👥 Contributing](#-contributing)
19+
<div align="center">
20+
21+
| [🎯 Purpose](#-purpose) | [🔮 Assumptions](#-assumptions) | [📦 Requirements](#-requirements) | [🚀 Quick Start](#-quick-start) |
22+
|:----------------------:|:------------------------------:|:--------------------------------:|:-------------------------------:|
23+
| [✨ Features](#-features) | [🎯 Use Cases](#-use-cases) | [⚠️ Limitations](#️-limitations) | [📚 Documentation](#-documentation) |
24+
| [🏆 Credits](#-credits) | [📜 License](#-license) | [👥 Contributing](#-contributing) | |
25+
26+
</div>
3127

3228
## 🎯 Purpose
3329

@@ -104,7 +100,7 @@ For complete examples with code samples and technical details, see our [Use Case
104100

105101
## 📚 Documentation
106102

107-
Comprehensive documentation is available in the [Wiki](https://github.com/darmado/postman2burp/wiki):
103+
Documentation is available in the [Wiki](https://github.com/darmado/postman2burp/wiki):
108104

109105
| Documentation | Description |
110106
|---------------|-------------|
@@ -115,6 +111,8 @@ Comprehensive documentation is available in the [Wiki](https://github.com/darmad
115111
| [Additional Features](https://github.com/darmado/postman2burp/wiki/Features) | Extended features and techniques |
116112
| [Configuration](https://github.com/darmado/postman2burp/wiki/Configuration) | Configuration options and settings |
117113
| [Troubleshooting](https://github.com/darmado/postman2burp/wiki/Troubleshooting) | Solutions for common issues |
114+
| [Function Map](https://github.com/darmado/postman2burp/wiki/Function-Map) | Overview of all functions and their roles |
115+
118116

119117
## 📜 License
120118

‎Wiki/Configuration/README.md

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22

33
This guide explains how to configure Postman2Burp using configuration files and command-line options.
44

5-
## Configuration File
5+
##
6+
7+
### Configuration File
68

79
Postman2Burp supports configuration via a `config.json` file, making it easier to maintain consistent settings across multiple runs.
810

9-
### Location
11+
***Location***
1012

1113
The tool looks for a `config.json` file in the `config` directory. If found, it loads settings from this file.
1214

13-
### Sample Configuration File
15+
***Sample Configuration File***
1416

1517
```json
1618
{
@@ -21,7 +23,7 @@ The tool looks for a `config.json` file in the `config` directory. If found, it
2123
}
2224
```
2325

24-
### Available Configuration Options
26+
***Available Configuration Options***
2527

2628
| Option | Type | Description |
2729
|--------|------|-------------|
@@ -30,39 +32,45 @@ The tool looks for a `config.json` file in the `config` directory. If found, it
3032
| `verify_ssl` | Boolean | Whether to verify SSL certificates |
3133
| `skip_proxy_check` | Boolean | Whether to skip the proxy connection check |
3234

33-
## Creating a Configuration File
35+
##
36+
37+
### Creating a Configuration File
3438

3539
You can create a configuration file in two ways:
3640

37-
### 1. Manually
41+
***1. Manually***
3842

3943
Create a `config.json` file in the `config` directory using the sample above.
4044

41-
### 2. Automatically
45+
***2. Automatically***
4246

4347
Run the script with your desired settings and add the `--save-config` flag:
4448

4549
```bash
4650
python postman2burp.py --collection your_collection.json --proxy-host your-proxy-host --proxy-port 9090 --save-config
4751
```
4852

49-
## Command-Line Priority
53+
##
54+
55+
### Command-Line Priority
5056

5157
Command-line arguments always take precedence over configuration file settings. This allows you to:
5258

5359
1. Maintain default settings in the configuration file
5460
2. Override specific settings as needed for individual runs
5561

56-
## Usage Examples
62+
##
5763

58-
### Using Configuration File Only
64+
### Usage Examples
65+
66+
***Using Configuration File Only***
5967

6068
```bash
6169
# Assuming config.json exists with your settings
6270
python postman2burp.py --collection your_collection.json
6371
```
6472

65-
### Overriding Configuration File
73+
***Overriding Configuration File***
6674

6775
```bash
6876
# Override proxy host from config file
@@ -72,18 +80,20 @@ python postman2burp.py --collection your_collection.json --proxy-host different-
7280
python postman2burp.py --collection your_collection.json --proxy-port 9090
7381
```
7482

75-
### Saving New Configuration
83+
***Saving New Configuration***
7684

7785
```bash
7886
# Save current settings to config.json
7987
python postman2burp.py --collection your_collection.json --proxy-host your-proxy --proxy-port 9090 --save-config
8088
```
8189

82-
## Environment Variables
90+
##
91+
92+
### Environment Variables
8393

8494
Postman2Burp also supports environment variables for configuration. These take precedence over the configuration file but are overridden by command-line arguments.
8595

86-
### Supported Environment Variables
96+
***Supported Environment Variables***
8797

8898
| Environment Variable | Description |
8999
|----------------------|-------------|
@@ -92,7 +102,7 @@ Postman2Burp also supports environment variables for configuration. These take p
92102
| `POSTMAN2BURP_VERIFY_SSL` | Set to "true" to verify SSL certificates |
93103
| `POSTMAN2BURP_SKIP_PROXY_CHECK` | Set to "true" to skip proxy check |
94104

95-
### Example Usage
105+
***Example Usage***
96106

97107
```bash
98108
# Set environment variables
@@ -103,6 +113,8 @@ export POSTMAN2BURP_PROXY_PORT=8080
103113
python postman2burp.py --collection your_collection.json
104114
```
105115

106-
## Next Steps
116+
##
117+
118+
### Next Steps
107119

108120
- [[Troubleshooting]]

‎Wiki/Features/README.md

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
# Additional Features
21

3-
This guide covers additional features and techniques for getting the most out of Postman2Burp.
2+
##
43

5-
## Batch Processing
4+
### Batch Processing
65

76
Process multiple collections at once using shell scripting:
87

@@ -11,30 +10,31 @@ for collection in ./collections/*.json; do
1110
python postman2burp.py --collection "$collection" --target-profile "your_profile.json"
1211
done
1312
```
13+
##
1414

15-
## Custom Proxy Configuration
15+
### Custom Proxy Configuration
1616

17-
### Combined Host:Port Format
17+
**Combined Host:Port Format**
1818

1919
```bash
2020
python postman2burp.py --collection "your_collection.json" --proxy 127.0.0.1:8888
2121
```
2222

23-
### Separate Host and Port
23+
**Separate Host and Port**
2424

2525
```bash
2626
python postman2burp.py --collection "your_collection.json" --proxy-host 127.0.0.1 --proxy-port 8888
2727
```
2828

29-
## SSL Verification
29+
**SSL Verification**
3030

3131
Enable SSL certificate verification (disabled by default):
3232

3333
```bash
3434
python postman2burp.py --collection "your_collection.json" --verify-ssl
3535
```
3636

37-
## Output Saving
37+
**Saving requests to a log file**
3838

3939
Save request and response details to a JSON file for later analysis:
4040

@@ -51,17 +51,19 @@ The output file contains an array of request/response pairs with details like:
5151
- Response body
5252
- Timing information
5353

54-
## Configuration Management
54+
##
5555

56-
### Saving Configuration
56+
### Configuration Management
57+
58+
**Saving Configuration**
5759

5860
Save your current settings to the config file for future use:
5961

6062
```bash
6163
python postman2burp.py --collection "your_collection.json" --proxy localhost:8080 --save-config
6264
```
6365

64-
### Loading Configuration
66+
**Loading Configuration**
6567

6668
The tool automatically loads settings from `config.json` if it exists. You can override specific settings with command-line arguments:
6769

@@ -70,9 +72,11 @@ The tool automatically loads settings from `config.json` if it exists. You can o
7072
python postman2burp.py --collection "different_collection.json"
7173
```
7274

73-
## CI/CD Integration
75+
##
76+
77+
### CI/CD Integration
7478

75-
### Jenkins Pipeline Example
79+
**Jenkins Pipeline Example**
7680

7781
```groovy
7882
pipeline {
@@ -105,8 +109,9 @@ pipeline {
105109
}
106110
}
107111
```
112+
##
108113

109-
## Environment Variables
114+
### Environment Variables
110115

111116
Use environment variables in your profile file:
112117

@@ -129,7 +134,7 @@ export PASSWORD="your-password"
129134
python postman2burp.py --collection "your_collection.json" --target-profile "your_profile.json"
130135
```
131136

132-
## Handling Large Collections
137+
**Handling Large Collections**
133138

134139
For large collections, you can:
135140

‎Wiki/Function Map/README.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Function Map
2+
3+
This page provides a comprehensive overview of all functions in the Postman2Burp tool, organized by category. This map helps developers understand the codebase structure and the role of each function.
4+
5+
## Core Functions
6+
7+
| Function | Description | Return Type |
8+
|----------|-------------|-------------|
9+
| `validate_json_file(file_path)` | Validates if a file contains valid JSON and returns the parsed content | `Tuple[bool, Optional[Dict]]` |
10+
| `load_config()` | Loads configuration from the config file | `Dict` |
11+
| `save_config(config)` | Saves configuration to the config file | `bool` |
12+
| `resolve_collection_path(collection_path)` | Resolves the full path to a collection file | `str` |
13+
14+
## Proxy Management
15+
16+
| Function | Description | Return Type |
17+
|----------|-------------|-------------|
18+
| `check_proxy_connection(host, port)` | Checks if a proxy is running at the specified host and port using socket connection | `bool` |
19+
| `detect_running_proxy()` | Auto-detects running proxy by checking common proxy configurations | `Tuple[Optional[str], Optional[int]]` |
20+
| `verify_proxy_with_request(host, port)` | Verifies proxy by making a test HTTP request through it | `bool` |
21+
22+
## Variable Management
23+
24+
| Function | Description | Return Type |
25+
|----------|-------------|-------------|
26+
| `extract_variables_from_text(text)` | Extracts variables ({{variable}}) from text | `Set[str]` |
27+
| `extract_variables_from_collection(collection_path)` | Extracts all variables from a Postman collection | `Tuple[Set[str], Optional[str]]` |
28+
| `generate_variables_template(collection_path, output_path)` | Generates a template file with all variables from a collection | `None` |
29+
30+
## PostmanToBurp Class Methods
31+
32+
| Method | Description | Return Type |
33+
|--------|-------------|-------------|
34+
| `__init__(collection_path, ...)` | Initializes the PostmanToBurp object with configuration | `None` |
35+
| `load_collection()` | Loads and validates the Postman collection | `bool` |
36+
| `load_profile()` | Loads and validates the profile with variables | `bool` |
37+
| `replace_variables(text)` | Replaces variables in text with values from the profile | `str` |
38+
| `extract_requests_from_item(item, folder_name)` | Extracts requests from a collection item | `List[Dict]` |
39+
| `extract_all_requests(collection)` | Extracts all requests from the collection | `List[Dict]` |
40+
| `prepare_request(request_data)` | Prepares a request for sending (replaces variables, etc.) | `Dict` |
41+
| `send_request(prepared_request)` | Sends a request through the proxy | `Dict` |
42+
| `process_collection()` | Processes the entire collection | `None` |
43+
| `run()` | Runs the entire process and returns results | `Dict` |
44+
| `check_proxy()` | Checks if the proxy is running | `bool` |
45+
| `save_results()` | Saves results to the output file | `None` |
46+
47+
## Helper Functions
48+
49+
| Function | Description | Return Type |
50+
|----------|-------------|-------------|
51+
| `process_url(url)` | Processes URL to extract variables (internal) | `Set[str]` |
52+
| `process_body(body)` | Processes request body to extract variables (internal) | `Set[str]` |
53+
| `process_headers(headers)` | Processes headers to extract variables (internal) | `Set[str]` |
54+
| `process_request(request)` | Processes a request to extract variables (internal) | `Set[str]` |
55+
| `process_item(item)` | Processes a collection item to extract variables (internal) | `Set[str]` |
56+
57+
## Main Function
58+
59+
| Function | Description | Return Type |
60+
|----------|-------------|-------------|
61+
| `main()` | Entry point for the command-line interface | `None` |
62+
63+
## Function Dependencies
64+
65+
The following diagram shows the main function dependencies:
66+
67+
```
68+
main()
69+
├── load_config()
70+
│ └── validate_json_file()
71+
├── resolve_collection_path()
72+
├── PostmanToBurp.run()
73+
│ ├── load_collection()
74+
│ │ └── validate_json_file()
75+
│ ├── load_profile()
76+
│ │ └── validate_json_file()
77+
│ ├── check_proxy()
78+
│ │ ├── check_proxy_connection()
79+
│ │ ├── detect_running_proxy()
80+
│ │ └── verify_proxy_with_request()
81+
│ ├── process_collection()
82+
│ │ ├── extract_all_requests()
83+
│ │ │ └── extract_requests_from_item()
84+
│ │ ├── prepare_request()
85+
│ │ │ └── replace_variables()
86+
│ │ └── send_request()
87+
│ └── save_results()
88+
└── save_config()
89+
```
90+
91+
## Testing Functions
92+
93+
The test suite includes tests for all major functions to ensure they work correctly. See the [[Tests]] page for more information on the test suite.

‎Wiki/Overview.md renamed to ‎Wiki/Overview/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
# Overview
2-
3-
## Introduction
1+
## Introducton
42

53
Postman2Burp bridges the gap between API development and security testing by automatically sending Postman collection requests through Burp Suite proxy. The tool solves the critical problem of translating existing API test collections into security testing workflows without manual recreation of complex request sequences.
64

‎Wiki/README.md

Lines changed: 0 additions & 46 deletions
This file was deleted.

‎RELEASE.md renamed to ‎Wiki/Relase Noes/RELEASE.md

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Postman2Burp v1.0.0 Release
1+
# Postman2Burp v0.0.6.9-alpha Release
22

33
## Changes
44

@@ -16,22 +16,6 @@
1616
- Specify custom proxy with `--proxy host:port`
1717
- Save results to file with `--output filename`
1818

19-
## Usage
20-
21-
Basic usage:
22-
```bash
23-
python postman2burp.py --collection "your_collection.json" --target-profile "your_profile.json"
24-
```
25-
26-
With proxy:
27-
```bash
28-
python postman2burp.py --collection "your_collection.json" --target-profile "your_profile.json" --proxy localhost:8080
29-
```
30-
31-
Extract variables:
32-
```bash
33-
python postman2burp.py --collection "your_collection.json" --extract-keys
34-
```
3519

3620
## Bug Fixes
3721

‎Wiki/Usage/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ Edit the generated profile file and fill in the values for each variable:
4141

4242
### 4. Running the Tool
4343

44-
#### Basic Usage
45-
44+
** Basic Usage
45+
**
4646
```bash
4747
python postman2burp.py --collection "your_collection.json" --target-profile "your_profile.json"
4848
```
@@ -53,16 +53,16 @@ python postman2burp.py --collection "your_collection.json" --target-profile "you
5353
python postman2burp.py --collection "your_collection.json" --target-profile "your_profile.json" --proxy localhost:8080
5454
```
5555

56-
#### Skip Proxy Check
57-
56+
** Skip Proxy Check
57+
**
5858
Useful if you're sure the proxy is running:
5959

6060
```bash
6161
python postman2burp.py --collection "your_collection.json" --target-profile "your_profile.json" --skip-proxy-check
6262
```
6363

64-
#### Save Configuration for Future Use
65-
64+
** Save Configuration for Future Use
65+
**
6666
```bash
6767
python postman2burp.py --collection "your_collection.json" --target-profile "your_profile.json" --proxy localhost:8080 --save-config
6868
```

‎collections/malformed.collection.json

Whitespace-only changes.

‎postman2burp.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ def verify_proxy_with_request(host: str, port: int) -> bool:
239239
except requests.exceptions.RequestException as e:
240240
logger.warning(f"Proxy test request failed: {str(e)}")
241241
return False
242+
except Exception as e:
243+
logger.warning(f"Unexpected error during proxy test: {str(e)}")
244+
return False
242245

243246
def extract_variables_from_text(text: str) -> Set[str]:
244247
"""

‎profiles/malformed_profile.json

Lines changed: 0 additions & 81 deletions
This file was deleted.

‎requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
requests>=2.28.0
22
urllib3>=1.26.0
33
python-dotenv>=0.20.0
4+
jsonschema>=4.17.3

‎run_tests.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Postman2Burp Test Runner
4+
-----------------------
5+
A simple script to run all tests for Postman2Burp.
6+
"""
7+
8+
import os
9+
import sys
10+
import unittest
11+
import argparse
12+
from pathlib import Path
13+
14+
def run_tests(test_type=None, verbose=False):
15+
"""Run the specified tests."""
16+
# Create the tests directory if it doesn't exist
17+
tests_dir = Path("tests")
18+
if not tests_dir.exists():
19+
tests_dir.mkdir(parents=True)
20+
print(f"Created tests directory: {tests_dir}")
21+
22+
# Create a test loader
23+
loader = unittest.TestLoader()
24+
25+
# Create a test suite
26+
if test_type == "all" or test_type is None:
27+
print("Running all tests...")
28+
test_suite = loader.discover("tests", pattern="test_*.py")
29+
elif test_type == "environment":
30+
print("Running environment tests...")
31+
test_suite = loader.discover("tests", pattern="test_environment.py")
32+
elif test_type == "core_functions":
33+
print("Running core functions tests...")
34+
test_suite = loader.discover("tests", pattern="test_core_functions.py")
35+
elif test_type == "json":
36+
print("Running JSON validation tests...")
37+
test_suite = loader.discover("tests", pattern="test_json_validation.py")
38+
elif test_type == "schema":
39+
print("Running schema validation tests...")
40+
test_suite = loader.discover("tests", pattern="test_schema.py")
41+
elif test_type == "lint":
42+
print("Running JSON lint tests...")
43+
test_suite = loader.discover("tests", pattern="test_json_lint.py")
44+
elif test_type == "proxy":
45+
print("Running proxy verification tests...")
46+
test_suite = loader.discover("tests", pattern="test_verify_proxy_with_request.py")
47+
else:
48+
print(f"Unknown test type: {test_type}")
49+
return 1
50+
51+
# Create a test runner
52+
runner = unittest.TextTestRunner(verbosity=2 if verbose else 1)
53+
54+
# Run the tests
55+
result = runner.run(test_suite)
56+
57+
# Return the result
58+
return 0 if result.wasSuccessful() else 1
59+
60+
def main():
61+
"""Main entry point."""
62+
parser = argparse.ArgumentParser(description="Run tests for Postman2Burp")
63+
parser.add_argument("--type", choices=["all", "environment", "core_functions", "json", "schema", "lint", "proxy"], default="all",
64+
help="Type of tests to run (default: all)")
65+
parser.add_argument("--verbose", action="store_true", help="Verbose output")
66+
args = parser.parse_args()
67+
68+
return run_tests(args.type, args.verbose)
69+
70+
if __name__ == "__main__":
71+
sys.exit(main())

‎schemas/collection.2.1.0.json

Lines changed: 1187 additions & 0 deletions
Large diffs are not rendered by default.

‎tests/README.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# Postman2Burp Test Suite
2+
3+
This directory contains tests for the Postman2Burp tool. The tests are designed to verify that the environment is properly set up and that the core functionality works as expected.
4+
5+
## Test Structure
6+
7+
The test suite is organized into the following files:
8+
9+
- `test_environment.py`: Tests to verify the environment is properly set up
10+
- `test_core_functions.py`: Comprehensive tests for individual functions in the main script
11+
- `test_json_validation.py`: Tests to verify that malformed JSON files are properly detected and handled
12+
- `test_schema.py`: Tests to validate Postman collections against the official schema
13+
- `test_json_lint.py`: Simple JSON lint tests to detect syntax errors in JSON files
14+
- `test_verify_proxy_with_request.py`: Specific tests for the proxy verification functionality
15+
16+
## Running the Tests
17+
18+
To run all tests:
19+
20+
```bash
21+
python -m unittest discover tests
22+
```
23+
24+
Or use the provided test runner:
25+
26+
```bash
27+
python run_tests.py
28+
```
29+
30+
To run a specific test file:
31+
32+
```bash
33+
python -m unittest tests/test_environment.py
34+
```
35+
36+
Or with the test runner:
37+
38+
```bash
39+
python run_tests.py --type environment
40+
python run_tests.py --type core_functions
41+
python run_tests.py --type json
42+
python run_tests.py --type schema
43+
python run_tests.py --type lint
44+
python run_tests.py --type proxy
45+
```
46+
47+
To run a specific test case:
48+
49+
```bash
50+
python -m unittest tests.test_environment.EnvironmentTests.test_python_version
51+
```
52+
53+
## Test Coverage
54+
55+
The tests cover the following aspects of Postman2Burp:
56+
57+
### Environment Tests
58+
59+
- Python version check
60+
- Required packages check
61+
- Directory structure check
62+
- Config file creation
63+
- Proxy connection check (skipped by default)
64+
- Postman collection validation
65+
66+
### Core Functions Tests
67+
68+
- JSON validation functions (`validate_json_file`)
69+
- Configuration management (`load_config`, `save_config`)
70+
- Proxy management (`check_proxy_connection`, `detect_running_proxy`, `verify_proxy_with_request`)
71+
- Variable extraction and management (`extract_variables_from_text`, `extract_variables_from_collection`, `generate_variables_template`)
72+
- Path resolution (`resolve_collection_path`)
73+
- PostmanToBurp class methods:
74+
- Collection and profile loading
75+
- Variable replacement
76+
- Request extraction and preparation
77+
- Request sending and processing
78+
79+
### JSON Validation Tests
80+
81+
- Detection of malformed JSON files (syntax errors)
82+
- Validation of Postman collection schema
83+
- Validation of profile schema
84+
- Validation of config schema
85+
- Type checking for configuration values
86+
87+
### Schema Validation Tests
88+
89+
- Validation against the official Postman Collection v2.1.0 schema
90+
- Detection of schema violations in collections
91+
- Structural validation of collections
92+
- Handling of malformed JSON files
93+
- Schema directory management
94+
95+
### JSON Lint Tests
96+
97+
- Simple syntax validation of JSON files
98+
- Detection of malformed JSON with clear error messages
99+
- Validation of files in collections, profiles, and config directories
100+
- Reporting of specific JSON syntax errors
101+
102+
### Proxy Verification Tests
103+
104+
- Testing successful proxy connections
105+
- Testing failed proxy connections
106+
- Testing exception handling in proxy verification
107+
- Ensuring robust error handling for network issues
108+
109+
## Adding New Tests
110+
111+
To add new tests:
112+
113+
1. Create a new test file in the `tests` directory
114+
2. Import the necessary modules
115+
3. Create a test class that inherits from `unittest.TestCase`
116+
4. Add test methods that start with `test_`
117+
5. Run the tests to verify they work
118+
119+
Example:
120+
121+
```python
122+
import unittest
123+
124+
class NewFeatureTests(unittest.TestCase):
125+
def test_new_feature(self):
126+
# Test code here
127+
self.assertTrue(True)
128+
129+
if __name__ == "__main__":
130+
unittest.main()
131+
```
132+
133+
## CI Integration
134+
135+
These tests are designed to be run in a CI environment. The `test_proxy_connection` test is skipped by default because it requires a running proxy, which may not be available in CI environments.
136+
137+
To run the tests in CI, add the following to your GitHub Actions workflow:
138+
139+
```yaml
140+
- name: Test with pytest
141+
run: |
142+
python -m unittest discover tests
143+
```

‎tests/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""
2+
Postman2Burp Test Suite
3+
----------------------
4+
Tests for the Postman2Burp tool.
5+
"""
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"info": {
3+
"_postman_id": "f1e8e5b7-dc12-4a1c-9e37-42a7df1f9ef2",
4+
"name": "API Security Assessment",
5+
"description": "Collection for API security assessment scope",
6+
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
7+
"_exporter_id": "12345678"
8+
},
9+
"item": [
10+
{
11+
"name": "Authentication",
12+
"item": [
13+
{
14+
"name": "Login",
15+
"event": [
16+
{
17+
"listen": "test",
18+
"script": {
19+
"exec": [
20+
"var jsonData = pm.response.json();",
21+
"pm.environment.set(\"access_token\", jsonData.token);"
22+
],
23+
"type": "text/javascript"
24+
}
25+
}
26+
],
27+
"request": {
28+
"method": "POST",
29+
"header": [
30+
{
31+
"key": "Content-Type",
32+
"value": "application/json"
33+
},
34+
{
35+
"key": "Accept",
36+
"value": "application/json"
37+
}
38+
],
39+
"body": {
40+
"mode": "raw",
41+
"raw": "{\n \"email\": \"{{username}}\",\n \"password\": \"{{password}}\"\n}"
42+
},
43+
"url": {
44+
"raw": "{{base_url}}/api/v1/auth/login",
45+
"host": [
46+
"{{base_url}}"
47+
],
48+
"path": [
49+
"api",
50+
"v1",
51+
"auth",
52+
"login"
53+
]
54+
},
55+
"description": "Authenticate user and get access token"
56+
},
57+
"response": []
58+
},
59+
],
60+
"description": "Authentication endpoints"
61+
},
62+
],
63+
}

‎tests/collections/postman_collection.json

Lines changed: 882 additions & 0 deletions
Large diffs are not rendered by default.

‎tests/config/config.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"proxy_host": "localhost",
3+
"proxy_port": 9090,
4+
"verify_ssl": false,
5+
"skip_proxy_check": false,
6+
"auto_detect_proxy": true,
7+
"output_path": "asdf",
8+
"verbose": true,
9+
"last_collection": "postman_collection.json",
10+
"last_target_profile": "f1e8e5b7-dc12-4a1c-9e37-42a7df1f9ef2.json"
11+
}

‎tests/config/malformed.config.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"proxy_host": "localhost",
3+
"proxy_port": 9090,
4+
"verify_ssl": false,
5+
"skip_proxy_check": false,
6+
"auto_detect_proxy": true,
7+
"output_path": "asdf",
8+
"verbose": true,
9+
"last_collection": "postman_collection.json",
10+
"last_target_profile": "f1e8e5b7-dc12-4a1c-9e37-42a7df1f9ef2.json",
11+
"default_proxy": {
12+
"host": "localhost",
13+
"port": 8080,
14+
},
15+
"default_profile": "development",
16+
"output_directory": "./output",
17+
"log_level": "info",
18+
"ssl_verification": false,
19+
"timeout": 30,
20+
"max_retries": 3,
21+
"concurrent_requests": 5
22+
},
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
{
2+
"id": "auto-generated-1741052438",
3+
"name": "Auto-Generated Environment",
4+
"values": [
5+
{
6+
"key": "access_token",
7+
"value": "",
8+
"type": "default",
9+
"enabled": true
10+
},
11+
{
12+
"key": "base_url",
13+
"value": "",
14+
"type": "default",
15+
"enabled": true
16+
},
17+
{
18+
"key": "card_token",
19+
"value": "",
20+
"type": "default",
21+
"enabled": true
22+
},
23+
{
24+
"key": "category_id",
25+
"value": "",
26+
"type": "default",
27+
"enabled": true
28+
},
29+
{
30+
"key": "order_id",
31+
"value": "",
32+
"type": "default",
33+
"enabled": true
34+
},
35+
{
36+
"key": "password",
37+
"value": "",
38+
"type": "default",
39+
"enabled": true
40+
},
41+
{
42+
"key": "product_id",
43+
"value": "",
44+
"type": "default",
45+
"enabled": true
46+
},
47+
{
48+
"key": "product_id_2",
49+
"value": "",
50+
"type": "default",
51+
"enabled": true
52+
},
53+
{
54+
"key": "smtp_password",
55+
"value": "",
56+
"type": "default",
57+
"enabled": true
58+
},
59+
{
60+
"key": "smtp_username",
61+
"value": "",
62+
"type": "default",
63+
"enabled": true
64+
},
65+
{
66+
"key": "user_id",
67+
"value": "",
68+
"type": "default",
69+
"enabled": true
70+
},
71+
{
72+
"key": "username",
73+
"value": "",
74+
"type": "default",
75+
"enabled": true
76+
}
77+
],
78+
"_postman_variable_scope": "environment",
79+
"_postman_exported_at": "2025-03-03T17:40:38.000Z",
80+
"_postman_exported_using": "Postman2Burp/Extract",
81+
"_postman_collection_id": "f1e8e5b7-dc12-4a1c-9e37-42a7df1f9ef2"
82+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
{
2+
"id": "auto-generated-1741052709",
3+
"name": "Auto-Generated Environment",
4+
"values": [
5+
{
6+
"key": "access_token",
7+
"value": "",
8+
"type": "default",
9+
"enabled": true
10+
},
11+
{
12+
"key": "base_url",
13+
"value": "",
14+
"type": "default",
15+
"enabled": true
16+
},
17+
{
18+
"key": "card_token",
19+
"value": "",
20+
"type": "default",
21+
"enabled": true
22+
},,
23+
{
24+
"key": "category_id",
25+
"value": "",
26+
"type": "default",
27+
"enabled": true
28+
},
29+
{
30+
"key": "order_id",
31+
"value": "",
32+
"type": "default",
33+
"enabled": true
34+
},
35+
{
36+
"key": "password",
37+
"value": "",
38+
"type": "default",
39+
"enabled": true
40+
},
41+
{
42+
"key": "product_id",
43+
"value": "",
44+
"type": "default",
45+
"enabled": true
46+
},
47+
{
48+
"key": "product_id_2",
49+
"value": "",
50+
"type": "default",
51+
"enabled": true
52+
},
53+
{
54+
"key": "smtp_password",
55+
"value": "",
56+
"type": "default",
57+
"enabled": true
58+
},
59+
{
60+
"key": "smtp_username",
61+
"value": "",
62+
"type": "default",
63+
"enabled": true
64+
},
65+
{
66+
"key": "user_id",
67+
"value": "",
68+
"type": "default",
69+
"enabled": true
70+
},
71+
{
72+
"key": "username",
73+
"value": "",
74+
"type": "default",
75+
"enabled": true
76+
}
77+
],
78+
"_postman_variable_scope": "environment",
79+
"_postman_exported_at": "2025-03-03T17:45:09.000Z",
80+
"_postman_exported_using": "Postman2Burp/Extract",
81+
"_postman_collection_id": "5daf5126-e247-4baa-91b8-f86be51a6672"
82+
}

‎tests/profiles/malformed.profile.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "Development Environment",
3+
"variables": {
4+
"base_url": "https://api-dev.example.com",
5+
"username": "testuser",
6+
"password": "password123",
7+
"api_key": "dev-api-key-12345"
8+
},
9+
"proxy": {
10+
"host": "localhost",
11+
"port": 8080
12+
},
13+
"ssl_verification": false,
14+
"timeout": 30,
15+
"headers": {
16+
"User-Agent": "PostmanRuntime/7.28.4",
17+
"Accept-Encoding": "gzip, deflate, br",
18+
"Connection": "keep-alive"
19+
},
20+
"retry_count": 3
21+
}

‎tests/test_core_functions.py

Lines changed: 673 additions & 0 deletions
Large diffs are not rendered by default.

‎tests/test_detect_running_proxy.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
@patch('postman2burp.check_proxy_connection')
2+
def test_detect_running_proxy(self, mock_check_proxy):
3+
"""Test the detect_running_proxy function."""
4+
# Test successful detection - update to match actual implementation
5+
# The third proxy in COMMON_PROXIES is localhost:8090
6+
mock_check_proxy.side_effect = [False, False, True, False]
7+
host, port = detect_running_proxy()
8+
self.assertEqual(host, "localhost")
9+
self.assertEqual(port, 8090) # Update expected port to 8090
10+
11+
# Test no proxy detected
12+
mock_check_proxy.side_effect = [False] * 10
13+
host, port = detect_running_proxy()
14+
self.assertIsNone(host)
15+
self.assertIsNone(port)

‎tests/test_environment.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Postman2Burp Environment Tests
4+
------------------------------
5+
Tests to verify the environment is properly set up for Postman2Burp.
6+
"""
7+
8+
import os
9+
import sys
10+
import json
11+
import socket
12+
import unittest
13+
from pathlib import Path
14+
15+
# Add parent directory to path to import from project root
16+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
17+
18+
class EnvironmentTests(unittest.TestCase):
19+
"""Tests to verify the environment is properly set up for Postman2Burp."""
20+
21+
def test_python_version(self):
22+
"""Test that Python version meets requirements."""
23+
required_version = (3, 6)
24+
current_version = sys.version_info
25+
26+
self.assertGreaterEqual(
27+
(current_version.major, current_version.minor),
28+
required_version,
29+
f"Python version {current_version.major}.{current_version.minor} is less than required version 3.6+"
30+
)
31+
32+
def test_required_packages(self):
33+
"""Test that required packages are installed."""
34+
required_packages = ["requests", "urllib3"]
35+
36+
for package in required_packages:
37+
try:
38+
__import__(package)
39+
except ImportError:
40+
self.fail(f"Required package '{package}' is not installed")
41+
42+
def test_directory_structure(self):
43+
"""Test that required directories exist."""
44+
required_dirs = ["collections", "profiles", "config"]
45+
46+
for directory in required_dirs:
47+
# Create directory if it doesn't exist for the test to pass
48+
if not os.path.exists(directory):
49+
os.makedirs(directory)
50+
51+
self.assertTrue(
52+
os.path.exists(directory) and os.path.isdir(directory),
53+
f"Required directory '{directory}' does not exist"
54+
)
55+
56+
def test_config_file_creation(self):
57+
"""Test that a config file can be created."""
58+
config_dir = Path("config")
59+
config_file = config_dir / "test_config.json"
60+
61+
# Remove test config if it exists
62+
if config_file.exists():
63+
config_file.unlink()
64+
65+
# Create config directory if it doesn't exist
66+
if not config_dir.exists():
67+
config_dir.mkdir(parents=True)
68+
69+
# Create a sample config file
70+
sample_config = {
71+
"proxy_host": "localhost",
72+
"proxy_port": 8080,
73+
"verify_ssl": False,
74+
"skip_proxy_check": False
75+
}
76+
77+
with open(config_file, 'w') as f:
78+
json.dump(sample_config, f, indent=4)
79+
80+
self.assertTrue(
81+
config_file.exists(),
82+
f"Failed to create config file at {config_file}"
83+
)
84+
85+
# Clean up
86+
config_file.unlink()
87+
88+
@unittest.skip("This test requires a running proxy and may fail in CI environments")
89+
def test_proxy_connection(self):
90+
"""Test that a proxy connection can be established."""
91+
host = "localhost"
92+
port = 8080
93+
94+
# Try to connect to the proxy
95+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
96+
sock.settimeout(3)
97+
result = sock.connect_ex((host, port))
98+
sock.close()
99+
100+
self.assertEqual(
101+
result, 0,
102+
f"Proxy is not running at {host}:{port}"
103+
)
104+
105+
def test_postman_collection_validation(self):
106+
"""Test that a Postman collection can be validated."""
107+
collections_dir = Path("collections")
108+
test_collection_path = collections_dir / "test_collection.json"
109+
110+
# Create collections directory if it doesn't exist
111+
if not collections_dir.exists():
112+
collections_dir.mkdir(parents=True)
113+
114+
# Create a minimal valid Postman collection
115+
minimal_collection = {
116+
"info": {
117+
"name": "Test Collection",
118+
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
119+
},
120+
"item": []
121+
}
122+
123+
with open(test_collection_path, 'w') as f:
124+
json.dump(minimal_collection, f, indent=4)
125+
126+
# Test validation
127+
try:
128+
with open(test_collection_path, 'r') as f:
129+
collection = json.load(f)
130+
131+
self.assertIn("info", collection, "Collection missing 'info' field")
132+
self.assertIn("item", collection, "Collection missing 'item' field")
133+
except Exception as e:
134+
self.fail(f"Failed to validate collection: {str(e)}")
135+
finally:
136+
# Clean up
137+
test_collection_path.unlink()
138+
139+
140+
if __name__ == "__main__":
141+
unittest.main()

‎tests/test_json.py

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Postman2Burp JSON Validation Tests
4+
---------------------------------
5+
Tests to verify that malformed JSON files are properly detected and handled.
6+
"""
7+
8+
import os
9+
import sys
10+
import json
11+
import unittest
12+
from pathlib import Path
13+
14+
# Add parent directory to path to import from project root
15+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
16+
17+
class JSONValidationTests(unittest.TestCase):
18+
"""Tests for validating JSON integrity in config, profile, and collection files."""
19+
20+
def setUp(self):
21+
"""Set up test fixtures."""
22+
# Create test directories if they don't exist
23+
self.collections_dir = Path("collections")
24+
self.profiles_dir = Path("profiles")
25+
self.config_dir = Path("config")
26+
27+
for directory in [self.collections_dir, self.profiles_dir, self.config_dir]:
28+
if not directory.exists():
29+
directory.mkdir(parents=True)
30+
31+
# Create test files with valid JSON
32+
self.valid_collection_path = self.collections_dir / "valid_collection.json"
33+
self.valid_profile_path = self.profiles_dir / "valid_profile.json"
34+
self.valid_config_path = self.config_dir / "valid_config.json"
35+
36+
# Create test files with malformed JSON
37+
self.malformed_collection_path = self.collections_dir / "malformed_collection.json"
38+
self.malformed_profile_path = self.profiles_dir / "malformed_profile.json"
39+
self.malformed_config_path = self.config_dir / "malformed_config.json"
40+
41+
# Write valid JSON to files
42+
with open(self.valid_collection_path, 'w') as f:
43+
f.write('{"info": {"name": "Valid Collection"}, "item": []}')
44+
45+
with open(self.valid_profile_path, 'w') as f:
46+
f.write('{"variables": {"base_url": "https://example.com"}}')
47+
48+
with open(self.valid_config_path, 'w') as f:
49+
f.write('{"proxy_host": "localhost", "proxy_port": 8080}')
50+
51+
# Write malformed JSON to files
52+
with open(self.malformed_collection_path, 'w') as f:
53+
f.write('{"info": {"name": "Malformed Collection", "item": []}') # Missing closing brace
54+
55+
with open(self.malformed_profile_path, 'w') as f:
56+
f.write('{"variables": {"base_url": "https://example.com"') # Missing closing braces
57+
58+
with open(self.malformed_config_path, 'w') as f:
59+
f.write('{"proxy_host": "localhost", "proxy_port": 8080,}') # Extra comma
60+
61+
def tearDown(self):
62+
"""Tear down test fixtures."""
63+
# Clean up test files
64+
for file_path in [
65+
self.valid_collection_path, self.valid_profile_path, self.valid_config_path,
66+
self.malformed_collection_path, self.malformed_profile_path, self.malformed_config_path
67+
]:
68+
if file_path.exists():
69+
file_path.unlink()
70+
71+
def test_validate_json_file(self):
72+
"""Test the JSON validation function with valid and malformed files."""
73+
# Define a function to validate JSON files
74+
def validate_json_file(file_path):
75+
"""Validate a JSON file."""
76+
try:
77+
with open(file_path, 'r') as f:
78+
json.load(f)
79+
return True
80+
except json.JSONDecodeError:
81+
return False
82+
83+
# Test with valid files
84+
self.assertTrue(validate_json_file(self.valid_collection_path),
85+
f"Failed to validate valid collection: {self.valid_collection_path}")
86+
self.assertTrue(validate_json_file(self.valid_profile_path),
87+
f"Failed to validate valid profile: {self.valid_profile_path}")
88+
self.assertTrue(validate_json_file(self.valid_config_path),
89+
f"Failed to validate valid config: {self.valid_config_path}")
90+
91+
# Test with malformed files
92+
self.assertFalse(validate_json_file(self.malformed_collection_path),
93+
f"Failed to detect malformed collection: {self.malformed_collection_path}")
94+
self.assertFalse(validate_json_file(self.malformed_profile_path),
95+
f"Failed to detect malformed profile: {self.malformed_profile_path}")
96+
self.assertFalse(validate_json_file(self.malformed_config_path),
97+
f"Failed to detect malformed config: {self.malformed_config_path}")
98+
99+
def test_collection_schema_validation(self):
100+
"""Test validation of Postman collection schema."""
101+
# Create a collection with valid structure but missing required fields
102+
invalid_schema_path = self.collections_dir / "invalid_schema.json"
103+
with open(invalid_schema_path, 'w') as f:
104+
f.write('{"item": []}') # Missing "info" field
105+
106+
# Define a function to validate collection schema
107+
def validate_collection_schema(file_path):
108+
"""Validate a Postman collection schema."""
109+
try:
110+
with open(file_path, 'r') as f:
111+
collection = json.load(f)
112+
113+
# Check for required fields
114+
if "info" not in collection:
115+
return False
116+
if "item" not in collection:
117+
return False
118+
119+
return True
120+
except Exception:
121+
return False
122+
123+
# Test with valid and invalid schemas
124+
self.assertTrue(validate_collection_schema(self.valid_collection_path),
125+
f"Failed to validate valid collection schema: {self.valid_collection_path}")
126+
self.assertFalse(validate_collection_schema(invalid_schema_path),
127+
f"Failed to detect invalid collection schema: {invalid_schema_path}")
128+
129+
# Clean up
130+
if invalid_schema_path.exists():
131+
invalid_schema_path.unlink()
132+
133+
def test_profile_schema_validation(self):
134+
"""Test validation of profile schema."""
135+
# Create a profile with valid structure but missing required fields
136+
invalid_schema_path = self.profiles_dir / "invalid_schema.json"
137+
with open(invalid_schema_path, 'w') as f:
138+
f.write('{"not_variables": {}}') # Missing "variables" field
139+
140+
# Define a function to validate profile schema
141+
def validate_profile_schema(file_path):
142+
"""Validate a profile schema."""
143+
try:
144+
with open(file_path, 'r') as f:
145+
profile = json.load(f)
146+
147+
# Check for required fields
148+
if "variables" not in profile:
149+
return False
150+
151+
return True
152+
except Exception:
153+
return False
154+
155+
# Test with valid and invalid schemas
156+
self.assertTrue(validate_profile_schema(self.valid_profile_path),
157+
f"Failed to validate valid profile schema: {self.valid_profile_path}")
158+
self.assertFalse(validate_profile_schema(invalid_schema_path),
159+
f"Failed to detect invalid profile schema: {invalid_schema_path}")
160+
161+
# Clean up
162+
if invalid_schema_path.exists():
163+
invalid_schema_path.unlink()
164+
165+
def test_config_schema_validation(self):
166+
"""Test validation of config schema."""
167+
# Create a config with valid structure but invalid field types
168+
invalid_schema_path = self.config_dir / "invalid_schema.json"
169+
with open(invalid_schema_path, 'w') as f:
170+
f.write('{"proxy_host": "localhost", "proxy_port": "not_a_number"}') # port should be a number
171+
172+
# Define a function to validate config schema
173+
def validate_config_schema(file_path):
174+
"""Validate a config schema."""
175+
try:
176+
with open(file_path, 'r') as f:
177+
config = json.load(f)
178+
179+
# Check field types
180+
if "proxy_port" in config and not isinstance(config["proxy_port"], int):
181+
return False
182+
183+
return True
184+
except Exception:
185+
return False
186+
187+
# Test with valid and invalid schemas
188+
self.assertTrue(validate_config_schema(self.valid_config_path),
189+
f"Failed to validate valid config schema: {self.valid_config_path}")
190+
self.assertFalse(validate_config_schema(invalid_schema_path),
191+
f"Failed to detect invalid config schema: {invalid_schema_path}")
192+
193+
# Clean up
194+
if invalid_schema_path.exists():
195+
invalid_schema_path.unlink()
196+
197+
198+
if __name__ == "__main__":
199+
unittest.main()

‎tests/test_json_lint.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#!/usr/bin/env python3
2+
"""
3+
JSON Lint Tests for Postman2Burp
4+
--------------------------------
5+
Tests to validate JSON files for syntax errors.
6+
"""
7+
8+
import os
9+
import unittest
10+
import tempfile
11+
import json
12+
import glob
13+
from pathlib import Path
14+
15+
16+
def validate_json_file(file_path):
17+
"""
18+
Validate a JSON file for syntax errors.
19+
20+
Args:
21+
file_path (str): Path to the JSON file
22+
23+
Returns:
24+
tuple: (is_valid, error_message)
25+
"""
26+
try:
27+
with open(file_path, 'r', encoding='utf-8') as f:
28+
json.load(f)
29+
return True, None
30+
except json.JSONDecodeError as e:
31+
return False, f"JSON syntax error in {os.path.basename(file_path)}: {str(e)}"
32+
except Exception as e:
33+
return False, f"Error reading {os.path.basename(file_path)}: {str(e)}"
34+
35+
36+
def validate_directory(directory_path, pattern="*.json"):
37+
"""
38+
Validate all JSON files in a directory.
39+
40+
Args:
41+
directory_path (str): Path to the directory containing JSON files
42+
pattern (str): Glob pattern to match files
43+
44+
Returns:
45+
list: List of tuples (file_path, is_valid, error_message)
46+
"""
47+
results = []
48+
49+
if not os.path.exists(directory_path):
50+
return [(None, False, f"Directory {directory_path} does not exist")]
51+
52+
for file_path in glob.glob(os.path.join(directory_path, pattern)):
53+
is_valid, error_message = validate_json_file(file_path)
54+
results.append((file_path, is_valid, error_message))
55+
56+
return results
57+
58+
59+
class JSONLintTests(unittest.TestCase):
60+
"""Test case for JSON linting functionality."""
61+
62+
def setUp(self):
63+
"""Set up test fixtures."""
64+
# Create test directories if they don't exist
65+
self.test_dirs = {
66+
'collections': Path('tests/collections'),
67+
'profiles': Path('tests/profiles'),
68+
'config': Path('tests/config')
69+
}
70+
71+
for dir_path in self.test_dirs.values():
72+
dir_path.mkdir(parents=True, exist_ok=True)
73+
74+
# Create a temporary valid JSON file for testing
75+
self.valid_json_path = tempfile.mktemp(suffix='.json')
76+
with open(self.valid_json_path, 'w', encoding='utf-8') as f:
77+
json.dump({"test": "valid"}, f)
78+
79+
def tearDown(self):
80+
"""Tear down test fixtures."""
81+
# Remove the temporary valid JSON file
82+
if os.path.exists(self.valid_json_path):
83+
os.remove(self.valid_json_path)
84+
85+
def test_validate_json_file(self):
86+
"""Test validation of individual JSON files."""
87+
# Test with valid JSON
88+
is_valid, error_message = validate_json_file(self.valid_json_path)
89+
self.assertTrue(is_valid)
90+
self.assertIsNone(error_message)
91+
92+
# Test with malformed collection
93+
malformed_collection = self.test_dirs['collections'] / 'malformed.collection.json'
94+
if malformed_collection.exists():
95+
is_valid, error_message = validate_json_file(str(malformed_collection))
96+
self.assertFalse(is_valid)
97+
self.assertIsNotNone(error_message)
98+
self.assertIn("JSON syntax error", error_message)
99+
100+
# Test with malformed profile
101+
malformed_profile = self.test_dirs['profiles'] / 'malformed.profile.json'
102+
if malformed_profile.exists():
103+
is_valid, error_message = validate_json_file(str(malformed_profile))
104+
self.assertFalse(is_valid)
105+
self.assertIsNotNone(error_message)
106+
self.assertIn("JSON syntax error", error_message)
107+
108+
# Test with malformed config
109+
malformed_config = self.test_dirs['config'] / 'malformed.config.json'
110+
if malformed_config.exists():
111+
is_valid, error_message = validate_json_file(str(malformed_config))
112+
self.assertFalse(is_valid)
113+
self.assertIsNotNone(error_message)
114+
self.assertIn("JSON syntax error", error_message)
115+
116+
# Test with non-existent file
117+
non_existent_file = "non_existent_file.json"
118+
is_valid, error_message = validate_json_file(non_existent_file)
119+
self.assertFalse(is_valid)
120+
self.assertIsNotNone(error_message)
121+
self.assertIn("Error reading", error_message)
122+
123+
def test_validate_directory(self):
124+
"""Test validation of directories containing JSON files."""
125+
# Test with valid directory
126+
temp_dir = tempfile.mkdtemp()
127+
valid_file_path = os.path.join(temp_dir, "valid.json")
128+
with open(valid_file_path, 'w', encoding='utf-8') as f:
129+
json.dump({"test": "valid"}, f)
130+
131+
results = validate_directory(temp_dir)
132+
self.assertEqual(len(results), 1)
133+
self.assertTrue(results[0][1]) # is_valid
134+
135+
# Test with non-existent directory
136+
results = validate_directory("non_existent_directory")
137+
self.assertEqual(len(results), 1)
138+
self.assertFalse(results[0][1]) # is_valid
139+
self.assertIn("Directory", results[0][2]) # error_message
140+
141+
# Test with collections directory
142+
if self.test_dirs['collections'].exists():
143+
results = validate_directory(str(self.test_dirs['collections']))
144+
self.assertGreater(len(results), 0)
145+
146+
# Check if we have at least one invalid file
147+
has_invalid = any(not is_valid for _, is_valid, _ in results)
148+
if has_invalid:
149+
self.assertTrue(has_invalid)
150+
151+
# Clean up
152+
os.remove(valid_file_path)
153+
os.rmdir(temp_dir)
154+
155+
156+
if __name__ == "__main__":
157+
unittest.main()

‎tests/test_resolve_collection_path.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
i@patch('postman2burp.COLLECTIONS_DIR')
2+
def test_resolve_collection_path(self, mock_collections_dir):
3+
"""Test the resolve_collection_path function."""
4+
# Set up mocks - make sure to set the return_value, not just patch the name
5+
mock_collections_dir.return_value = self.test_collections_dir
6+
7+
# Test with absolute path
8+
with patch('os.path.isabs', return_value=True):
9+
path = resolve_collection_path("/absolute/path/collection.json")
10+
self.assertEqual(path, "/absolute/path/collection.json")
11+
12+
# Test with existing file
13+
with patch('os.path.isabs', return_value=False):
14+
with patch('os.path.exists', return_value=True):
15+
path = resolve_collection_path("existing_file.json")
16+
self.assertEqual(path, "existing_file.json")
17+
18+
# Test with file in collections directory - use a more specific approach
19+
with patch('os.path.isabs', return_value=False):
20+
with patch('os.path.exists', side_effect=[False, True]):
21+
with patch('os.path.join', return_value=os.path.join(self.test_collections_dir, "collection_in_dir.json")):
22+
path = resolve_collection_path("collection_in_dir.json")
23+
self.assertEqual(path, os.path.join(self.test_collections_dir, "collection_in_dir.json"))
24+
25+
# Test with non-existent file
26+
with patch('os.path.isabs', return_value=False):
27+
with patch('os.path.exists', return_value=False):
28+
path = resolve_collection_path("nonexistent.json")
29+
self.assertEqual(path, "nonexistent.json")

‎tests/test_schema.py

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Test Schema Validation for Postman2Burp
4+
---------------------------------------
5+
This module tests the schema validation functionality for Postman collections.
6+
"""
7+
8+
import os
9+
import json
10+
import unittest
11+
import tempfile
12+
import shutil
13+
import jsonschema
14+
from pathlib import Path
15+
16+
17+
def ensure_schema_directory():
18+
"""
19+
Ensure the schema directory exists and contains the Postman collection schema.
20+
21+
Returns:
22+
str: Path to the schema directory
23+
"""
24+
schema_dir = Path("schemas")
25+
if not schema_dir.exists():
26+
schema_dir.mkdir(parents=True)
27+
print(f"Created schema directory: {schema_dir}")
28+
29+
schema_file = schema_dir / "collection.2.1.0.json"
30+
if not schema_file.exists():
31+
print(f"Warning: Schema file not found at {schema_file}")
32+
33+
return str(schema_dir)
34+
35+
36+
def load_schema(schema_path=None):
37+
"""
38+
Load the Postman collection schema from the specified path or the default location.
39+
40+
Args:
41+
schema_path (str, optional): Path to the schema file. If None, uses the default schema.
42+
43+
Returns:
44+
dict: The loaded schema as a dictionary
45+
"""
46+
if schema_path is None:
47+
schema_dir = ensure_schema_directory()
48+
schema_path = os.path.join(schema_dir, "collection.2.1.0.json")
49+
50+
try:
51+
with open(schema_path, 'r', encoding='utf-8') as f:
52+
return json.load(f)
53+
except Exception as e:
54+
raise Exception(f"Failed to load schema from {schema_path}: {str(e)}")
55+
56+
57+
def validate_collection_structure(collection_path):
58+
"""
59+
Validate the basic structure of a Postman collection.
60+
61+
Args:
62+
collection_path (str): Path to the collection file
63+
64+
Returns:
65+
tuple: (is_valid, error_message)
66+
"""
67+
try:
68+
with open(collection_path, 'r', encoding='utf-8') as f:
69+
collection = json.load(f)
70+
71+
# Check for required fields
72+
if 'info' not in collection:
73+
return False, "Collection missing 'info' field"
74+
75+
if 'item' not in collection:
76+
return False, "Collection missing 'item' field"
77+
78+
if 'name' not in collection['info']:
79+
return False, "Collection info missing 'name' field"
80+
81+
if 'schema' not in collection['info']:
82+
return False, "Collection info missing 'schema' field"
83+
84+
return True, None
85+
except json.JSONDecodeError as e:
86+
return False, f"Invalid JSON in collection file: {str(e)}"
87+
except Exception as e:
88+
return False, f"Error validating collection structure: {str(e)}"
89+
90+
91+
def validate_collection(collection_path, schema_path=None):
92+
"""
93+
Validate a Postman collection against the schema.
94+
95+
Args:
96+
collection_path (str): Path to the collection file
97+
schema_path (str, optional): Path to the schema file. If None, uses the default schema.
98+
99+
Returns:
100+
tuple: (is_valid, error_message)
101+
"""
102+
try:
103+
# First validate the basic structure
104+
is_valid, error_message = validate_collection_structure(collection_path)
105+
if not is_valid:
106+
return is_valid, error_message
107+
108+
# Load the collection
109+
with open(collection_path, 'r', encoding='utf-8') as f:
110+
collection = json.load(f)
111+
112+
# Load the schema
113+
schema = load_schema(schema_path)
114+
115+
# Validate against the schema
116+
jsonschema.validate(collection, schema)
117+
118+
return True, None
119+
except jsonschema.exceptions.ValidationError as e:
120+
return False, f"Schema validation error: {str(e)}"
121+
except Exception as e:
122+
return False, f"Error validating collection: {str(e)}"
123+
124+
125+
class SchemaValidationTests(unittest.TestCase):
126+
"""Test case for schema validation functionality."""
127+
128+
def setUp(self):
129+
"""Set up test fixtures."""
130+
# Create test directories if they don't exist
131+
self.test_dirs = {
132+
'collections': Path('tests/collections'),
133+
'schemas': Path('schemas')
134+
}
135+
136+
for dir_path in self.test_dirs.values():
137+
dir_path.mkdir(parents=True, exist_ok=True)
138+
139+
# Define paths to test files
140+
self.valid_collection = str(self.test_dirs['collections'] / 'postman_collection.json')
141+
self.malformed_collection = str(self.test_dirs['collections'] / 'malformed.collection.json')
142+
self.schema_file = str(self.test_dirs['schemas'] / 'collection.2.1.0.json')
143+
144+
# Create a temporary valid collection for testing
145+
self.temp_collection_path = tempfile.mktemp(suffix='.json')
146+
with open(self.temp_collection_path, 'w', encoding='utf-8') as f:
147+
json.dump({
148+
"info": {
149+
"name": "Test Collection",
150+
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
151+
},
152+
"item": []
153+
}, f)
154+
155+
def tearDown(self):
156+
"""Tear down test fixtures."""
157+
# Remove the temporary collection file
158+
if os.path.exists(self.temp_collection_path):
159+
os.remove(self.temp_collection_path)
160+
161+
def test_load_schema(self):
162+
"""Test loading the schema."""
163+
# Test loading the schema from the default location
164+
schema = load_schema()
165+
self.assertIsNotNone(schema)
166+
self.assertIn('$schema', schema)
167+
self.assertIn('properties', schema)
168+
169+
# Test loading the schema from a specific location
170+
if os.path.exists(self.schema_file):
171+
schema = load_schema(self.schema_file)
172+
self.assertIsNotNone(schema)
173+
self.assertIn('$schema', schema)
174+
self.assertIn('properties', schema)
175+
176+
def test_validate_collection_structure(self):
177+
"""Test validating the structure of a collection."""
178+
# Test with a valid collection
179+
if os.path.exists(self.valid_collection):
180+
is_valid, error_message = validate_collection_structure(self.valid_collection)
181+
self.assertTrue(is_valid, f"Valid collection should pass structure validation: {error_message}")
182+
183+
# Test with a minimal valid collection
184+
is_valid, error_message = validate_collection_structure(self.temp_collection_path)
185+
self.assertTrue(is_valid, f"Minimal valid collection should pass structure validation: {error_message}")
186+
187+
# Test with a malformed collection
188+
if os.path.exists(self.malformed_collection):
189+
is_valid, error_message = validate_collection_structure(self.malformed_collection)
190+
self.assertFalse(is_valid, "Malformed collection should fail structure validation")
191+
192+
def test_validate_collection(self):
193+
"""Test validating a collection against the schema."""
194+
# Test with a valid collection
195+
if os.path.exists(self.valid_collection) and os.path.exists(self.schema_file):
196+
is_valid, error_message = validate_collection(self.valid_collection, self.schema_file)
197+
self.assertTrue(is_valid, f"Valid collection should pass schema validation: {error_message}")
198+
199+
# Test with a malformed collection
200+
if os.path.exists(self.malformed_collection) and os.path.exists(self.schema_file):
201+
is_valid, error_message = validate_collection(self.malformed_collection, self.schema_file)
202+
self.assertFalse(is_valid, "Malformed collection should fail schema validation")
203+
204+
def test_ensure_schema_directory(self):
205+
"""Test ensuring the schema directory exists."""
206+
schema_dir = ensure_schema_directory()
207+
self.assertTrue(os.path.exists(schema_dir), "Schema directory should exist")
208+
self.assertTrue(os.path.isdir(schema_dir), "Schema directory should be a directory")
209+
210+
211+
if __name__ == "__main__":
212+
unittest.main()
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Test for verify_proxy_with_request function
4+
"""
5+
6+
import unittest
7+
import sys
8+
import os
9+
from unittest.mock import patch, MagicMock
10+
11+
# Add parent directory to path to import from project root
12+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
13+
14+
# Import the function to test
15+
import postman2burp
16+
17+
class TestVerifyProxyWithRequest(unittest.TestCase):
18+
"""Test the verify_proxy_with_request function."""
19+
20+
def test_verify_proxy_with_request_success(self):
21+
"""Test successful proxy verification."""
22+
# Mock the requests.get function to return a successful response
23+
with patch('requests.get') as mock_get:
24+
mock_response = MagicMock()
25+
mock_response.status_code = 200
26+
mock_get.return_value = mock_response
27+
28+
# Call the function
29+
result = postman2burp.verify_proxy_with_request("localhost", 8080)
30+
31+
# Verify the result
32+
self.assertTrue(result)
33+
34+
# Verify that requests.get was called with the expected arguments
35+
mock_get.assert_called_once()
36+
args, kwargs = mock_get.call_args
37+
self.assertEqual(kwargs['proxies']['http'], "http://localhost:8080")
38+
self.assertEqual(kwargs['proxies']['https'], "http://localhost:8080")
39+
40+
def test_verify_proxy_with_request_failure(self):
41+
"""Test failed proxy verification."""
42+
# Mock the requests.get function to return a failed response
43+
with patch('requests.get') as mock_get:
44+
mock_response = MagicMock()
45+
mock_response.status_code = 500
46+
mock_get.return_value = mock_response
47+
48+
# Call the function
49+
result = postman2burp.verify_proxy_with_request("localhost", 8080)
50+
51+
# Verify the result
52+
self.assertFalse(result)
53+
54+
def test_verify_proxy_with_request_exception(self):
55+
"""Test exception handling in proxy verification."""
56+
# Mock the requests.get function to raise an exception
57+
with patch('requests.get') as mock_get:
58+
mock_get.side_effect = Exception("Test error")
59+
60+
# Call the function - this should not raise an exception
61+
result = postman2burp.verify_proxy_with_request("localhost", 8080)
62+
63+
# Verify the result
64+
self.assertFalse(result)
65+
66+
if __name__ == "__main__":
67+
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.