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
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
# Wait 7 days after a release is published before opening an update PR,
# so freshly published (potentially compromised) versions are not pinned
# immediately. Keeps the SHA pins refreshed as reviewable PRs.
cooldown:
default-days: 7
28 changes: 28 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Lint

# Static checks for the repo's shell scripts. Runs the same tests/run.sh that
# developers run locally, so local and CI can never check different things.
on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
shell-lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- name: Ensure shellcheck is available
run: |
if ! command -v shellcheck >/dev/null 2>&1; then
sudo apt-get update && sudo apt-get install -y shellcheck
fi
shellcheck --version

- name: Run lint entrypoint
run: ./tests/run.sh
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Python bytecode / caches (sarif/ converters)
__pycache__/
*.py[cod]

# OS metadata
.DS_Store
Thumbs.db

# Editor / IDE
.idea/
.vscode/
*.swp

# Local checkout the demo scripts clone into, plus any scan artifacts
# (openapi-spec.yml, scan-results.txt, results.sarif) written inside it.
/java-github-actions-demo/

# Agent sandbox state: local tooling, never part of the repo.
.agent-sandbox-config/

# Local analysis / work products, kept out of version control by convention.
analysis/
100 changes: 73 additions & 27 deletions demo-scripts/github-actions-demo.sh
Original file line number Diff line number Diff line change
@@ -1,42 +1,88 @@
#!/bin/bash
set -e
#!/usr/bin/env bash
set -euo pipefail

# Clone the repository locally from your terminal
git clone https://github.com/$(git config user.name)/java-github-actions-demo.git
cd java-github-actions-demo

# add the nightvision token as the github action secret NIGHTVISION_TOKEN
nightvision login
nightvision token create > token
# ---------------------------------------------------------------------------------------------------------------------
# NightVision GitHub Actions demo
#
# Clones your fork of java-github-actions-demo, wires the NIGHTVISION_TOKEN Actions secret,
# creates a NightVision app + target, records auth, and pushes a commit to trigger the
# workflow.
#
# Re-run behaviour: this script is intended to be safe to re-run. The create steps
# (NightVision app/target) are guarded so a second run reuses existing resources with a
# logged warning instead of aborting. The NightVision token is the exception: each run
# creates a fresh one and overwrites the Actions secret (the value cannot be read back, so
# it is rotated, not reused); older tokens stay in NightVision, so revoke them there if you
# re-run often. The script does NOT delete anything; see the cleanup notes at the bottom.
# ---------------------------------------------------------------------------------------------------------------------

# Check if GitHub CLI is authenticated
# Authenticate to GitHub and NightVision first, so the steps below only run after both
# logins are confirmed - an aborted login then leaves nothing half-created.
if ! gh auth status 2>&1 | grep -q 'Logged in to github.com'; then
echo "GitHub CLI not authenticated. Running 'gh auth login'..."
gh auth login
else
echo "Logged in to github already..."
fi
nightvision login

# Clone your fork of the demo repository. The owner comes from the authenticated GitHub
# login: 'git config user.name' is a display name, not a valid URL segment for most users.
# Idempotent: reuse an existing checkout instead of failing on a second run, but only
# after confirming it points at the expected fork - a stale checkout of another repo
# would otherwise receive the push at the end of the script.
OWNER="$(gh api user --jq .login)"
if [ ! -d java-github-actions-demo ]; then
git clone "https://github.com/$OWNER/java-github-actions-demo.git"
elif ! git -C java-github-actions-demo remote get-url origin | grep -qiE "github\.com[:/]$OWNER/java-github-actions-demo(\.git)?$"; then
echo "ERROR: existing ./java-github-actions-demo does not point at $OWNER/java-github-actions-demo; move it aside and re-run." >&2
exit 1
fi
cd java-github-actions-demo

# Create a fresh NightVision token for the NIGHTVISION_TOKEN Actions secret, without
# writing it to disk. tr strips any stray whitespace; the guard catches an empty result
# (an exit-0 token create with no output) before it becomes an empty secret.
TOKEN="$(nightvision token create | tr -d '[:space:]')"
if [ -z "$TOKEN" ]; then
echo "ERROR: 'nightvision token create' returned an empty token; aborting." >&2
exit 1
fi

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

# Create app and target
# Username: user
# Password: password
# ---------------------------------------------------------------------------------------------------------------------
# NightVision commands
# ---------------------------------------------------------------------------------------------------------------------
# Create app and target. Guarded so a re-run reuses the existing app/target.
URL="https://localhost:9000"
APP="javaspringvulny-api"
nightvision app create $APP
nightvision target create $APP $URL --type API
nightvision app create "$APP" || echo "WARNING: 'nightvision app create $APP' failed (it may already exist); continuing."
nightvision target create "$APP" "$URL" --type API || echo "WARNING: 'nightvision target create $APP' failed (it may already exist); continuing."

