diff --git a/.github/workflows/owasp-depcheck-evidence-example.yml b/.github/workflows/owasp-depcheck-evidence-example.yml new file mode 100644 index 0000000..35b7fdd --- /dev/null +++ b/.github/workflows/owasp-depcheck-evidence-example.yml @@ -0,0 +1,90 @@ +name: "Dependency Check with Evidence Integration" +on: + workflow_dispatch: + +permissions: + id-token: write + contents: read + +env: + REGISTRY_DOMAIN: ${{ vars.JF_URL }} + REPO_NAME: 'maven-depcheck-local' + PACKAGE_NAME: 'simple-java-depcheck' + VERSION: ${{ github.run_number }} + BUILD_NAME: 'depcheck-maven-build' + ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE: true + +jobs: + package-java-project-with-depcheck-evidence: + runs-on: ubuntu-latest + steps: + # Setup JFrog CLI + - name: Setup jfrog cli + uses: jfrog/setup-jfrog-cli@v4 + env: + JF_URL: ${{ vars.ARTIFACTORY_URL }} + JF_ACCESS_TOKEN: ${{ secrets.ARTIFACTORY_ACCESS_TOKEN }} + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + + - name: Cache Maven dependencies + uses: actions/cache@v4 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + + # Build and publish the Java package to JFrog Artifactory + - name: Build and publish Java package to Artifactory + run: | + cd examples/depcheck/src + mvn clean compile + mvn package + echo "Deploying package to Artifactory" + jf rt upload target/*.jar $REPO_NAME/$PACKAGE_NAME/$VERSION/ --build-name=$BUILD_NAME --build-number=${{ github.run_number }} + echo "Publishing build info" + jf rt build-publish $BUILD_NAME ${{ github.run_number }} + + - name: Dependency Check Scan + uses: dependency-check/Dependency-Check_Action@1.1.0 + env: + # actions/setup-java@v1 changes JAVA_HOME so it needs to be reset to match the depcheck image + JAVA_HOME: /opt/jdk + with: + project: '${{ env.PACKAGE_NAME }}' + format: 'JSON' + path: examples/depcheck/src/target + out: ${{ github.workspace }}/reports + args: > + --noupdate + + # This is an optional step to generate a custom markdown report + - name: Generate optional custom markdown report + if: env.ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE == 'true' + run: | + cd ${{ github.workspace }} + if [ -f "reports/dependency-check-report.json" ]; then + python examples/depcheck/scripts/markdown-converter.py reports/dependency-check-report.json + echo "Custom markdown report generated" + else + echo "Warning: dependency-check-report.json not found" + exit 1 + fi + + # Attaching the evidence to associated build + - name: Attach evidence to associated build + run: | + jf evd create \ + --build-name $BUILD_NAME \ + --build-number ${{ github.run_number }} \ + --key "${{ secrets.PRIVATE_KEY }}" \ + --key-alias "${{ vars.EVIDENCE_KEY_ALIAS }}" \ + --predicate ./reports/dependency-check-report.json \ + --predicate-type https://owasp.org/dependency-check \ + ${{ env.ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE == 'true' && '--markdown "dependency-check-report.md"' || '' }} \ No newline at end of file diff --git a/examples/depcheck/.gitignore b/examples/depcheck/.gitignore new file mode 100644 index 0000000..524f096 --- /dev/null +++ b/examples/depcheck/.gitignore @@ -0,0 +1,24 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* diff --git a/examples/depcheck/README.md b/examples/depcheck/README.md new file mode 100644 index 0000000..0dfdb95 --- /dev/null +++ b/examples/depcheck/README.md @@ -0,0 +1,221 @@ +# **OWASP Dependency Check Evidence Example** + +This repository provides a working example of a GitHub Actions workflow that automates Java Maven project security scanning using **OWASP Dependency Check**. It then attaches the resulting vulnerability report as signed, verifiable evidence to the package in **JFrog Artifactory**. + +This workflow is an essential pattern for DevSecOps, creating a traceable, compliant, and secure software supply chain with comprehensive security vulnerability assessment. + +### **Key Features** + +* **Automated Build & Deploy**: Builds a Java Maven project and deploys it to Artifactory. +* **OWASP Dependency Check**: Runs comprehensive security vulnerability scanning using OWASP Dependency Check. +* **Vulnerability Report Generation**: Generates detailed JSON vulnerability reports with CVSS scoring. +* **Optional Markdown Report**: Includes a helper script to generate a human-readable Markdown summary from the Dependency Check JSON results. +* **Signed Evidence Attachment**: Attaches the vulnerability scan results to the corresponding package version in Artifactory using jf evd create, cryptographically signing it for integrity. +* **OWASP Dependency Check**: [OWASP Dependency Check](https://owasp.org/www-project-dependency-check/) security vulnerability scanner + +### **Workflow** + +The following diagram illustrates the sequence of operations performed by the GitHub Actions workflow. + +```mermaid +graph TD + A[Workflow Dispatch Trigger] --> B[Setup JFrog CLI] + B --> C[Checkout Repository] + C --> D[Setup JDK 21] + D --> E[Build and Publish Java Package to Artifactory] + E --> F[Run OWASP Dependency Check Scan] + F --> G[Generate Vulnerability Report] + G --> H{Attach Optional Custom Markdown Report?} + H -->|Yes| I[Generate Custom Markdown Report] + H -->|No| J[Skip Markdown Report] + I --> K[Attach Evidence to Package] + J --> K[Attach Evidence to Package] +``` + +--- + +### **1. Prerequisites** + +Before running this workflow, you must have: + +* JFrog CLI 2.65.0 or above (installed automatically in the workflow) +* An Artifactory repository of type maven (e.g., maven-depcheck-local). +* A private key and a corresponding key alias configured in your JFrog Platform for signing evidence. +* The following GitHub repository variables: + * `JF_URL` (Artifactory base URL, e.g. `https://mycompany.jfrog.io`) + * `EVIDENCE_KEY_ALIAS` (Key alias for signing evidence) +* The following GitHub repository secrets: + * `ARTIFACTORY_ACCESS_TOKEN` (Artifactory access token) + * `PRIVATE_KEY` (Private key for signing evidence) + +### Environment Variables Used + +* `REGISTRY_DOMAIN` - Maven registry domain +* `REPO_NAME` - Maven repository name +* `PACKAGE_NAME` - Maven artifact name +* `VERSION` - Build version (uses GitHub run number) +* `BUILD_NAME` - Build information name in Artifactory +* `ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE` - Controls markdown report generation + +### **2. Configuration** + +To use this workflow, you must configure the following GitHub Repository Secrets and Variables. + +#### **GitHub Secrets** + +Navigate to Settings > Secrets and variables > Actions and create the following secrets: + +| Secret Name | Description | +| :---- | :---- | +| ARTIFACTORY_ACCESS_TOKEN | A valid JFrog Access Token with permissions to read, write, and annotate in your target repository. | +| PRIVATE_KEY | The private key used to sign the evidence. This key corresponds to the alias configured in JFrog Platform. | + +#### **GitHub Variables** + +Navigate to Settings > Secrets and variables > Actions and create the following variables: + +| Variable Name | Description | Example Value | +| :---- | :---- | :---- | +| JF_URL | The base URL of your JFrog Platform instance. | https://mycompany.jfrog.io | +| EVIDENCE_KEY_ALIAS | The alias for the public key in JFrog Platform used to verify the evidence signature. | my-signing-key-alias | + +#### **Workflow Environment Variables** + +You can also customize the workflow's behavior by modifying the env block in the .github/workflows/owasp-depcheck-evidence-example.yml file: + +| Variable Name | Description | Default Value | +| :---- | :---- | :---- | +| REPO_NAME | The name of the target Maven repository in Artifactory. | maven-depcheck-local | +| PACKAGE_NAME | The name of the Maven artifact to be built and deployed. | simple-java-depcheck | +| BUILD_NAME | The name assigned to the build information in Artifactory. | depcheck-maven-build | +| ATTACH_OPTIONAL_CUSTOM_MARKDOWN_TO_EVIDENCE | Set to true to generate and attach a Markdown report alongside the JSON evidence. Set to false to skip this step. | true | + +--- + +### **3. Usage** + +This workflow is triggered manually. + +1. Navigate to the **Actions** tab of your forked repository. +2. In the left sidebar, click on the **Dependency Check with Evidence Integration** workflow. +3. Click the **Run workflow** dropdown button. You can leave the default branch selected. +4. Click the green **Run workflow** button. + +## Repository Structure + +``` +examples/depcheck/ +├── .github/workflows/ +│ └── owasp-depcheck-evidence-example.yml # Main workflow with evidence integration +├── examples/depcheck/ +│ ├── src/ # Java Maven project +│ │ ├── main/java/com/example/ # Source code +│ │ └── pom.xml # Maven configuration +│ └── scripts/ +│ └── markdown-converter.py # Evidence report converter +└── README.md # This file +``` + +## Workflow Overview + +The GitHub Actions workflow (`owasp-depcheck-evidence-example.yml`) performs the following steps: + +### 1. **Build and Package** +- Sets up Java 21 environment +- Builds the Maven project +- Deploys artifacts to JFrog Artifactory + +### 2. **Security Scanning** +- Runs OWASP Dependency Check on built artifacts +- Scans for known vulnerabilities in dependencies +- Generates JSON vulnerability reports + +### 3. **Evidence Generation** +- Converts JSON reports to readable markdown format (optional) +- Creates evidence reports suitable for attachment to packages +- Provides comprehensive vulnerability analysis + +### 4. **Evidence Attachment** +- Publishes build information to JFrog Artifactory +- Attaches signed evidence to builds in JFrog Artifactory +- Uses cryptographic signing for evidence integrity +- Creates verifiable security attestation + +## Evidence Integration + +This project follows the evidence management pattern from [JFrog Evidence Examples](https://github.com/jfrog/Evidence-Examples), where: + +- **Security scan results** become verifiable evidence +- **Markdown reports** provide human-readable evidence +- **Evidence is attached** to packages for security attestation +- **Signed evidence** ensures integrity and authenticity + +## Local Development + +For local development and testing: + +```bash +# Build the project locally +cd examples/depcheck/src +mvn clean compile package + +# Run dependency check (requires OWASP Dependency Check CLI) +dependency-check --scan target/ --format JSON --out reports/ + +# Convert to markdown evidence +python ../scripts/markdown-converter.py reports/dependency-check-report.json +``` + +## Dependency Check Configuration + +The workflow configures OWASP Dependency Check with: + +- **Project Name**: Uses the package name for consistency +- **Output Format**: JSON for processing, optional Markdown for evidence +- **Scan Path**: Maven target directory (`examples/depcheck/src/target`) +- **No Updates**: Uses cached vulnerability database (`--noupdate`) +- **Evidence Type**: `https://owasp.org/dependency-check` predicate type +- **Evidence Attachment**: Attaches evidence to build information in Artifactory + +## Evidence Management + +### Attaching Evidence to Builds + +This workflow follows the JFrog Evidence Examples pattern by attaching evidence to builds: + +1. **Build Evidence** - Security scan results attached to build information in Artifactory +2. **Build Publishing** - Build information is published to Artifactory for evidence attachment +3. **Signed Evidence** - Cryptographically signed vulnerability reports +4. **Verifiable Evidence** - Evidence that can be verified using the configured key alias + +### Evidence Types Generated + +- **Dependency Check JSON Report** - Raw vulnerability data in JSON format +- **Markdown Evidence Report** - Human-readable security summary (optional) +- **Build Evidence** - Evidence attached to build information in Artifactory +- **Signed Evidence** - Cryptographically signed evidence for integrity verification + +## Security Considerations + +- **CVSS Scoring**: Uses industry-standard Common Vulnerability Scoring System +- **Vulnerability Assessment**: Comprehensive scanning of dependencies for known vulnerabilities +- **Evidence Integrity**: Generated evidence is cryptographically signed for authenticity +- **Audit Trail**: Complete record of security assessments with verifiable evidence +- **CVE References**: Direct links to vulnerability databases for detailed information + +## Contributing + +This project follows the evidence management patterns established by JFrog Evidence Examples. Contributions should: + +1. Maintain evidence integrity and verifiability +2. Follow the established workflow patterns for security scanning +3. Ensure vulnerability scan results are properly formatted +4. Include appropriate documentation for evidence types +5. Maintain consistency with the OWASP Dependency Check integration + +## References + +- [JFrog Evidence Examples](https://github.com/jfrog/Evidence-Examples) - Original inspiration and patterns +- [OWASP Dependency Check](https://owasp.org/www-project-dependency-check/) - Vulnerability scanning tool +- [JFrog Evidence Management](https://jfrog.com/help/r/jfrog-artifactory-documentation/evidence-management-overview) - Evidence management documentation +- [CVSS Scoring System](https://www.first.org/cvss/) - Vulnerability severity scoring \ No newline at end of file diff --git a/examples/depcheck/scripts/markdown-converter.py b/examples/depcheck/scripts/markdown-converter.py new file mode 100644 index 0000000..8238896 --- /dev/null +++ b/examples/depcheck/scripts/markdown-converter.py @@ -0,0 +1,352 @@ +#!/usr/bin/env python3 +""" +Dependency Check Markdown Converter +Inspired by JFrog Evidence Examples + +This script converts OWASP Dependency Check JSON reports to readable markdown format +for better integration with documentation and reporting systems. +""" + +import json +import sys +import os +from datetime import datetime +from typing import Dict, List, Any, Optional + + +class DependencyCheckMarkdownConverter: + """Converts Dependency Check JSON reports to markdown format.""" + + def __init__(self, json_file_path: str): + """Initialize the converter with a JSON report file.""" + self.json_file_path = json_file_path + self.report_data = self._load_json_report() + + def _load_json_report(self) -> Dict[str, Any]: + """Load and parse the JSON report file.""" + try: + with open(self.json_file_path, 'r', encoding='utf-8') as file: + return json.load(file) + except FileNotFoundError: + print(f"Error: Report file '{self.json_file_path}' not found.") + sys.exit(1) + except json.JSONDecodeError as e: + print(f"Error: Invalid JSON in report file: {e}") + sys.exit(1) + + def _format_cvss_score(self, score: Optional[float]) -> str: + """Format CVSS score with color coding.""" + if score is None: + return "N/A" + + if score >= 9.0: + return f"**{score:.1f} (Critical)**" + elif score >= 7.0: + return f"**{score:.1f} (High)**" + elif score >= 4.0: + return f"**{score:.1f} (Medium)**" + elif score >= 0.1: + return f"**{score:.1f} (Low)**" + else: + return f"**{score:.1f} (None)**" + + def _format_cve_references(self, references: List[Dict[str, str]]) -> str: + """Format CVE references as markdown links.""" + if not references: + return "No references available" + + formatted_refs = [] + for ref in references: + name = ref.get('name', 'Unknown') + url = ref.get('url', '') + if url: + formatted_refs.append(f"[{name}]({url})") + else: + formatted_refs.append(name) + + return ", ".join(formatted_refs) + + def _format_vulnerability_table(self, vulnerabilities: List[Dict[str, Any]]) -> str: + """Create a markdown table for vulnerabilities.""" + if not vulnerabilities: + return "No vulnerabilities found." + + table = "| CVE ID | Severity | CVSS Score | Description | References |\n" + table += "|--------|----------|------------|-------------|------------|\n" + + for vuln in vulnerabilities: + cve_id = vuln.get('name', 'N/A') + severity = vuln.get('severity', 'Unknown') + cvss_score = self._format_cvss_score(vuln.get('cvssv3', {}).get('baseScore')) + description = vuln.get('description', 'No description available') + # Truncate description if too long + if len(description) > 100: + description = description[:97] + "..." + + references = self._format_cve_references(vuln.get('references', [])) + if len(references) > 50: + references = references[:47] + "..." + + table += f"| {cve_id} | {severity} | {cvss_score} | {description} | {references} |\n" + + return table + + def _format_dependency_details(self, dependencies: List[Dict[str, Any]]) -> str: + """Format dependency details with vulnerabilities.""" + if not dependencies: + return "No dependencies analyzed." + + markdown = "" + + for dep in dependencies: + file_path = dep.get('filePath', 'Unknown') + file_name = dep.get('fileName', 'Unknown') + is_virtual = dep.get('isVirtual', False) + + markdown += f"### {file_name}\n\n" + markdown += f"- **File Path:** `{file_path}`\n" + markdown += f"- **Is Virtual:** {is_virtual}\n" + + # Add SHA information + md5 = dep.get('md5', 'N/A') + sha1 = dep.get('sha1', 'N/A') + sha256 = dep.get('sha256', 'N/A') + + markdown += f"- **MD5:** `{md5}`\n" + markdown += f"- **SHA1:** `{sha1}`\n" + markdown += f"- **SHA256:** `{sha256}`\n" + + # Add all packages + packages = dep.get('packages', []) + if packages: + markdown += "\n#### Packages\n\n" + for i, pkg in enumerate(packages, 1): + pkg_id = pkg.get('id', 'Unknown') + confidence = pkg.get('confidence', 'N/A') + markdown += f"**Package {i}:**\n" + markdown += f"- **ID:** `{pkg_id}`\n" + markdown += f"- **Confidence:** {confidence}\n" + markdown += "\n" + + # Add evidence collected + evidence = dep.get('evidenceCollected', {}) + if evidence: + markdown += "#### Evidence Collected\n\n" + + # Product evidence + product_evidence = evidence.get('productEvidence', []) + if product_evidence: + markdown += "**Product Evidence:**\n" + for ev in product_evidence: + name = ev.get('name', 'N/A') + value = ev.get('value', 'N/A') + confidence = ev.get('confidence', 'N/A') + source = ev.get('source', 'N/A') + markdown += f"- **{name}:** {value} (Confidence: {confidence}, Source: {source})\n" + markdown += "\n" + + # Vendor evidence + vendor_evidence = evidence.get('vendorEvidence', []) + if vendor_evidence: + markdown += "**Vendor Evidence:**\n" + for ev in vendor_evidence: + name = ev.get('name', 'N/A') + value = ev.get('value', 'N/A') + confidence = ev.get('confidence', 'N/A') + source = ev.get('source', 'N/A') + markdown += f"- **{name}:** {value} (Confidence: {confidence}, Source: {source})\n" + markdown += "\n" + + # Version evidence + version_evidence = evidence.get('versionEvidence', []) + if version_evidence: + markdown += "**Version Evidence:**\n" + for ev in version_evidence: + name = ev.get('name', 'N/A') + value = ev.get('value', 'N/A') + confidence = ev.get('confidence', 'N/A') + source = ev.get('source', 'N/A') + markdown += f"- **{name}:** {value} (Confidence: {confidence}, Source: {source})\n" + markdown += "\n" + + vulnerabilities = dep.get('vulnerabilities', []) + if vulnerabilities: + markdown += f"#### Vulnerabilities Found: {len(vulnerabilities)}\n\n" + markdown += self._format_vulnerability_table(vulnerabilities) + else: + markdown += "#### Vulnerabilities Found: 0\n\n" + + markdown += "\n---\n\n" + + return markdown + + def generate_summary(self) -> str: + """Generate a summary section of the report.""" + summary = self.report_data.get('summary', {}) + + total_deps = summary.get('totalDependencies', 0) + vulnerable_deps = summary.get('vulnerableDependencies', 0) + total_vulns = summary.get('totalVulnerabilities', 0) + + markdown = "## Security Summary\n\n" + markdown += f"- **Total Dependencies Analyzed:** {total_deps}\n" + markdown += f"- **Vulnerable Dependencies:** {vulnerable_deps}\n" + markdown += f"- **Total Vulnerabilities Found:** {total_vulns}\n" + + if total_deps > 0: + vuln_percentage = (vulnerable_deps / total_deps) * 100 + markdown += f"- **Vulnerability Rate:** {vuln_percentage:.1f}%\n" + + # Add severity breakdown + severity_counts = summary.get('severityCounts', {}) + if severity_counts: + markdown += "\n### Severity Breakdown\n\n" + for severity, count in severity_counts.items(): + if count > 0: + markdown += f"- **{severity.title()}:** {count}\n" + + return markdown + + def generate_report(self) -> str: + """Generate the complete markdown report.""" + scan_info = self.report_data.get('scanInfo', {}) + + markdown = "# OWASP Dependency Check Security Report\n\n" + markdown += f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}\n" + + # Add report schema version + report_schema = self.report_data.get('reportSchema', 'N/A') + markdown += f"**Report Schema Version:** {report_schema}\n\n" + + # Report metadata + project_info = self.report_data.get('projectInfo', {}) + if project_info: + markdown += "## Report Information\n\n" + markdown += f"- **Project Name:** {project_info.get('name', 'N/A')}\n" + markdown += f"- **Report Date:** {project_info.get('reportDate', 'N/A')}\n" + + # Add credits if available + credits = project_info.get('credits', {}) + if credits: + markdown += "\n### Data Sources and Credits\n\n" + for source, description in credits.items(): + markdown += f"- **{source}:** {description}\n" + + markdown += "\n" + + # Scan information + if scan_info: + markdown += "## Scan Information\n\n" + markdown += f"- **Scan Engine Version:** {scan_info.get('engineVersion', 'N/A')}\n" + + # Data Source table + data_sources = scan_info.get('dataSource', []) + if data_sources: + markdown += "\n### Data Sources\n\n" + markdown += "| Data Source | Last Checked | Last Modified |\n" + markdown += "|-------------|--------------|---------------|\n" + + for source in data_sources: + name = source.get('name', 'N/A') + timestamp = source.get('timestamp', 'N/A') + # Format timestamp for better readability + if timestamp != 'N/A': + try: + # Parse ISO timestamp and format it + dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00')) + formatted_timestamp = dt.strftime('%Y-%m-%d %H:%M:%S UTC') + except: + formatted_timestamp = timestamp + else: + formatted_timestamp = 'N/A' + + markdown += f"| {name} | {formatted_timestamp} | {formatted_timestamp} |\n" + markdown += "\n" + + # Only add scan duration if it's not N/A + scan_duration = scan_info.get('scanDuration', 'N/A') + if scan_duration != 'N/A': + markdown += f"- **Scan Duration:** {scan_duration}\n" + markdown += "\n" + + # Summary + markdown += self.generate_summary() + + # Dependencies and vulnerabilities + dependencies = self.report_data.get('dependencies', []) + if dependencies: + markdown += "\n## Dependencies Analysis\n\n" + markdown += self._format_dependency_details(dependencies) + + # Footer + markdown += "\n---\n\n" + + return markdown + + def save_markdown(self, output_file: str = None) -> str: + """Save the markdown report to a file.""" + if output_file is None: + base_name = os.path.splitext(self.json_file_path)[0] + # Remove any existing "-report" suffix to avoid duplication + if base_name.endswith('-report'): + base_name = base_name[:-7] # Remove "-report" + output_file = f"{base_name}-report.md" + + markdown_content = self.generate_report() + + try: + # Ensure the output directory exists + output_dir = os.path.dirname(output_file) + if output_dir and not os.path.exists(output_dir): + os.makedirs(output_dir, exist_ok=True) + + with open(output_file, 'w', encoding='utf-8') as file: + file.write(markdown_content) + print(f"Markdown report saved to: {output_file}") + return output_file + except PermissionError as e: + print(f"Permission denied: {e}") + print(f"Trying to save to current directory instead...") + # Try saving to current directory as fallback + fallback_file = os.path.basename(output_file) + try: + with open(fallback_file, 'w', encoding='utf-8') as file: + file.write(markdown_content) + print(f"Markdown report saved to: {fallback_file}") + return fallback_file + except Exception as fallback_e: + print(f"Error saving to fallback location: {fallback_e}") + return None + except Exception as e: + print(f"Error saving markdown file: {e}") + return None + + +def main(): + """Main function to run the converter.""" + if len(sys.argv) < 2: + print("Usage: python markdown-converter.py [output-markdown-file]") + print("Example: python markdown-converter.py dependency-check-report.json") + sys.exit(1) + + json_file = sys.argv[1] + output_file = sys.argv[2] if len(sys.argv) > 2 else None + + try: + converter = DependencyCheckMarkdownConverter(json_file) + output_path = converter.save_markdown(output_file) + + if output_path: + print(f"\nConversion completed successfully!") + print(f"Input: {json_file}") + print(f"Output: {output_path}") + else: + sys.exit(1) + + except Exception as e: + print(f"Error during conversion: {e}") + sys.exit(1) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/depcheck/src/main/java/com/example/Addition.java b/examples/depcheck/src/main/java/com/example/Addition.java new file mode 100644 index 0000000..b090620 --- /dev/null +++ b/examples/depcheck/src/main/java/com/example/Addition.java @@ -0,0 +1,30 @@ +package com.example; + +/** + * A class dedicated to addition operations. + */ +public class Addition { + + /** + * Adds two integers. + * @param a The first integer. + * @param b The second integer. + * @return The sum of a and b. + */ + public int add(int a, int b) { + return a + b; + } + + /** + * Adds multiple integers. + * @param numbers The integers to add. + * @return The sum of all numbers. + */ + public int addMultiple(int... numbers) { + int sum = 0; + for (int number : numbers) { + sum += number; + } + return sum; + } +} \ No newline at end of file diff --git a/examples/depcheck/src/main/java/com/example/Calculator.java b/examples/depcheck/src/main/java/com/example/Calculator.java new file mode 100644 index 0000000..5daa9ad --- /dev/null +++ b/examples/depcheck/src/main/java/com/example/Calculator.java @@ -0,0 +1,59 @@ +package com.example; + +/** + * A calculator class that uses separate Addition and Subtraction classes. + */ +public class Calculator { + + private final Addition addition; + private final Subtraction subtraction; + + public Calculator() { + this.addition = new Addition(); + this.subtraction = new Subtraction(); + } + + public Calculator(Addition addition, Subtraction subtraction) { + this.addition = addition; + this.subtraction = subtraction; + } + + /** + * Adds two integers using the Addition class. + * @param a The first integer. + * @param b The second integer. + * @return The sum of a and b. + */ + public int add(int a, int b) { + return addition.add(a, b); + } + + /** + * Subtracts two integers using the Subtraction class. + * @param a The first integer (minuend). + * @param b The second integer (subtrahend). + * @return The difference of a and b. + */ + public int subtract(int a, int b) { + return subtraction.subtract(a, b); + } + + /** + * Adds multiple integers using the Addition class. + * @param numbers The integers to add. + * @return The sum of all numbers. + */ + public int addMultiple(int... numbers) { + return addition.addMultiple(numbers); + } + + /** + * Subtracts multiple integers from the first number using the Subtraction class. + * @param first The first number to subtract from. + * @param numbers The integers to subtract. + * @return The result after subtracting all numbers from the first. + */ + public int subtractMultiple(int first, int... numbers) { + return subtraction.subtractMultiple(first, numbers); + } +} \ No newline at end of file diff --git a/examples/depcheck/src/main/java/com/example/Subtraction.java b/examples/depcheck/src/main/java/com/example/Subtraction.java new file mode 100644 index 0000000..151a493 --- /dev/null +++ b/examples/depcheck/src/main/java/com/example/Subtraction.java @@ -0,0 +1,31 @@ +package com.example; + +/** + * A class dedicated to subtraction operations. + */ +public class Subtraction { + + /** + * Subtracts two integers. + * @param a The first integer (minuend). + * @param b The second integer (subtrahend). + * @return The difference of a and b. + */ + public int subtract(int a, int b) { + return a - b; + } + + /** + * Subtracts multiple integers from the first number. + * @param first The first number to subtract from. + * @param numbers The integers to subtract. + * @return The result after subtracting all numbers from the first. + */ + public int subtractMultiple(int first, int... numbers) { + int result = first; + for (int number : numbers) { + result -= number; + } + return result; + } +} \ No newline at end of file diff --git a/examples/depcheck/src/pom.xml b/examples/depcheck/src/pom.xml new file mode 100644 index 0000000..18209bd --- /dev/null +++ b/examples/depcheck/src/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + com.example + simple-java-depcheck + 1.0-SNAPSHOT + + + UTF-8 + 21 + 21 + + + + + + + + main/java + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + + + \ No newline at end of file