Skip to content
Report library
Purpose / Other

Wizard Skill Security Audit

What the author says it does (original text)

Generate an interactive bash wizard that walks a human through steps only they can perform. Use when provisioning infrastructure, setting up credentials or CI secrets, walking an unfamiliar third-party dashboard, or running a one-off migration or cutover. Don't invoke this for steps the agent can perform itself.

Independent security check

Do not install or run it yet

Files checked
3
Risks found
4
Could it run dangerous commands?Looks for programs run straight after downloading, remote control of your computer, and hidden commands.No risks found
Could it expose your files or keys?Looks for uploads of files containing passwords or keys, and keys written directly in the code.Risks found: 3
High risk

GitHub secret uploads rely on the current directory's implicit repository context

Source references: 2
What we found

`set_secret` invokes `gh secret set` without naming a repository and does not display or confirm the GitHub account and repository before upload. The command therefore uses the destination inferred by `gh` from the current environment.

Why this matters

If the wizard runs from the wrong project, the remote is misconfigured, or `gh` is authenticated to an unintended account, credentials can be stored in another repository and become usable by that repository's administrators or workflows.

When gh is installed and authenticated, the function directly runs `gh secret set` without an explicit target such as `--repo`, and it does not display or confirm the account or repository. The destination therefore depends on the runtime gh/current-directory context; running from the wrong repository could place the secret in an unintended repository. A user can require the script to display and confirm the resolved repository or pass an explicit repository.

template.sh:141In the codeOpen original file
# set_secret NAME VALUE sets a GitHub Actions repo secret via gh. Falls back# to a warning (and records it) if gh is unavailable or unauthenticated.set_secret() {  local name="$1" value="$2"  if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then    if printf '%s' "$value" | gh secret set "$name" >/dev/null 2>&1; then      WRITTEN_SECRET+=("$name")      printf '  %s✓ set%s GitHub secret %s\n' "$GREEN" "$RESET" "$name"      return
Show 1 other places
template.sh:199In the codeOpen original file
ask_secret STRIPE_SECRET_KEY "Paste the secret key:"write_env STRIPE_PUBLISHABLE_KEY "$STRIPE_PUBLISHABLE_KEY"write_env STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"set_secret STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"   # CI needs this one# ──────────────────────────────────────────────────────────────────────────
Medium risk

The example template is a live Stripe credential workflow, not an inert example

Source references: 2
What we found

Although comments say to replace the example, the bottom of the file contains executable shell statements that open Stripe, request publishable and secret keys, write `.env`, and attempt to set a GitHub repository secret.

Why this matters

If a user runs `template.sh` believing it is only a template, entering credentials modifies the local environment file and may send the Stripe secret key to the current GitHub repository.

The section is labeled as an example to replace, but it remains active top-level shell code. If template.sh is run before a generator replaces it, it opens Stripe, collects keys, writes both to .env in the current directory, and attempts to set the secret key in the current GitHub repository. A user can ask that the template fail safely by default, or verify that the example was replaced and the repository target is correct before running it.

template.sh:187In the codeOpen original file
TOTAL_STAGES=1banner "Stripe setup"# ── Example stage: replace with your real steps ───────────────────────────stage "Stripe: API keys"say "We'll grab your Stripe test keys and store them for local dev + CI."open_url "https://dashboard.stripe.com/test/apikeys"step "On the API keys page, copy the Publishable key (starts pk_test_)."ask STRIPE_PUBLISHABLE_KEY "Paste the publishable key:"step "Click 'Reveal test key' on the Secret key row, then copy it."ask_secret STRIPE_SECRET_KEY "Paste the secret key:"write_env STRIPE_PUBLISHABLE_KEY "$STRIPE_PUBLISHABLE_KEY"write_env STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"set_secret STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"   # CI needs this one# ──────────────────────────────────────────────────────────────────────────finish
Show 1 other places
template.sh:195In the codeOpen original file
open_url "https://dashboard.stripe.com/test/apikeys"step "On the API keys page, copy the Publishable key (starts pk_test_)."ask STRIPE_PUBLISHABLE_KEY "Paste the publishable key:"step "Click 'Reveal test key' on the Secret key row, then copy it."ask_secret STRIPE_SECRET_KEY "Paste the secret key:"write_env STRIPE_PUBLISHABLE_KEY "$STRIPE_PUBLISHABLE_KEY"write_env STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"set_secret STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"   # CI needs this one# ──────────────────────────────────────────────────────────────────────────
Medium risk