# Start the application
docker compose up -d; sleep 10
# Record authentication - click on Form Auth
echo "Click on Form Auth and use these credentials: "
echo "\tUsername: user"
echo "\tPassword: password"
nightvision auth playwright create $APP $URL

# Add a commit and trigger the CI/CD
echo "foobar" >> README.md
git commit -am 'trigger a github action'
# Record authentication - click on Form Auth.
# These are the demo application's default credentials (the javaspringvulny sample app),
# not real secrets.
echo "Click on Form Auth and use the javaspringvulny demo defaults:"
echo " Username: user"
echo " Password: password"
nightvision auth playwright create "$APP" "$URL"

# ---------------------------------------------------------------------------------------------------------------------
# Add an empty commit and push to trigger the GitHub Actions workflow. An empty
# commit always provides a fresh commit to push (including on a re-run against an
# existing checkout) without modifying tracked files.
# ---------------------------------------------------------------------------------------------------------------------
git commit --allow-empty -m "Trigger GitHub Actions workflow"
git push

# Notes:
# To clean up (substitute your GitHub login):
# gh secret delete NIGHTVISION_TOKEN --repo <your-github-login>/java-github-actions-demo
# rm -rf ./java-github-actions-demo
116 changes: 85 additions & 31 deletions demo-scripts/gitlab-demo.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
#!/usr/bin/env bash
set -e
set -euo pipefail

# ---------------------------------------------------------------------------------------------------------------------
# NightVision GitLab "Easy Mode" demo (NV-4418)
#
# Creates a GitLab project, wires the NIGHTVISION_TOKEN CI variable, creates a NightVision
# app + target, records auth, and pushes the demo repo to trigger the CI/CD pipeline.
#
# Re-run behaviour: this script is intended to be safe to re-run. The create steps
# (GitLab repo, NightVision app/target) are guarded so a second run reuses existing
# resources with a logged warning instead of aborting, and the gitlab remote is
# reconciled to the current "$GROUP/$REPO" on each run. The NightVision token is
# the exception: each run creates a fresh one and overwrites the CI variable (the value
# cannot be read back, so it is rotated, not reused); older tokens stay in NightVision, so
# revoke them there if you re-run often. The script does NOT delete anything; see the
# cleanup notes at the bottom to tear a demo down.
# ---------------------------------------------------------------------------------------------------------------------

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

# Assign positional arguments to variables
GROUP=$1
REPO=$2
GROUP="$1"
REPO="$2"

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

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

# ---------------------------------------------------------------------------------------------------------------------
# Set up the GitLab repository
# ---------------------------------------------------------------------------------------------------------------------
# Create a repository
echo "NOTE: Select NO for 'Create a local project directory'"
glab repo create $REPO

# add the nightvision token as the GitLab secret NIGHTVISION_TOKEN
nightvision login
TOKEN=$(nightvision token create)

# Check if GitLab CLI is authenticated
# Authenticate to GitLab and NightVision first, so the create steps below only run after
# both logins are confirmed - an aborted login then leaves nothing half-created.
if ! glab auth status 2>&1 | grep -q 'Logged in to gitlab.com'; then
echo "GitLab CLI not authenticated. Running 'glab auth login'..."
glab auth login
else
echo "Logged in to gitlab already..."
fi
nightvision login

glab variable set NIGHTVISION_TOKEN --masked --repo $GROUP/$REPO $TOKEN < token
# Create a repository in the target namespace. If it already exists, keep going.
echo "NOTE: Select NO for 'Create a local project directory'"
glab repo create "$GROUP/$REPO" || echo "WARNING: 'glab repo create $GROUP/$REPO' failed (the project may already exist); continuing."

# Create a fresh NightVision token for the NIGHTVISION_TOKEN CI variable. tr strips any
# stray whitespace so the value GitLab masks is clean; the guard catches an empty result
# (an exit-0 token create with no output) before it becomes an invalid masked variable.
TOKEN="$(nightvision token create | tr -d '[:space:]')"
if [ -z "$TOKEN" ]; then
echo "ERROR: 'nightvision token create' returned an empty token; aborting." >&2
exit 1
fi

# Set the masked CI variable to the freshly created token (rotated every run, not reused,
# because a token's value cannot be read back). If it already exists (older glab errors
# instead of upserting), update it. This must succeed: failing both set and update aborts
# the script (set -e) rather than silently leaving CI a stale token.
glab variable set NIGHTVISION_TOKEN --masked --repo "$GROUP/$REPO" "$TOKEN" \
|| glab variable update NIGHTVISION_TOKEN --masked --repo "$GROUP/$REPO" "$TOKEN"

