Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions bitbucket-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ pipelines:
caches:
- pip
script:
# 1. Configuration Variables (Mirroring GitHub Action Inputs)
# 1. Configuration Variables
- export GG_TOOL="galaxyscope"
- export GG_TARGET="."
- export GG_ARGS="--config .galaxyscope.yaml --max-risk-exposure 120 --fail-on-malware --sarif-only --output gitgalaxy-results_sarif.json"
- export GG_ARGS="--config .galaxyscope.yaml --max-risk-exposure 120 --fail-on-malware --sarif-only --output gitgalaxy-results.json"
- export GG_VERSION="latest"
- export GG_FULL_PRECISION="true"
- export GITGALAXY_LICENSE_KEY="COMMUNITY_FREE_TIER"
Expand Down Expand Up @@ -44,11 +44,20 @@ pipelines:

# 3. Dynamic Execution
- |
echo "Running: ${GG_TOOL} ${GG_TARGET} ${GG_ARGS}"
${GG_TOOL} ${GG_TARGET} ${GG_ARGS}
echo "Running: ${GG_TOOL} ${GG_TARGET}${GG_ARGS}"
${GG_TOOL} ${GG_TARGET}${GG_ARGS}

# 4. Bitbucket Code Insights Native Integration
# Automatically translates the SARIF output into inline Pull Request annotations
- |
if [ -f "templates/bitbucket/bitbucket_insights.py" ]; then
echo "Publishing Code Insights..."
python templates/bitbucket/bitbucket_insights.py gitgalaxy-results_sarif.json
else
echo "Code Insights utility not found at templates/bitbucket/bitbucket_insights.py. Skipping annotations."
fi

artifacts:
# 4. Publish SARIF telemetry for Bitbucket Code Insights
- gitgalaxy-results_sarif.json

custom:
Expand All @@ -60,7 +69,7 @@ pipelines:
script:
- export GG_TOOL="galaxyscope"
- export GG_TARGET="."
- export GG_ARGS="--config .galaxyscope.yaml --max-risk-exposure 120 --fail-on-malware --sarif-only --output gitgalaxy-results_sarif.json"
- export GG_ARGS="--config .galaxyscope.yaml --max-risk-exposure 120 --fail-on-malware --sarif-only --output gitgalaxy-results.json"
- export GG_VERSION="latest"
- export GG_FULL_PRECISION="true"
- export GITGALAXY_LICENSE_KEY="COMMUNITY_FREE_TIER"
Expand All @@ -78,7 +87,11 @@ pipelines:
pip install networkx tiktoken xgboost pandas numpy
fi
- |
echo "Running: ${GG_TOOL} ${GG_TARGET} ${GG_ARGS}"
${GG_TOOL} ${GG_TARGET} ${GG_ARGS}
echo "Running: ${GG_TOOL} ${GG_TARGET}${GG_ARGS}"
${GG_TOOL} ${GG_TARGET}${GG_ARGS}
- |
if [ -f "templates/bitbucket/bitbucket_insights.py" ]; then
python templates/bitbucket/bitbucket_insights.py gitgalaxy-results_sarif.json
fi
artifacts:
- gitgalaxy-results_sarif.json
- gitgalaxy-results_sarif.json
7 changes: 2 additions & 5 deletions templates/azure/azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ trigger:
variables:
GG_TOOL: 'galaxyscope'
GG_TARGET: '.'
GG_ARGS: '--max-systemic-threat 150.0 --fail-on-secrets --fail-on-malware --sarif-only --output $(Build.ArtifactStagingDirectory)/gitgalaxy-results.json'
GG_ARGS: '--config .galaxyscope.yaml --max-risk-exposure 120 --fail-on-malware --sarif-only --output $(Build.ArtifactStagingDirectory)/gitgalaxy-results.json'
PIP_CACHE_DIR: '$(Pipeline.Workspace)/.pip-cache'
GITGALAXY_LICENSE_KEY: 'COMMUNITY_FREE_TIER'

Expand All @@ -37,26 +37,23 @@ stages:
cacheHitVar: PIP_CACHE_RESTORED
displayName: 'Configure Dependency Caching'

# 1. Install the tool globally FIRST (Immune to Poisoned PRs)
- script: |
python -m pip install --upgrade pip
pip install gitgalaxy networkx tiktoken xgboost pandas numpy
displayName: 'Install GitGalaxy & Heavy Physics Engines'

# 2. NOW pull the potentially untrusted code onto the runner
- checkout: self
fetchDepth: 0
displayName: 'Checkout Repository (Full Depth)'

