@shieldly/cli 1.0.0 → 1.0.2
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/README.md +31 -3
- package/dist/cli.cjs +40 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,10 +10,28 @@ any terminal, shell script, or CI/CD pipeline. Powered by
|
|
|
10
10
|
npm install -g @shieldly/cli
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
## Try it free — no account needed
|
|
14
|
+
|
|
15
|
+
`analyze-iam` runs in demo mode without an API key (5 lifetime analyses):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
shieldly analyze-iam policy.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
To unlock `analyze-cf`, history, and 20 free analyses per day,
|
|
22
|
+
[create a free account](https://www.shieldly.io/app/api) and set your key:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
export SHIELDLY_API_KEY=sk_live_...
|
|
26
|
+
shieldly analyze-cf template.yaml
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Upgrade to a paid plan for higher daily limits, team access, and priority AI.
|
|
30
|
+
|
|
13
31
|
## Quick start
|
|
14
32
|
|
|
15
33
|
```bash
|
|
16
|
-
#
|
|
34
|
+
# Set your API key (get one free at https://www.shieldly.io/app/api)
|
|
17
35
|
export SHIELDLY_API_KEY=sk_live_...
|
|
18
36
|
|
|
19
37
|
# Analyze an IAM policy
|
|
@@ -26,8 +44,18 @@ shieldly analyze-cf template.yaml
|
|
|
26
44
|
shieldly api-keys list
|
|
27
45
|
```
|
|
28
46
|
|
|
29
|
-
|
|
30
|
-
|
|
47
|
+
## Free tier
|
|
48
|
+
|
|
49
|
+
| Mode | Limit | Requires |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| Demo (no key) | 5 analyses total | No account |
|
|
52
|
+
| Free account | 20 units/day | Free sign-up |
|
|
53
|
+
| Builder | 150 units/day | Paid plan |
|
|
54
|
+
| Pro | 300 units/day | Paid plan |
|
|
55
|
+
| Team | 600 units/day | Paid plan |
|
|
56
|
+
|
|
57
|
+
Analysis units depend on input size — a small IAM policy costs 1 unit;
|
|
58
|
+
large CloudFormation templates cost more. [Compare plans →](https://www.shieldly.io/#pricing)
|
|
31
59
|
|
|
32
60
|
## Commands
|
|
33
61
|
|
package/dist/cli.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
`),c;continue}if(
|
|
4
|
-
`),{...
|
|
5
|
-
`),new Error(
|
|
6
|
-
`),new Error("Analysis timed out after polling")}async function v(e,o){let t=
|
|
7
|
-
${l}[${
|
|
2
|
+
var B=Object.create;var C=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var Z=Object.getPrototypeOf,Q=Object.prototype.hasOwnProperty;var X=(e,o,t,n)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of V(o))!Q.call(e,s)&&s!==t&&C(e,s,{get:()=>o[s],enumerable:!(n=q(o,s))||n.enumerable});return e};var z=(e,o,t)=>(t=e!=null?B(Z(e)):{},X(o||!e||!e.__esModule?C(t,"default",{value:e,enumerable:!0}):t,e));var g=require("node:fs"),j=require("node:os"),D=require("node:path"),w="Shieldly-CLI/1.0.2",O=(0,D.join)((0,j.homedir)(),".shieldly","config.json"),ee="https://api.shieldly.io",oe="https://www.shieldly.io";function I(e){if(e)return e;if(process.env.SHIELDLY_API_KEY)return process.env.SHIELDLY_API_KEY;if((0,g.existsSync)(O))try{let o=JSON.parse((0,g.readFileSync)(O,"utf8"));if(o.apiKey)return o.apiKey}catch{}return null}function L(){return(process.env.SHIELDLY_API_URL||ee).replace(/\/$/,"")}function se(){return(process.env.SHIELDLY_WEB_URL||oe).replace(/\/$/,"")}async function R(e,o){let t=se(),n=await fetch(`${t}${e}`,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":w},body:JSON.stringify(o)});if(!n.ok){let s=await n.json().catch(()=>({}));throw new Error(s.error||`API error ${n.status}`)}return n.json()}async function k(e,o,t){let n=L(),s=await fetch(`${n}${e}`,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":w,Authorization:`Bearer ${t}`},body:JSON.stringify(o)});if(s.status===202){let r=await s.json().catch(()=>({}));if(r.jobId)return te(r.jobId,t)}if(!s.ok){let r=await s.json().catch(()=>({}));throw new Error(r.error||`API error ${s.status}`)}return s.json()}async function te(e,o){let t=[2e3,3e3,5e3],n=Date.now(),s=0;for(let r=0;r<180;r++){let a=t[Math.min(r,t.length-1)];await new Promise(c=>setTimeout(c,a));let l=Math.round((Date.now()-n)/1e3);process.stderr.write(`\rAI-Powered analysis in progress\u2026 (${l}s)`);let i;try{i=await v(`/v1/jobs/${encodeURIComponent(e)}`,o),s=0}catch(c){if(++s>=3)throw process.stderr.write(`
|
|
3
|
+
`),c;continue}if(i.status==="complete")return process.stderr.write(`
|
|
4
|
+
`),{...i.result,unitInfo:i.unitInfo};if(i.status==="failed")throw process.stderr.write(`
|
|
5
|
+
`),new Error(i.error||"Analysis failed")}throw process.stderr.write(`
|
|
6
|
+
`),new Error("Analysis timed out after polling")}async function v(e,o){let t=L(),n=await fetch(`${t}${e}`,{headers:{Authorization:`Bearer ${o}`,"User-Agent":w}});if(!n.ok){let s=await n.json().catch(()=>({}));throw new Error(s.error||`API error ${n.status}`)}return n.json()}async function M(e,o,t){let n=L(),s=await fetch(`${n}${e}`,{method:"DELETE",headers:{"Content-Type":"application/json","User-Agent":w,Authorization:`Bearer ${t}`},body:JSON.stringify(o)});if(!s.ok){let r=await s.json().catch(()=>({}));throw new Error(r.error||`API error ${s.status}`)}return s.json()}function P(e){return(0,g.existsSync)(e)||(console.error(`Error: File not found: ${e}`),process.exit(1)),(0,g.readFileSync)(e,"utf8")}var K={CRITICAL:"\x1B[31m",HIGH:"\x1B[33m",MEDIUM:"\x1B[36m",LOW:"\x1B[32m",INFO:"\x1B[90m"},y="\x1B[0m",A="\x1B[1m",_="\x1B[2m",Y="\x1B[36m";function x(e,o){if(o==="json"){console.log(JSON.stringify(e,null,2));return}let{score:t,riskLevel:n,findings:s=[],cached:r}=e;if(console.log(""),console.log(`${A}AI-Powered Security Analysis \u2014 Shieldly${y}`),console.log(`${_}${"\u2500".repeat(50)}${y}`),console.log(` ${A}Security Score:${y} ${ne(t)}${t}/100${y}`),console.log(` ${A}Risk Level:${y} ${le(n)}${n}${y}`),r&&console.log(` ${_}(cached result)${y}`),console.log(""),s.length===0)console.log(` ${Y}[PASS] No findings${y}`);else{console.log(`${A}Findings (${s.length}):${y}`);for(let a of s){let l=K[(a.severity||"").toUpperCase()]||"";console.log(`
|
|
7
|
+
${l}[${a.severity}]${y} ${A}${a.title}${y}`),a.description&&console.log(` ${_}${a.description}${y}`),a.remediation&&console.log(` ${Y}Fix:${y} ${a.remediation}`)}}console.log("")}function ne(e){return e>=80?"\x1B[32m":e>=50?"\x1B[33m":"\x1B[31m"}function le(e){let o=(e||"").toUpperCase();return K[o]||""}var re=`
|
|
8
8
|
Usage: shieldly analyze-cf <template-file> [options]
|
|
9
9
|
|
|
10
10
|
Analyze a CloudFormation template for security issues using AI.
|
|
@@ -20,8 +20,12 @@ Options:
|
|
|
20
20
|
Examples:
|
|
21
21
|
shieldly analyze-cf template.json
|
|
22
22
|
shieldly analyze-cf template.yaml --format json
|
|
23
|
-
`;async function H(e){if(e.includes("-h")||e.includes("--help")||e.length===0){console.log(
|
|
24
|
-
|
|
23
|
+
`;async function H(e){if(e.includes("-h")||e.includes("--help")||e.length===0){console.log(re);return}let o=e.find(l=>!l.startsWith("--"));o||(console.error("Error: template-file argument is required"),process.exit(1));let t=e.indexOf("--format"),n=t!==-1?e[t+1]:"table",s=e.indexOf("--api-key"),r=I(s!==-1?e[s+1]:null);r||(console.error(`CloudFormation analysis requires an API key.
|
|
24
|
+
|
|
25
|
+
Free account: 20 analyses/day \u2014 https://www.shieldly.io/app/api
|
|
26
|
+
|
|
27
|
+
Set SHIELDLY_API_KEY or use --api-key once you have your key.
|
|
28
|
+
Tip: shieldly analyze-iam runs without a key (5 free demo analyses).`),process.exit(1));let a=P(o);n!=="json"&&console.log(`Analyzing ${o}\u2026`);try{let l=await k("/v1/analyze/cf",{template:a},r);x(l,n);let i=(l.findings||[]).filter(h=>h.severity==="CRITICAL").length,c=(l.findings||[]).filter(h=>h.severity==="HIGH").length;(i>0||c>0)&&process.exit(1)}catch(l){console.error(`Error: ${l.message}`),process.exit(1)}}var ie=`
|
|
25
29
|
Usage: shieldly analyze-iam <policy-file> [options]
|
|
26
30
|
|
|
27
31
|
Analyze an AWS IAM policy for security issues using AI.
|
|
@@ -41,7 +45,7 @@ Examples:
|
|
|
41
45
|
shieldly analyze-iam policy.json
|
|
42
46
|
shieldly analyze-iam policy.json --type resource --format json
|
|
43
47
|
SHIELDLY_API_KEY=sk_... shieldly analyze-iam policy.json
|
|
44
|
-
`;async function F(e){if(e.includes("-h")||e.includes("--help")||e.length===0){console.log(
|
|
48
|
+
`;async function F(e){if(e.includes("-h")||e.includes("--help")||e.length===0){console.log(ie);return}let o=e.find(u=>!u.startsWith("--"));o||(console.error("Error: policy-file argument is required"),process.exit(1));let t=e.indexOf("--type"),n=t!==-1?e[t+1]:"identity",s=e.indexOf("--format"),r=s!==-1?e[s+1]:"table",a=e.indexOf("--api-key"),l=I(a!==-1?e[a+1]:null),i=P(o);try{JSON.parse(i)}catch{console.error("Error: policy-file must be valid JSON"),process.exit(1)}let c=i.trim(),h=n==="identity"||n==="iam_identity"?"iam_identity":n==="cross_account"?"cross_account":"iam_identity";r!=="json"&&(console.log(`Analyzing ${o} (type: ${h})\u2026`),l||console.log("Demo mode (5 lifetime analyses). Get a free key at https://www.shieldly.io/app/api"));try{let u=l?await k("/v1/analyze/iam",{policy:c,policyType:h},l):await R("/api/demo/analyze-iam",{policy:c,policyType:h});x(u,r);let d=(u.findings||[]).filter(S=>S.severity==="CRITICAL").length,J=(u.findings||[]).filter(S=>S.severity==="HIGH").length;(d>0||J>0)&&process.exit(1)}catch(u){console.error(`Error: ${u.message}`),process.exit(1)}}var U=`
|
|
45
49
|
Usage: shieldly api-keys <subcommand> [options]
|
|
46
50
|
|
|
47
51
|
Manage Shieldly API keys.
|
|
@@ -61,9 +65,12 @@ Examples:
|
|
|
61
65
|
shieldly api-keys list
|
|
62
66
|
shieldly api-keys create --label "CI/CD Key" --scopes iam,cf
|
|
63
67
|
shieldly api-keys revoke key_abc123
|
|
64
|
-
`,
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
`,f="\x1B[1m",N="\x1B[2m",T="\x1B[36m",m="\x1B[0m";function ae(e){return e?new Date(e).toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"}):"\u2014"}async function W(e){if(e.includes("-h")||e.includes("--help")||e.length===0){console.log(U);return}let o=e[0],t=e.slice(1),n=t.indexOf("--api-key"),s=I(n!==-1?t[n+1]:null),r=t.indexOf("--format"),a=r!==-1?t[r+1]:"table";if(s||(console.error(`API key management requires an API key to authenticate.
|
|
69
|
+
|
|
70
|
+
Create your free account: https://www.shieldly.io/app/api
|
|
71
|
+
|
|
72
|
+
Set SHIELDLY_API_KEY or use --api-key once you have your key.`),process.exit(1)),o==="list"){try{let i=(await v("/v1/api-keys",s)).keys||[];if(a==="json"){console.log(JSON.stringify(i,null,2));return}if(i.length===0){console.log("No API keys found. Create one at https://www.shieldly.io/app/api");return}console.log(""),console.log(`${f}API Keys (${i.length}):${m}`),console.log(`${N}${"\u2500".repeat(60)}${m}`);for(let c of i){let h=(c.scopes||[]).join(", ")||"all";console.log(` ${T}${c.keyId}${m}`),console.log(` ${f}Label:${m} ${c.label||"(unlabeled)"}`),console.log(` ${f}Scopes:${m} ${h}`),console.log(` ${f}Uses:${m} ${c.usageCount||0}`),console.log(` ${f}Created:${m} ${ae(c.createdAt)}`),console.log("")}}catch(l){console.error(`Error: ${l.message}`),process.exit(1)}return}if(o==="create"){let l=t.indexOf("--label"),i=l!==-1?t[l+1]:"CLI Key",c=t.indexOf("--scopes"),u=(c!==-1?t[c+1]:"iam,cf").split(",").map(d=>d.trim()).filter(Boolean);try{let d=await k("/v1/api-keys",{label:i,scopes:u},s);if(a==="json"){console.log(JSON.stringify(d,null,2));return}console.log(""),console.log(`${f}[OK] API key created${m}`),console.log(` ${f}Key ID:${m} ${d.keyId}`),console.log(` ${f}API Key:${m} ${T}${d.apiKey}${m}`),console.log(` ${N}Store this key securely \u2014 it won't be shown again.${m}`),console.log("")}catch(d){console.error(`Error: ${d.message}`),process.exit(1)}return}if(o==="revoke"){let l=t.find(i=>!i.startsWith("--")&&i!=="revoke");l||(console.error(`Error: key-id argument is required
|
|
73
|
+
Usage: shieldly api-keys revoke <key-id>`),process.exit(1));try{if(await M("/v1/api-keys",{keyId:l},s),a==="json"){console.log(JSON.stringify({success:!0,keyId:l}));return}console.log(`[OK] API key ${l} revoked`)}catch(i){console.error(`Error: ${i.message}`),process.exit(1)}return}console.error(`Unknown subcommand: ${o}`),console.log(U),process.exit(1)}var ce=`
|
|
67
74
|
_shieldly() {
|
|
68
75
|
local cur prev words cword
|
|
69
76
|
_init_completion || return
|
|
@@ -123,7 +130,7 @@ _shieldly() {
|
|
|
123
130
|
fi
|
|
124
131
|
} &&
|
|
125
132
|
complete -F _shieldly shieldly
|
|
126
|
-
`,
|
|
133
|
+
`,pe=`
|
|
127
134
|
#compdef shieldly
|
|
128
135
|
|
|
129
136
|
_shieldly() {
|
|
@@ -194,7 +201,7 @@ _shieldly() {
|
|
|
194
201
|
}
|
|
195
202
|
|
|
196
203
|
_shieldly
|
|
197
|
-
`,
|
|
204
|
+
`,ye=`
|
|
198
205
|
Usage: shieldly completion <shell>
|
|
199
206
|
|
|
200
207
|
Generate shell completion scripts for the shieldly CLI.
|
|
@@ -208,42 +215,42 @@ Examples:
|
|
|
208
215
|
eval "$(shieldly completion bash)" # Source bash completion
|
|
209
216
|
shieldly completion zsh > /usr/local/share/zsh/site-functions/_shieldly # Install zsh completion
|
|
210
217
|
shieldly completion install # Auto-detect and install
|
|
211
|
-
`;async function
|
|
218
|
+
`;async function G(e){if(!e.length||e.includes("-h")||e.includes("--help")){console.log(ye);return}let o=e[0];switch(o){case"bash":console.log(ce.trimStart());break;case"zsh":console.log(pe.trimStart());break;case"install":await me();break;default:console.error(`Unsupported shell: ${o}`),console.log("Supported shells: bash, zsh"),process.exit(1)}}async function me(){let e=process.env.SHELL||"",o=process.env.HOME||"";if(e.includes("zsh")){let t=`${o}/.zshrc`,n=`
|
|
212
219
|
# shieldly CLI completion
|
|
213
220
|
autoload -Uz compinit && compinit -C 2>/dev/null
|
|
214
|
-
eval "$(shieldly completion zsh)" 2>/dev/null`;try{let{readFileSync:s,appendFileSync:r,existsSync:
|
|
221
|
+
eval "$(shieldly completion zsh)" 2>/dev/null`;try{let{readFileSync:s,appendFileSync:r,existsSync:a}=await import("node:fs");if(a(t)&&s(t,"utf8").includes("shieldly completion")){console.log("shieldly completion already installed in ~/.zshrc");return}r(t,n),console.log("Installed shieldly completion in ~/.zshrc"),console.log("Run: source ~/.zshrc")}catch(s){console.error(`Failed to install: ${s.message}`),process.exit(1)}}else if(e.includes("bash")){let t=`${o}/.bashrc`,n=`
|
|
215
222
|
# shieldly CLI completion
|
|
216
|
-
source /dev/stdin <<< "$(shieldly completion bash)" 2>/dev/null`;try{let{readFileSync:s,appendFileSync:r,existsSync:
|
|
217
|
-
${
|
|
223
|
+
source /dev/stdin <<< "$(shieldly completion bash)" 2>/dev/null`;try{let{readFileSync:s,appendFileSync:r,existsSync:a}=await import("node:fs");if(a(t)&&s(t,"utf8").includes("shieldly completion")){console.log("shieldly completion already installed in ~/.bashrc");return}r(t,n),console.log("Installed shieldly completion in ~/.bashrc"),console.log("Run: source ~/.bashrc")}catch(s){console.error(`Failed to install: ${s.message}`),process.exit(1)}}else console.error(`Unsupported shell: ${e}. Install manually:`),console.log(' eval "$(shieldly completion bash)" # For bash'),console.log(' eval "$(shieldly completion zsh)" # For zsh'),process.exit(1)}var $="\x1B[1m",b="\x1B[36m",E="\x1B[2m",p="\x1B[0m",he="1.0.0",ue=`
|
|
224
|
+
${$}shieldly${p} \u2014 AI-Powered Security Analysis for AWS
|
|
218
225
|
|
|
219
|
-
${
|
|
226
|
+
${$}Usage:${p}
|
|
220
227
|
shieldly <command> [args] [options]
|
|
221
228
|
|
|
222
|
-
${
|
|
223
|
-
${
|
|
224
|
-
${
|
|
225
|
-
${
|
|
226
|
-
${
|
|
229
|
+
${$}Commands:${p}
|
|
230
|
+
${b}analyze-iam${p} <policy-file> Analyze an IAM policy for security issues
|
|
231
|
+
${b}analyze-cf${p} <template-file> Analyze a CloudFormation template
|
|
232
|
+
${b}api-keys${p} list|create|revoke Manage API keys
|
|
233
|
+
${b}completion${p} bash|zsh|install Generate shell completion
|
|
227
234
|
|
|
228
|
-
${
|
|
235
|
+
${$}Global Options:${p}
|
|
229
236
|
--api-key <key> API key (or set SHIELDLY_API_KEY env var)
|
|
230
237
|
--version Show version
|
|
231
238
|
-h, --help Show this help
|
|
232
239
|
|
|
233
|
-
${
|
|
240
|
+
${$}Authentication:${p}
|
|
234
241
|
Set your API key via env var: export SHIELDLY_API_KEY=sk_...
|
|
235
|
-
Get a free API key at: ${
|
|
242
|
+
Get a free API key at: ${b}https://www.shieldly.io/app/api${p}
|
|
236
243
|
|
|
237
|
-
${
|
|
238
|
-
${
|
|
244
|
+
${$}Examples:${p}
|
|
245
|
+
${E}# Analyze an IAM policy${p}
|
|
239
246
|
shieldly analyze-iam policy.json
|
|
240
247
|
|
|
241
|
-
${
|
|
248
|
+
${E}# Analyze a CloudFormation template${p}
|
|
242
249
|
shieldly analyze-cf template.yaml
|
|
243
250
|
|
|
244
|
-
${
|
|
251
|
+
${E}# List API keys${p}
|
|
245
252
|
shieldly api-keys list
|
|
246
253
|
|
|
247
|
-
${
|
|
254
|
+
${E}# Use in CI${p}
|
|
248
255
|
SHIELDLY_API_KEY=\${{ secrets.SHIELDLY_API_KEY }} shieldly analyze-iam policy.json
|
|
249
|
-
`;async function de(){let[,,e,...o]=process.argv;switch((!e||e==="-h"||e==="--help")&&(console.log(
|
|
256
|
+
`;async function de(){let[,,e,...o]=process.argv;switch((!e||e==="-h"||e==="--help")&&(console.log(ue),process.exit(0)),(e==="--version"||e==="-v")&&(console.log(he),process.exit(0)),e){case"analyze-iam":await F(o);break;case"analyze-cf":await H(o);break;case"api-keys":await W(o);break;case"completion":await G(o);break;default:console.error(`Unknown command: ${e}`),console.log(`Run ${$}shieldly --help${p} for usage`),process.exit(1)}}de().catch(e=>{console.error("Fatal error:",e.message),process.exit(1)});
|