# ---------------------------------------------------------------------------------------------------------------------
# Note that GitLab has additional requirements vs other CI/CD providers.
# Instead of `localhost` you must use the `docker` hostname.
# First add the docker hostname reference to your `/etc/hosts` file on your laptop
# First add the docker hostname reference to your `/etc/hosts` file on your laptop.
#
# This edits a system file with sudo. It is left in place after the demo so repeat runs work.
# To revert it afterwards run:
# sudo sed -i.bak '/^127\.0\.0\.1 docker$/d' /etc/hosts
# ---------------------------------------------------------------------------------------------------------------------
if ! grep -q "127.0.0.1 docker" /etc/hosts; then \
echo "127.0.0.1 docker" | sudo tee -a /etc/hosts; \
if ! grep -q "127.0.0.1 docker" /etc/hosts; then
echo "NOTICE: adding '127.0.0.1 docker' to /etc/hosts (sudo). See the revert command in this script's comments."
echo "127.0.0.1 docker" | sudo tee -a /etc/hosts
fi

# ---------------------------------------------------------------------------------------------------------------------
# NightVision commands
# ---------------------------------------------------------------------------------------------------------------------
# Create app and target
# Username: user
# Password: password
# Create app and target. Guarded so a re-run reuses the existing app/target.
URL="https://docker:9000"
APP="javaspringvulny-api-gitlab"
nightvision app create $APP
nightvision target create $APP https://docker:9000 --type api
nightvision app create "$APP" || echo "WARNING: 'nightvision app create $APP' failed (it may already exist); continuing."
nightvision target create "$APP" "$URL" --type API || echo "WARNING: 'nightvision target create $APP' failed (it may already exist); continuing."

# Start the application
docker compose up -d; sleep 10
# Record authentication - click on Form Auth
echo "Click on Form Auth and use these credentials: "
echo "\tUsername: user"
echo "\tPassword: password"
nightvision auth playwright create $APP $URL
# Record authentication - click on Form Auth.
# These are the demo application's default credentials (the javaspringvulny sample app),
# not real secrets.
echo "Click on Form Auth and use the javaspringvulny demo defaults:"
echo " Username: user"
echo " Password: password"
nightvision auth playwright create "$APP" "$URL"

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

# Notes:
# To delete the project:
# glab repo delete $GROUP/$REPO
# To delete the project and local checkout (substitute the group and repo you ran this script with):
# glab repo delete <group>/<repo>
# rm -rf ./java-github-actions-demo
# The NightVision app and target are not deleted; they are reused on re-run
# (auth is re-recorded each run, and each run mints a fresh token that persists,
# as noted in the header). Remove the app, target, and stale tokens from the
# NightVision UI for a full teardown.
# To revert the /etc/hosts entry:
# sudo sed -i.bak '/^127\.0\.0\.1 docker$/d' /etc/hosts
52 changes: 52 additions & 0 deletions tests/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# Shared lint entrypoint for nv-public-reference. CI (.github/workflows/lint.yml)
# invokes this exact script, so a local run and a CI run can never check different
# things or drift apart over time.
#
# What it checks, for every tracked shell script:
# 1. bash -n - syntax / parse check
# 2. shellcheck - static analysis (quoting, set -e pitfalls, unsafe expansions)
#
# Usage: tests/run.sh
set -euo pipefail

# Run from the repository root regardless of the caller's working directory, so the
# git file list and relative paths resolve identically in local and CI runs.
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$repo_root"

# The shellcheck tool is required, not optional: silently skipping it when absent
# would let a local run pass on a weaker check than CI, which is the exact drift
# this shared entrypoint exists to prevent.
if ! command -v shellcheck >/dev/null 2>&1; then
echo "ERROR: shellcheck not found on PATH. Install it and re-run:" >&2
echo " macOS: brew install shellcheck" >&2
echo " Debian: sudo apt-get install -y shellcheck" >&2
echo " other: https://github.com/koalaman/shellcheck#installing" >&2
exit 1
fi

# Enumerate tracked shell scripts via git so untracked / vendored files (for example
# the .agent-sandbox-config tree) are never linted. A read loop (rather than mapfile)
# keeps this working on bash 3.2, the default on macOS, so local runs match CI.
scripts=()
while IFS= read -r script_path; do
scripts+=("$script_path")
done < <(git ls-files '*.sh')
if [ "${#scripts[@]}" -eq 0 ]; then
echo "No tracked *.sh files found; nothing to lint."
exit 0
fi

echo "Linting ${#scripts[@]} shell script(s):"
printf ' %s\n' "${scripts[@]}"

# Cheap parse check first; set -e aborts on the first failure so CI fails the job.
for script in "${scripts[@]}"; do
bash -n "$script"
done

# Then the deeper static analysis pass over the whole set in one invocation.
shellcheck "${scripts[@]}"

echo "OK: all shell scripts passed bash -n and shellcheck."