Skip to content

Commit 155a9df

Browse files
authored
Merge pull request #3 from nvsecurity/NV-4418-gitlab-poc-unblockers
NV-4418 Fix and harden the GitLab and GitHub demo scripts
2 parents 80b8a50 + 7e7d040 commit 155a9df

6 files changed

Lines changed: 271 additions & 58 deletions

File tree

.github/dependabot.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "github-actions"
4+
directory: "/"
5+
schedule:
6+
interval: "weekly"
7+
# Wait 7 days after a release is published before opening an update PR,
8+
# so freshly published (potentially compromised) versions are not pinned
9+
# immediately. Keeps the SHA pins refreshed as reviewable PRs.
10+
cooldown:
11+
default-days: 7

.github/workflows/lint.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Lint
2+
3+
# Static checks for the repo's shell scripts. Runs the same tests/run.sh that
4+
# developers run locally, so local and CI can never check different things.
5+
on:
6+
push:
7+
branches: [main]
8+
pull_request:
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
shell-lint:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
19+
20+
- name: Ensure shellcheck is available
21+
run: |
22+
if ! command -v shellcheck >/dev/null 2>&1; then
23+
sudo apt-get update && sudo apt-get install -y shellcheck
24+
fi
25+
shellcheck --version
26+
27+
- name: Run lint entrypoint
28+
run: ./tests/run.sh

.gitignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Python bytecode / caches (sarif/ converters)
2+
__pycache__/
3+
*.py[cod]
4+
5+
# OS metadata
6+
.DS_Store
7+
Thumbs.db
8+
9+
# Editor / IDE
10+
.idea/
11+
.vscode/
12+
*.swp
13+
14+
# Local checkout the demo scripts clone into, plus any scan artifacts
15+
# (openapi-spec.yml, scan-results.txt, results.sarif) written inside it.
16+
/java-github-actions-demo/
17+
18+
# Agent sandbox state: local tooling, never part of the repo.
19+
.agent-sandbox-config/
20+
21+
# Local analysis / work products, kept out of version control by convention.
22+
analysis/
Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,88 @@
1-
#!/bin/bash
2-
set -e
1+
#!/usr/bin/env bash
2+
set -euo pipefail
33

4-
# Clone the repository locally from your terminal
5-
git clone https://github.com/$(git config user.name)/java-github-actions-demo.git
6-
cd java-github-actions-demo
7-
8-
# add the nightvision token as the github action secret NIGHTVISION_TOKEN
9-
nightvision login
10-
nightvision token create > token
4+
# ---------------------------------------------------------------------------------------------------------------------
5+
# NightVision GitHub Actions demo
6+
#
7+
# Clones your fork of java-github-actions-demo, wires the NIGHTVISION_TOKEN Actions secret,
8+
# creates a NightVision app + target, records auth, and pushes a commit to trigger the
9+
# workflow.
10+
#
11+
# Re-run behaviour: this script is intended to be safe to re-run. The create steps
12+
# (NightVision app/target) are guarded so a second run reuses existing resources with a
13+
# logged warning instead of aborting. The NightVision token is the exception: each run
14+
# creates a fresh one and overwrites the Actions secret (the value cannot be read back, so
15+
# it is rotated, not reused); older tokens stay in NightVision, so revoke them there if you
16+
# re-run often. The script does NOT delete anything; see the cleanup notes at the bottom.
17+
# ---------------------------------------------------------------------------------------------------------------------
1118