Persisted secrets are stored in plaintext in the default `.env` file

Source references: 4
What we found

The default destination is `.env` in the current directory, and `write_env` writes literal `KEY=VALUE` records. The Skill also directs authors to call it for every persisted value; hidden terminal entry prevents screen echo but does not encrypt the disk copy.

Why this matters

People or processes able to read the project directory, backups, synchronized copies, or an accidentally committed file may obtain API keys. The implementation does not verify that the destination is excluded from version control before writing.

Secret entry only suppresses terminal echo through `read -s`; the default destination is `.env` in the current directory, and `write_env` stores the value as ordinary `KEY=VALUE` text. The included active example passes the Stripe secret key to that function. If `.env` has permissive access, is backed up, or is accidentally committed, the credential may be exposed. Users can restrict permissions, verify `.gitignore`, and require disk persistence only when necessary.

template.sh:25In the codeOpen original file
_STAGE_INDEX=0ENV_FILE="${ENV_FILE:-.env}"WRITTEN_ENV=()    # KEYs written to ENV_FILE this runWRITTEN_SECRET=() # secret NAMEs set this runSKIPPED=()        # things we couldn't do (e.g. gh missing)
Show 3 other places
template.sh:128In the codeOpen original file
# write_env KEY VALUE upserts KEY=VALUE into ENV_FILE (creates it; replaces# any existing line). Idempotent.write_env() {  local key="$1" value="$2" tmp  touch "$ENV_FILE"  tmp=$(mktemp)  grep -vE "^${key}=" "$ENV_FILE" > "$tmp" || true  printf '%s=%s\n' "$key" "$value" >> "$tmp"  mv "$tmp" "$ENV_FILE"  WRITTEN_ENV+=("$key")  printf '  %s✓ wrote%s %s → %s\n' "$GREEN" "$RESET" "$key" "$ENV_FILE"}
template.sh:197In the codeOpen original file
ask STRIPE_PUBLISHABLE_KEY "Paste the publishable key:"step "Click 'Reveal test key' on the Secret key row, then copy it."ask_secret STRIPE_SECRET_KEY "Paste the secret key:"write_env STRIPE_PUBLISHABLE_KEY "$STRIPE_PUBLISHABLE_KEY"write_env STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"set_secret STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"   # CI needs this one# ──────────────────────────────────────────────────────────────────────────
template.sh:113In the codeOpen original file
# ask_secret KEY "Prompt" is like ask, but input is hidden.ask_secret() {  local key="$1" prompt="$2" current input  current=$(_existing "$key" || true)  if [[ -n "$current" ]]; then    printf '  %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"  else    printf '  %s%s%s ' "$BOLD" "$prompt" "$RESET"  fi  read -rs input || true  printf '\n'  [[ -z "$input" && -n "$current" ]] && input="$current"  printf -v "$key" '%s' "$input"}
Could it delete files or keep running?Looks for broad file deletion, disk overwrites, and programs set to start automatically.Risks found: 1
Medium risk

Environment updates replace the file object, which can break symlinks and metadata

Source references: 2
What we found

`write_env` copies content into a `mktemp` file and then moves that temporary file over the `ENV_FILE` path. It does not modify the original file in place.

Why this matters

If `.env` is a symlink to centrally managed configuration, the link is replaced by a regular file while its original target retains stale values. Existing permissions, ownership, or other metadata may also change, potentially leaving an application on old credentials or changing configuration access.

