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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:

- name: Upload coverage reports
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: rust-coverage-${{ github.run_id }}
path: coverage/*.json
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deterministic-dashboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:

- name: Upload dashboard and verification evidence
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: shadow-dashboard-${{ github.run_id }}
path: |
Expand Down
39 changes: 31 additions & 8 deletions actions/shadow-ci/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,17 +205,18 @@ fn first_existing(root: &Path, paths: &[&str]) -> Option<PathBuf> {
}

fn complete_artifact_under(root: &Path, relative: &str, needle: &str) -> Option<PathBuf> {
fn walk(dir: &Path, needle: &str) -> Option<PathBuf> {
fn walk(dir: &Path, scan_root: &Path, needle: &str) -> Option<PathBuf> {
let Ok(entries) = std::fs::read_dir(dir) else {
return None;
};
for entry in entries.flatten() {
let path = entry.path();
let name_matches = path
.file_name()
.and_then(|n| n.to_str())
.is_some_and(|n| n.to_lowercase().contains(needle));
if path.is_file() && name_matches {
let relative_path = path.strip_prefix(scan_root).unwrap_or(&path);
let path_matches = relative_path
.to_string_lossy()
.to_lowercase()
.contains(needle);
if path.is_file() && path_matches {
let body = std::fs::read_to_string(&path).unwrap_or_default();
let lower = body.to_lowercase();
if body.trim().len() >= 80
Expand All @@ -226,14 +227,15 @@ fn complete_artifact_under(root: &Path, relative: &str, needle: &str) -> Option<
}
}
if path.is_dir() {
if let Some(found) = walk(&path, needle) {
if let Some(found) = walk(&path, scan_root, needle) {
return Some(found);
}
}
}
None
}
walk(&root.join(relative), &needle.to_lowercase())
let scan_root = root.join(relative);
walk(&scan_root, &scan_root, &needle.to_lowercase())
}

fn attestation_observations(root: &Path, observed_at: &str) -> Vec<Value> {
Expand Down Expand Up @@ -1355,4 +1357,25 @@ mod tests {
assert!(DEPENDENCY_LOCK_PATHS.contains(&"app/package-lock.json"));
assert!(DEPENDENCY_LOCK_PATHS.contains(&"functions/package-lock.json"));
}

#[test]
fn evidence_directory_name_can_identify_a_complete_artifact() {
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("target/evidence-path-test");
if root.exists() {
std::fs::remove_dir_all(&root).unwrap();
}
let artifact = root.join("evidence/restore-tests/2026-Q3.md");
std::fs::create_dir_all(artifact.parent().unwrap()).unwrap();
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
std::fs::write(
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
&artifact,
"# Restore test\n\nPerformed: 2026-08-05\nResult: PASS\nA real isolated restore completed successfully and the temporary database was removed.",
)
.unwrap();

assert_eq!(
complete_artifact_under(&root, "evidence", "restore"),
Some(artifact)
);
std::fs::remove_dir_all(root).unwrap();
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
}
}
2 changes: 1 addition & 1 deletion actions/workflows/compliance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:

- name: Upload report
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: compliance-report-${{ matrix.key }}-${{ github.event.pull_request.number }}
path: compliance_report.json
Expand Down
4 changes: 2 additions & 2 deletions actions/workflows/daily-verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:

- name: Upload verify report
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: shadow-verify-${{ github.run_id }}
path: shadow/verify-*.md
Expand All @@ -75,7 +75,7 @@ jobs:
- name: Upload gauge site
if: always()
continue-on-error: true
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: shadow-gauge-${{ github.run_id }}
path: dist/
Expand Down
2 changes: 1 addition & 1 deletion actions/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5

- uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2
- uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3
with:
workload_identity_provider: ${{ vars.GCP_WIF_PROVIDER }}
service_account: ${{ vars.GCP_DEPLOY_SA }}
Expand Down
6 changes: 3 additions & 3 deletions actions/workflows/deterministic-verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:

- name: Authenticate the read-only verifier to Google Cloud
if: ${{ vars.GCP_VERIFY_SA != '' }}
uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3
with:
workload_identity_provider: ${{ vars.GCP_WIF_PROVIDER }}
service_account: ${{ vars.GCP_VERIFY_SA }}
Expand Down Expand Up @@ -65,7 +65,7 @@ jobs:

- name: Upload verification evidence
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: deterministic-shadow-verify-${{ github.run_id }}
path: |
Expand All @@ -77,7 +77,7 @@ jobs:

- name: Upload dashboard snapshot
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: shadow-dashboard-${{ github.run_id }}
path: dist/
Expand Down
2 changes: 1 addition & 1 deletion actions/workflows/quarterly-rituals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
run: cargo build --release --manifest-path .shadow/ci/Cargo.toml

# Optional: authenticate gcloud for the GCP IAM sections.
# - uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2
# - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3
# with:
# credentials_json: ${{ secrets.GCP_READONLY_SA_KEY }}

Expand Down
2 changes: 1 addition & 1 deletion actions/workflows/restore-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
with:
fetch-depth: 0

- uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2
- uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3
with:
# a scoped SA via WIF is ideal here too; a datastore.owner key-less
# federation identity needs: roles/datastore.owner on the project
Expand Down
2 changes: 1 addition & 1 deletion actions/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:

- name: Upload coverage reports
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: rust-coverage-${{ github.run_id }}
path: coverage/*.json
Expand Down
6 changes: 3 additions & 3 deletions provision/gcp-cloudsql/restore-test-cloudsql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,18 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
fetch-depth: 0

- uses: google-github-actions/auth@v2
- uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3
with:
# needs roles/cloudsql.admin on the project (a dedicated restore SA
# via the same WIF pool is the clean setup)
workload_identity_provider: ${{ vars.GCP_WIF_PROVIDER }}
service_account: ${{ vars.GCP_RESTORE_SA || vars.GCP_DEPLOY_SA }}

- uses: google-github-actions/setup-gcloud@v2
- uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3

- name: Restore latest backup into a throwaway clone
id: restore
Expand Down