Skip to content
Closed
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
57 changes: 57 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Bug report
description: Something doesn't work as expected
labels: [bug]
body:
- type: markdown
attributes:
value: |
Thanks for reporting! Please **remove your Wi-Fi passphrase and other private data** from anything you paste.
Security problems: use private vulnerability reporting instead (see SECURITY.md).
- type: textarea
id: what
attributes:
label: What happened?
description: What did you do, what did you expect, and what happened instead?
validations:
required: true
- type: input
id: version
attributes:
label: Fiero version
description: Output of `fiero-hotspot version`
placeholder: fiero-hotspot v1.5.0
validations:
required: true
- type: input
id: distro
attributes:
label: Distribution and kernel
placeholder: Arch Linux, 6.x.y-arch1-1
validations:
required: true
- type: textarea
id: card
attributes:
label: Wi-Fi card
description: Output of `lspci -k | grep -A3 -i network` (or `lsusb` for USB adapters)
render: text
validations:
required: true
- type: textarea
id: iwlist
attributes:
label: Interface combinations
description: The "valid interface combinations" part of `iw list`
render: text
- type: textarea
id: journal
attributes:
label: Service log
description: Output of `journalctl -u fiero-hotspot.service -b --no-pager`
render: text
- type: textarea
id: status
attributes:
label: Status
description: Output of `sudo fiero-hotspot status`
render: text
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Security vulnerability
url: https://github.com/Fiero1186/Fiero-Hotspot/security/advisories/new
about: Please report security problems privately.
50 changes: 50 additions & 0 deletions .github/ISSUE_TEMPLATE/hardware_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Hardware report
description: Tell us whether Fiero works on your laptop / Wi-Fi card
labels: [hardware]
body:
- type: markdown
attributes:
value: |
These reports build the hardware compatibility list. Both "it works" and "it doesn't" are useful!
- type: dropdown
id: result
attributes:
label: Does it work?
options:
- "Yes - hotspot starts and clients can connect"
- "Partly (explain below)"
- "No"
validations:
required: true
- type: input
id: laptop
attributes:
label: Laptop model
placeholder: ASUS Vivobook 16 (X1605ZA)
validations:
required: true
- type: textarea
id: card
attributes:
label: Wi-Fi card
description: Output of `lspci -k | grep -A3 -i network` (or `lsusb` for USB adapters)
render: text
validations:
required: true
- type: input
id: distro
attributes:
label: Distribution and kernel
placeholder: Fedora 44, 6.x.y
validations:
required: true
- type: input
id: bands
attributes:
label: Upstream band / channel you tested
placeholder: 2.4 GHz channel 6
- type: textarea
id: notes
attributes:
label: Notes
description: Anything special - e.g. 5 GHz refused, needed --no-virt, USB-C charger not detected
9 changes: 9 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
# Let new releases age a week before adopting them.
cooldown:
default-days: 7
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: CI

on:
push:
branches: [main]
pull_request:

# Least privilege: jobs only get what they ask for below.
permissions: {}

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: ShellCheck, systemd-analyze, bats
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

# Same container and entry point as `tests/run.sh` on a laptop.
- name: Run test suite
run: tests/run.sh
54 changes: 54 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Contributing to Fiero Hotspot

Thanks for helping! Bug reports, hardware reports and pull requests are all welcome.

## Reporting a problem

Open an issue with the **Bug report** template. Because Fiero depends so much on the Wi-Fi card, please include the hardware details it asks for (`lspci -k`, `iw list`, the service journal). Remove your passphrase and anything else private from logs first.

Did Fiero work (or not) on your laptop? The **Hardware report** template helps grow the compatibility list.

Security problems: please use [private vulnerability reporting](https://github.com/Fiero1186/Fiero-Hotspot/security/advisories/new) instead of a public issue (see [SECURITY.md](SECURITY.md)).

## Making a change

1. Fork the repository and create a branch per topic (`fix/...`, `feat/...`, `docs/...`).
2. Keep pull requests small: one topic each, easy to review.
3. Run the automated tests (below) and, for anything that touches the hotspot lifecycle, the on-hardware harness.
4. Describe *what* changed and *why* in the pull request, and add an entry under `[Unreleased]` in [CHANGELOG.md](CHANGELOG.md).

Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/): `fix(daemon): ...`, `feat(cli): ...`, `docs: ...`, `test: ...`.