`write_env` writes filtered content to a newly created `mktemp` file and then moves it over the `ENV_FILE` path rather than updating the original in place. If the target is a symbolic link, this commonly replaces the link rather than its referent; the new file may also carry temporary-file permissions and lose prior ownership or extended metadata. This occurs only when `write_env` is called. Users can verify `.env` is not a link and preserve its metadata, or require a metadata-preserving update method.

template.sh:130In the codeOpen original file
# any existing line). Idempotent.write_env() {  local key="$1" value="$2" tmp  touch "$ENV_FILE"  tmp=$(mktemp)  grep -vE "^${key}=" "$ENV_FILE" > "$tmp" || true  printf '%s=%s\n' "$key" "$value" >> "$tmp"  mv "$tmp" "$ENV_FILE"  WRITTEN_ENV+=("$key")
Show 1 other places
template.sh:128In the codeOpen original file
# write_env KEY VALUE upserts KEY=VALUE into ENV_FILE (creates it; replaces# any existing line). Idempotent.write_env() {  local key="$1" value="$2" tmp  touch "$ENV_FILE"  tmp=$(mktemp)  grep -vE "^${key}=" "$ENV_FILE" > "$tmp" || true  printf '%s=%s\n' "$key" "$value" >> "$tmp"  mv "$tmp" "$ENV_FILE"  WRITTEN_ENV+=("$key")  printf '  %s✓ wrote%s %s → %s\n' "$GREEN" "$RESET" "$key" "$ENV_FILE"}
Could it bypass safety checks?Looks for skipped website security checks, excessive file access, or actions that skip your approval.No risks found
Could it mislead the AI or hide text?Checks the skill instructions for requests to ignore you, influence the report, or hide text in invisible characters.No risks found
Could it change links or payment recipients without asking?Looks for forced referral or payment changes combined with instructions to hide the change.No risks found

Inside this skill

1 instruction sections

The Skill first inspects environment files, documentation, and CI workflows, then asks the user to confirm the stage order, each value's source and destination, and whether it is secret.

View source
SKILL.md:18In the instructionsOpen original file
Work out every manual step the human must take and every value that gets captured along the way. Read the repo first, don't ask cold:- For setup: `.env`, `.env.example`, `.env.*`, `README`, `docker-compose*`, framework config, and `.github/workflows/*` (every `secrets.*` / `vars.*` reference is a value the wizard must produce).- For a migration or transition: the current state, the target state, and the irreversible actions between them.Then show the user the ordered list of stages and the values each produces, and confirm: they may add, drop, or reorder.**Done when:** every stage is named in order, and for each captured value you know (a) where the human gets it, (b) where it's written (`.env`, a GitHub secret, both, or nowhere; some stages are pure actions), and (c) whether it's secret (hidden entry) or public.

A generated wizard can open author-selected URLs, collect visible or hidden input, and write the values to a local environment file, GitHub Actions repository secrets, or repository variables.

View source
SKILL.md:35In the instructionsOpen original file
Copy `template.sh` to the target path. Replace the example stage with one `stage` per step, in dependency order. Use the library helpers: `stage`, `say`/`step`, `open_url`, `ask`/`ask_secret`, `write_env`, `set_secret`/`set_var`, `pause`/`confirm`. Set `TOTAL_STAGES` to the number of stages you wrote.Hold the bar the template sets: open the URL before asking for its value, use `ask_secret` for anything secret, `write_env` every persisted value, `set_secret` only the values CI actually needs, and `confirm` before any irreversible action. Each `stage` clears the screen so only the current step is visible: keep a stage to one focused task so nothing the human needs scrolls away. Don't touch the library above the marker.

The Skill explicitly says the agent should not run the wizard end to end; it should statically trace value destinations and CI secret names, then tell the user how to run it.

View source
SKILL.md:41In the instructionsOpen original file
- `bash -n <script>`; run `shellcheck` if available.- `chmod +x <script>`.- Don't run it end-to-end yourself: it opens browsers and blocks on human input. Trace it statically instead: every value from step 1 is captured and lands where step 1 said, and every `set_secret` name exactly matches a `secrets.*` reference in CI.- Tell the user how to run it. If it's a repeatable setup path, commit it and link it from the README so the next person runs the script instead of asking an AI.

