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
38 changes: 3 additions & 35 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -1,39 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail

# Prevent committing unencrypted secrets in deploy/secrets.
changed_files=$(git diff --cached --name-only --diff-filter=ACMR | rg '^deploy/secrets/.*\.ya?ml$' || true)
ROOT_DIR=$(git rev-parse --show-toplevel)

if [[ -z "${changed_files}" ]]; then
exit 0
fi

fail=0
for file in ${changed_files}; do
if ! rg -q '^sops:' "${file}"; then
echo "ERROR: ${file} is missing sops metadata." >&2
fail=1
continue
fi

# Require encrypted values for secret-like keys.
while IFS= read -r line; do
# Skip comments and empty lines.
[[ "${line}" =~ ^[[:space:]]*# ]] && continue
[[ "${line}" =~ ^[[:space:]]*$ ]] && continue

# Match KEY: value for secret-like keys.
if [[ "${line}" =~ ^[[:space:]]*([A-Za-z0-9_]*(TOKEN|SECRET|PASSWORD|KEY)[A-Za-z0-9_]*)[[:space:]]*:[[:space:]]*(.+)$ ]]; then
value="${BASH_REMATCH[3]}"
if [[ "${value}" != ENC[* ]]; then
echo "ERROR: ${file} contains unencrypted value for ${BASH_REMATCH[1]}." >&2
fail=1
fi
fi
done < "${file}"
done

if [[ "${fail}" -ne 0 ]]; then
echo "Commit blocked. Encrypt secrets with SOPS before committing." >&2
exit 1
fi
"${ROOT_DIR}/scripts/check-sops-secrets.sh" --staged
"${ROOT_DIR}/scripts/check-pii.sh" --staged
42 changes: 42 additions & 0 deletions .github/workflows/pii-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: PII Check

on:
push:
branches:
- "**"
pull_request:
branches:
- main
workflow_dispatch:

permissions:
contents: read

jobs:
pii-check:
name: Changed files PII check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Scan changed files for PII and obvious tokens
shell: bash
run: |
set -euo pipefail
chmod +x scripts/check-pii.sh

if [[ "${{ github.event_name }}" == "pull_request" ]]; then
from_ref="${{ github.event.pull_request.base.sha }}"
to_ref="${{ github.event.pull_request.head.sha }}"
elif [[ -n "${{ github.event.before }}" && "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]]; then
from_ref="${{ github.event.before }}"
to_ref="${{ github.sha }}"
else
from_ref="$(git rev-list --max-parents=0 HEAD)"
to_ref="${{ github.sha }}"
fi

scripts/check-pii.sh --ref-range "${from_ref}" "${to_ref}"
3 changes: 3 additions & 0 deletions .github/workflows/web-bdd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,11 @@ jobs:

- name: Run web BDD tests
env:
CONTAINER_TOOL: docker
FOSSA_API_TOKEN: dummy-token
MD_DB_DRIVER: postgres
MD_DB_DSN: host=localhost port=5432 user=maintainerd password=maintainerd dbname=maintainerd_test sslmode=disable
WEB_BDD_USE_MICROCKS: "true"
run: make web-bdd

- name: Dump web-bdd logs
Expand Down
40 changes: 34 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -503,12 +503,15 @@ test-web:
TESTDATA_DIR="$${TESTDATA_DIR:-$$(pwd)/testdata}"; \
HOST_LOG_DIR="$${HOST_LOG_DIR:-$$(pwd)/testdata}"; \
mkdir -p "$$TESTDATA_DIR"; \
bff_pid=""; web_pid=""; \
bff_pid=""; web_pid=""; microcks_started=""; \
cleanup() { \
status=$$?; \
if [ -n "$$web_pid" ] || [ -n "$$bff_pid" ]; then \
kill $$web_pid $$bff_pid >/dev/null 2>&1 || true; \
fi; \
if [ -n "$$microcks_started" ]; then \
CONTAINER_TOOL="$${CONTAINER_TOOL:-podman}" scripts/microcks-down.sh >/dev/null 2>&1 || true; \
fi; \
if command -v lsof >/dev/null 2>&1; then \
lsof -ti TCP:9001 -ti TCP:4001 2>/dev/null | xargs -r kill >/dev/null 2>&1 || true; \
fi; \
Expand Down Expand Up @@ -564,15 +567,27 @@ test-web:
echo "MD_DB_DSN is required for test-web."; \
exit 1; \
fi; \
USE_MICROCKS="$${WEB_BDD_USE_MICROCKS:-false}"; \
if [ "$$USE_MICROCKS" = "true" ]; then \
if ! command -v "$${CONTAINER_TOOL:-podman}" >/dev/null 2>&1; then \
echo "WEB_BDD_USE_MICROCKS=true but container tool $${CONTAINER_TOOL:-podman} is not available."; \
exit 1; \
fi; \
microcks_env="$$(CONTAINER_TOOL="$${CONTAINER_TOOL:-podman}" scripts/microcks-up.sh)"; \
eval "$$microcks_env"; \
microcks_started=1; \
export FOSSA_API_BASE; \
export FOSSA_API_TOKEN="$${FOSSA_API_TOKEN:-dummy-token}"; \
fi; \
MD_DB_DRIVER=postgres MD_DB_DSN="$$DB_DSN" go run ./cmd/testdb-reset; \
MD_DB_DRIVER=postgres MD_DB_DSN="$$DB_DSN" go run ./cmd/migrate; \
go run ./cmd/web-bff-seed -driver postgres -dsn "$$DB_DSN"; \
export MD_DB_DRIVER=postgres; \
export MD_DB_DSN="$$DB_DSN"; \
unset MD_DB_PATH; \
BFF_ADDR=:9001 BFF_TEST_MODE=true BFF_TEST_FOSSA_TEAM_ID=999001 SESSION_COOKIE_SECURE=false SESSION_COOKIE_DOMAIN= \
BFF_ADDR=:9001 BFF_TEST_MODE=true BFF_ALLOW_LIVE_FOSSA="$$USE_MICROCKS" BFF_TEST_FOSSA_TEAM_ID=999001 SESSION_COOKIE_SECURE=false SESSION_COOKIE_DOMAIN= \
WEB_APP_BASE_URL=http://localhost:4001 GITHUB_OAUTH_REDIRECT_URL=http://localhost:9001/auth/callback \
GITHUB_OAUTH_CLIENT_ID=test GITHUB_OAUTH_CLIENT_SECRET=test \
GITHUB_OAUTH_CLIENT_ID=test GITHUB_OAUTH_CLIENT_SECRET=test FOSSA_API_BASE="$${FOSSA_API_BASE:-}" FOSSA_API_TOKEN="$${FOSSA_API_TOKEN:-}" \
go run ./cmd/web-bff > >(tee "$$TESTDATA_DIR/web-bff-test.log") 2>&1 & \
bff_pid=$$!; \
mkdir -p "$$TESTDATA_DIR/tmp" web/tmp || true; \
Expand All @@ -584,9 +599,12 @@ test-web:
npm --prefix web run start > "$$TESTDATA_DIR/web-app-test.log" 2>&1 & \
web_pid=$$!; \
npx --prefix web wait-on http://localhost:9001/healthz http://localhost:4001 > /dev/null 2>&1; \
WEB_BASE_URL=http://localhost:4001 BFF_BASE_URL=http://localhost:9001 TEST_STAFF_LOGIN=staff-tester \
TEST_MAINTAINER_LOGIN=antonio-example TEST_OTHER_MAINTAINER_LOGIN=renee-sample \
NEXT_TELEMETRY_DISABLED=1 NPM_CONFIG_UPDATE_NOTIFIER=false WEB_TEST_ARTIFACTS_DIR="$$TESTDATA_DIR/web-artifacts" npm --prefix web run test:bdd; \
BDD_FEATURE_PATH="$${BDD_FEATURE:-}"; \
if [ -n "$$BDD_FEATURE_PATH" ]; then \
cd web && WEB_BDD_FEATURE="$$BDD_FEATURE_PATH" WEB_BASE_URL=http://localhost:4001 BFF_BASE_URL=http://localhost:9001 TEST_STAFF_LOGIN=staff-tester TEST_MAINTAINER_LOGIN=antonio-example TEST_OTHER_MAINTAINER_LOGIN=renee-sample NEXT_TELEMETRY_DISABLED=1 NPM_CONFIG_UPDATE_NOTIFIER=false WEB_TEST_ARTIFACTS_DIR="$$TESTDATA_DIR/web-artifacts" npx cucumber-js --config ./cucumber.js; \
else \
WEB_BASE_URL=http://localhost:4001 BFF_BASE_URL=http://localhost:9001 TEST_STAFF_LOGIN=staff-tester TEST_MAINTAINER_LOGIN=antonio-example TEST_OTHER_MAINTAINER_LOGIN=renee-sample NEXT_TELEMETRY_DISABLED=1 NPM_CONFIG_UPDATE_NOTIFIER=false WEB_TEST_ARTIFACTS_DIR="$$TESTDATA_DIR/web-artifacts" npm --prefix web run test:bdd; \
fi; \
'

.PHONY: test-web-license-checker
Expand Down Expand Up @@ -691,6 +709,15 @@ web-bdd:
exit $$status; \
'

.PHONY: web-bdd-maintainer-profile
web-bdd-maintainer-profile:
@bash -c 'set -euo pipefail; \
status=0; \
WEB_BDD_USE_MICROCKS=true BDD_FEATURE=../features/web/maintainer_profile.feature $(MAKE) test-web || status=$$?; \
$(MAKE) web-bdd-report || true; \
exit $$status; \
'

.PHONY: web-bdd-report
web-bdd-report:
@bash -c 'set -euo pipefail; \
Expand Down Expand Up @@ -1089,6 +1116,7 @@ images-show:
@echo " maintainerd-sync $(SYNC_IMAGE)"
@echo " maintainerd-sanitize $(SANITIZE_IMAGE)"
@echo " maintainerd-migrate $(MIGRATE_IMAGE)"
@echo " maintainerd-fossa-poller $(FOSSA_POLLER_IMAGE)"
@echo " maintainerd-web $(WEB_IMAGE)"
@echo " maintainerd-web-bff $(WEB_BFF_IMAGE)"

Expand Down
5 changes: 5 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,10 @@ Notes:
- `BFF_TEST_MODE=true` enables `/auth/test-login?login=<github>` for local sign-in.
- `NEXT_PUBLIC_BFF_BASE_URL` should match the web-bff address.

## Web BDD With Microcks

For the web BDD test setup that mocks the FOSSA API with Microcks, see
[web/tests/MICROCKS.MD](/home/rk/cncf/gh/maintainer-d/web/tests/MICROCKS.MD).

## License
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FRobertKielty%2Fmaintainerd.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FRobertKielty%2Fmaintainerd?ref=badge_large)
15 changes: 13 additions & 2 deletions cmd/fossa-poller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ type fossaAPI interface {
FetchTeamUserEmails(teamID uint) ([]string, error)
FetchTeamMembersRaw(teamID uint) (fossa.TeamMembers, []byte, error)
FindUserIDByEmail(email string) (uint, error)
ResolveTeamAdminRoleID() (int, error)
}

func pollFossaInvites(ctx context.Context, logger *log.Logger, store *db.SQLStore, client fossaAPI) error {
Expand Down Expand Up @@ -203,8 +204,18 @@ func pollFossaInvites(ctx context.Context, logger *log.Logger, store *db.SQLStor
continue
}

const fossaTeamAdminRoleID = 3
addResp, err := client.AddUserToTeamByEmailWithResponse(invite.RemoteTeamID, email, fossaTeamAdminRoleID)
teamAdminRoleID, err := client.ResolveTeamAdminRoleID()
if err != nil {
msg := err.Error()
invite.Status = "error"
invite.LastError = &msg
invite.LastCheckedAt = &now
if _, upsertErr := store.UpsertServiceInvitation(&invite); upsertErr != nil {
logger.Printf("upsert invite failed %s err=%v", formatInviteSummary(store, invite), upsertErr)
}
continue
}
addResp, err := client.AddUserToTeamByEmailWithResponse(invite.RemoteTeamID, email, teamAdminRoleID)
if err != nil {
if errors.Is(err, fossa.ErrUserAlreadyMember) {
invite.Status = "accepted"
Expand Down
9 changes: 8 additions & 1 deletion cmd/fossa-poller/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
type fakeFossaClient struct {
hasPending bool
userID uint
roleID int
teamEmails []string
addResp *fossa.TeamAddResponse
addErr error
Expand All @@ -37,7 +38,7 @@ func (f *fakeFossaClient) AddUserToTeamByEmailWithResponse(uint, string, int) (*
if f.addResp != nil || f.addErr != nil {
return f.addResp, f.addErr
}
return &fossa.TeamAddResponse{Status: "200 OK", Body: []byte(`{"action":"add"}`), UserID: f.userID, RoleID: 3}, nil
return &fossa.TeamAddResponse{Status: "200 OK", Body: []byte(`{"action":"add"}`), UserID: f.userID, RoleID: f.roleID}, nil
}
func (f *fakeFossaClient) FetchTeamUserEmails(uint) ([]string, error) { return f.teamEmails, nil }
func (f *fakeFossaClient) FetchTeamMembersRaw(uint) (fossa.TeamMembers, []byte, error) {
Expand All @@ -48,6 +49,12 @@ func (f *fakeFossaClient) FetchTeamMembersRaw(uint) (fossa.TeamMembers, []byte,
return fossa.TeamMembers{Results: []fossa.TeamMember{{Email: "dana@example.com"}}, TotalCount: 1}, body, nil
}
func (f *fakeFossaClient) FindUserIDByEmail(string) (uint, error) { return f.userID, nil }
func (f *fakeFossaClient) ResolveTeamAdminRoleID() (int, error) {
if f.roleID == 0 {
return 4, nil
}
return f.roleID, nil
}

func setupPollerTestDB(t *testing.T) *gorm.DB {
dbConn, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
Expand Down
5 changes: 3 additions & 2 deletions cmd/web-bff-seed/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ func main() {
log.Fatalf("seed: association failed: %v", err)
}
if err := dbConn.Model(projectMap["Project Fossa Partial"]).Association("Maintainers").Replace(
&maintainers[0],
&maintainers[1],
&maintainers[2],
&maintainers[3],
); err != nil {
Expand Down Expand Up @@ -273,7 +275,7 @@ func main() {

remoteUsers := []model.RemoteUser{
{ServiceID: fossa.ID, RemoteUserID: 1, ServiceEmail: maintainers[0].Email},
{ServiceID: fossa.ID, RemoteUserID: 2, ServiceEmail: maintainers[1].Email},
{ServiceID: fossa.ID, RemoteUserID: 2, ServiceEmail: maintainers[1].GitHubEmail},
{ServiceID: fossa.ID, RemoteUserID: 3, ServiceEmail: maintainers[2].Email},
{ServiceID: fossa.ID, RemoteUserID: 4, ServiceEmail: maintainers[5].Email},
}
Expand All @@ -286,7 +288,6 @@ func main() {
serviceUserTeams := []model.RemoteTeamUser{
{ServiceID: fossa.ID, TeamID: teams[0].ID, UserID: remoteUsers[0].ID, MaintainerID: &maintainers[0].ID},
{ServiceID: fossa.ID, TeamID: teams[0].ID, UserID: remoteUsers[1].ID, MaintainerID: &maintainers[1].ID},
{ServiceID: fossa.ID, TeamID: teams[1].ID, UserID: remoteUsers[2].ID, MaintainerID: &maintainers[2].ID},
{ServiceID: fossa.ID, TeamID: teams[2].ID, UserID: remoteUsers[3].ID, MaintainerID: &maintainers[5].ID},
}
for i := range serviceUserTeams {
Expand Down
Loading
Loading