## Tests

### Automated (no Wi-Fi hardware needed)

```bash
tests/run.sh # everything
tests/run.sh daemon # one suite: daemon, prompt or install
```

This needs Docker. It builds a small Debian image and runs, inside a throwaway container:

- **ShellCheck** on every script
- **`systemd-analyze security`**: the service's exposure score must stay at or below 5.0
- **bats** suites that run the *real* `fiero-hotspot.sh`, `fiero-prompt.sh`, `install.sh` and `uninstall.sh` against test doubles of `create_ap`, `iw`, `notify-send`, `systemctl` and a fake charger (`tests/mocks/`)

The same command runs in CI on every pull request. The suites replace system commands, so they refuse to run outside the container.

On Windows, run it from Git Bash with Docker Desktop started. Make sure your clone uses LF line endings (the repository's `.gitattributes` takes care of this for fresh clones).

### On real hardware

```bash
sudo ./install.sh
sudo ./test_harness.sh
```

Exit code `0` means everything passed **including a real start/stop of the hotspot**. Exit code `2` means the start/stop was skipped (upstream Wi-Fi on an unsupported channel); connect to a supported channel and run it again before reporting a pass.

## Style

- Bash, 4-space indentation (tabs in `test_harness.sh`), see [.editorconfig](.editorconfig).
- Root scripts set a fixed `PATH` and must never trust the caller's environment.
- Only ever touch Fiero's own `create_ap` instance and files; other hotspots on the system must be left alone.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

![Version](https://img.shields.io/badge/version-v1.5.0-blue)
![License](https://img.shields.io/badge/license-GPL--3.0-green)
[![CI](https://github.com/Fiero1186/Fiero-Hotspot/actions/workflows/ci.yml/badge.svg)](https://github.com/Fiero1186/Fiero-Hotspot/actions/workflows/ci.yml)

## Overview

Expand Down Expand Up @@ -210,7 +211,15 @@ Removes all installed files, stops/disables the service, and reloads udev/system

## Diagnostics & Test Suite

### Running
### Automated Tests (no Wi-Fi hardware needed)

```bash
tests/run.sh
```

Runs in a throwaway Docker container, exactly like CI does on every pull request: ShellCheck on every script, a `systemd-analyze security` check (exposure must stay at or below 5.0), and bats suites that drive the real `fiero-hotspot.sh`, `fiero-prompt.sh`, `install.sh` and `uninstall.sh` against test doubles of `create_ap`, `iw`, `notify-send` and a fake charger. See [CONTRIBUTING.md](CONTRIBUTING.md).

### Running the On-Hardware Harness

```bash
sudo ./test_harness.sh
Expand Down
12 changes: 12 additions & 0 deletions tests/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Throwaway test environment for tests/run.sh and CI. The suites replace
# system commands with test doubles, so they only run where
# FIERO_TEST_CONTAINER=1 is set.
FROM debian:trixie-slim

RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
bash bats ca-certificates iw perl procps shellcheck sudo systemd util-linux \
&& rm -rf /var/lib/apt/lists/*

ENV FIERO_TEST_CONTAINER=1
WORKDIR /src
143 changes: 143 additions & 0 deletions tests/daemon.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#!/usr/bin/env bats
# End-to-end tests of the real fiero-hotspot.sh against create_ap/iw doubles.

load helpers

setup_file() {
require_container
install_mocks
}

setup() {
reset_state
write_config
}

teardown() {
stop_daemon >/dev/null 2>&1 || true
kill_doubles
}

@test "start brings the AP up and records our own instance" {
start_daemon
pid=$(cat /run/fiero-hotspot/create_ap.pid)
tr '\0' ' ' <"/proc/$pid/cmdline" | grep -q create_ap
[ "$(cat /run/fiero-hotspot/ap_iface)" = ap0 ]
[[ "$(cat /run/fiero-hotspot/confdir)" == /tmp/create_ap.wlan0.conf.* ]]
}

@test "status and clients report the running hotspot" {
start_daemon
run bash "$FH" status
[ "$status" -eq 0 ]
[[ "$output" == *"AP iface : ap0"* ]]
[[ "$output" == *"Clients : 1"* ]]

run bash "$FH" clients
[ "$status" -eq 0 ]
[[ "$output" == *"aa:bb:cc:dd:ee:ff -45 dBm 192.168.12.10 phone"* ]]
}

@test "stop waits for create_ap's own cleanup and leaves nothing behind" {
echo 3 >"$M/stop-delay"
start_daemon
t0=$(date +%s)
stop_daemon
[ $(($(date +%s) - t0)) -ge 3 ]
grep -q clean-exit "$M/events"
grep -q "stopped on request" "$M/start.log"
refute grep -q unexpectedly "$M/start.log"
refute sh -c 'ls -d /tmp/create_ap.* 2>/dev/null'
refute pgrep -f "^hostapd"
[ ! -e /run/fiero-hotspot/create_ap.pid ]
}

@test "passphrase never appears on a command line" {
start_daemon
refute grep -q "s3cret-pass" "$M/argv"
grep -q "^--config /run/fiero-hotspot/create_ap.conf" "$M/argv"
grep -qx "PASSPHRASE=s3cret-pass" "$M/last-config"
grep -qx "CHANNEL=6" "$M/last-config"
[ "$(cat "$M/conf-mode")" = 600 ]
[ ! -e /run/fiero-hotspot/create_ap.conf ]
refute sh -c 'ps -eo args | grep -v grep | grep -q "s3cret-pass"'
}

@test "a passphrase create_ap's config parser would mangle falls back to argv with a warning" {
write_config
sed -i "s/^PASSWORD=.*/PASSWORD='back\\\\slash-pass'/" /etc/fiero-hotspot.conf
start_daemon
grep -q "Passing them on the command line" "$M/start.log"
grep -qF 'back\slash-pass' "$M/argv"
}

@test "another tool's create_ap on the interface is left alone" {
bash -c "exec -a 'create_ap wlan0 wlan0 Other pass' sleep 1000" 3>&- &
foreign=$!
run bash "$FH" start
[ "$status" -eq 0 ]
[[ "$output" == *"Not touching it"* ]]
kill -0 "$foreign"
kill "$foreign"
}

@test "--no-virt fallback (AP on the physical card) is detected and never deleted" {
touch "$M/no-virt"
start_daemon
[ "$(cat /run/fiero-hotspot/ap_iface)" = wlan0 ]
stop_daemon
refute grep -q "del" "$M/events"
}

@test "AP follows the upstream to a new channel" {
start_daemon
first=$(cat /run/fiero-hotspot/create_ap.pid)
echo 11 >"$M/channel"
wait_for_log "restarted on channel 11" "$M/start.log" 15
grep -q "clean-exit $first" "$M/events"
[ "$(cat /run/fiero-hotspot/create_ap.pid)" != "$first" ]
}

@test "hung create_ap is forced down; only its own hostapd is killed" {
touch "$M/ignore-usr1"
bash -c "exec -a 'hostapd /tmp/other/hostapd.conf' sleep 1000" 3>&- &
other=$!
start_daemon
stop_daemon
grep -q "forcing it" "$M/stop.log"
refute pgrep -f "hostapd /tmp/create_ap"
kill -0 "$other"
kill "$other"
}

@test "a failing 'iw dev link' counts as upstream lost and shuts down" {
start_daemon
touch "$M/link-fails"
wait_for_log "disconnected permanently" "$M/start.log" 25
}

@test "unsupported upstream channel aborts without starting create_ap" {
echo 36 >"$M/channel"
run bash "$FH" start
[ "$status" -eq 0 ]
[[ "$output" == *"Channel 36 is not supported"* ]]
[ ! -s "$M/argv" ]
}

@test "help and version need no root, config or create_ap" {
rm -f /etc/fiero-hotspot.conf
run su nobody -s /bin/bash -c "bash $FH version"
[ "$status" -eq 0 ]
[[ "$output" == "fiero-hotspot v"* ]]
run su nobody -s /bin/bash -c "bash $FH help"
[ "$status" -eq 0 ]
[[ "$output" == *"Usage: fiero-hotspot"* ]]
}

@test "root-only commands explain themselves to normal users" {
for cmd in start stop status clients mode; do
run su nobody -s /bin/bash -c "bash $FH $cmd"
[ "$status" -eq 1 ]
[[ "$output" == *"requires root"* ]]
done
}
Loading