Generated stages are expected to call a confirmation function before irreversible actions; the function defaults to rejection and succeeds only for input beginning with y or Y.

View source
SKILL.md:37In the instructionsOpen original file
Hold the bar the template sets: open the URL before asking for its value, use `ask_secret` for anything secret, `write_env` every persisted value, `set_secret` only the values CI actually needs, and `confirm` before any irreversible action. Each `stage` clears the screen so only the current step is visible: keep a stage to one focused task so nothing the human needs scrolls away. Don't touch the library above the marker.
template.sh:83In the codeOpen original file
# confirm "question" is a y/N gate; returns success on yes.confirm() {  local reply=""  printf '  %s? %s [y/N] ' "$YELLOW" "$1"  read -r reply || true  [[ "$reply" =~ ^[Yy] ]]}
Start here · InstructionsSKILL.md
wizard
Lines connect the instruction file to its sections, not an observed execution order. Select a section to read the source.

File reference map

References: 1
Files making referencesReferenced content
Lines show actual file references, not execution order. Select a node to highlight its connections and inspect the files and source locations. Dashed lines include files that still need locating.
Files and check records3 files

Coverage and gaps

Content covered in each file

These are the source ranges included in this check, not a guarantee that every issue has been resolved.

  • SKILL.mdFull text included
  • template.shFull text included
  • agents/openai.yamlFull text included

This report is for the version above. We read the available code and instructions without running the skill or checking extra packages it installs. This is not a promise of safety: a different version or setup may behave differently.

  • SKILL.mdInstructions
  • agents/openai.yamlSupporting file
  • template.shScript

Operations mentioned in code and instructions

Run commands
template.sh:1In the codeOpen original file
#!/usr/bin/env bash#
SKILL.md:3In the instructionsOpen original file
name: wizarddescription: Generate an interactive bash wizard that walks a human through steps only they can perform. Use when provisioning infrastructure, setting up credentials or CI secrets, walking an unfamiliar third-party dashboard, or running a one-off migration or cutover. Don't invoke this for steps the agent can perform itself.---
SKILL.md:8In the instructionsOpen original file
A **wizard** is a bash script that walks a human, step by step, through a manual procedure that's tedious to do by hand and tedious to re-explain to an AI every time. It opens each URL, says exactly what to click and copy, captures the values, writes them where they belong (`.env`, GitHub secrets), confirms at every stage, and shows how many stages are left. It might configure third-party services, run a one-off migration, or move the project from one state to another.
Read keys or account settings
template.sh:26In the codeOpen original file
_STAGE_INDEX=0ENV_FILE="${ENV_FILE:-.env}"WRITTEN_ENV=()    # KEYs written to ENV_FILE this run
template.sh:98In the codeOpen original file
# ask KEY "Prompt" reads a value into $KEY. Offers the existing .env value as# a default on re-runs (Enter keeps it). Visible input (non-secret).
template.sh:194In the codeOpen original file
say "We'll grab your Stripe test keys and store them for local dev + CI."open_url "https://dashboard.stripe.com/test/apikeys"step "On the API keys page, copy the Publishable key (starts pk_test_)."
Change files
template.sh:136In the codeOpen original file
  printf '%s=%s\n' "$key" "$value" >> "$tmp"  mv "$tmp" "$ENV_FILE"  WRITTEN_ENV+=("$key")
Connect to websites
template.sh:194In the codeOpen original file
say "We'll grab your Stripe test keys and store them for local dev + CI."open_url "https://dashboard.stripe.com/test/apikeys"step "On the API keys page, copy the Publishable key (starts pk_test_)."
Lines read
254
File checksum (to compare versions)
dd770a3b52cf881a086256dd0e7cdbf8c5951e473142c2255f2c9848644ad1f3