@shieldly/cli 1.0.0 → 1.0.1
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/dist/cli.cjs +33 -33
- package/package.json +1 -1
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 O=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&&O(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?O(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.1",C=(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)(C))try{let o=JSON.parse((0,g.readFileSync)(C,"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 x(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 P(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,8 @@ 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
|
-
Get your free key at https://www.shieldly.io/app/api`),process.exit(1));let
|
|
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(`Error: API key required. Set SHIELDLY_API_KEY env var or use --api-key.
|
|
24
|
+
Get your free key at https://www.shieldly.io/app/api`),process.exit(1));let a=x(o);n!=="json"&&console.log(`Analyzing ${o}\u2026`);try{let l=await k("/v1/analyze/cf",{template:a},r);P(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
25
|
Usage: shieldly analyze-iam <policy-file> [options]
|
|
26
26
|
|
|
27
27
|
Analyze an AWS IAM policy for security issues using AI.
|
|
@@ -41,7 +41,7 @@ Examples:
|
|
|
41
41
|
shieldly analyze-iam policy.json
|
|
42
42
|
shieldly analyze-iam policy.json --type resource --format json
|
|
43
43
|
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(
|
|
44
|
+
`;async function F(e){if(e.includes("-h")||e.includes("--help")||e.length===0){console.log(ie);return}let o=e.find(d=>!d.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=x(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 d=l?await k("/v1/analyze/iam",{policy:c,policyType:h},l):await R("/api/demo/analyze-iam",{policy:c,policyType:h});P(d,r);let u=(d.findings||[]).filter(S=>S.severity==="CRITICAL").length,J=(d.findings||[]).filter(S=>S.severity==="HIGH").length;(u>0||J>0)&&process.exit(1)}catch(d){console.error(`Error: ${d.message}`),process.exit(1)}}var U=`
|
|
45
45
|
Usage: shieldly api-keys <subcommand> [options]
|
|
46
46
|
|
|
47
47
|
Manage Shieldly API keys.
|
|
@@ -61,9 +61,9 @@ Examples:
|
|
|
61
61
|
shieldly api-keys list
|
|
62
62
|
shieldly api-keys create --label "CI/CD Key" --scopes iam,cf
|
|
63
63
|
shieldly api-keys revoke key_abc123
|
|
64
|
-
`,
|
|
65
|
-
Get your free key at https://www.shieldly.io/app/api`),process.exit(1)),o==="list"){try{let
|
|
66
|
-
Usage: shieldly api-keys revoke <key-id>`),process.exit(1));try{if(await M("/v1/api-keys",{keyId:l},s),
|
|
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 G(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(`Error: API key required. Set SHIELDLY_API_KEY env var or use --api-key.
|
|
65
|
+
Get your free key at https://www.shieldly.io/app/api`),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"),d=(c!==-1?t[c+1]:"iam,cf").split(",").map(u=>u.trim()).filter(Boolean);try{let u=await k("/v1/api-keys",{label:i,scopes:d},s);if(a==="json"){console.log(JSON.stringify(u,null,2));return}console.log(""),console.log(`${f}[OK] API key created${m}`),console.log(` ${f}Key ID:${m} ${u.keyId}`),console.log(` ${f}API Key:${m} ${T}${u.apiKey}${m}`),console.log(` ${N}Store this key securely \u2014 it won't be shown again.${m}`),console.log("")}catch(u){console.error(`Error: ${u.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
|
+
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
67
|
_shieldly() {
|
|
68
68
|
local cur prev words cword
|
|
69
69
|
_init_completion || return
|
|
@@ -123,7 +123,7 @@ _shieldly() {
|
|
|
123
123
|
fi
|
|
124
124
|
} &&
|
|
125
125
|
complete -F _shieldly shieldly
|
|
126
|
-
`,
|
|
126
|
+
`,pe=`
|
|
127
127
|
#compdef shieldly
|
|
128
128
|
|
|
129
129
|
_shieldly() {
|
|
@@ -194,7 +194,7 @@ _shieldly() {
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
_shieldly
|
|
197
|
-
`,
|
|
197
|
+
`,ye=`
|
|
198
198
|
Usage: shieldly completion <shell>
|
|
199
199
|
|
|
200
200
|
Generate shell completion scripts for the shieldly CLI.
|
|
@@ -208,42 +208,42 @@ Examples:
|
|
|
208
208
|
eval "$(shieldly completion bash)" # Source bash completion
|
|
209
209
|
shieldly completion zsh > /usr/local/share/zsh/site-functions/_shieldly # Install zsh completion
|
|
210
210
|
shieldly completion install # Auto-detect and install
|
|
211
|
-
`;async function W(e){if(!e.length||e.includes("-h")||e.includes("--help")){console.log(
|
|
211
|
+
`;async function W(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
212
|
# shieldly CLI completion
|
|
213
213
|
autoload -Uz compinit && compinit -C 2>/dev/null
|
|
214
|
-
eval "$(shieldly completion zsh)" 2>/dev/null`;try{let{readFileSync:s,appendFileSync:r,existsSync:
|
|
214
|
+
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
215
|
# shieldly CLI completion
|
|
216
|
-
source /dev/stdin <<< "$(shieldly completion bash)" 2>/dev/null`;try{let{readFileSync:s,appendFileSync:r,existsSync:
|
|
217
|
-
${
|
|
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",de=`
|
|
217
|
+
${$}shieldly${p} \u2014 AI-Powered Security Analysis for AWS
|
|
218
218
|
|
|
219
|
-
${
|
|
219
|
+
${$}Usage:${p}
|
|
220
220
|
shieldly <command> [args] [options]
|
|
221
221
|
|
|
222
|
-
${
|
|
223
|
-
${
|
|
224
|
-
${
|
|
225
|
-
${
|
|
226
|
-
${
|
|
222
|
+
${$}Commands:${p}
|
|
223
|
+
${b}analyze-iam${p} <policy-file> Analyze an IAM policy for security issues
|
|
224
|
+
${b}analyze-cf${p} <template-file> Analyze a CloudFormation template
|
|
225
|
+
${b}api-keys${p} list|create|revoke Manage API keys
|
|
226
|
+
${b}completion${p} bash|zsh|install Generate shell completion
|
|
227
227
|
|
|
228
|
-
${
|
|
228
|
+
${$}Global Options:${p}
|
|
229
229
|
--api-key <key> API key (or set SHIELDLY_API_KEY env var)
|
|
230
230
|
--version Show version
|
|
231
231
|
-h, --help Show this help
|
|
232
232
|
|
|
233
|
-
${
|
|
233
|
+
${$}Authentication:${p}
|
|
234
234
|
Set your API key via env var: export SHIELDLY_API_KEY=sk_...
|
|
235
|
-
Get a free API key at: ${
|
|
235
|
+
Get a free API key at: ${b}https://www.shieldly.io/app/api${p}
|
|
236
236
|
|
|
237
|
-
${
|
|
238
|
-
${
|
|
237
|
+
${$}Examples:${p}
|
|
238
|
+
${E}# Analyze an IAM policy${p}
|
|
239
239
|
shieldly analyze-iam policy.json
|
|
240
240
|
|
|
241
|
-
${
|
|
241
|
+
${E}# Analyze a CloudFormation template${p}
|
|
242
242
|
shieldly analyze-cf template.yaml
|
|
243
243
|
|
|
244
|
-
${
|
|
244
|
+
${E}# List API keys${p}
|
|
245
245
|
shieldly api-keys list
|
|
246
246
|
|
|
247
|
-
${
|
|
247
|
+
${E}# Use in CI${p}
|
|
248
248
|
SHIELDLY_API_KEY=\${{ secrets.SHIELDLY_API_KEY }} shieldly analyze-iam policy.json
|
|
249
|
-
`;async function
|
|
249
|
+
`;async function ue(){let[,,e,...o]=process.argv;switch((!e||e==="-h"||e==="--help")&&(console.log(de),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 G(o);break;case"completion":await W(o);break;default:console.error(`Unknown command: ${e}`),console.log(`Run ${$}shieldly --help${p} for usage`),process.exit(1)}}ue().catch(e=>{console.error("Fatal error:",e.message),process.exit(1)});
|