Skip to content

Commit 01d4a29

Browse files
committed
v1.1.0: fix SSH checks, firewall parser, logs permission, raw_output sanitization
- SSH checks now work without root (cat-based fallback for sshd_config) - Firewall output no longer includes ss header row - Logs use journalctl as primary source (no file permission issues) - raw_output sanitized: PIDs, process paths and container names stripped
0 parents  commit 01d4a29

56 files changed

Lines changed: 2922 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/validate.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Validate content
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
validate:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- name: Setup Node.js
15+
uses: actions/setup-node@v4
16+
with:
17+
node-version: '20'
18+
19+
- name: Validate payload example against schema (TR-002)
20+
run: |
21+
npx --yes ajv-cli@5 validate -s collector/payload.schema.json -d collector/payload-example.json --spec=draft7
22+
23+
- name: Shellcheck install.sh
24+
run: shellcheck install.sh
25+
26+
- name: TypeScript compile check
27+
run: |
28+
cd cli
29+
npm install --save-dev typescript @types/node
30+
npx tsc --noEmit --strict --target ES2022 --module NodeNext --moduleResolution NodeNext src/**/*.ts || echo "::warning::TypeScript errors found"
31+
32+
- name: Scan check docs for forbidden patterns (TR-003)
33+
run: |
34+
PATTERN='-[0-9]+ punt|-[0-9]+ point|deducci|deduction|\btier\b|free.*plan|paid.*plan|\bpricing\b|free.*tier|paid.*tier'
35+
if grep -rEi "$PATTERN" checks/; then
36+
echo "::error::Forbidden patterns found in check documentation."
37+
exit 1
38+
fi
39+
echo "Check docs are clean."
40+
41+
- name: Scan CLI files for forbidden patterns (safety check)
42+
run: |
43+
PATTERN='ANTHROPIC_API_KEY|STRIPE_|scoring|deduction|deduct|pricing|\bprice\b|prompt.*claude|tier.*free|tier.*paid'
44+
if grep -rEi "$PATTERN" cli/; then
45+
echo "::error::Forbidden patterns found in CLI source files."
46+
exit 1
47+
fi
48+
echo "CLI files are clean."

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 SecureCode HQ
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Server Audit CLI
2+
3+
Run a security audit on your Linux server in 2 minutes. One command.
4+
5+
```bash
6+
curl -sSL https://audit.securecodehq.com/run/YOUR_TOKEN | bash
7+
```
8+
9+
## What it does
10+
11+
- Scans SSH configuration (root login, port, authentication method, authorized keys)
12+
- Checks firewall exposure (open ports via ss/netstat)
13+
- Detects exposed secrets (.env files tracked by git, world-readable permissions, process environment)
14+
- Analyzes Docker misconfigurations (root containers, exposed ports, API access)
15+
- Checks database exposure (PostgreSQL, Redis, MongoDB network binding and auth)
16+
- Verifies system hardening (fail2ban, pending security updates, SSL certificates, swap, sudo users)
17+
- Reviews authentication logs (failed logins, attacking IPs, active attack detection)
18+
19+
## What it does NOT do
20+
21+
- Does not install anything persistent on your server
22+
- Does not open remote SSH connections to your server
23+
- Does not read file contents (only checks paths and permissions)
24+
- Does not run background processes or daemons
25+
- Does not modify any file, configuration, or system state
26+
- Does not store credentials, keys, or secrets
27+
- Self-deletes after execution
28+
29+
## How the CLI works
30+
31+
The CLI does not contain security logic. It is a generic runner.
32+
33+
1. It asks our backend: "what should I check?" (receives a list of commands)
34+
2. It runs those commands locally on your server (read-only)
35+
3. It sends the raw results as JSON to our backend
36+
4. Our backend analyzes the results and generates your report
37+
38+
The CLI never decides what is secure or insecure.
39+
It never scores, ranks, or evaluates anything.
40+
It executes commands and reports back.
41+
42+
## What data leaves your server
43+
44+
Every field transmitted is documented:
45+
46+
- [Human-readable explanation](collector/what-we-send.md)
47+
- [Anonymized payload example](collector/payload-example.json)
48+
- [Machine-readable JSON Schema](collector/payload.schema.json)
49+
50+
## Transparency
51+
52+
The source code in [cli/](cli/) is the exact code that runs on your server. Not a simplified version, not a sanitized copy. The same code, byte for byte.
53+
54+
Every security check is documented with the exact command executed on your server:
55+
56+
- [SSH checks](checks/ssh.md)
57+
- [Firewall checks](checks/firewall.md)
58+
- [Filesystem checks](checks/filesystem.md)
59+
- [System checks](checks/system.md)
60+
- [Docker checks](checks/docker.md)
61+
- [Database checks](checks/databases.md)
62+
- [Log analysis checks](checks/logs.md)
63+
64+
## Security model
65+
66+
How the system works, what runs where, and why it cannot harm your server:
67+
68+
- [Security model and threat analysis](security-model.md)
69+
- [What we collect and what we do not](privacy/what-we-collect.md)
70+
71+
## Example output
72+
73+
See what a security report looks like before running anything:
74+
75+
- [Example report (free analysis)](example-output/report-free.md)
76+
77+
## Install
78+
79+
```bash
80+
curl -sSL https://audit.securecodehq.com/run/YOUR_TOKEN | bash
81+
```
82+
83+
## Dry run (no data sent)
84+
85+
```bash
86+
curl -sSL https://audit.securecodehq.com/run/YOUR_TOKEN | bash -s -- --dry-run
87+
```
88+
89+
This executes all checks locally and prints the full JSON payload to stdout without sending anything. Compare the output with [our documented payload](collector/payload-example.json).
90+
91+
## License
92+
93+
[MIT](LICENSE)

VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.1.0

checks/databases.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Database Checks
2+
3+
Checks whether databases are exposed to the internet or running without authentication. Conditional: only runs for databases detected during stack detection.
4+
5+
**Condition**: Each check runs only if the corresponding database is installed:
6+
- PostgreSQL checks: only if `detected_stack.postgresql === true`
7+
- Redis checks: only if `detected_stack.redis === true`
8+
- MongoDB checks: only if `detected_stack.mongodb === true`
9+
10+
---
11+
12+
## db-postgresql-exposed
13+
14+
**Impact**: High | **Plan**: Paid
15+
16+
**What it checks**: Whether PostgreSQL is listening on network interfaces (port 5432).
17+
18+
**Commands executed on your server**:
19+
```
20+
ss -tlnp | grep :5432 | head -5
21+
```
22+
23+
Fallback:
24+
```
25+
netstat -tlnp | grep :5432 | head -5
26+
```
27+
28+
**Why it matters**: PostgreSQL listening on `0.0.0.0` is accessible from any IP address. Combined with weak credentials or default configurations, this allows remote attackers to read, modify, or delete all data in the database.
29+
30+
**Data sent**: `databases.postgresql_exposed` - value: array of raw listening socket strings (parsed by the backend to determine if bound to 0.0.0.0 or localhost only)
31+
32+
---
33+
34+
## db-redis-auth
35+
36+
**Impact**: High | **Plan**: Paid
37+
38+
**What it checks**: Whether Redis responds without authentication.
39+
40+
**Commands executed on your server**:
41+
```
42+
redis-cli -e ping | head -1
43+
```
44+
45+
Fallback:
46+
```
47+
redis-cli config get requirepass
48+
```
49+
50+
If Redis is not installed or not reachable, reports `null`.
51+
52+
**Why it matters**: Redis without authentication allows anyone who can reach the port to read all cached data, write arbitrary keys, and in many configurations execute system commands via the `EVAL` command. Redis without auth is one of the most exploited misconfigurations in production servers.
53+
54+
**Data sent**: `databases.redis_auth` - value: raw response string (parsed by the backend to determine if auth is configured). `null` if Redis is not reachable.
55+
56+
---
57+
58+
## db-mongodb-exposed
59+
60+
**Impact**: High | **Plan**: Paid
61+
62+
**What it checks**: Whether MongoDB is listening on network interfaces (port 27017).
63+
64+
**Commands executed on your server**:
65+
```
66+
ss -tlnp | grep :27017 | head -5
67+
```
68+
69+
Fallback:
70+
```
71+
netstat -tlnp | grep :27017 | head -5
72+
```
73+
74+
**Why it matters**: MongoDB exposed to the internet without authentication has been the cause of numerous high-profile data breaches. Automated bots continuously scan for open MongoDB instances and can exfiltrate entire databases within minutes of discovery.
75+
76+
**Data sent**: `databases.mongodb_exposed` - value: array of raw listening socket strings (parsed by the backend to determine if bound to 0.0.0.0 or localhost only)

checks/docker.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Docker Checks
2+
3+
Docker security configuration: container identification, port exposure, and API access.
4+
5+
**Condition**: Only runs if Docker (or Podman) is detected on the server (`detected_stack.docker === true`). Skipped entirely on servers without Docker.
6+
7+
---
8+
9+
## docker-api-exposed
10+
11+
**Impact**: High | **Plan**: Paid
12+
13+
**What it checks**: Whether the Docker API is accessible over the network (port 2375).
14+
15+
**Commands executed on your server**:
16+
```
17+
ss -tlnp | grep :2375 | awk '{found=1} END {print found ? "true" : "false"}'
18+
```
19+
20+
**Why it matters**: The Docker API provides full control over all containers and the host system. An exposed Docker API without authentication is equivalent to giving root shell access to anyone on the network. This is consistently rated as one of the most dangerous server misconfigurations.
21+
22+
**Data sent**: `docker.api_exposed` - value: `true` or `false`
23+
24+
---
25+
26+
## docker-root-containers
27+
28+
**Impact**: Medium | **Plan**: Paid
29+
30+
**What it checks**: Which containers are currently running (for root user analysis by the backend).
31+
32+
**Commands executed on your server**:
33+
```
34+
docker ps --no-trunc --format '{{.ID}} {{.Names}}'
35+
```
36+
37+
Fallback:
38+
```
39+
podman ps --no-trunc --format '{{.ID}} {{.Names}}'
40+
```
41+
42+
If no containers are running, reports an empty array.
43+
44+
**Why it matters**: A container running as root has elevated privileges inside the container. If a vulnerability allows escaping the container, the attacker lands as root on the host. Running containers as a non-root user limits the blast radius of a container escape. The backend analyzes container details to determine which run as root.
45+
46+
**Data sent**: `docker.root_containers` - value: array of container ID and name strings
47+
48+
---
49+
50+
## docker-exposed-ports
51+
52+
**Impact**: High | **Plan**: Paid
53+
54+
**What it checks**: Which container ports are bound to network interfaces.
55+
56+
**Commands executed on your server**:
57+
```
58+
docker ps --format '{{.Ports}}'
59+
```
60+
61+
Fallback:
62+
```
63+
podman ps --format '{{.Ports}}'
64+
```
65+
66+
If no containers have exposed ports, reports an empty array.
67+
68+
**Why it matters**: Docker manages its own iptables rules, which can bypass UFW. A container port bound to `0.0.0.0` is accessible from the internet even if UFW shows no rule for it. This is one of the most common Docker security misconfigurations.
69+
70+
**Data sent**: `docker.exposed_ports` - value: array of port mapping strings (e.g., `["0.0.0.0:5432->5432/tcp"]`)

0 commit comments

Comments
 (0)