shield-checkup 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -0
  3. package/dist/cli.js +179 -0
  4. package/package.json +52 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SherDore
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.
package/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # shield-checkup
2
+
3
+ [![CI](https://github.com/sherdore/shield-checkup/actions/workflows/ci.yml/badge.svg)](https://github.com/sherdore/shield-checkup/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/shield-checkup.svg)](https://www.npmjs.com/package/shield-checkup)
5
+ [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
6
+
7
+ A one-command baseline security scan for any website. Point it at a URL and
8
+ find out in seconds whether it's leaking a `.git` folder, an `.env` file, a
9
+ database backup, or is just missing basic security headers — the kind of
10
+ mistakes that are trivial to find and embarrassing to have.
11
+
12
+ ```
13
+ npx shield-checkup example.com
14
+ ```
15
+
16
+ ## What it checks
17
+
18
+ 8 platform-agnostic, black-box checks — nothing that needs credentials or
19
+ special access, the same category of thing a real attacker's first automated
20
+ pass would try:
21
+
22
+ - Exposed `.git` directory
23
+ - Exposed `.env` file
24
+ - Exposed backup files (`.sql`, `.zip`, `.bak`, etc.)
25
+ - Directory listing enabled
26
+ - Missing security headers (HSTS, CSP, X-Frame-Options, and more)
27
+ - Server/framework version disclosure
28
+ - Exposed JS/CSS source maps
29
+ - Exposed `.DS_Store`
30
+
31
+ This is **8 of [SherDore Shield](https://shield.sherdore.com)'s 35+ checks** —
32
+ the rest are platform-specific packs (WordPress, Laravel, Node, Python,
33
+ Magento) plus malware detection and uptime/SSL monitoring, which need a real
34
+ scan against a verified site, not an anonymous CLI call. This tool runs the
35
+ scan **server-side** against Shield's own API — nothing about how a check
36
+ works ships in this package, so there's no detection logic to inspect or
37
+ copy here, just the client.
38
+
39
+ ## Sample
40
+
41
+ ```
42
+ example.com
43
+ 8 baseline checks run 0 critical 1 high 1 medium 0 low
44
+
45
+ ✗ Exposed .git directory (high)
46
+ 🔒 full detail + remediation — unlock with a free Shield account
47
+ ▲ Security response headers are missing (medium)
48
+ Not sent on the homepage: Content-Security-Policy (limits XSS / data exfiltration)
49
+ → Add the missing headers at the web server or application layer.
50
+
51
+ This is 8 of Shield's 35+ checks. Get the full scan — WordPress, Laravel,
52
+ Node, Python, and Magento packs, plus malware detection and monitoring:
53
+ https://shield.sherdore.com/register?utm_source=cli&utm_medium=shield-checkup&domain=example.com
54
+ ```
55
+
56
+ The lowest-severity finding(s) show full evidence and remediation right in
57
+ the terminal — a real, honest taste of what the full product gives you.
58
+ Everything more serious is flagged but locked, since walking through exactly
59
+ how it was found isn't something to hand out anonymously over the network.
60
+
61
+ ## Usage
62
+
63
+ ```bash
64
+ npx shield-checkup https://example.com
65
+
66
+ # machine-readable, e.g. in a pipeline
67
+ npx shield-checkup example.com --json
68
+ ```
69
+
70
+ ### Options
71
+
72
+ | Flag | Description |
73
+ | --- | --- |
74
+ | `--json` | Output the full result as JSON instead of a report |
75
+ | `--timeout <ms>` | Request timeout (default `15000`) |
76
+ | `--no-color` | Disable coloured output (also respects `NO_COLOR`) |
77
+ | `-h, --help` | Help |
78
+ | `-v, --version` | Version |
79
+
80
+ ### Exit codes
81
+
82
+ | Code | Meaning |
83
+ | --- | --- |
84
+ | `0` | No critical or high findings |
85
+ | `1` | At least one critical or high finding — fail the build |
86
+ | `2` | The request failed, you're rate-limited, or the URL is invalid |
87
+
88
+ ## How it works
89
+
90
+ The CLI is a thin client — it makes one API call to
91
+ `shield.sherdore.com/api/v1/public/quick-scan`, which runs the 8 checks above
92
+ against your URL and returns the result. Rate-limited to keep it fair for
93
+ everyone. Requires **Node 18.17+**.
94
+
95
+ ## Contributing
96
+
97
+ Issues and PRs on the CLI itself (output, flags, packaging) are welcome.
98
+ `npm run typecheck && npm run lint && npm run build` should pass. The scan
99
+ logic lives server-side and isn't part of this repo.
100
+
101
+ ---
102
+
103
+ Built and maintained by **[SherDore](https://www.sherdore.com)**. Shield
104
+ monitors websites for malware, vulnerabilities, uptime, and SSL/domain
105
+ expiry — [start a free scan](https://shield.sherdore.com) to see everything
106
+ this teaser doesn't show you.
package/dist/cli.js ADDED
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { parseArgs } from "util";
5
+
6
+ // src/version.ts
7
+ var VERSION = "0.1.0";
8
+ var USER_AGENT = `shield-checkup/${VERSION} (+https://github.com/sherdore/shield-checkup)`;
9
+
10
+ // src/api.ts
11
+ var API_URL = "https://shield.sherdore.com/api/v1/public/quick-scan";
12
+ var ApiError = class extends Error {
13
+ };
14
+ async function runQuickScan(url, timeoutMs) {
15
+ const controller = new AbortController();
16
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
17
+ let response;
18
+ try {
19
+ response = await fetch(API_URL, {
20
+ method: "POST",
21
+ headers: {
22
+ "Content-Type": "application/json",
23
+ Accept: "application/json",
24
+ "User-Agent": USER_AGENT
25
+ },
26
+ body: JSON.stringify({ url }),
27
+ signal: controller.signal
28
+ });
29
+ } catch (e) {
30
+ throw new ApiError(`could not reach shield.sherdore.com \u2014 ${e instanceof Error ? e.message : String(e)}`);
31
+ } finally {
32
+ clearTimeout(timeout);
33
+ }
34
+ if (response.status === 429) {
35
+ throw new ApiError("rate limited \u2014 try again in a minute");
36
+ }
37
+ if (response.status === 422) {
38
+ const body = await response.json().catch(() => null);
39
+ throw new ApiError(body?.errors?.url?.[0] ?? "that URL doesn't look valid");
40
+ }
41
+ if (!response.ok) {
42
+ throw new ApiError(`unexpected response from shield.sherdore.com (${response.status})`);
43
+ }
44
+ return await response.json();
45
+ }
46
+
47
+ // src/colors.ts
48
+ import { createColors } from "picocolors";
49
+ var current = createColors();
50
+ function configureColor(enabled) {
51
+ current = createColors(enabled);
52
+ }
53
+ function colors() {
54
+ return current;
55
+ }
56
+
57
+ // src/render.ts
58
+ function renderJson(report) {
59
+ return JSON.stringify(report, null, 2) + "\n";
60
+ }
61
+ function renderHuman(report) {
62
+ const c = colors();
63
+ const mark = {
64
+ critical: c.red("\u2717"),
65
+ high: c.red("\u2717"),
66
+ medium: c.yellow("\u25B2"),
67
+ low: c.yellow("\u25B2")
68
+ };
69
+ const out = [""];
70
+ out.push(` ${c.bold(report.domain)}`);
71
+ out.push(
72
+ ` ${c.dim(`${report.checks_run} baseline checks run`)} ${c.red(`${report.severity_counts.critical} critical`)} ${c.red(`${report.severity_counts.high} high`)} ${c.yellow(`${report.severity_counts.medium} medium`)} ${c.yellow(`${report.severity_counts.low} low`)}`
73
+ );
74
+ out.push("");
75
+ if (report.findings.length === 0) {
76
+ out.push(` ${c.green("\u2713")} Nothing found by the baseline checks \u2014 nice.`);
77
+ out.push("");
78
+ } else {
79
+ for (const finding of report.findings) {
80
+ out.push(` ${mark[finding.severity]} ${c.bold(finding.title)} ${c.dim(`(${finding.severity})`)}`);
81
+ if (finding.locked) {
82
+ out.push(` ${c.dim("\u{1F512} full detail + remediation \u2014 unlock with a free Shield account")}`);
83
+ } else {
84
+ if (finding.evidence) {
85
+ out.push(` ${c.dim(finding.evidence)}`);
86
+ }
87
+ if (finding.remediation) {
88
+ out.push(` ${c.dim(`\u2192 ${finding.remediation}`)}`);
89
+ }
90
+ }
91
+ }
92
+ out.push("");
93
+ }
94
+ out.push(` ${c.cyan("This is 8 of Shield's 35+ checks.")} Get the full scan \u2014 WordPress, Laravel,`);
95
+ out.push(` Node, Python, and Magento packs, plus malware detection and monitoring:`);
96
+ out.push(` ${c.underline(report.unlock_url)}`);
97
+ out.push("");
98
+ return out.join("\n") + "\n";
99
+ }
100
+
101
+ // src/cli.ts
102
+ var HELP = `
103
+ shield-checkup ${VERSION}
104
+ Free baseline security scan for any website \u2014 powered by SherDore Shield.
105
+
106
+ USAGE
107
+ npx shield-checkup <url> [options]
108
+
109
+ OPTIONS
110
+ --json Output machine-readable JSON instead of a report
111
+ --timeout <ms> Request timeout (default: 15000)
112
+ --no-color Disable coloured output
113
+ -h, --help Show this help
114
+ -v, --version Print the version
115
+
116
+ EXIT CODE
117
+ 0 no critical or high findings
118
+ 1 at least one critical or high finding (useful in CI)
119
+ 2 the request failed / bad arguments
120
+
121
+ This runs 8 of Shield's 35+ baseline checks (exposed .git/.env/backup
122
+ files, missing security headers, directory listing, version disclosure,
123
+ source maps). Full platform-aware scans, malware detection, and
124
+ uptime/SSL monitoring: https://shield.sherdore.com
125
+
126
+ Built by SherDore \u2014 https://www.sherdore.com
127
+ `;
128
+ function fail(message) {
129
+ process.stderr.write(`${colors().red("error:")} ${message}
130
+ `);
131
+ process.exit(2);
132
+ }
133
+ async function main() {
134
+ const { values, positionals } = parseArgs({
135
+ allowPositionals: true,
136
+ options: {
137
+ json: { type: "boolean", default: false },
138
+ timeout: { type: "string" },
139
+ "no-color": { type: "boolean", default: false },
140
+ help: { type: "boolean", short: "h", default: false },
141
+ version: { type: "boolean", short: "v", default: false }
142
+ }
143
+ });
144
+ configureColor(values["no-color"] ? false : void 0);
145
+ if (values.help) {
146
+ process.stdout.write(HELP);
147
+ return 0;
148
+ }
149
+ if (values.version) {
150
+ process.stdout.write(`${VERSION}
151
+ `);
152
+ return 0;
153
+ }
154
+ const target = positionals[0];
155
+ if (!target) {
156
+ process.stderr.write(HELP);
157
+ fail("a URL is required");
158
+ }
159
+ const timeoutMs = Number(values.timeout) > 0 ? Number(values.timeout) : 15e3;
160
+ if (!values.json) {
161
+ process.stderr.write(`${colors().dim(`Scanning ${target} \u2026`)}
162
+ `);
163
+ }
164
+ try {
165
+ const report = await runQuickScan(target, timeoutMs);
166
+ process.stdout.write(values.json ? renderJson(report) : renderHuman(report));
167
+ return report.severity_counts.critical > 0 || report.severity_counts.high > 0 ? 1 : 0;
168
+ } catch (e) {
169
+ if (e instanceof ApiError) {
170
+ fail(e.message);
171
+ }
172
+ throw e;
173
+ }
174
+ }
175
+ main().then((code) => process.exit(code)).catch((e) => {
176
+ process.stderr.write(`${e instanceof Error ? e.stack : String(e)}
177
+ `);
178
+ process.exit(2);
179
+ });
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "shield-checkup",
3
+ "version": "0.1.0",
4
+ "description": "Free baseline security scan for any website — powered by SherDore Shield.",
5
+ "keywords": [
6
+ "security",
7
+ "website",
8
+ "scanner",
9
+ "vulnerability",
10
+ "security-headers",
11
+ "cli"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "SherDore (https://www.sherdore.com)",
15
+ "homepage": "https://github.com/sherdore/shield-checkup#readme",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/sherdore/shield-checkup.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/sherdore/shield-checkup/issues"
22
+ },
23
+ "type": "module",
24
+ "bin": {
25
+ "shield-checkup": "dist/cli.js"
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18.17"
32
+ },
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "dev": "tsup --watch",
36
+ "typecheck": "tsc --noEmit",
37
+ "lint": "eslint .",
38
+ "start": "node dist/cli.js",
39
+ "prepublishOnly": "npm run build"
40
+ },
41
+ "dependencies": {
42
+ "picocolors": "^1.1.1"
43
+ },
44
+ "devDependencies": {
45
+ "@eslint/js": "^9.17.0",
46
+ "@types/node": "^22.10.5",
47
+ "eslint": "^9.17.0",
48
+ "tsup": "^8.3.5",
49
+ "typescript": "^5.7.3",
50
+ "typescript-eslint": "^8.19.1"
51
+ }
52
+ }