12-
# Check if GitHub CLI is authenticated
19+
# Authenticate to GitHub and NightVision first, so the steps below only run after both
20+
# logins are confirmed - an aborted login then leaves nothing half-created.
1321
if ! gh auth status 2>&1 | grep -q 'Logged in to github.com'; then
1422
echo "GitHub CLI not authenticated. Running 'gh auth login'..."
1523
gh auth login
1624
else
1725
echo "Logged in to github already..."
1826
fi
27+
nightvision login
28+
29+
# Clone your fork of the demo repository. The owner comes from the authenticated GitHub
30+
# login: 'git config user.name' is a display name, not a valid URL segment for most users.
31+
# Idempotent: reuse an existing checkout instead of failing on a second run, but only
32+
# after confirming it points at the expected fork - a stale checkout of another repo
33+
# would otherwise receive the push at the end of the script.
34+
OWNER="$(gh api user --jq .login)"
35+
if [ ! -d java-github-actions-demo ]; then
36+
git clone "https://github.com/$OWNER/java-github-actions-demo.git"
37+
elif ! git -C java-github-actions-demo remote get-url origin | grep -qiE "github\.com[:/]$OWNER/java-github-actions-demo(\.git)?$"; then
38+
echo "ERROR: existing ./java-github-actions-demo does not point at $OWNER/java-github-actions-demo; move it aside and re-run." >&2
39+
exit 1
40+
fi
41+
cd java-github-actions-demo
42+
43+
# Create a fresh NightVision token for the NIGHTVISION_TOKEN Actions secret, without
44+
# writing it to disk. tr strips any stray whitespace; the guard catches an empty result
45+
# (an exit-0 token create with no output) before it becomes an empty secret.
46+
TOKEN="$(nightvision token create | tr -d '[:space:]')"
47+
if [ -z "$TOKEN" ]; then
48+
echo "ERROR: 'nightvision token create' returned an empty token; aborting." >&2
49+
exit 1
50+
fi
1951

20-
# Use GitHub CLI to set NIGHTVISION_TOKEN
21-
gh secret set NIGHTVISION_TOKEN < token
22-
rm token
52+
# Set the Actions secret to the freshly created token (rotated every run, not reused,
53+
# because a token's value cannot be read back). 'gh secret set' upserts, so no
54+
# already-exists guard is needed; a real failure aborts the script (set -e). The explicit
55+
# --repo pins the secret to your fork rather than inferring it from the checkout's remote.
56+
gh secret set NIGHTVISION_TOKEN --body "$TOKEN" --repo "$OWNER/java-github-actions-demo"
2357

24-
# Create app and target
25-
# Username: user
26-
# Password: password
58+
# ---------------------------------------------------------------------------------------------------------------------
59+
# NightVision commands
60+
# ---------------------------------------------------------------------------------------------------------------------
61+
# Create app and target. Guarded so a re-run reuses the existing app/target.
2762
URL="https://localhost:9000"
2863
APP="javaspringvulny-api"
29-
nightvision app create $APP
30-
nightvision target create $APP $URL --type API
64+
nightvision app create "$APP" || echo "WARNING: 'nightvision app create $APP' failed (it may already exist); continuing."
65+
nightvision target create "$APP" "$URL" --type API || echo "WARNING: 'nightvision target create $APP' failed (it may already exist); continuing."
66+
3167
# Start the application
3268
docker compose up -d; sleep 10
33-
# Record authentication - click on Form Auth
34-
echo "Click on Form Auth and use these credentials: "
35-
echo "\tUsername: user"
36-
echo "\tPassword: password"
37-
nightvision auth playwright create $APP $URL
38-
39-
# Add a commit and trigger the CI/CD
40-
echo "foobar" >> README.md
41-
git commit -am 'trigger a github action'
69+
# Record authentication - click on Form Auth.
70+
# These are the demo application's default credentials (the javaspringvulny sample app),
71+
# not real secrets.
72+
echo "Click on Form Auth and use the javaspringvulny demo defaults:"
73+
echo " Username: user"
74+
echo " Password: password"
75+
nightvision auth playwright create "$APP" "$URL"
76+
77+
# ---------------------------------------------------------------------------------------------------------------------
78+
# Add an empty commit and push to trigger the GitHub Actions workflow. An empty
79+
# commit always provides a fresh commit to push (including on a re-run against an
80+
# existing checkout) without modifying tracked files.
81+
# ---------------------------------------------------------------------------------------------------------------------
82+
git commit --allow-empty -m "Trigger GitHub Actions workflow"
4283
git push
84+
85+
# Notes:
86+
# To clean up (substitute your GitHub login):
87+
# gh secret delete NIGHTVISION_TOKEN --repo <your-github-login>/java-github-actions-demo
88+
# rm -rf ./java-github-actions-demo

