diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..d37cd62 --- /dev/null +++ b/.flake8 @@ -0,0 +1,25 @@ +[flake8] +# Increase the max line length to 120 characters +max-line-length = 120 + +# Ignore specific errors/warnings: +# E501: Line too long +# W291: Trailing whitespace +# E128: Continuation line under-indented for visual indent +# E126: Continuation line over-indented for hanging indent +# E127: Continuation line over-indented for visual indent +# W503: Line break occurred before a binary operator +# E266: Too many leading '#' for block comment +ignore = E501, W291, E128, E126, E127, W503, E266, W605, C901 + +# Exclude some directories from checking +exclude = + .git, + __pycache__, + build, + dist, + .venv + + +# Maximum allowed complexity for functions +max-complexity = 10 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b76a33..59ed4b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,53 +1,79 @@ -stages: - - lint - - test - - build - - deploy +name: CI/CD Pipeline -variables: - PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip-cache" +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] -cache: - paths: - - .pip-cache/ +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 + - name: Lint with flake8 + run: flake8 . -lint: - stage: lint - image: python:3.9 - before_script: - - pip install flake8 - script: - - flake8 . - -test: - stage: test - image: python:3.9 - before_script: - - pip install -r requirements.txt - - pip install pytest pytest-cov - script: - - pytest tests/ --cov=./ --cov-report=xml - artifacts: - reports: - coverage_report: - coverage_format: cobertura + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pytest pytest-cov + - name: List directory contents + run: ls -R + - name: Run tests + run: | + export PYTHONPATH=$PYTHONPATH:$(pwd) + pytest tests/ -v --cov=./ --cov-report=xml + - name: Upload coverage report + uses: actions/upload-artifact@v2 + with: + name: coverage-report path: coverage.xml -build: - stage: build - image: python:3.9 - script: - - pip install pyinstaller - - pyinstaller --onefile main.py - artifacts: - paths: - - dist/main + build: + runs-on: ubuntu-latest + needs: [lint, test] + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pyinstaller + - name: Build executable + run: pyinstaller --onefile main.py + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: abap-code-scanner + path: dist/main -deploy: - stage: deploy - image: python:3.9 - script: - - echo "Deploying application..." - # Add your deployment steps here - only: - - main # This job will only run on the main branch \ No newline at end of file + deploy: + runs-on: ubuntu-latest + needs: build + if: github.ref == 'refs/heads/main' + steps: + - name: Deploy application + run: | + echo "Deploying application..." + # Add your deployment steps here \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1d2cafb --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# ABAP Code Scanner Framework + +## Overview + +The ABAP Code Scanner Framework is a powerful tool designed to analyze ABAP (Advanced Business Application Programming) code for potential security vulnerabilities, code quality issues, and best practice violations. This framework provides a flexible and extensible way to scan ABAP code and generate comprehensive reports on various aspects of code security and quality. + +## Features + +- Multiple security checks including: + - Cross-Site Scripting (XSS) vulnerabilities + - Directory Traversal vulnerabilities + - Hardcoded credentials + - Weak cryptographic algorithms + - And many more... +- Customizable and extensible architecture +- Command-line interface for easy integration into CI/CD pipelines +- Detailed reporting in XLSX format +- Configurable scan settings + +## Prerequisites + +- Python 3.9 or higher +- pip (Python package installer) + +## Installation + +1. Clone the repository: + ``` + git clone https://github.com/yourusername/AbapCodeScannerFramework.git + cd AbapCodeScannerFramework + ``` + +2. Install the required dependencies: + ``` + pip install -r requirements.txt + ``` + +## Usage + +To run the ABAP Code Scanner: + +``` +python main.py path/to/abap/code +``` + +Optional arguments: +- `-c`, `--config`: Path to the configuration file (default: config.yml) + +## Configuration + +The scanner can be configured using a YAML file. By default, it looks for `config.yml` in the project root. You can specify a different configuration file using the `-c` or `--config` option. + +Example configuration: + +```yaml +checks: + - CheckCrossSiteScripting + - CheckSQLInjection + - CheckDirectoryTraversal + +file_extensions: + - .abap + - .txt + +exclude_patterns: + - "**/test/**" +``` + +## Adding New Checks + +To add a new security check: + +1. Create a new Python file in the `checks` directory. +2. Define a class that inherits from a base check class. +3. Implement the required methods, including the main `run` method. +4. Add the new check to the configuration file. + +## Running Tests + +To run the test suite: + +On Windows: +``` +run_tests.bat +``` + +On Unix-like systems: +``` +./run_tests.sh +``` + +## Contributing + +Contributions to the ABAP Code Scanner Framework are welcome! Please feel free to submit pull requests, create issues or spread the word. + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Acknowledgments + +- Thanks to all contributors who have helped to improve this framework. +- Special thanks to the ABAP community for their invaluable resources and documentation. + diff --git a/checks/CheckAbapOutgoingFtpConn.py b/checks/CheckAbapOutgoingFtpConn.py index c5d1fe6..c603a2b 100644 --- a/checks/CheckAbapOutgoingFtpConn.py +++ b/checks/CheckAbapOutgoingFtpConn.py @@ -1,16 +1,17 @@ -# checks/check_abap_outgoing_ftp_conn.py - import re from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckAbapOutgoingFtpConn: title = "Outgoing FTP Connection" + confidence = "Definitive" severity = "Low" vulnerability_type = "Unencrypted Communications" @@ -26,4 +27,3 @@ def run(self, file_content: str) -> List[CheckResult]: line_number = file_content[:match.start()].count('\n') + 1 return [CheckResult(line_number, match.group().strip())] return [] - diff --git a/checks/CheckBrokenAuthCheck.py b/checks/CheckBrokenAuthCheck.py index 901b085..b3968e2 100644 --- a/checks/CheckBrokenAuthCheck.py +++ b/checks/CheckBrokenAuthCheck.py @@ -1,5 +1,3 @@ -# checks/check_broken_auth_check.py - import re from dataclasses import dataclass from typing import List diff --git a/checks/CheckCallTransformation.py b/checks/CheckCallTransformation.py index 452f66a..4367758 100644 --- a/checks/CheckCallTransformation.py +++ b/checks/CheckCallTransformation.py @@ -2,11 +2,13 @@ from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckCallTransformation: title = "XML Injection via \"CALL TRANSFORMATION\"" severity = "High" diff --git a/checks/CheckCrossSiteScripting.py b/checks/CheckCrossSiteScripting.py index bc41168..93ac68d 100644 --- a/checks/CheckCrossSiteScripting.py +++ b/checks/CheckCrossSiteScripting.py @@ -1,14 +1,14 @@ -# checks/CheckCrossSiteScripting.py - import re from dataclasses import dataclass from typing import List, Dict + @dataclass class CheckResult: line_number: int line_content: str + class CheckCrossSiteScripting: title = "Potential Cross-Site Scripting vulnerability" severity = "High" @@ -60,4 +60,4 @@ def run(self, file_content: str) -> List[CheckResult]: results.append(CheckResult(i, line.strip())) break # Stop searching after finding the first vulnerability in the line - return results \ No newline at end of file + return results diff --git a/checks/CheckDangerousAbapCommands.py b/checks/CheckDangerousAbapCommands.py index 719abdb..7f517bf 100644 --- a/checks/CheckDangerousAbapCommands.py +++ b/checks/CheckDangerousAbapCommands.py @@ -1,14 +1,14 @@ -# checks/check_dangerous_abap_commands.py - import re from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckDangerousAbapCommands: title = "Dangerous ABAP statements" severity = "Medium" diff --git a/checks/CheckDeleteDynpro.py b/checks/CheckDeleteDynpro.py index ae5cbd1..038a3fc 100644 --- a/checks/CheckDeleteDynpro.py +++ b/checks/CheckDeleteDynpro.py @@ -2,11 +2,13 @@ from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckDeleteDynpro: title = "Critical actions via deleting a screen" severity = "High" diff --git a/checks/CheckDirectoryTraversalCRstrbReadBuffered.py b/checks/CheckDirectoryTraversalCRstrbReadBuffered.py index c2dcac0..1db70b4 100644 --- a/checks/CheckDirectoryTraversalCRstrbReadBuffered.py +++ b/checks/CheckDirectoryTraversalCRstrbReadBuffered.py @@ -2,11 +2,13 @@ from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckDirectoryTraversalCRstrbReadBuffered: title = "Path Traversal - CALL C_RSTRB_READ_BUFFERED" severity = "Medium" @@ -23,4 +25,4 @@ def run(self, file_content: str) -> List[CheckResult]: if self.pattern2.search(call_statement): line_number = file_content[:match1.start()].count('\n') + 1 results.append(CheckResult(line_number, call_statement.strip())) - return results \ No newline at end of file + return results diff --git a/checks/CheckDirectoryTraversalCallAlerts.py b/checks/CheckDirectoryTraversalCallAlerts.py index 4d4ffb0..4cc6781 100644 --- a/checks/CheckDirectoryTraversalCallAlerts.py +++ b/checks/CheckDirectoryTraversalCallAlerts.py @@ -2,11 +2,13 @@ from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckDirectoryTraversalCallAlerts: title = "Path Traversal - CALL ALERTS" severity = "Medium" @@ -23,4 +25,4 @@ def run(self, file_content: str) -> List[CheckResult]: if self.pattern2.search(call_statement): line_number = file_content[:match1.start()].count('\n') + 1 results.append(CheckResult(line_number, call_statement.strip())) - return results \ No newline at end of file + return results diff --git a/checks/CheckDirectoryTraversalDeleteDataset.py b/checks/CheckDirectoryTraversalDeleteDataset.py index 8e56e53..dea1d81 100644 --- a/checks/CheckDirectoryTraversalDeleteDataset.py +++ b/checks/CheckDirectoryTraversalDeleteDataset.py @@ -1,15 +1,14 @@ -# checks/check_directory_traversal_read_dataset.py - import re from dataclasses import dataclass from typing import List -from enum import Enum + @dataclass class CheckResult: line_number: int line_content: str + class CheckDirectoryTraversalDeleteDataset: title = "Path Traversal - DELETE DATASET" severity = "HIGH" diff --git a/checks/CheckDirectoryTraversalReadDataset.py b/checks/CheckDirectoryTraversalReadDataset.py index 269519a..6e45c8e 100644 --- a/checks/CheckDirectoryTraversalReadDataset.py +++ b/checks/CheckDirectoryTraversalReadDataset.py @@ -1,15 +1,14 @@ -# checks/check_directory_traversal_read_dataset.py - import re from dataclasses import dataclass from typing import List -from enum import Enum + @dataclass class CheckResult: line_number: int line_content: str + class CheckDirectoryTraversalReadDataset: title = "Path Traversal - READ DATASET" severity = "HIGH" diff --git a/checks/CheckDirectoryTraversalRfcRemoteFile.py b/checks/CheckDirectoryTraversalRfcRemoteFile.py index 948e347..a8202a7 100644 --- a/checks/CheckDirectoryTraversalRfcRemoteFile.py +++ b/checks/CheckDirectoryTraversalRfcRemoteFile.py @@ -1,21 +1,19 @@ -# checks/check_directory_traversal_rfc_remote_file.py - import re from dataclasses import dataclass from typing import List -from enum import Enum + @dataclass class CheckResult: line_number: int line_content: str + class CheckDirectoryTraversalRfcRemoteFile: title = "Potential Path Traversal detected - RFC_REMOTE_FILE" severity = "HIGH" vulnerability_type = "Path Traversal" - def __init__(self): self.main_pattern = re.compile( r"(?ims)^[\s]*(\bCALL FUNCTION\b)(.+?\.)", diff --git a/checks/CheckDirectoryTraversalTransfer.py b/checks/CheckDirectoryTraversalTransfer.py index dd5a3e6..47c7df7 100644 --- a/checks/CheckDirectoryTraversalTransfer.py +++ b/checks/CheckDirectoryTraversalTransfer.py @@ -2,20 +2,24 @@ from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckDirectoryTraversalTransfer: title = "Directory traversal via \"TRANSFER\" statement" severity = "High" vulnerability_type = "Directory Traversal" def __init__(self): - self.transfer_pattern = re.compile(r'(^\s*|\.\s*)TRANSFER\s+[\S]+\s+TO\s+(\w+)\.?', re.IGNORECASE | re.MULTILINE) + self.transfer_pattern = re.compile(r'(^\s*|\.\s*)TRANSFER\s+[\S]+\s+TO\s+(\w+)\.?', + re.IGNORECASE | re.MULTILINE) self.validation_pattern = re.compile(r'CALL\s+FUNCTION\s+(\'|\`)FILE_VALIDATE_NAME(\'|\`)', re.IGNORECASE) - self.subrc_check_pattern = re.compile(r'(IF\s+sy(st)?-subrc\s*(=|EQ)\s*0|CHECK\s+sy(st)?-subrc\s*(=|EQ)\s*0)', re.IGNORECASE) + self.subrc_check_pattern = re.compile(r'(IF\s+sy(st)?-subrc\s*(=|EQ)\s*0|CHECK\s+sy(st)?-subrc\s*(=|EQ)\s*0)', + re.IGNORECASE) def run(self, file_content: str) -> List[CheckResult]: results = [] @@ -30,7 +34,8 @@ def run(self, file_content: str) -> List[CheckResult]: def is_filename_validated(self, file_content: str, filename_var: str) -> bool: # Check if FILE_VALIDATE_NAME is called with the filename variable - validation_call = re.search(rf'CALL\s+FUNCTION\s+(\'|\`)FILE_VALIDATE_NAME(\'|\`).*?{filename_var}', file_content, re.IGNORECASE | re.DOTALL) + validation_call = re.search(rf'CALL\s+FUNCTION\s+(\'|\`)FILE_VALIDATE_NAME(\'|\`).*?{filename_var}', + file_content, re.IGNORECASE | re.DOTALL) if validation_call: # Check if there's a proper subrc check after the validation validation_pos = validation_call.start() diff --git a/checks/CheckDosInDoLoop.py b/checks/CheckDosInDoLoop.py index 26e1afb..caf23d6 100644 --- a/checks/CheckDosInDoLoop.py +++ b/checks/CheckDosInDoLoop.py @@ -1,13 +1,14 @@ import re from dataclasses import dataclass from typing import List -from enum import Enum + @dataclass class CheckResult: line_number: int line_content: str + class CheckDosInDoLoop: title = "Denial of Service (DOS) in do/enddo loop." severity = "Medium" @@ -25,4 +26,3 @@ def run(self, file_content: str) -> List[CheckResult]: line_number = file_content[:match.start()].count('\n') + 1 results.append(CheckResult(line_number, match.group().strip())) return results - diff --git a/checks/CheckDummyAuthCheck.py b/checks/CheckDummyAuthCheck.py index 13d2ab0..3249901 100644 --- a/checks/CheckDummyAuthCheck.py +++ b/checks/CheckDummyAuthCheck.py @@ -1,5 +1,3 @@ -# checks/check_dummy_auth_check.py - import re from dataclasses import dataclass from typing import List @@ -54,4 +52,3 @@ def run(self, file_content: str) -> List[CheckResult]: line_number = file_content[:match.start()].count('\n') + 1 results.append(CheckResult(line_number, match.group().strip())) return results - diff --git a/checks/CheckExecuteProcedure.py b/checks/CheckExecuteProcedure.py index 77814e4..a55ffe4 100644 --- a/checks/CheckExecuteProcedure.py +++ b/checks/CheckExecuteProcedure.py @@ -2,11 +2,13 @@ from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckExecuteProcedure: title = "ADBC Injection via \"execute_procedure\"" severity = "High" @@ -22,4 +24,3 @@ def run(self, file_content: str) -> List[CheckResult]: if self.pattern.search(line): results.append(CheckResult(i, line.strip())) return results - diff --git a/checks/CheckExposedSystemCalls.py b/checks/CheckExposedSystemCalls.py index cd6c41b..2f749f1 100644 --- a/checks/CheckExposedSystemCalls.py +++ b/checks/CheckExposedSystemCalls.py @@ -1,14 +1,14 @@ -# checks/check_exposed_system_calls.py - import re from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckExposedSystemCalls: title = "Exposed System Call" severity = "High" diff --git a/checks/CheckGenerateSubroutinePool.py b/checks/CheckGenerateSubroutinePool.py index 64590c9..5023711 100644 --- a/checks/CheckGenerateSubroutinePool.py +++ b/checks/CheckGenerateSubroutinePool.py @@ -2,11 +2,13 @@ from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckGenerateSubroutinePool: title = "Critical actions via generating a subroutine pool" severity = "High" diff --git a/checks/CheckGetPersistentByQuery.py b/checks/CheckGetPersistentByQuery.py index 1b37057..bf37909 100644 --- a/checks/CheckGetPersistentByQuery.py +++ b/checks/CheckGetPersistentByQuery.py @@ -2,11 +2,13 @@ from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckGetPersistentByQuery: title = "ADBC Injection via \"get_persistent_by_query\" method" severity = "High" @@ -22,4 +24,3 @@ def run(self, file_content: str) -> List[CheckResult]: if self.pattern.search(line): results.append(CheckResult(i, line.strip())) return results - diff --git a/checks/CheckHardcodedCredentials.py b/checks/CheckHardcodedCredentials.py index bdb6d4f..7353f81 100644 --- a/checks/CheckHardcodedCredentials.py +++ b/checks/CheckHardcodedCredentials.py @@ -1,7 +1,6 @@ import re from dataclasses import dataclass from typing import List -from enum import Enum @dataclass @@ -9,6 +8,7 @@ class CheckResult: line_number: int line_content: str + class CheckHardcodedCredentials: title = "Hard-coded credentials are security-sensitive" severity = "High" diff --git a/checks/CheckHardcodedITIN.py b/checks/CheckHardcodedITIN.py index bef531d..208a933 100644 --- a/checks/CheckHardcodedITIN.py +++ b/checks/CheckHardcodedITIN.py @@ -2,18 +2,21 @@ from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckHardcodedITIN: title = "Hardcoded ITIN" severity = "High" vulnerability_type = "Information Exposure" def __init__(self): - self.pattern = re.compile(r'[^w0-8](?!999999999)(9\d{2})([ \-]?)(([7]\d|8[0-8])|[9][0-24-9])([ \-]?)(\d{4})[^w0-9]') + self.pattern = re.compile( + r'[^w0-8](?!999999999)(9\d{2})([ \-]?)(([7]\d|8[0-8])|[9][0-24-9])([ \-]?)(\d{4})[^w0-9]') def run(self, file_content: str) -> List[CheckResult]: results = [] @@ -23,4 +26,3 @@ def run(self, file_content: str) -> List[CheckResult]: if matches: results.append(CheckResult(i, line.strip())) return results - diff --git a/checks/CheckHardcodedIpAddresses.py b/checks/CheckHardcodedIpAddresses.py index b33b85a..9101bed 100644 --- a/checks/CheckHardcodedIpAddresses.py +++ b/checks/CheckHardcodedIpAddresses.py @@ -1,14 +1,14 @@ -# checks/check_hardcoded_ip_addresses.py - import re from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckHardcodedIpAddresses: title = "Using hardcoded IP addresses is security-sensitive" severity = "Minor" @@ -26,14 +26,14 @@ def __init__(self): def is_exception(self, ip: str) -> bool: exceptions = [ - r'^127\.', # Loopback - r'^255\.255\.255\.255$', # Broadcast - r'^0\.0\.0\.0$', # Non-routable + r'^127\.', # Loopback + r'^255\.255\.255\.255$', # Broadcast + r'^0\.0\.0\.0$', # Non-routable r'^2\.5\.\d{1,3}\.\d{1,3}$', # Potential OID - r'^192\.0\.2\.', # Documentation (RFC 5737) - r'^198\.51\.100\.', # Documentation (RFC 5737) - r'^203\.0\.113\.', # Documentation (RFC 5737) - r'^2001:db8::1' # IPv6 Documentation (RFC 3849) + r'^192\.0\.2\.', # Documentation (RFC 5737) + r'^198\.51\.100\.', # Documentation (RFC 5737) + r'^203\.0\.113\.', # Documentation (RFC 5737) + r'^2001:db8::1' # IPv6 Documentation (RFC 3849) ] return any(re.match(pattern, ip) for pattern in exceptions) @@ -50,4 +50,3 @@ def run(self, file_content: str) -> List[CheckResult]: results.append(CheckResult(i, line.strip())) break # Only report one issue per line return results - diff --git a/checks/CheckHardcodedUrls.py b/checks/CheckHardcodedUrls.py index 339c8c1..e775895 100644 --- a/checks/CheckHardcodedUrls.py +++ b/checks/CheckHardcodedUrls.py @@ -1,14 +1,14 @@ -# checks/check_hardcoded_urls.py - import re from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckHardcodedUrls: title = "Hardcoded URLs detected" severity = "Medium" @@ -17,7 +17,7 @@ class CheckHardcodedUrls: def __init__(self): self.pattern = re.compile( r'\s+["\']https?://\w+.*?$', - re.IGNORECASE| re.IGNORECASE| re.MULTILINE + re.IGNORECASE | re.IGNORECASE | re.MULTILINE ) def run(self, file_content: str) -> List[CheckResult]: diff --git a/checks/CheckHardcodedUserAuth.py b/checks/CheckHardcodedUserAuth.py index dfb0ff0..235069f 100644 --- a/checks/CheckHardcodedUserAuth.py +++ b/checks/CheckHardcodedUserAuth.py @@ -1,5 +1,3 @@ -# checks/check_hardcoded_user_auth.py - import re from dataclasses import dataclass from typing import List @@ -35,4 +33,3 @@ def run(self, file_content: str) -> List[CheckResult]: results.append(CheckResult(i, line.strip())) return results - diff --git a/checks/CheckOSCommandInjectionCFunction.py b/checks/CheckOSCommandInjectionCFunction.py index fe4ca1b..b51702a 100644 --- a/checks/CheckOSCommandInjectionCFunction.py +++ b/checks/CheckOSCommandInjectionCFunction.py @@ -1,14 +1,14 @@ -# checks/check_os_command_injection_c_function.py - import re from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckOSCommandInjectionCFunction: title = "Potential OS Command injection detected - C Function" severity = "High" diff --git a/checks/CheckOSCommandInjectionCallSystem.py b/checks/CheckOSCommandInjectionCallSystem.py index bf5eb8a..ce6f6aa 100644 --- a/checks/CheckOSCommandInjectionCallSystem.py +++ b/checks/CheckOSCommandInjectionCallSystem.py @@ -1,5 +1,3 @@ -# checks/check_os_command_injection_call_system.py - import re from dataclasses import dataclass from typing import List diff --git a/checks/CheckOSCommandInjectionClientOS.py b/checks/CheckOSCommandInjectionClientOS.py index 67ff96e..f2da536 100644 --- a/checks/CheckOSCommandInjectionClientOS.py +++ b/checks/CheckOSCommandInjectionClientOS.py @@ -2,11 +2,13 @@ from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckOSCommandInjectionClientOS: title = "Potential OS Command injection detected - GUI Function" severity = "High" @@ -24,4 +26,3 @@ def run(self, file_content: str) -> List[CheckResult]: line_number = file_content[:match.start()].count('\n') + 1 results.append(CheckResult(line_number, match.group().strip())) return results - diff --git a/checks/CheckOSCommandInjectionOpenDatasetFilter.py b/checks/CheckOSCommandInjectionOpenDatasetFilter.py index e6bcf6d..2bf5197 100644 --- a/checks/CheckOSCommandInjectionOpenDatasetFilter.py +++ b/checks/CheckOSCommandInjectionOpenDatasetFilter.py @@ -1,14 +1,14 @@ -# checks/check_os_command_injection_open_dataset_filter.py - import re from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckOSCommandInjectionOpenDatasetFilter: title = "Potential OS Command injection detected - OPEN DATASET FILTER" severity = "High" diff --git a/checks/CheckOSCommandInjectionRfcRemoteExec.py b/checks/CheckOSCommandInjectionRfcRemoteExec.py index 3ac05d5..aa603a3 100644 --- a/checks/CheckOSCommandInjectionRfcRemoteExec.py +++ b/checks/CheckOSCommandInjectionRfcRemoteExec.py @@ -1,14 +1,14 @@ -# checks/check_os_command_injection_rfc_remote_exec.py - import re from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckOSCommandInjectionRfcRemoteExec: title = "Potential OS Command injection detected - RFC_REMOTE_EXEC" severity = "High" diff --git a/checks/CheckOSCommandInjectionRfcRemotePipe.py b/checks/CheckOSCommandInjectionRfcRemotePipe.py index 0079a7a..74c2779 100644 --- a/checks/CheckOSCommandInjectionRfcRemotePipe.py +++ b/checks/CheckOSCommandInjectionRfcRemotePipe.py @@ -1,14 +1,14 @@ -# checks/check_os_command_injection_rfc_remote_pipe.py - import re from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckOSCommandInjectionRfcRemotePipe: title = "Potential OS Command injection detected - RFC_REMOTE_PIPE" severity = "High" diff --git a/checks/CheckOSCommandInjectionSxpg.py b/checks/CheckOSCommandInjectionSxpg.py index 9a04607..9c5e9bb 100644 --- a/checks/CheckOSCommandInjectionSxpg.py +++ b/checks/CheckOSCommandInjectionSxpg.py @@ -1,14 +1,14 @@ -# checks/check_os_command_injection_sxpg.py - import re from dataclasses import dataclass from typing import List + @dataclass class CheckResult: line_number: int line_content: str + class CheckOSCommandInjectionSxpg: title = "Potential OS Command injection detected - SXPG Function" severity = "High" diff --git a/checks/CheckWeakHashingAlgorithms.py b/checks/CheckWeakHashingAlgorithms.py index 26963c0..461008f 100644 --- a/checks/CheckWeakHashingAlgorithms.py +++ b/checks/CheckWeakHashingAlgorithms.py @@ -1,5 +1,3 @@ -# checks/check_weak_hashing_algorithms.py - import re from dataclasses import dataclass from typing import List @@ -32,5 +30,3 @@ def run(self, file_content: str) -> List[CheckResult]: if self.pattern.search(line): results.append(CheckResult(i, line.strip())) return results - - diff --git a/checks/__init__.py b/checks/__init__.py index 20b98da..30574e1 100644 --- a/checks/__init__.py +++ b/checks/__init__.py @@ -1,36 +1,34 @@ -# checks/__init__.py - -from .CheckCrossSiteScripting import CheckCrossSiteScripting +from .CheckAbapOutgoingFtpConn import CheckAbapOutgoingFtpConn from .CheckBrokenAuthCheck import CheckBrokenAuthCheck -from .CheckDirectoryTraversalCallAlerts import CheckDirectoryTraversalCallAlerts +from .CheckCallTransformation import CheckCallTransformation +from .CheckCrossSiteScripting import CheckCrossSiteScripting +from .CheckDangerousAbapCommands import CheckDangerousAbapCommands +from .CheckDeleteDynpro import CheckDeleteDynpro from .CheckDirectoryTraversalCRstrbReadBuffered import CheckDirectoryTraversalCRstrbReadBuffered +from .CheckDirectoryTraversalCallAlerts import CheckDirectoryTraversalCallAlerts +from .CheckDirectoryTraversalDeleteDataset import CheckDirectoryTraversalDeleteDataset from .CheckDirectoryTraversalReadDataset import CheckDirectoryTraversalReadDataset from .CheckDirectoryTraversalRfcRemoteFile import CheckDirectoryTraversalRfcRemoteFile -from .CheckDirectoryTraversalDeleteDataset import CheckDirectoryTraversalDeleteDataset +from .CheckDirectoryTraversalTransfer import CheckDirectoryTraversalTransfer from .CheckDosInDoLoop import CheckDosInDoLoop +from .CheckDummyAuthCheck import CheckDummyAuthCheck +from .CheckExecuteProcedure import CheckExecuteProcedure from .CheckExposedSystemCalls import CheckExposedSystemCalls -from .CheckOSCommandInjectionCallSystem import CheckOSCommandInjectionCallSystem -from .CheckOSCommandInjectionCFunction import CheckOSCommandInjectionCFunction +from .CheckGenerateSubroutinePool import CheckGenerateSubroutinePool +from .CheckGetPersistentByQuery import CheckGetPersistentByQuery from .CheckHardcodedCredentials import CheckHardcodedCredentials +from .CheckHardcodedITIN import CheckHardcodedITIN +from .CheckHardcodedIpAddresses import CheckHardcodedIpAddresses +from .CheckHardcodedUrls import CheckHardcodedUrls +from .CheckHardcodedUserAuth import CheckHardcodedUserAuth +from .CheckOSCommandInjectionCFunction import CheckOSCommandInjectionCFunction +from .CheckOSCommandInjectionCallSystem import CheckOSCommandInjectionCallSystem from .CheckOSCommandInjectionClientOS import CheckOSCommandInjectionClientOS -from .CheckDummyAuthCheck import CheckDummyAuthCheck from .CheckOSCommandInjectionOpenDatasetFilter import CheckOSCommandInjectionOpenDatasetFilter from .CheckOSCommandInjectionRfcRemoteExec import CheckOSCommandInjectionRfcRemoteExec from .CheckOSCommandInjectionRfcRemotePipe import CheckOSCommandInjectionRfcRemotePipe -from .CheckHardcodedUrls import CheckHardcodedUrls from .CheckOSCommandInjectionSxpg import CheckOSCommandInjectionSxpg -from .CheckDangerousAbapCommands import CheckDangerousAbapCommands -from .CheckAbapOutgoingFtpConn import CheckAbapOutgoingFtpConn from .CheckWeakHashingAlgorithms import CheckWeakHashingAlgorithms -from .CheckHardcodedIpAddresses import CheckHardcodedIpAddresses -from .CheckHardcodedUserAuth import CheckHardcodedUserAuth -from .CheckDeleteDynpro import CheckDeleteDynpro -from .CheckCallTransformation import CheckCallTransformation -from .CheckGetPersistentByQuery import CheckGetPersistentByQuery -from .CheckExecuteProcedure import CheckExecuteProcedure -from .CheckGenerateSubroutinePool import CheckGenerateSubroutinePool -from .CheckDirectoryTraversalTransfer import CheckDirectoryTraversalTransfer -from .CheckHardcodedITIN import CheckHardcodedITIN __all__ = [ 'CheckCrossSiteScripting', @@ -64,4 +62,4 @@ 'CheckGenerateSubroutinePool', 'CheckDirectoryTraversalTransfer', 'CheckHardcodedITIN' -] \ No newline at end of file +] diff --git a/config.py b/config.py index 4ab4176..a0e2bbb 100644 --- a/config.py +++ b/config.py @@ -2,6 +2,7 @@ import yaml + class Config: def __init__(self, config_file): with open(config_file, 'r') as f: @@ -14,4 +15,4 @@ def get_file_extensions(self): return self.config.get('file_extensions', ['.abap']) def get_exclude_patterns(self): - return self.config.get('exclude_patterns', []) \ No newline at end of file + return self.config.get('exclude_patterns', []) diff --git a/generate_xlsx_report.py b/generate_xlsx_report.py index 76889f8..433db1f 100644 --- a/generate_xlsx_report.py +++ b/generate_xlsx_report.py @@ -1,8 +1,10 @@ +from dataclasses import dataclass +from typing import List + import openpyxl from openpyxl.styles import Font, PatternFill, Alignment from openpyxl.utils import get_column_letter -from typing import List, Dict -from dataclasses import dataclass + @dataclass class ScanResult: @@ -12,6 +14,7 @@ class ScanResult: message: str severity: str + def generate_xlsx_report(results: List[ScanResult], output_file: str): wb = openpyxl.Workbook() ws = wb.active @@ -25,10 +28,10 @@ def generate_xlsx_report(results: List[ScanResult], output_file: str): severity_colors = { "Critical": "FF0000", # Red - "High": "FFA500", # Orange - "Medium": "FFFF00", # Yellow - "Low": "90EE90", # Light Green - "Info": "ADD8E6" # Light Blue + "High": "FFA500", # Orange + "Medium": "FFFF00", # Yellow + "Low": "90EE90", # Light Green + "Info": "ADD8E6" # Light Blue } # Write headers @@ -68,6 +71,7 @@ def generate_xlsx_report(results: List[ScanResult], output_file: str): # Save the workbook wb.save(output_file) + # Example usage if __name__ == "__main__": # Sample data @@ -80,4 +84,4 @@ def generate_xlsx_report(results: List[ScanResult], output_file: str): ] generate_xlsx_report(sample_results, "security_scan_report.xlsx") - print("XLSX report generated successfully.") \ No newline at end of file + print("XLSX report generated successfully.") diff --git a/main.py b/main.py index 0198106..3b39d61 100644 --- a/main.py +++ b/main.py @@ -1,23 +1,22 @@ # main.py import argparse -from email.policy import default -from scanner import Scanner from config import Config from generate_xlsx_report import generate_xlsx_report, ScanResult +from scanner import Scanner def main(): parser = argparse.ArgumentParser(description="ABAP Code Scanner") - #parser.add_argument("path", default=r"C:\Users\admin\Desktop\research\SAP_ABAP_DEMO_CODE", help="Path to the ABAP code directory or file") + parser.add_argument("path", help="Path to the ABAP code directory or file") parser.add_argument("-c", "--config", help="Path to configuration file", default="config.yml") args = parser.parse_args() config = Config(args.config) scanner = Scanner(config) - results = scanner.scan(r"C:\Users\admin\Desktop\research\SAP_ABAP_DEMO_CODE") #args.path) + results = scanner.scan(args.path) # Convert scanner results to ScanResult objects, now including severity report_results = [ @@ -35,9 +34,7 @@ def main(): print("Scan complete. XLSX report generated: abap_security_scan_report.xlsx") - """ for result in results: - print(f"{result.file_path}\r\nLine:{result.line_number} - {result.check_name}: {result.message}") - """ + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..f00836d --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +python_files = Test*.py +python_classes = Test* +python_functions = test_* \ No newline at end of file diff --git a/scanner.py b/scanner.py index 9341e4d..828ff05 100644 --- a/scanner.py +++ b/scanner.py @@ -1,9 +1,10 @@ # scanner.py -import os import importlib +import os from typing import List, NamedTuple + class ScanResult(NamedTuple): file_path: str line_number: int @@ -51,4 +52,4 @@ def _scan_file(self, file_path: str) -> List[ScanResult]: message=result.line_content, severity=check.severity )) - return results \ No newline at end of file + return results diff --git a/tests/TestCheckAbapOutgoingFtpConn.py b/tests/TestCheckAbapOutgoingFtpConn.py index e1731bf..cec5262 100644 --- a/tests/TestCheckAbapOutgoingFtpConn.py +++ b/tests/TestCheckAbapOutgoingFtpConn.py @@ -1,7 +1,7 @@ -# tests/test_check_abap_outgoing_ftp_conn.py - import unittest -from checks.CheckAbapOutgoingFtpConn import CheckAbapOutgoingFtpConn, CheckResult + +from checks.CheckAbapOutgoingFtpConn import CheckAbapOutgoingFtpConn + class TestCheckAbapOutgoingFtpConn(unittest.TestCase): @@ -57,5 +57,6 @@ def test_multiline_function_call(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 2) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckBrokenAuthCheck.py b/tests/TestCheckBrokenAuthCheck.py index 92bde07..60b6626 100644 --- a/tests/TestCheckBrokenAuthCheck.py +++ b/tests/TestCheckBrokenAuthCheck.py @@ -1,7 +1,7 @@ -# tests/test_check_broken_auth_check.py - import unittest -from checks.CheckBrokenAuthCheck import CheckBrokenAuthCheck, CheckResult + +from checks.CheckBrokenAuthCheck import CheckBrokenAuthCheck + class TestCheckBrokenAuthCheck(unittest.TestCase): @@ -78,5 +78,6 @@ def test_auth_check_with_syst_subrc(self): results = self.checker.run(code) self.assertEqual(len(results), 0) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckCallTransformation.py b/tests/TestCheckCallTransformation.py index 9cba0d7..83d097f 100644 --- a/tests/TestCheckCallTransformation.py +++ b/tests/TestCheckCallTransformation.py @@ -1,5 +1,7 @@ import unittest -from checks.CheckCallTransformation import CheckCallTransformation, CheckResult + +from checks.CheckCallTransformation import CheckCallTransformation + class TestCheckCallTransformation(unittest.TestCase): @@ -54,5 +56,6 @@ def test_call_transformation_in_comment(self): results = self.checker.run(code) self.assertEqual(len(results), 0) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckCrossSiteScripting.py b/tests/TestCheckCrossSiteScripting.py index 6462f00..0e49f69 100644 --- a/tests/TestCheckCrossSiteScripting.py +++ b/tests/TestCheckCrossSiteScripting.py @@ -1,7 +1,7 @@ -# tests/test_check_cross_site_scripting.py - import unittest -from checks.CheckCrossSiteScripting import CheckCrossSiteScripting, CheckResult + +from checks.CheckCrossSiteScripting import CheckCrossSiteScripting + class TestCheckCrossSiteScripting(unittest.TestCase): @@ -52,5 +52,6 @@ def test_false_positive(self): results = self.checker.run(code) self.assertEqual(len(results), 0) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckDangerousAbapCommands.py b/tests/TestCheckDangerousAbapCommands.py index 26fd2c7..5a1dcd3 100644 --- a/tests/TestCheckDangerousAbapCommands.py +++ b/tests/TestCheckDangerousAbapCommands.py @@ -1,7 +1,7 @@ -# tests/test_check_dangerous_abap_commands.py - import unittest -from checks.CheckDangerousAbapCommands import CheckDangerousAbapCommands, CheckResult + +from checks.CheckDangerousAbapCommands import CheckDangerousAbapCommands + class TestCheckDangerousAbapCommands(unittest.TestCase): @@ -58,5 +58,6 @@ def test_multiline_statement(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 1) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckDeleteDynpro.py b/tests/TestCheckDeleteDynpro.py index d453c6c..595bfa4 100644 --- a/tests/TestCheckDeleteDynpro.py +++ b/tests/TestCheckDeleteDynpro.py @@ -1,5 +1,7 @@ import unittest -from checks.CheckDeleteDynpro import CheckDeleteDynpro, CheckResult + +from checks.CheckDeleteDynpro import CheckDeleteDynpro + class TestCheckDeleteDynpro(unittest.TestCase): @@ -60,5 +62,6 @@ def test_delete_dynpro_in_comment(self): results = self.checker.run(code) self.assertEqual(len(results), 0) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckDirectoryTraversalCRstrbReadBuffered.py b/tests/TestCheckDirectoryTraversalCRstrbReadBuffered.py index ad12360..35cc9ed 100644 --- a/tests/TestCheckDirectoryTraversalCRstrbReadBuffered.py +++ b/tests/TestCheckDirectoryTraversalCRstrbReadBuffered.py @@ -1,7 +1,8 @@ -# tests/test_check_directory_traversal_c_rstrb_read_buffered.py - import unittest -from checks.CheckDirectoryTraversalCRstrbReadBuffered import CheckDirectoryTraversalCRstrbReadBuffered, CheckResult + +from checks.CheckDirectoryTraversalCRstrbReadBuffered import CheckDirectoryTraversalCRstrbReadBuffered + + class TestCheckDirectoryTraversalCRstrbReadBuffered(unittest.TestCase): def setUp(self): @@ -52,5 +53,6 @@ def test_case_insensitivity(self): results = self.checker.run(code) self.assertEqual(len(results), 1) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckDirectoryTraversalCallAlerts.py b/tests/TestCheckDirectoryTraversalCallAlerts.py index 8f85542..c49165b 100644 --- a/tests/TestCheckDirectoryTraversalCallAlerts.py +++ b/tests/TestCheckDirectoryTraversalCallAlerts.py @@ -1,5 +1,6 @@ import unittest -from checks.CheckDirectoryTraversalCallAlerts import CheckDirectoryTraversalCallAlerts, CheckResult + +from checks.CheckDirectoryTraversalCallAlerts import CheckDirectoryTraversalCallAlerts class TestCheckABAPDirectoryTraversalAlerts(unittest.TestCase): @@ -72,7 +73,7 @@ def test_multiple_calls(self): ID 'TYPE' FIELD 'DATA' ID 'RECTYP' FIELD 'U------' ID 'RC' FIELD _RC - ID 'ERRMSG' FIELD ERRMSG. + ID 'ERRMSG' FIELD ERRMSG. """ results = self.checker.run(code) self.assertEqual(len(results), 1) @@ -109,5 +110,6 @@ def test_multiline_call(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 2) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckDirectoryTraversalDeleteDataset.py b/tests/TestCheckDirectoryTraversalDeleteDataset.py index 97e6d5a..1731b48 100644 --- a/tests/TestCheckDirectoryTraversalDeleteDataset.py +++ b/tests/TestCheckDirectoryTraversalDeleteDataset.py @@ -1,7 +1,7 @@ -# tests/test_check_directory_traversal_delete_dataset.py - import unittest -from checks.CheckDirectoryTraversalDeleteDataset import CheckDirectoryTraversalDeleteDataset, CheckResult + +from checks.CheckDirectoryTraversalDeleteDataset import CheckDirectoryTraversalDeleteDataset + class TestCheckDirectoryTraversalDeleteDataset(unittest.TestCase): @@ -72,5 +72,6 @@ def test_delete_dataset_with_options(self): self.assertEqual(results[0].line_number, 3) self.assertIn("DELETE DATASET file", results[0].line_content) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckDirectoryTraversalReadDataset.py b/tests/TestCheckDirectoryTraversalReadDataset.py index e9b15cf..988c356 100644 --- a/tests/TestCheckDirectoryTraversalReadDataset.py +++ b/tests/TestCheckDirectoryTraversalReadDataset.py @@ -1,7 +1,7 @@ -# tests/test_check_directory_traversal_read_dataset.py - import unittest -from checks.CheckDirectoryTraversalReadDataset import CheckDirectoryTraversalReadDataset, CheckResult + +from checks.CheckDirectoryTraversalReadDataset import CheckDirectoryTraversalReadDataset + class TestCheckDirectoryTraversalReadDataset(unittest.TestCase): @@ -97,5 +97,6 @@ def test_case_insensitivity(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 1) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckDirectoryTraversalRfcRemoteFile.py b/tests/TestCheckDirectoryTraversalRfcRemoteFile.py index cb01ce7..ac3c2d9 100644 --- a/tests/TestCheckDirectoryTraversalRfcRemoteFile.py +++ b/tests/TestCheckDirectoryTraversalRfcRemoteFile.py @@ -1,7 +1,7 @@ -# tests/test_check_directory_traversal_rfc_remote_file.py - import unittest -from checks.CheckDirectoryTraversalRfcRemoteFile import CheckDirectoryTraversalRfcRemoteFile, CheckResult + +from checks.CheckDirectoryTraversalRfcRemoteFile import CheckDirectoryTraversalRfcRemoteFile + class TestCheckDirectoryTraversalRfcRemoteFile(unittest.TestCase): @@ -71,5 +71,6 @@ def test_multiline_call(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 1) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckDirectoryTraversalTransfer.py b/tests/TestCheckDirectoryTraversalTransfer.py index c386b02..b620f97 100644 --- a/tests/TestCheckDirectoryTraversalTransfer.py +++ b/tests/TestCheckDirectoryTraversalTransfer.py @@ -1,5 +1,7 @@ import unittest -from checks.CheckDirectoryTraversalTransfer import CheckDirectoryTraversalTransfer, CheckResult + +from checks.CheckDirectoryTraversalTransfer import CheckDirectoryTraversalTransfer + class TestCheckDirectoryTraversalTransfer(unittest.TestCase): @@ -68,5 +70,6 @@ def test_transfer_in_comment(self): results = self.checker.run(code) self.assertEqual(len(results), 0) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckDosInDoLoop.py b/tests/TestCheckDosInDoLoop.py index cc80f74..f6c9b93 100644 --- a/tests/TestCheckDosInDoLoop.py +++ b/tests/TestCheckDosInDoLoop.py @@ -1,7 +1,7 @@ -# tests/test_check_dos_in_do_loop.py - import unittest -from checks.CheckDosInDoLoop import CheckDosInDoLoop, CheckResult + +from checks.CheckDosInDoLoop import CheckDosInDoLoop + class TestCheckDosInDoLoop(unittest.TestCase): @@ -77,5 +77,6 @@ def test_multiline_do_statement(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 1) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckDummyAuthCheck.py b/tests/TestCheckDummyAuthCheck.py index bb17c3e..926ca40 100644 --- a/tests/TestCheckDummyAuthCheck.py +++ b/tests/TestCheckDummyAuthCheck.py @@ -1,7 +1,7 @@ -# tests/test_check_dummy_auth_check.py - import unittest -from checks.CheckDummyAuthCheck import CheckDummyAuthCheck, CheckResult + +from checks.CheckDummyAuthCheck import CheckDummyAuthCheck + class TestCheckDummyAuthCheck(unittest.TestCase): @@ -74,5 +74,6 @@ def test_multiline_auth_check(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 1) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckExecuteProcedure.py b/tests/TestCheckExecuteProcedure.py index 1781dc2..5186687 100644 --- a/tests/TestCheckExecuteProcedure.py +++ b/tests/TestCheckExecuteProcedure.py @@ -1,5 +1,7 @@ import unittest -from checks.CheckExecuteProcedure import CheckExecuteProcedure, CheckResult + +from checks.CheckExecuteProcedure import CheckExecuteProcedure + class TestCheckExecuteProcedure(unittest.TestCase): @@ -50,5 +52,6 @@ def test_method_in_string(self): results = self.checker.run(code) self.assertEqual(len(results), 1) # Note: This detects the method call even in strings + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckExposedSystemCalls.py b/tests/TestCheckExposedSystemCalls.py index aa8749e..8e64a93 100644 --- a/tests/TestCheckExposedSystemCalls.py +++ b/tests/TestCheckExposedSystemCalls.py @@ -1,7 +1,7 @@ -# tests/test_check_exposed_system_calls.py - import unittest -from checks.CheckExposedSystemCalls import CheckExposedSystemCalls, CheckResult + +from checks.CheckExposedSystemCalls import CheckExposedSystemCalls + class TestCheckExposedSystemCalls(unittest.TestCase): @@ -83,5 +83,6 @@ def test_multiline_system_call(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 1) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckGenerateSubroutinePool.py b/tests/TestCheckGenerateSubroutinePool.py index c409774..ba17a38 100644 --- a/tests/TestCheckGenerateSubroutinePool.py +++ b/tests/TestCheckGenerateSubroutinePool.py @@ -1,5 +1,7 @@ import unittest -from checks.CheckGenerateSubroutinePool import CheckGenerateSubroutinePool, CheckResult + +from checks.CheckGenerateSubroutinePool import CheckGenerateSubroutinePool + class TestCheckGenerateSubroutinePool(unittest.TestCase): @@ -56,5 +58,6 @@ def test_mid_line_occurrence(self): results = self.checker.run(code) self.assertEqual(len(results), 0) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckGetPersistentByQuery.py b/tests/TestCheckGetPersistentByQuery.py index 3c7298a..91164d5 100644 --- a/tests/TestCheckGetPersistentByQuery.py +++ b/tests/TestCheckGetPersistentByQuery.py @@ -1,5 +1,7 @@ import unittest -from checks.CheckGetPersistentByQuery import CheckGetPersistentByQuery, CheckResult + +from checks.CheckGetPersistentByQuery import CheckGetPersistentByQuery + class TestCheckGetPersistentByQuery(unittest.TestCase): @@ -50,5 +52,6 @@ def test_method_in_string(self): results = self.checker.run(code) self.assertEqual(len(results), 1) # Note: This detects the method call even in strings + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckHardcodedCredentials.py b/tests/TestCheckHardcodedCredentials.py index 50906aa..ff1d50e 100644 --- a/tests/TestCheckHardcodedCredentials.py +++ b/tests/TestCheckHardcodedCredentials.py @@ -1,7 +1,7 @@ -# tests/test_check_hardcoded_credentials.py - import unittest -from checks.CheckHardcodedCredentials import CheckHardcodedCredentials, CheckResult + +from checks.CheckHardcodedCredentials import CheckHardcodedCredentials + class TestCheckHardcodedCredentials(unittest.TestCase): @@ -71,5 +71,6 @@ def test_multiline_assignment(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 2) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckHardcodedITIN.py b/tests/TestCheckHardcodedITIN.py index 31f843e..76a1c50 100644 --- a/tests/TestCheckHardcodedITIN.py +++ b/tests/TestCheckHardcodedITIN.py @@ -1,5 +1,7 @@ import unittest -from checks.CheckHardcodedITIN import CheckHardcodedITIN, CheckResult + +from checks.CheckHardcodedITIN import CheckHardcodedITIN + class TestCheckHardcodedITIN(unittest.TestCase): @@ -51,5 +53,6 @@ def test_no_itin(self): results = self.checker.run(code) self.assertEqual(len(results), 0) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckHardcodedIpAddresses.py b/tests/TestCheckHardcodedIpAddresses.py index c106c60..f09bc2c 100644 --- a/tests/TestCheckHardcodedIpAddresses.py +++ b/tests/TestCheckHardcodedIpAddresses.py @@ -1,7 +1,7 @@ -# tests/test_check_hardcoded_ip_addresses.py - import unittest -from checks.CheckHardcodedIpAddresses import CheckHardcodedIpAddresses, CheckResult + +from checks.CheckHardcodedIpAddresses import CheckHardcodedIpAddresses + class TestCheckHardcodedIpAddresses(unittest.TestCase): @@ -49,5 +49,6 @@ def test_ip_in_string(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 1) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckHardcodedUrls.py b/tests/TestCheckHardcodedUrls.py index c3a3f2b..49c000a 100644 --- a/tests/TestCheckHardcodedUrls.py +++ b/tests/TestCheckHardcodedUrls.py @@ -1,7 +1,7 @@ -# tests/test_check_hardcoded_urls.py - import unittest -from checks.CheckHardcodedUrls import CheckHardcodedUrls, CheckResult + +from checks.CheckHardcodedUrls import CheckHardcodedUrls + class TestCheckHardcodedUrls(unittest.TestCase): @@ -59,5 +59,6 @@ def test_multiple_urls_one_line(self): self.assertIn("http://example1.com", results[0].line_content) self.assertIn("https://example2.com", results[0].line_content) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckHardcodedUserAuth.py b/tests/TestCheckHardcodedUserAuth.py index 17aecf4..dbdb73e 100644 --- a/tests/TestCheckHardcodedUserAuth.py +++ b/tests/TestCheckHardcodedUserAuth.py @@ -1,7 +1,7 @@ -# tests/test_check_hardcoded_user_auth.py - import unittest -from checks.CheckHardcodedUserAuth import CheckHardcodedUserAuth, CheckResult + +from checks.CheckHardcodedUserAuth import CheckHardcodedUserAuth + class TestCheckHardcodedUserAuth(unittest.TestCase): @@ -72,5 +72,6 @@ def test_case_insensitivity(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 1) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckOSCommandInjectionCFunction.py b/tests/TestCheckOSCommandInjectionCFunction.py index ca08650..71a4677 100644 --- a/tests/TestCheckOSCommandInjectionCFunction.py +++ b/tests/TestCheckOSCommandInjectionCFunction.py @@ -1,7 +1,7 @@ -# tests/test_check_os_command_injection_c_function.py - import unittest -from checks.CheckOSCommandInjectionCFunction import CheckOSCommandInjectionCFunction, CheckResult + +from checks.CheckOSCommandInjectionCFunction import CheckOSCommandInjectionCFunction + class TestCheckOSCommandInjectionCFunction(unittest.TestCase): @@ -69,5 +69,6 @@ def test_no_field_parameter(self): results = self.checker.run(code) self.assertEqual(len(results), 0) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckOSCommandInjectionCallSystem.py b/tests/TestCheckOSCommandInjectionCallSystem.py index 1b77f0c..08386c4 100644 --- a/tests/TestCheckOSCommandInjectionCallSystem.py +++ b/tests/TestCheckOSCommandInjectionCallSystem.py @@ -1,7 +1,6 @@ -# tests/test_check_os_command_injection_call_system.py - import unittest -from checks.CheckOSCommandInjectionCallSystem import CheckOSCommandInjectionCallSystem, CheckResult + +from checks.CheckOSCommandInjectionCallSystem import CheckOSCommandInjectionCallSystem class TestCheckOSCommandInjectionCallSystem(unittest.TestCase): @@ -78,4 +77,4 @@ def test_multiline_call(self): if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckOSCommandInjectionClientOS.py b/tests/TestCheckOSCommandInjectionClientOS.py index cc88a90..810f55e 100644 --- a/tests/TestCheckOSCommandInjectionClientOS.py +++ b/tests/TestCheckOSCommandInjectionClientOS.py @@ -1,7 +1,7 @@ -# tests/test_check_os_command_injection_client_os.py - import unittest -from checks.CheckOSCommandInjectionClientOS import CheckOSCommandInjectionClientOS, CheckResult + +from checks.CheckOSCommandInjectionClientOS import CheckOSCommandInjectionClientOS + class TestCheckOSCommandInjectionClientOS(unittest.TestCase): @@ -83,5 +83,6 @@ def test_multiline_call(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 3) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckOSCommandInjectionOpenDatasetFilter.py b/tests/TestCheckOSCommandInjectionOpenDatasetFilter.py index 4af3b1f..790c0d3 100644 --- a/tests/TestCheckOSCommandInjectionOpenDatasetFilter.py +++ b/tests/TestCheckOSCommandInjectionOpenDatasetFilter.py @@ -1,7 +1,7 @@ -# tests/test_check_os_command_injection_open_dataset_filter.py - import unittest -from checks.CheckOSCommandInjectionOpenDatasetFilter import CheckOSCommandInjectionOpenDatasetFilter, CheckResult + +from checks.CheckOSCommandInjectionOpenDatasetFilter import CheckOSCommandInjectionOpenDatasetFilter + class TestCheckOSCommandInjectionOpenDatasetFilter(unittest.TestCase): @@ -70,5 +70,6 @@ def test_filter_without_open_dataset(self): results = self.checker.run(code) self.assertEqual(len(results), 0) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckOSCommandInjectionRfcRemoteExec.py b/tests/TestCheckOSCommandInjectionRfcRemoteExec.py index fe29205..7102acd 100644 --- a/tests/TestCheckOSCommandInjectionRfcRemoteExec.py +++ b/tests/TestCheckOSCommandInjectionRfcRemoteExec.py @@ -1,7 +1,7 @@ -# tests/test_check_os_command_injection_rfc_remote_exec.py - import unittest -from checks.CheckOSCommandInjectionRfcRemoteExec import CheckOSCommandInjectionRfcRemoteExec, CheckResult + +from checks.CheckOSCommandInjectionRfcRemoteExec import CheckOSCommandInjectionRfcRemoteExec + class TestCheckOSCommandInjectionRfcRemoteExec(unittest.TestCase): @@ -80,5 +80,6 @@ def test_multiline_call(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 1) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckOSCommandInjectionRfcRemotePipe.py b/tests/TestCheckOSCommandInjectionRfcRemotePipe.py index 597a422..560446f 100644 --- a/tests/TestCheckOSCommandInjectionRfcRemotePipe.py +++ b/tests/TestCheckOSCommandInjectionRfcRemotePipe.py @@ -1,14 +1,13 @@ -# tests/test_check_os_command_injection_rfc_remote_pipe.py - import unittest -from checks.CheckOSCommandInjectionRfcRemotePipe import CheckOSCommandInjectionRfcRemotePipe, CheckResult + +from checks.CheckOSCommandInjectionRfcRemotePipe import CheckOSCommandInjectionRfcRemotePipe + class TestCheckOSCommandInjectionRfcRemotePipe(unittest.TestCase): def setUp(self): self.checker = CheckOSCommandInjectionRfcRemotePipe() - def test_vulnerable_rfc_remote_pipe(self): code = """ CALL FUNCTION 'RFC_REMOTE_PIPE' DESTINATION DEST @@ -81,5 +80,6 @@ def test_multiline_call(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 1) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckOSCommandInjectionSxpg.py b/tests/TestCheckOSCommandInjectionSxpg.py index 7596b65..162dcbf 100644 --- a/tests/TestCheckOSCommandInjectionSxpg.py +++ b/tests/TestCheckOSCommandInjectionSxpg.py @@ -1,7 +1,7 @@ -# tests/test_check_os_command_injection_sxpg.py - import unittest -from checks.CheckOSCommandInjectionSxpg import CheckOSCommandInjectionSxpg, CheckResult + +from checks.CheckOSCommandInjectionSxpg import CheckOSCommandInjectionSxpg + class TestCheckOSCommandInjectionSxpg(unittest.TestCase): @@ -79,5 +79,6 @@ def test_multiline_call(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].line_number, 1) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/TestCheckWeakHashingAlgorithms.py b/tests/TestCheckWeakHashingAlgorithms.py index 06158cd..c2831d8 100644 --- a/tests/TestCheckWeakHashingAlgorithms.py +++ b/tests/TestCheckWeakHashingAlgorithms.py @@ -1,14 +1,13 @@ -# tests/test_check_weak_hashing_algorithms.py - import unittest -from checks.CheckWeakHashingAlgorithms import CheckWeakHashingAlgorithms, CheckResult + +from checks.CheckWeakHashingAlgorithms import CheckWeakHashingAlgorithms + class TestCheckWeakHashingAlgorithms(unittest.TestCase): def setUp(self): self.checker = CheckWeakHashingAlgorithms() - def test_all_weak_algorithms(self): weak_algorithms = [ "MD2", "MD4", "MD5", "MD6", "HAVAL128", "HMACMD5", @@ -52,5 +51,6 @@ def test_no_false_positives(self): results = self.checker.run(code) self.assertEqual(len(results), 0) + if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main()