# 3. Execute the scan (No package managers allowed down here)
- script: |
echo "Running: $(GG_TOOL) $(GG_TARGET)$(GG_ARGS)"
$(GG_TOOL) $(GG_TARGET)$(GG_ARGS)
displayName: 'Execute Zero-Trust Spectral Audit'

- task: PublishPipelineArtifact@1
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/gitgalaxy-results.json'
targetPath: '$(Build.ArtifactStagingDirectory)/gitgalaxy-results_sarif.json'
artifact: 'GitGalaxySecurityLogs'
publishLocation: 'pipeline'
displayName: 'Upload SARIF Security Metrics'
Expand Down
44 changes: 4 additions & 40 deletions templates/bitbucket/bitbucket-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,13 @@ pipelines:
caches:
- pip
script:
# 1. Configuration Variables (Mirroring GitHub Action Inputs)
- export GG_TOOL="galaxyscope"
- export GG_TARGET="."
- export GG_ARGS="--config .galaxyscope.yaml --max-risk-exposure 120 --fail-on-malware --sarif-only --output gitgalaxy-results_sarif.json"
- export GG_ARGS="--config .galaxyscope.yaml --max-risk-exposure 120 --fail-on-malware --sarif-only --output gitgalaxy-results.json"
- export GG_VERSION="latest"
- export GG_FULL_PRECISION="true"
- export GITGALAXY_LICENSE_KEY="COMMUNITY_FREE_TIER"

# 2. Dynamic Installation Logic
- pip install --upgrade pip
- |
if [ "$GG_VERSION" = "local" ]; then
Expand All @@ -42,43 +40,9 @@ pipelines:
echo "Running in ultra-fast Zero-Dependency Mode."
fi

# 3. Dynamic Execution
- |
echo "Running: ${GG_TOOL} ${GG_TARGET} ${GG_ARGS}"
${GG_TOOL} ${GG_TARGET} ${GG_ARGS}
echo "Running: ${GG_TOOL} ${GG_TARGET}${GG_ARGS}"
${GG_TOOL} ${GG_TARGET}${GG_ARGS}

artifacts:
# 4. Publish SARIF telemetry for Bitbucket Code Insights
- gitgalaxy-results_sarif.json

custom:
manual-security-scan:
- step:
name: 'GitGalaxy Zero-Trust Spectral Audit (Manual)'
caches:
- pip
script:
- export GG_TOOL="galaxyscope"
- export GG_TARGET="."
- export GG_ARGS="--config .galaxyscope.yaml --max-risk-exposure 120 --fail-on-malware --sarif-only --output gitgalaxy-results_sarif.json"
- export GG_VERSION="latest"
- export GG_FULL_PRECISION="true"
- export GITGALAXY_LICENSE_KEY="COMMUNITY_FREE_TIER"
- pip install --upgrade pip
- |
if [ "$GG_VERSION" = "local" ]; then
pip install -e .
elif [ "$GG_VERSION" = "latest" ]; then
pip install gitgalaxy
else
pip install "gitgalaxy==${GG_VERSION}"
fi
- |
if [ "$GG_FULL_PRECISION" = "true" ]; then
pip install networkx tiktoken xgboost pandas numpy
fi
- |
echo "Running: ${GG_TOOL} ${GG_TARGET} ${GG_ARGS}"
${GG_TOOL} ${GG_TARGET} ${GG_ARGS}
artifacts:
- gitgalaxy-results_sarif.json
- gitgalaxy-results_sarif.json
129 changes: 129 additions & 0 deletions templates/bitbucket/bitbucket_insights.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/usr/bin/env python3
"""
GitGalaxy Utility: Bitbucket Code Insights Publisher
Parses GitGalaxy SARIF telemetry and publishes native PR annotations via the Bitbucket REST API.
"""

import os
import sys
import json
import urllib.request
import urllib.error

def publish_insights(sarif_path: str):
# 1. Extract Bitbucket Pipeline Environment Variables
workspace = os.environ.get("BITBUCKET_WORKSPACE")
repo = os.environ.get("BITBUCKET_REPO_SLUG")
commit = os.environ.get("BITBUCKET_COMMIT")

if not all([workspace, repo, commit]):
print("⚠️ Skipping Code Insights: Not running inside a Bitbucket Pipeline environment.")
sys.exit(0)

if not os.path.exists(sarif_path):
print(f"❌ Failed to locate SARIF payload at: {sarif_path}")
sys.exit(1)