demo-scripts/gitlab-demo.sh

Lines changed: 85 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
#!/usr/bin/env bash
2-
set -e
2+
set -euo pipefail
3+
4+
# ---------------------------------------------------------------------------------------------------------------------
5+
# NightVision GitLab "Easy Mode" demo (NV-4418)
6+
#
7+
# Creates a GitLab project, wires the NIGHTVISION_TOKEN CI variable, creates a NightVision
8+
# app + target, records auth, and pushes the demo repo to trigger the CI/CD pipeline.
9+
#
10+
# Re-run behaviour: this script is intended to be safe to re-run. The create steps
11+
# (GitLab repo, NightVision app/target) are guarded so a second run reuses existing
12+
# resources with a logged warning instead of aborting, and the gitlab remote is
13+
# reconciled to the current "$GROUP/$REPO" on each run. The NightVision token is
14+
# the exception: each run creates a fresh one and overwrites the CI variable (the value
15+
# cannot be read back, so it is rotated, not reused); older tokens stay in NightVision, so
16+
# revoke them there if you re-run often. The script does NOT delete anything; see the
17+
# cleanup notes at the bottom to tear a demo down.
18+
# ---------------------------------------------------------------------------------------------------------------------
319

420
# Check if the correct number of arguments is provided
521
if [ "$#" -ne 2 ]; then
@@ -9,70 +25,108 @@ if [ "$#" -ne 2 ]; then
925
fi
1026

1127
# Assign positional arguments to variables
12-
GROUP=$1
13-
REPO=$2
28+
GROUP="$1"
29+
REPO="$2"
1430

1531
echo "Creating a repository under: $GROUP/$REPO"
1632

17-
# Clone the GitHub repository that we will mirror to GitLab
18-
git clone https://github.com/nvsecurity/java-github-actions-demo
33+
# Clone the GitHub repository that we will mirror to GitLab.
34+
# Idempotent: reuse an existing checkout instead of failing on a second run.
35+
if [ ! -d java-github-actions-demo ]; then
36+
git clone https://github.com/nvsecurity/java-github-actions-demo
37+
fi
1938
cd java-github-actions-demo
2039

2140
# ---------------------------------------------------------------------------------------------------------------------
2241
# Set up the GitLab repository
2342
# ---------------------------------------------------------------------------------------------------------------------
24-
# Create a repository
25-
echo "NOTE: Select NO for 'Create a local project directory'"
26-
glab repo create $REPO
27-
28-
# add the nightvision token as the GitLab secret NIGHTVISION_TOKEN
29-
nightvision login
30-
TOKEN=$(nightvision token create)
31-
32-
# Check if GitLab CLI is authenticated
43+
# Authenticate to GitLab and NightVision first, so the create steps below only run after
44+
# both logins are confirmed - an aborted login then leaves nothing half-created.
3345
if ! glab auth status 2>&1 | grep -q 'Logged in to gitlab.com'; then
3446
echo "GitLab CLI not authenticated. Running 'glab auth login'..."
3547
glab auth login
3648
else
3749
echo "Logged in to gitlab already..."
3850
fi
51+
nightvision login
3952

