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.
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/cli.js +179 -0
- 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
|
+
[](https://github.com/sherdore/shield-checkup/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/shield-checkup)
|
|
5
|
+
[](./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
|
+
}
|