# 2. Parse the SARIF File
try:
with open(sarif_path, 'r', encoding='utf-8') as f:
sarif_data = json.load(f)
except Exception as e:
print(f"❌ Failed to parse SARIF: {e}")
sys.exit(1)

findings = []
runs = sarif_data.get("runs", [])
if runs:
findings = runs[0].get("results", [])

if not findings:
print("✅ No security threats found. Skipping annotation publish.")
sys.exit(0)

# 3. Translate SARIF to Bitbucket Annotations
bitbucket_annotations = []
for idx, finding in enumerate(findings):
rule_id = finding.get("ruleId", "GG-UNKNOWN")
message = finding.get("message", {}).get("text", "Unknown structural threat detected.")
level = finding.get("level", "warning")

locations = finding.get("locations", [])
if not locations:
continue

region = locations[0].get("physicalLocation", {})
path = region.get("artifactLocation", {}).get("uri", "")
line = region.get("region", {}).get("startLine", 1)

# Map severities and types
bb_severity = "HIGH" if level == "error" else "MEDIUM"
bb_type = "VULNERABILITY" if "SAST" in rule_id or "ML" in rule_id else "CODE_SMELL"

bitbucket_annotations.append({
"external_id": f"{rule_id}-{idx}",
"title": "GitGalaxy Zero-Trust Audit",
"annotation_type": bb_type,
"summary": message,
"severity": bb_severity,
"path": path,
"line": line
})

report_id = "gitgalaxy-audit-report"
base_url = f"http://localhost:29418/2.0/repositories/{workspace}/{repo}/commit/{commit}/reports/{report_id}"

# 4. Create the Parent Report (PUT)
report_payload = json.dumps({
"title": "GitGalaxy Spectral Audit",
"details": f"GitGalaxy mapped {len(bitbucket_annotations)} architectural threats.",
"report_type": "SECURITY",
"result": "FAILED" if any(a["severity"] == "HIGH" for a in bitbucket_annotations) else "PASSED"
}).encode("utf-8")

# Enforce loopback destination boundaries to neutralize remote protocol exploits
if not base_url.startswith("http://localhost:29418/"):
print("❌ Security Violation: Invalid localized API destination.")
sys.exit(1)

req = urllib.request.Request(base_url, data=report_payload, method="PUT")
req.add_header("Content-Type", "application/json")

try:
# nosemgrep: python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected
urllib.request.urlopen(req)
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
print(f"✅ Created Code Insights Report: {report_id}")
except urllib.error.URLError as e:
print(f"❌ Failed to create Bitbucket report: {e}")
sys.exit(1)

# 5. Bulk Upload Annotations in Chunks of 100 (POST)
annotations_url = f"{base_url}/annotations"

chunk_size = 100
for i in range(0, len(bitbucket_annotations), chunk_size):
chunk = bitbucket_annotations[i:i + chunk_size]
chunk_payload = json.dumps(chunk).encode("utf-8")

if not annotations_url.startswith("http://localhost:29418/"):
print("❌ Security Violation: Invalid localized API destination.")
sys.exit(1)

req = urllib.request.Request(annotations_url, data=chunk_payload, method="POST")
req.add_header("Content-Type", "application/json")

try:
# nosemgrep: python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected
urllib.request.urlopen(req)
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
print(f"✅ Published chunk of {len(chunk)} inline annotations.")
except urllib.error.URLError as e:
print(f"❌ Failed to publish annotations chunk: {e}")

print("🚀 Code Insights sync complete.")

if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python bitbucket_insights.py <path_to_sarif.json>")
sys.exit(1)

publish_insights(sys.argv[1])
9 changes: 4 additions & 5 deletions templates/gitlab/scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ gitgalaxy_scan:
image: python:3.12-slim
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
GIT_DEPTH: "0" # Forces full chronological fetch to prevent Temporal Collapse
GIT_DEPTH: "0"
cache:
paths:
- .cache/pip
Expand Down Expand Up @@ -59,11 +59,10 @@ gitgalaxy_scan:
echo "Running in ultra-fast Zero-Dependency Mode."
fi

- 'echo "Running: ${GG_TOOL} ${GG_TARGET} ${GG_ARGS}"'
- ${GG_TOOL} ${GG_TARGET} ${GG_ARGS}
- 'echo "Running: ${GG_TOOL} ${GG_TARGET}${GG_ARGS}"'
- ${GG_TOOL} ${GG_TARGET}${GG_ARGS}
artifacts:
when: always
reports:
# Automatically maps the SARIF output directly into GitLab's native Security Dashboard
sast: gitgalaxy-results_sarif.json
allow_failure: false
allow_failure: false
Loading