@shieldly/cli 1.0.1 → 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 +17 -10
- 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,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var B=Object.create;var
|
|
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
3
|
`),c;continue}if(i.status==="complete")return process.stderr.write(`
|
|
4
4
|
`),{...i.result,unitInfo:i.unitInfo};if(i.status==="failed")throw process.stderr.write(`
|
|
5
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
|
|
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
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
|
|
|
@@ -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(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(`
|
|
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(ie);return}let o=e.find(
|
|
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,8 +65,11 @@ 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
|
-
`,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
|
|
65
|
-
|
|
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
|
|
66
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
|
|
@@ -208,12 +215,12 @@ 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
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: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",
|
|
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=`
|
|
217
224
|
${$}shieldly${p} \u2014 AI-Powered Security Analysis for AWS
|
|
218
225
|
|
|
219
226
|
${$}Usage:${p}
|
|
@@ -246,4 +253,4 @@ ${$}Examples:${p}
|
|
|
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
|
|
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)});
|