diff --git a/fogwall-dashboard/frontend/src/pages/PushDetail.tsx b/fogwall-dashboard/frontend/src/pages/PushDetail.tsx index 71e73a69..816e4798 100644 --- a/fogwall-dashboard/frontend/src/pages/PushDetail.tsx +++ b/fogwall-dashboard/frontend/src/pages/PushDetail.tsx @@ -5,7 +5,14 @@ import { ColorSchemeType } from 'diff2html/lib/types' import 'diff2html/bundles/css/diff2html.min.css' import { approvePush, cancelPush, fetchDiff, fetchProviders, fetchPush, rejectPush } from '../api' import { StatusBadge } from '../components/StatusBadge' -import type { AttestationQuestion, CurrentUser, Provider, PushRecord, Step } from '../types' +import type { + AttestationLink, + AttestationQuestion, + CurrentUser, + Provider, + PushRecord, + Step, +} from '../types' // Steps that are infrastructure/pre-processing, not user-visible validation checks const NON_VALIDATION_STEPS = new Set([ @@ -271,18 +278,37 @@ function AttestationQuestionField({ ) + const linksEl = question.links && question.links.length > 0 && ( +
+ {question.links.map((link: AttestationLink) => ( + + {link.text} ↗ + + ))} +
+ ) + if (question.type === 'checkbox') { return ( - +
+ + {linksEl} +
) } @@ -303,6 +329,7 @@ function AttestationQuestionField({ ))} + {linksEl} ) } @@ -315,9 +342,10 @@ function AttestationQuestionField({ value={value} disabled={disabled} onChange={(e) => onChange(e.target.value)} - placeholder={question.tooltip ?? ''} + placeholder="" className="border border-gray-300 rounded px-2 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-300 disabled:bg-gray-50 disabled:text-gray-400 dark:bg-slate-700 dark:border-slate-600 dark:text-gray-200 dark:disabled:bg-gray-800 dark:disabled:text-gray-500" /> + {linksEl} ) } diff --git a/fogwall-dashboard/frontend/src/types.ts b/fogwall-dashboard/frontend/src/types.ts index 137570b3..2fcc8f99 100644 --- a/fogwall-dashboard/frontend/src/types.ts +++ b/fogwall-dashboard/frontend/src/types.ts @@ -22,13 +22,18 @@ export interface Commit { signedOffBy?: string[] } +export interface AttestationLink { + text: string + url: string +} + export interface AttestationQuestion { id: string type: 'checkbox' | 'text' | 'dropdown' label: string required: boolean options?: string[] - tooltip?: string + links?: AttestationLink[] } export interface Attestation { diff --git a/fogwall-server/src/main/java/com/rbc/fogwall/config/AttestationQuestion.java b/fogwall-server/src/main/java/com/rbc/fogwall/config/AttestationQuestion.java index 6c8a165a..47748c9b 100644 --- a/fogwall-server/src/main/java/com/rbc/fogwall/config/AttestationQuestion.java +++ b/fogwall-server/src/main/java/com/rbc/fogwall/config/AttestationQuestion.java @@ -23,6 +23,13 @@ public class AttestationQuestion { /** Options for {@code dropdown} type questions. Ignored for other types. */ private List options = new ArrayList<>(); - /** Optional tooltip / help text shown alongside the question. */ - private String tooltip; + /** Optional policy or reference links rendered below the question label. */ + private List links = new ArrayList<>(); + + /** A labelled URL rendered as an anchor below an attestation question. */ + @Data + public static class AttestationLink { + private String text; + private String url; + } } diff --git a/test/gitea-push-fail.sh b/test/gitea-push-fail.sh new file mode 100755 index 00000000..0dc3dab1 --- /dev/null +++ b/test/gitea-push-fail.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Failure-path store-and-forward push to local Gitea via fogwall. +# Verifies secret scanning correctly rejects a commit containing an AWS key. +# Requires: docker compose stack up + docker/gitea-setup.sh already run. +set -euo pipefail + +source "$(dirname "${BASH_SOURCE[0]}")/env.sh" +source "$(dirname "${BASH_SOURCE[0]}")/gitea/tokens.env" + +GITEA_HOST="${GITEA_HOST:-localhost:3000}" +GIT_USERNAME="${GIT_USERNAME:-me}" +GIT_PASSWORD="${GITEA_TESTUSER_TOKEN}" +PUSH_URL="http://${GIT_USERNAME}:${GIT_PASSWORD}@localhost:8080/push/${GITEA_HOST}/test-owner/test-repo.git" +TEST_BRANCH="test/gitea-fail-$(date +%s)" +REPO_DIR=$(mktemp -d "${_SYS_TMPDIR}/gitea-push-fail-XXXX") + +cleanup() { + git -C "${REPO_DIR}" remote set-url origin "http://${GIT_USERNAME}:${GIT_PASSWORD}@${GITEA_HOST}/test-owner/test-repo.git" 2>/dev/null || true + git -C "${REPO_DIR}" push origin --delete "${TEST_BRANCH}" 2>/dev/null || true + safe_rm_rf "${REPO_DIR}" +} +trap cleanup EXIT + +git clone "${PUSH_URL}" "${REPO_DIR}" +cd "${REPO_DIR}" +git checkout -b "${TEST_BRANCH}" +git config user.name "test-user" +git config user.email "testuser@example.com" + +cat > aws-credentials << 'EOF' +[default] +aws_access_key_id = AKIAYVP4CIPPH3TESTKEY +aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY +EOF +git add aws-credentials +git commit -m "chore: add deployment credentials" + +push_exit=0 +git push origin "${TEST_BRANCH}" 2>&1 || push_exit=$? + +if [[ ${push_exit} -ne 0 ]]; then + echo "PASSED (push correctly rejected by secret scanning)" +else + echo "FAILED (push should have been rejected)" + exit 1 +fi diff --git a/test/gitea-push-pass.sh b/test/gitea-push-pass.sh new file mode 100755 index 00000000..65f2fc72 --- /dev/null +++ b/test/gitea-push-pass.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Golden-path store-and-forward push to local Gitea via fogwall. +# Requires: docker compose stack up + docker/gitea-setup.sh already run. +set -euo pipefail + +source "$(dirname "${BASH_SOURCE[0]}")/env.sh" +source "$(dirname "${BASH_SOURCE[0]}")/gitea/tokens.env" + +GITEA_HOST="${GITEA_HOST:-localhost:3000}" +GIT_USERNAME="${GIT_USERNAME:-me}" +GIT_PASSWORD="${GITEA_TESTUSER_TOKEN}" +PUSH_URL="http://${GIT_USERNAME}:${GIT_PASSWORD}@localhost:8080/push/${GITEA_HOST}/test-owner/test-repo.git" +TEST_BRANCH="test/gitea-pass-$(date +%s)" +REPO_DIR=$(mktemp -d "${_SYS_TMPDIR}/gitea-push-pass-XXXX") + +cleanup() { + git -C "${REPO_DIR}" remote set-url origin "http://${GIT_USERNAME}:${GIT_PASSWORD}@${GITEA_HOST}/test-owner/test-repo.git" 2>/dev/null || true + git -C "${REPO_DIR}" push origin --delete "${TEST_BRANCH}" 2>/dev/null || true + safe_rm_rf "${REPO_DIR}" +} +trap cleanup EXIT + +git clone "${PUSH_URL}" "${REPO_DIR}" +cd "${REPO_DIR}" +git checkout -b "${TEST_BRANCH}" +git config user.name "test-user" +git config user.email "testuser@example.com" + +echo "pass - $(date)" >> test-file.txt +git add test-file.txt +git commit -m "feat: golden-path gitea store-and-forward test" +git push origin "${TEST_BRANCH}" + +echo "PASSED"