GitHub Workspace Root
├── scripts/ (Epyon checked out here directly)
├── scans/
├── target-repo-REPONAME/ (Cloned from external URL)
│ └── .epyon-ignore.yml (Target's ignore file)
└── /tmp/epyon-env
Key Characteristics:
- Epyon is checked out to workspace root (line 50:
uses: actions/checkout@v4- no path parameter) - Target repository is cloned dynamically from user-provided URL
- Scripts execute from workspace root (no
cd epyonneeded) - Used for scanning ANY external public GitHub repository
Workflow Trigger:
- Manual (
workflow_dispatch) with URL input - User provides: repository URL, subdirectory (optional), scan mode
Target Directory:
- Dynamically created:
$PWD/target-repo-$REPO_NAME - Set in
/tmp/epyon-envduring initialization
GitHub Workspace Root
├── target-repo/ (Current repo checked out here)
│ └── .epyon-ignore.yml (This repo's ignore file)
├── epyon/ (Epyon checked out as subdirectory)
│ ├── scripts/
│ └── scans/
└── /tmp/epyon-env
Key Characteristics:
- Target repo checked out first to
target-repo/subdirectory (line 62-65) - Epyon checked out to
epyon/subdirectory (line 67-71) - All script steps start with
cd epyonbefore execution - Used for scanning the repository WHERE the workflow runs
Workflow Trigger:
- Automatic: push, PR, schedule
- Manual:
workflow_dispatchwith optional subdirectory
Target Directory:
- Static path:
${{ github.workspace }}/target-repo - Set in
/tmp/epyon-envduring initialization
scan-public-repo.yml (Before fix):
# BUG: Lines 112, 117 used > instead of >>
echo "TARGET_DIR=$PWD/target-repo-$REPO_NAME" > /tmp/epyon-env # OVERWRITES!This erased all previously set variables (TARGET_NAME, SCAN_MODE, etc.)
scan-private-repo.yml (Always correct):
echo "TARGET_DIR=$TARGET_DIR" > /tmp/epyon-env # First write
echo "SCAN_MODE=$SCAN_MODE" >> /tmp/epyon-env # AppendsUses > for FIRST write, then >> for all subsequent appends.
Impact: Public scans had corrupted environment, causing TARGET_DIR to be wrong or undefined.
scan-public-repo.yml:
- name: Layer 1 - Generate SBOM
run: |
source /tmp/epyon-env
chmod +x scripts/shell/run-complete-sbom-scan.sh
./scripts/shell/run-complete-sbom-scan.sh
# Runs from workspace root (Epyon location)scan-private-repo.yml:
- name: Layer 1 - Generate SBOM
run: |
source /tmp/epyon-env
cd epyon # ← Changes directory!
chmod +x scripts/shell/run-complete-sbom-scan.sh
./scripts/shell/run-complete-sbom-scan.shImpact: Different relative path resolution, but both work because environment variables provide absolute paths.
Before Fix:
# consolidate-security-reports.sh line 624
SCAN_DIR="$SCAN_DIR" "$DASHBOARD_GENERATOR"
# Missing TARGET_DIR! Dashboard couldn't find .epyon-ignore.ymlAfter Fix:
SCAN_DIR="$SCAN_DIR" TARGET_DIR="$TARGET_DIR" "$DASHBOARD_GENERATOR"
# Now dashboard receives TARGET_DIR and can find ignore fileWhy Private Scans Worked: Private scans MAY have worked by accident because:
- The dashboard tries multiple fallback paths (line 156-159 in generate-security-dashboard.sh)
- One fallback:
"${LATEST_SCAN}/../../.epyon-ignore.yml" - From
epyon/scans/NAME/,../../goes to workspace root - Might accidentally find
target-repo/.epyon-ignore.yml
Why Public Scans Failed:
- Fallback paths don't match the
target-repo-$REPO_NAMEstructure - Without TARGET_DIR, dashboard couldn't locate the ignore file
- Result: 0 suppressions loaded, all findings counted
- Lines 112, 117: Changed
>to>>to append TARGET_DIR instead of overwriting - Line 255: Added
TARGET_DIR="$TARGET_DIR"to dashboard generation step
- Line 276: Added
TARGET_DIR="$TARGET_DIR"to dashboard generation step
- Line 624: Added
TARGET_DIR="$TARGET_DIR"when calling dashboard generator
- Line 304-308: Added
log_suppressed()call for Checkov suppressions
- Lines 832, 937: Fixed Checkov JSON parsing (
.[]?instead of[.[] | select()]) - Lines 1738-1744: Fixed HTML quoting in suppressed findings table
- Lines 1700-1704: Added debug output for troubleshooting
- Scanning external/third-party repositories
- Testing security of open-source dependencies
- One-off security assessments of any public repo
- Need to scan a repository you don't own
- Scanning your own repository (where workflow runs)
- Automated security checks on push/PR
- Scheduled security scans of your codebase
- CI/CD pipeline integration
The workflows appeared to behave differently because:
- Bug in scan-public-repo.yml: Environment variable overwrite caused TARGET_DIR to be lost
- Missing TARGET_DIR propagation: Dashboard couldn't find
.epyon-ignore.ymlin either workflow - Accidental success in private scans: Fallback path resolution might have worked by luck
All issues are now fixed in both workflows. They should behave identically in terms of suppression handling.