A GitHub Action wrapper for the Cortex CLI that enables security scanning for Code Security, Container Workload Protection (CWP), and API Security.
- Code Security: Scan source code for secrets, IaC misconfigurations, and dependency vulnerabilities (SCA)
- Container Workload Protection: Scan container images for vulnerabilities and malware
- API Security: Test APIs for security vulnerabilities and misconfigurations
- Automatic Installation: Cortexcli is automatically downloaded from the Cortex API using your credentials
This action uses your Cortex API credentials to download the latest cortexcli binary for your platform. The download happens at runtime via the Cortex API's signed URL endpoint, ensuring you always get the version compatible with your Cortex tenant.
- Active Cortex Cloud license with appropriate module add-ons
- Cortex Cloud API credentials (API Key, API Key ID, and API Base URL)
- For API Security scans: Java 11 or higher must be available
- name: Cortex Code Security Scan
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: code
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
directory: .
repo-id: ${{ github.repository }}
branch: ${{ github.ref_name }}
upload-mode: upload- name: Cortex Container Scan
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: image
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
image: myapp:latest- name: Cortex API Security Scan
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: api
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
scanned-app-url: https://api.example.com
api-spec-file: ./openapi.yaml| Input | Description |
|---|---|
api-base-url |
Cortex Cloud API base URL |
api-key |
Cortex Cloud API key |
api-key-id |
Cortex Cloud API key ID |
scan-type |
Type of scan: code, image, or api |
| Input | Description | Default |
|---|---|---|
directory |
Directory path to scan | - |
file |
Single file path to scan | - |
repo-id |
Repository ID (format: owner/repo) | - |
branch |
Branch name | - |
upload-mode |
Upload mode: upload, no-upload, or no-code |
upload |
framework |
Frameworks to scan (comma-separated) | - |
skip-framework |
Frameworks to skip (comma-separated) | - |
severity |
Filter by severity: critical, high, medium, low, unknown |
- |
output-format |
Output format: cli, json, spdx, junitxml, sarif, cyclonedx, cyclonedx_json |
cli |
output-file-path |
Path for output file | - |
soft-fail |
Do not fail pipeline on findings | false |
no-fail-on-crash |
Return exit code 0 on internal errors | false |
compact |
Do not display code blocks in output | false |
skip-path |
Path to skip during scan | - |
create-repo-if-missing |
Create repository if missing | false |
validate-secrets |
Validate detected secrets | false |
| Input | Description | Default |
|---|---|---|
image |
Container image name to scan | - |
archive |
Scan from archive file | false |
archive-format |
Archive format: docker-archive or oci-archive |
docker-archive |
docker-host |
Path to Docker socket | - |
ci-pipeline-id |
CI pipeline identifier | - |
ci-build-id |
CI build identifier | - |
image-timeout |
Timeout in seconds | 60 |
image-output-format |
Output format: human-readable or json |
human-readable |
soft-fail |
Do not fail pipeline on findings | false |
no-fail-on-crash |
Return exit code 0 on internal errors | false |
| Input | Description | Default |
|---|---|---|
scanned-app-url |
Base URL of the application to scan | - |
api-spec-file |
Path to API specification file | - |
api-spec-type |
API specification type | openapi |
auth-file |
Path to authentication file | - |
java-location |
Path to Java binary (version >= 11) | java |
api-timeout |
Scan timeout in seconds | 300 |
concurrency |
Concurrency limit for scan requests | 5 |
api-output-file |
Output path for report file | - |
| Input | Description | Default |
|---|---|---|
ca-certificate |
Path to CA certificate for proxy/TLS inspection | - |
no-cert-verify |
Disable TLS certificate verification | false |
http-proxy |
HTTP proxy server URL | - |
log-level |
Logging level: INFO, WARNING, ERROR |
INFO |
support |
Generate support bundle with detailed logs | false |
Note: The cortexcli binary is automatically downloaded from the Cortex API using your credentials. The latest available version for your tenant is always used.
| Output | Description |
|---|---|
scan-result |
Path to scan result file (if output file was specified) |
exit-code |
Exit code from the scan |
support-bundle |
Path to support bundle file (if support was enabled) |
name: Code Security Scan
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
code-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Cortex Code Security Scan
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: code
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
directory: .
repo-id: ${{ github.repository }}
branch: ${{ github.ref_name }}
upload-mode: upload
framework: terraform,kubernetes,secrets
severity: critical,high
output-format: sarif
output-file-path: cortex-results.sarif
- name: Upload SARIF results
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: cortex-results.sarif- name: Scan Terraform and Secrets Only
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: code
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
directory: ./infrastructure
repo-id: ${{ github.repository }}
branch: ${{ github.ref_name }}
framework: terraform,secrets
soft-fail: true- name: Local Code Scan
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: code
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
directory: .
upload-mode: no-upload
severity: critical,high
output-format: json
output-file-path: scan-results.jsonname: Build and Scan Container
on:
push:
branches: [ main ]
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Scan container image
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: image
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
image: myapp:${{ github.sha }}
ci-pipeline-id: ${{ github.run_id }}
ci-build-id: ${{ github.run_number }}
soft-fail: false- name: Scan container from archive
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: image
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
image: ./myapp.tar
archive: true
archive-format: docker-archive- name: API Security Scan with Auth
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: api
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
scanned-app-url: https://api.example.com
api-spec-file: ./specs/openapi.yaml
auth-file: ./auth-config.yaml
api-timeout: 600
concurrency: 10
api-output-file: api-scan-results.jsonname: Full Security Scan
on:
push:
branches: [ main ]
pull_request:
jobs:
code-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Code Security Scan
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: code
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
directory: .
repo-id: ${{ github.repository }}
branch: ${{ github.ref_name }}
container-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:latest .
- name: Container Scan
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: image
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
image: myapp:latest
api-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: API Security Scan
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: api
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
scanned-app-url: https://api.example.com
api-spec-file: ./openapi.yaml- name: Scan with Corporate Proxy
uses: PaloAltoNetworks/cortex-cloud-scan@v1
with:
scan-type: code
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
directory: .
repo-id: ${{ github.repository }}
branch: ${{ github.ref_name }}
http-proxy: http://proxy.company.com:8080
ca-certificate: /path/to/company-ca.crt- Store Credentials Securely: Always use GitHub Secrets for API credentials
- Limit Permissions: Use API keys with minimum required permissions
- Review Results: Set
soft-fail: falseto block deployments on findings - Upload Results: Use
upload-mode: uploadto track findings in Cortex Cloud - Regular Scans: Run scans on every push and pull request
Issue: "Failed to get download URL from Cortex API"
- Solution: Verify your API credentials are correct and have the necessary permissions
- Check: Ensure
api-base-urlis in the correct format (e.g.,https://api-tenant.xdr.us.paloaltonetworks.com) - Verify: API key has permissions to download the CLI tool
Issue: "scan-type must be one of: code, image, api"
- Solution: Ensure the
scan-typeinput is set to exactlycode,image, orapi
Issue: "For code scans, either 'directory' or 'file' input is required"
- Solution: Provide either
directoryorfileinput for code scans
Issue: "TLS certificate verification failed"
- Solution: Use the
ca-certificateinput to provide your CA certificate, or setno-cert-verify: true(not recommended for production)
Issue: API scan fails with Java error
- Solution: Ensure Java 11 or higher is installed. Use
actions/setup-java@v4before the scan
Issue: "context deadline exceeded" during image scan
- Solution: Increase the
image-timeoutvalue (default is 60 seconds) - Recommended: Set to 300+ seconds for large images
- Example:
image-timeout: 600for very large images
For troubleshooting scan failures or unexpected behavior, enable the support flag to generate a detailed diagnostic bundle:
- name: Scan with diagnostics
id: scan
uses: PaloAltoNetworks/cortex-cloud-scan@v1
continue-on-error: true
with:
scan-type: image
api-base-url: ${{ secrets.CORTEX_API_BASE_URL }}
api-key: ${{ secrets.CORTEX_API_KEY }}
api-key-id: ${{ secrets.CORTEX_API_KEY_ID }}
image: myapp:latest
support: true # ← Generate support bundle
- name: Upload support bundle
uses: actions/upload-artifact@v4
if: always() && steps.scan.outputs.support-bundle != ''
with:
name: cortex-support-bundle
path: ${{ steps.scan.outputs.support-bundle }}
retention-days: 30Note: The if condition checks that a support bundle was actually generated before attempting upload.
The support bundle will be available in the workflow artifacts and can be downloaded for analysis or shared with Palo Alto Networks support.
- 1.0.0
- Initial Release
- Support for Code Security, Container Workload Protection, and API Security scans
- Comprehensive input options for all scan types
This project is licensed under the MIT License - see the LICENSE file for details
For issues and questions:
- Review the Cortex CLI documentation
- Check existing GitHub Issues
- Contact Palo Alto Networks support
See CONTRIBUTING for details on how to contribute to this project.