40-
glab variable set NIGHTVISION_TOKEN --masked --repo $GROUP/$REPO $TOKEN < token
53+
# Create a repository in the target namespace. If it already exists, keep going.
54+
echo "NOTE: Select NO for 'Create a local project directory'"
55+
glab repo create "$GROUP/$REPO" || echo "WARNING: 'glab repo create $GROUP/$REPO' failed (the project may already exist); continuing."
56+
57+
# Create a fresh NightVision token for the NIGHTVISION_TOKEN CI variable. tr strips any
58+
# stray whitespace so the value GitLab masks is clean; the guard catches an empty result
59+
# (an exit-0 token create with no output) before it becomes an invalid masked variable.
60+
TOKEN="$(nightvision token create | tr -d '[:space:]')"
61+
if [ -z "$TOKEN" ]; then
62+
echo "ERROR: 'nightvision token create' returned an empty token; aborting." >&2
63+
exit 1
64+
fi
65+
66+
# Set the masked CI variable to the freshly created token (rotated every run, not reused,
67+
# because a token's value cannot be read back). If it already exists (older glab errors
68+
# instead of upserting), update it. This must succeed: failing both set and update aborts
69+
# the script (set -e) rather than silently leaving CI a stale token.
70+
glab variable set NIGHTVISION_TOKEN --masked --repo "$GROUP/$REPO" "$TOKEN" \
71+
|| glab variable update NIGHTVISION_TOKEN --masked --repo "$GROUP/$REPO" "$TOKEN"
4172

4273
# ---------------------------------------------------------------------------------------------------------------------
4374
# Note that GitLab has additional requirements vs other CI/CD providers.
4475
# Instead of `localhost` you must use the `docker` hostname.
45-
# First add the docker hostname reference to your `/etc/hosts` file on your laptop
76+
# First add the docker hostname reference to your `/etc/hosts` file on your laptop.
77+
#
78+
# This edits a system file with sudo. It is left in place after the demo so repeat runs work.
79+
# To revert it afterwards run:
80+
# sudo sed -i.bak '/^127\.0\.0\.1 docker$/d' /etc/hosts
4681
# ---------------------------------------------------------------------------------------------------------------------
47-
if ! grep -q "127.0.0.1 docker" /etc/hosts; then \
48-
echo "127.0.0.1 docker" | sudo tee -a /etc/hosts; \
82+
if ! grep -q "127.0.0.1 docker" /etc/hosts; then
83+
echo "NOTICE: adding '127.0.0.1 docker' to /etc/hosts (sudo). See the revert command in this script's comments."
84+
echo "127.0.0.1 docker" | sudo tee -a /etc/hosts
4985
fi
86+
5087
# ---------------------------------------------------------------------------------------------------------------------
5188
# NightVision commands
5289
# ---------------------------------------------------------------------------------------------------------------------
53-
# Create app and target
54-
# Username: user
55-
# Password: password
90+
# Create app and target. Guarded so a re-run reuses the existing app/target.
5691
URL="https://docker:9000"
5792
APP="javaspringvulny-api-gitlab"
58-
nightvision app create $APP
59-
nightvision target create $APP https://docker:9000 --type api
93+
nightvision app create "$APP" || echo "WARNING: 'nightvision app create $APP' failed (it may already exist); continuing."
94+
nightvision target create "$APP" "$URL" --type API || echo "WARNING: 'nightvision target create $APP' failed (it may already exist); continuing."
6095

6196
# Start the application
6297
docker compose up -d; sleep 10
63-
# Record authentication - click on Form Auth
64-
echo "Click on Form Auth and use these credentials: "
65-
echo "\tUsername: user"
66-
echo "\tPassword: password"
67-
nightvision auth playwright create $APP $URL
98+
# Record authentication - click on Form Auth.
99+
# These are the demo application's default credentials (the javaspringvulny sample app),
100+
# not real secrets.
101+
echo "Click on Form Auth and use the javaspringvulny demo defaults:"
102+
echo " Username: user"
103+
echo " Password: password"
104+
nightvision auth playwright create "$APP" "$URL"
68105

69106
# ---------------------------------------------------------------------------------------------------------------------
70107
# sync it back with GitLab and trigger the CI/CD job.
71108
# ---------------------------------------------------------------------------------------------------------------------
72-
git remote add gitlab git@gitlab.com:$GROUP/$REPO.git
109+
# Point the gitlab remote at the requested project. A reused checkout may carry the
110+
# remote from a previous run with different arguments; reconciling it to the current
111+
# "$GROUP/$REPO" keeps the push from silently targeting the previous run's project.
112+
if git remote get-url gitlab >/dev/null 2>&1; then
113+
git remote set-url gitlab "git@gitlab.com:$GROUP/$REPO.git"
114+
else
115+
git remote add gitlab "git@gitlab.com:$GROUP/$REPO.git"
116+
fi
117+
# Add an empty commit so each run pushes a fresh commit and triggers the
118+
# pipeline. A re-run reuses the existing checkout, which has no new commits, so a
119+
# bare push would be "Everything up-to-date" and fire nothing.
120+
git commit --allow-empty -m "Trigger GitLab pipeline"
73121
git push gitlab main
74122

75123
# Notes:
76-
# To delete the project:
77-
# glab repo delete $GROUP/$REPO
124+
# To delete the project and local checkout (substitute the group and repo you ran this script with):
125+
# glab repo delete <group>/<repo>
78126
# rm -rf ./java-github-actions-demo
127+
# The NightVision app and target are not deleted; they are reused on re-run
128+
# (auth is re-recorded each run, and each run mints a fresh token that persists,
129+
# as noted in the header). Remove the app, target, and stale tokens from the
130+
# NightVision UI for a full teardown.
131+
# To revert the /etc/hosts entry:
132+
# sudo sed -i.bak '/^127\.0\.0\.1 docker$/d' /etc/hosts

tests/run.sh

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/usr/bin/env bash
2+
# Shared lint entrypoint for nv-public-reference. CI (.github/workflows/lint.yml)
3+
# invokes this exact script, so a local run and a CI run can never check different
4+
# things or drift apart over time.
5+
#
6+
# What it checks, for every tracked shell script:
7+
# 1. bash -n - syntax / parse check
8+
# 2. shellcheck - static analysis (quoting, set -e pitfalls, unsafe expansions)
9+
#
10+
# Usage: tests/run.sh
11+
set -euo pipefail
12+
13+
# Run from the repository root regardless of the caller's working directory, so the
14+
# git file list and relative paths resolve identically in local and CI runs.
15+
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
16+
cd "$repo_root"
17+
18+
# The shellcheck tool is required, not optional: silently skipping it when absent
19+
# would let a local run pass on a weaker check than CI, which is the exact drift
20+
# this shared entrypoint exists to prevent.
21+
if ! command -v shellcheck >/dev/null 2>&1; then
22+
echo "ERROR: shellcheck not found on PATH. Install it and re-run:" >&2
23+
echo " macOS: brew install shellcheck" >&2
24+
echo " Debian: sudo apt-get install -y shellcheck" >&2
25+
echo " other: https://github.com/koalaman/shellcheck#installing" >&2
26+
exit 1
27+
fi
28+
29+
# Enumerate tracked shell scripts via git so untracked / vendored files (for example
30+
# the .agent-sandbox-config tree) are never linted. A read loop (rather than mapfile)
31+
# keeps this working on bash 3.2, the default on macOS, so local runs match CI.
32+
scripts=()
33+
while IFS= read -r script_path; do
34+
scripts+=("$script_path")
35+
done < <(git ls-files '*.sh')
36+
if [ "${#scripts[@]}" -eq 0 ]; then
37+
echo "No tracked *.sh files found; nothing to lint."
38+
exit 0
39+
fi
40+
41+
echo "Linting ${#scripts[@]} shell script(s):"
42+
printf ' %s\n' "${scripts[@]}"
43+
44+
# Cheap parse check first; set -e aborts on the first failure so CI fails the job.
45+
for script in "${scripts[@]}"; do
46+
bash -n "$script"
47+
done
48+
49+
# Then the deeper static analysis pass over the whole set in one invocation.
50+
shellcheck "${scripts[@]}"
51+
52+
echo "OK: all shell scripts passed bash -n and shellcheck."

0 commit comments

Comments
 (0)