@shieldly/cli 1.0.2 → 1.0.3
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 +4 -2
- package/dist/cli.cjs +72 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,13 +12,15 @@ npm install -g @shieldly/cli
|
|
|
12
12
|
|
|
13
13
|
## Try it free — no account needed
|
|
14
14
|
|
|
15
|
-
`analyze-iam`
|
|
15
|
+
Both `analyze-iam` and `analyze-cf` run in demo mode without an API key
|
|
16
|
+
(5 free analyses, no signup required):
|
|
16
17
|
|
|
17
18
|
```bash
|
|
18
19
|
shieldly analyze-iam policy.json
|
|
20
|
+
shieldly analyze-cf template.yaml
|
|
19
21
|
```
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
For higher limits and analysis history,
|
|
22
24
|
[create a free account](https://www.shieldly.io/app/api) and set your key:
|
|
23
25
|
|
|
24
26
|
```bash
|
package/dist/cli.cjs
CHANGED
|
@@ -1,31 +1,55 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
`)
|
|
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
|
-
Usage: shieldly analyze-cf <template-file> [options]
|
|
2
|
+
var ne=Object.create;var G=Object.defineProperty;var re=Object.getOwnPropertyDescriptor;var ie=Object.getOwnPropertyNames;var le=Object.getPrototypeOf,ae=Object.prototype.hasOwnProperty;var ce=(e,s,o,n)=>{if(s&&typeof s=="object"||typeof s=="function")for(let t of ie(s))!ae.call(e,t)&&t!==o&&G(e,t,{get:()=>s[t],enumerable:!(n=re(s,t))||n.enumerable});return e};var W=(e,s,o)=>(o=e!=null?ne(le(e)):{},ce(s||!e||!e.__esModule?G(o,"default",{value:e,enumerable:!0}):o,e));var k=require("node:fs"),Y=require("node:path");var $=require("node:fs"),B=require("node:os"),z=require("node:path"),O="Shieldly-CLI/1.0.3",U=(0,z.join)((0,B.homedir)(),".shieldly","config.json"),pe="https://api.shieldly.io",ye="https://www.shieldly.io",H=5;function K(){try{return JSON.parse((0,$.readFileSync)(U,"utf8"))}catch{return{}}}function de(e){try{(0,$.mkdirSync)((0,z.dirname)(U),{recursive:!0}),(0,$.writeFileSync)(U,`${JSON.stringify(e,null,2)}
|
|
3
|
+
`)}catch{}}function S(e){return e||(process.env.SHIELDLY_API_KEY?process.env.SHIELDLY_API_KEY:K().apiKey||null)}function q(){let e=K().demoCount;return typeof e=="number"&&e>0?e:0}function _(){return q()>=H}function j(){let e=K();return e.demoCount=q()+1,de(e),Math.max(0,H-e.demoCount)}function D(){return`You've used all ${H} free demo analyses.
|
|
9
4
|
|
|
10
|
-
|
|
5
|
+
Get a free API key for higher limits: https://www.shieldly.io/app/api
|
|
6
|
+
|
|
7
|
+
Then set SHIELDLY_API_KEY or pass --api-key.`}function N(){return(process.env.SHIELDLY_API_URL||pe).replace(/\/$/,"")}function me(){return(process.env.SHIELDLY_WEB_URL||ye).replace(/\/$/,"")}async function C(e,s){let o=me(),n=await fetch(`${o}${e}`,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":O},body:JSON.stringify(s)});if(n.status===429)throw new Error("Demo rate limit reached. Get a free API key for higher limits: https://www.shieldly.io/app/api");if(!n.ok){let t=await n.json().catch(()=>({}));throw new Error(t.error||`API error ${n.status}`)}return n.json()}async function A(e,s,o){let n=N(),t=await fetch(`${n}${e}`,{method:"POST",headers:{"Content-Type":"application/json","User-Agent":O,Authorization:`Bearer ${o}`},body:JSON.stringify(s)});if(t.status===202){let i=await t.json().catch(()=>({}));if(i.jobId)return ue(i.jobId,o);throw new Error("Analysis queued but no job ID returned \u2014 try again")}if(!t.ok){let i=await t.json().catch(()=>({}));throw new Error(i.error||`API error ${t.status}`)}return t.json()}async function ue(e,s){let o=[2e3,3e3,5e3],n=Date.now(),t=0;for(let i=0;i<180;i++){let l=o[Math.min(i,o.length-1)];await new Promise(r=>setTimeout(r,l));let y=Math.round((Date.now()-n)/1e3);process.stderr.write(`\rAI-Powered analysis in progress\u2026 (${y}s)`);let a;try{a=await T(`/v1/jobs/${encodeURIComponent(e)}`,s),t=0}catch(r){if(++t>=3)throw process.stderr.write(`
|
|
8
|
+
`),r;continue}if(a.status==="complete")return process.stderr.write(`
|
|
9
|
+
`),{...a.result,unitInfo:a.unitInfo};if(a.status==="failed")throw process.stderr.write(`
|
|
10
|
+
`),new Error(a.error||"Analysis failed")}throw process.stderr.write(`
|
|
11
|
+
`),new Error("Analysis timed out after polling")}async function T(e,s){let o=N(),n=await fetch(`${o}${e}`,{headers:{Authorization:`Bearer ${s}`,"User-Agent":O}});if(!n.ok){let t=await n.json().catch(()=>({}));throw new Error(t.error||`API error ${n.status}`)}return n.json()}async function V(e,s,o){let n=N(),t=await fetch(`${n}${e}`,{method:"DELETE",headers:{"Content-Type":"application/json","User-Agent":O,Authorization:`Bearer ${o}`},body:JSON.stringify(s)});if(!t.ok){let i=await t.json().catch(()=>({}));throw new Error(i.error||`API error ${t.status}`)}return t.json()}function R(e){return(0,$.existsSync)(e)||(console.error(`Error: File not found: ${e}`),process.exit(1)),(0,$.statSync)(e).isDirectory()&&(console.error(`Error: ${e} is a directory, not a file.
|
|
12
|
+
For a CDK output directory, use: shieldly analyze-cf ${e}`),process.exit(1)),(0,$.readFileSync)(e,"utf8")}var F={CRITICAL:"\x1B[31m",HIGH:"\x1B[33m",MEDIUM:"\x1B[36m",LOW:"\x1B[32m",INFO:"\x1B[90m"},d="\x1B[0m",E="\x1B[1m",w="\x1B[2m",J="\x1B[36m";function P(e,s){if(s==="json"){console.log(JSON.stringify(e,null,2));return}let{score:o,riskLevel:n,findings:t=[],cached:i,summary:l,positives:y=[],unitInfo:a}=e,r=o==null?"\u2014":`${o}/100`;if(console.log(""),console.log(`${E}AI-Powered Security Analysis \u2014 Shieldly${d}`),console.log(`${w}${"\u2500".repeat(50)}${d}`),console.log(` ${E}Security Score:${d} ${fe(o)}${r}${d}`),console.log(` ${E}Risk Level:${d} ${he(n)}${n||"Unknown"}${d}`),i&&console.log(` ${w}(cached result)${d}`),l&&(console.log(""),console.log(` ${w}${l}${d}`)),console.log(""),y.length>0){console.log(`${E}What's good:${d}`);for(let c of y)console.log(` ${F.LOW}[+]${d} ${w}${c}${d}`);console.log("")}if(t.length===0)console.log(` ${J}[PASS] No findings${d}`);else{console.log(`${E}Findings (${t.length}):${d}`);for(let c of t){let u=F[(c.severity||"").toUpperCase()]||"";console.log(`
|
|
13
|
+
${u}[${c.severity}]${d} ${E}${c.title}${d}`),c.resource&&c.resource!=="*"&&console.log(` ${w}Resource: ${c.resource}${d}`),c.description&&console.log(` ${w}${c.description}${d}`),c.remediation&&console.log(` ${J}Fix:${d} ${c.remediation}`)}}a&&typeof a.unitsUsed=="number"&&typeof a.cap=="number"&&(console.log(""),console.log(` ${w}Units used: ${a.unitsUsed}/${a.cap}${d}`)),e.demoInfo&&typeof e.demoInfo.analysesRemaining=="number"&&(console.log(""),console.log(` ${w}Demo analyses remaining: ${e.demoInfo.analysesRemaining}. Get a free key for more: https://www.shieldly.io/app/api${d}`)),console.log("")}function fe(e){return e==null?"":e>=80?"\x1B[32m":e>=50?"\x1B[33m":"\x1B[31m"}function he(e){let s=(e||"").toUpperCase();return F[s]||""}var $e=`
|
|
14
|
+
Usage: shieldly analyze-cf <template-file-or-dir> [options]
|
|
15
|
+
|
|
16
|
+
Analyze a CloudFormation template (or directory of templates) for security issues using AI.
|
|
11
17
|
|
|
12
18
|
Arguments:
|
|
13
|
-
template-file
|
|
19
|
+
template-file-or-dir Path to a JSON CF template, or a directory (e.g. cdk.out/)
|
|
20
|
+
containing synthesized stacks. All *.template.json files are
|
|
21
|
+
analyzed automatically.
|
|
14
22
|
|
|
15
23
|
Options:
|
|
16
24
|
--format <fmt> Output format: table | json (default: table)
|
|
17
25
|
--api-key <key> API key (or set SHIELDLY_API_KEY env var)
|
|
18
26
|
-h, --help Show this help
|
|
19
27
|
|
|
28
|
+
Authentication:
|
|
29
|
+
No key needed for demo mode (rate-limited). Set SHIELDLY_API_KEY for full access.
|
|
30
|
+
Get a free key: https://www.shieldly.io/app/api
|
|
31
|
+
|
|
20
32
|
Examples:
|
|
21
33
|
shieldly analyze-cf template.json
|
|
22
|
-
shieldly analyze-cf
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
shieldly analyze-cf cdk.out/
|
|
35
|
+
shieldly analyze-cf cdk.out/ --format json
|
|
36
|
+
|
|
37
|
+
CDK integration (add to package.json scripts):
|
|
38
|
+
"synth:check": "cdk synth && shieldly analyze-cf cdk.out/"
|
|
39
|
+
|
|
40
|
+
cdk.json hook (runs after every cdk synth):
|
|
41
|
+
{
|
|
42
|
+
"hooks": {
|
|
43
|
+
"afterSynth": ["sh", "-c", "shieldly analyze-cf cdk.out/ || true"]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
`;function ge(e){try{let s=(0,k.readFileSync)((0,Y.join)(e,"manifest.json"),"utf8"),o=JSON.parse(s);if(!o.artifacts||typeof o.artifacts!="object")return null;let n=Object.values(o.artifacts).filter(t=>t.type==="aws:cloudformation:stack"&&t.properties?.templateFile).map(t=>(0,Y.join)(e,t.properties.templateFile)).filter(t=>(0,k.existsSync)(t));return n.length>0?n:null}catch{return null}}function Ie(e){let s=ge(e);if(s)return s;let o=[],n;try{n=(0,k.readdirSync)(e,{withFileTypes:!0})}catch{return o}for(let t of n){if(!t.isFile())continue;let i=t.name;(i.endsWith(".template.json")||i.endsWith(".template.yaml"))&&o.push((0,Y.join)(e,i))}return o}async function ke(e,s,o,n,t){let i=(0,k.readFileSync)(e,"utf8");o!=="json"&&t>1?process.stdout.write(`[${n}/${t}] Analyzing ${e}\u2026
|
|
47
|
+
`):o!=="json"&&process.stdout.write(`Analyzing ${e}\u2026
|
|
48
|
+
`);let l=s?await A("/v1/analyze/cf",{template:i},s):await C("/api/demo/analyze-iam",{template:i,policyType:"cf"});return!s&&!l.cached&&j(),{filePath:e,data:l}}async function Z(e){if(e.includes("-h")||e.includes("--help")||e.length===0){console.log($e);return}let s=e.indexOf("--format"),o=s!==-1?e[s+1]:"table";s!==-1&&!["table","json"].includes(o)&&(console.error(`Error: invalid --format "${o}". Use: table | json`),process.exit(1));let n=e.indexOf("--api-key"),t=S(n!==-1?e[n+1]:null),i=new Set;for(let r of[s,n])r!==-1&&(i.add(r),i.add(r+1));let l=e.find((r,c)=>!i.has(c)&&!r.startsWith("--"));l||(console.error("Error: template-file-or-dir argument is required"),process.exit(1)),!t&&_()&&(console.error(D()),process.exit(1)),!t&&o!=="json"&&console.log("Demo mode (rate-limited, no signup required). Get a free API key for higher limits: https://www.shieldly.io/app/api");let y=(0,k.statSync)(l,{throwIfNoEntry:!1});if(y||(console.error(`Error: path not found: ${l}`),process.exit(1)),y.isDirectory()){let r=Ie(l);r.length===0&&(console.error(`Error: no CloudFormation templates (*.template.json / *.template.yaml) found in ${l}
|
|
49
|
+
Run "cdk synth" first to generate stack templates.`),process.exit(1)),o!=="json"&&console.log(`Found ${r.length} stack template(s) in ${l}
|
|
50
|
+
`);let c=[],u=0,p=0;for(let f=0;f<r.length;f++){if(!t&&_()){o!=="json"&&console.error(`
|
|
51
|
+
Demo allowance reached \u2014 remaining stacks skipped. Get a free key: https://www.shieldly.io/app/api`);break}try{let{filePath:g,data:I}=await ke(r[f],t,o,f+1,r.length);c.push({filePath:g,data:I}),u+=(I.findings||[]).filter(M=>M.severity?.toUpperCase()==="CRITICAL").length,p+=(I.findings||[]).filter(M=>M.severity?.toUpperCase()==="HIGH").length,o!=="json"&&P(I,o)}catch(g){if(console.error(`Error analyzing ${r[f]}: ${g.message}`),!t&&/rate limit/i.test(g.message))break}}if(o==="json")console.log(JSON.stringify(c.map(({filePath:f,data:g})=>({stack:f,...g})),null,2));else if(r.length>1){let f=c.reduce((g,I)=>g+(I.data.findings||[]).length,0);console.log(`
|
|
52
|
+
Summary: ${r.length} stacks \xB7 ${f} total findings \xB7 ${u} critical \xB7 ${p} high`)}(u>0||p>0)&&process.exit(1);return}let a=R(l);o!=="json"&&console.log(`Analyzing ${l}\u2026`);try{let r=t?await A("/v1/analyze/cf",{template:a},t):await C("/api/demo/analyze-iam",{template:a,policyType:"cf"});P(r,o);let c=(r.findings||[]).filter(p=>p.severity?.toUpperCase()==="CRITICAL").length,u=(r.findings||[]).filter(p=>p.severity?.toUpperCase()==="HIGH").length;(c>0||u>0)&&process.exit(1)}catch(r){console.error(`Error: ${r.message}`),process.exit(1)}}var we=`
|
|
29
53
|
Usage: shieldly analyze-iam <policy-file> [options]
|
|
30
54
|
|
|
31
55
|
Analyze an AWS IAM policy for security issues using AI.
|
|
@@ -43,9 +67,9 @@ Options:
|
|
|
43
67
|
|
|
44
68
|
Examples:
|
|
45
69
|
shieldly analyze-iam policy.json
|
|
46
|
-
shieldly analyze-iam policy.json --type
|
|
70
|
+
shieldly analyze-iam policy.json --type cross_account --format json
|
|
47
71
|
SHIELDLY_API_KEY=sk_... shieldly analyze-iam policy.json
|
|
48
|
-
`;async function
|
|
72
|
+
`;async function Q(e){if(e.includes("-h")||e.includes("--help")||e.length===0){console.log(we);return}let s=e.indexOf("--type"),o=s!==-1?e[s+1]:"identity",n=e.indexOf("--format"),t=n!==-1?e[n+1]:"table";s!==-1&&!["identity","iam_identity","cross_account"].includes(o)&&(console.error(`Error: invalid --type "${o}". Use: identity | cross_account`),process.exit(1)),n!==-1&&!["table","json"].includes(t)&&(console.error(`Error: invalid --format "${t}". Use: table | json`),process.exit(1));let i=e.indexOf("--api-key"),l=S(i!==-1?e[i+1]:null);!l&&_()&&(console.error(D()),process.exit(1));let y=new Set;for(let p of[s,n,i])p!==-1&&(y.add(p),y.add(p+1));let a=e.find((p,f)=>!y.has(f)&&!p.startsWith("--"));a||(console.error("Error: policy-file argument is required"),process.exit(1));let r=R(a);try{JSON.parse(r)}catch{console.error("Error: policy-file must be valid JSON"),process.exit(1)}let c=r.trim(),u=o==="identity"||o==="iam_identity"?"iam_identity":o==="cross_account"?"cross_account":"iam_identity";t!=="json"&&(console.log(`Analyzing ${a} (type: ${u})\u2026`),l||console.log("Demo mode (rate-limited, no signup required). Get a free API key for higher limits: https://www.shieldly.io/app/api"));try{let p=l?await A("/v1/analyze/iam",{policy:c,policyType:u},l):await C("/api/demo/analyze-iam",{policy:c,policyType:u});!l&&!p.cached&&j(),P(p,t);let f=(p.findings||[]).filter(I=>I.severity?.toUpperCase()==="CRITICAL").length,g=(p.findings||[]).filter(I=>I.severity?.toUpperCase()==="HIGH").length;(f>0||g>0)&&process.exit(1)}catch(p){console.error(`Error: ${p.message}`),process.exit(1)}}var X=`
|
|
49
73
|
Usage: shieldly api-keys <subcommand> [options]
|
|
50
74
|
|
|
51
75
|
Manage Shieldly API keys.
|
|
@@ -65,12 +89,12 @@ Examples:
|
|
|
65
89
|
shieldly api-keys list
|
|
66
90
|
shieldly api-keys create --label "CI/CD Key" --scopes iam,cf
|
|
67
91
|
shieldly api-keys revoke key_abc123
|
|
68
|
-
`,
|
|
92
|
+
`,x="\x1B[1m",ee="\x1B[2m",oe="\x1B[36m",h="\x1B[0m";function xe(e){return e?new Date(e).toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"}):"\u2014"}async function te(e){if(e.includes("-h")||e.includes("--help")||e.length===0){console.log(X);return}let s=e[0],o=e.slice(1),n=o.indexOf("--api-key"),t=S(n!==-1?o[n+1]:null),i=o.indexOf("--format"),l=i!==-1?o[i+1]:"table";if(i!==-1&&!["table","json"].includes(l)&&(console.error(`Error: invalid --format "${l}". Use: table | json`),process.exit(1)),t||(console.error(`API key management requires an API key to authenticate.
|
|
69
93
|
|
|
70
94
|
Create your free account: https://www.shieldly.io/app/api
|
|
71
95
|
|
|
72
|
-
Set SHIELDLY_API_KEY or use --api-key once you have your key.`),process.exit(1)),
|
|
73
|
-
Usage: shieldly api-keys revoke <key-id>`),process.exit(1));try{if(await
|
|
96
|
+
Set SHIELDLY_API_KEY or use --api-key once you have your key.`),process.exit(1)),s==="list"){try{let a=(await T("/v1/api-keys",t)).keys||[];if(l==="json"){console.log(JSON.stringify(a,null,2));return}if(a.length===0){console.log("No API keys found. Create one at https://www.shieldly.io/app/api");return}console.log(""),console.log(`${x}API Keys (${a.length}):${h}`),console.log(`${ee}${"\u2500".repeat(60)}${h}`);for(let r of a){let c=(r.scopes||[]).join(", ")||"all";console.log(` ${oe}${r.keyId}${h}`),console.log(` ${x}Label:${h} ${r.label||"(unlabeled)"}`),console.log(` ${x}Scopes:${h} ${c}`),console.log(` ${x}Uses:${h} ${r.usageCount||0}`),console.log(` ${x}Created:${h} ${xe(r.createdAt)}`),console.log("")}}catch(y){console.error(`Error: ${y.message}`),process.exit(1)}return}if(s==="create"){let y=o.indexOf("--label"),a=y!==-1?o[y+1]:"CLI Key",r=o.indexOf("--scopes"),u=(r!==-1?o[r+1]:"iam,cf").split(",").map(p=>p.trim()).filter(Boolean);try{let p=await A("/v1/api-keys",{label:a,scopes:u},t);if(l==="json"){console.log(JSON.stringify(p,null,2));return}console.log(""),console.log(`${x}[OK] API key created${h}`),console.log(` ${x}Key ID:${h} ${p.keyId}`),console.log(` ${x}API Key:${h} ${oe}${p.apiKey}${h}`),console.log(` ${ee}Store this key securely \u2014 it won't be shown again.${h}`),console.log("")}catch(p){console.error(`Error: ${p.message}`),process.exit(1)}return}if(s==="revoke"){let y=new Set;n!==-1&&(y.add(n),y.add(n+1)),i!==-1&&(y.add(i),y.add(i+1));let a=o.find((r,c)=>!y.has(c)&&!r.startsWith("--"));a||(console.error(`Error: key-id argument is required
|
|
97
|
+
Usage: shieldly api-keys revoke <key-id>`),process.exit(1));try{if(await V("/v1/api-keys",{keyId:a},t),l==="json"){console.log(JSON.stringify({success:!0,keyId:a}));return}console.log(`[OK] API key ${a} revoked`)}catch(r){console.error(`Error: ${r.message}`),process.exit(1)}return}console.error(`Unknown subcommand: ${s}`),console.log(X),process.exit(1)}var Ae=`
|
|
74
98
|
_shieldly() {
|
|
75
99
|
local cur prev words cword
|
|
76
100
|
_init_completion || return
|
|
@@ -104,7 +128,7 @@ _shieldly() {
|
|
|
104
128
|
return
|
|
105
129
|
;;
|
|
106
130
|
--type)
|
|
107
|
-
COMPREPLY=($(compgen -W "identity
|
|
131
|
+
COMPREPLY=($(compgen -W "identity cross_account" -- "$cur"))
|
|
108
132
|
return
|
|
109
133
|
;;
|
|
110
134
|
--format)
|
|
@@ -130,7 +154,7 @@ _shieldly() {
|
|
|
130
154
|
fi
|
|
131
155
|
} &&
|
|
132
156
|
complete -F _shieldly shieldly
|
|
133
|
-
`,
|
|
157
|
+
`,be=`
|
|
134
158
|
#compdef shieldly
|
|
135
159
|
|
|
136
160
|
_shieldly() {
|
|
@@ -152,7 +176,7 @@ _shieldly() {
|
|
|
152
176
|
args)
|
|
153
177
|
case $words[1] in
|
|
154
178
|
analyze-iam)
|
|
155
|
-
_arguments '--type[Policy type]:type:(identity
|
|
179
|
+
_arguments '--type[Policy type]:type:(identity cross_account)' '--format[Output format]:format:(table json)' '--api-key[API key]:api key' '(-h --help)'{-h,--help}'[Show help]' '*:policy file:_files' && ret=0
|
|
156
180
|
;;
|
|
157
181
|
analyze-cf)
|
|
158
182
|
_arguments '--format[Output format]:format:(table json)' '--api-key[API key]:api key' '(-h --help)'{-h,--help}'[Show help]' '*:template file:_files' && ret=0
|
|
@@ -201,7 +225,7 @@ _shieldly() {
|
|
|
201
225
|
}
|
|
202
226
|
|
|
203
227
|
_shieldly
|
|
204
|
-
`,
|
|
228
|
+
`,Ee=`
|
|
205
229
|
Usage: shieldly completion <shell>
|
|
206
230
|
|
|
207
231
|
Generate shell completion scripts for the shieldly CLI.
|
|
@@ -215,42 +239,45 @@ Examples:
|
|
|
215
239
|
eval "$(shieldly completion bash)" # Source bash completion
|
|
216
240
|
shieldly completion zsh > /usr/local/share/zsh/site-functions/_shieldly # Install zsh completion
|
|
217
241
|
shieldly completion install # Auto-detect and install
|
|
218
|
-
`;async function
|
|
242
|
+
`;async function se(e){if(!e.length||e.includes("-h")||e.includes("--help")){console.log(Ee);return}let s=e[0];switch(s){case"bash":console.log(Ae.trimStart());break;case"zsh":console.log(be.trimStart());break;case"install":await Se();break;default:console.error(`Unsupported shell: ${s}`),console.log("Supported shells: bash, zsh"),process.exit(1)}}async function Se(){let e=process.env.SHELL||"",s=process.env.HOME||"";if(e.includes("zsh")){let o=`${s}/.zshrc`,n=`
|
|
219
243
|
# shieldly CLI completion
|
|
220
244
|
autoload -Uz compinit && compinit -C 2>/dev/null
|
|
221
|
-
eval "$(shieldly completion zsh)" 2>/dev/null`;try{let{readFileSync:
|
|
245
|
+
eval "$(shieldly completion zsh)" 2>/dev/null`;try{let{readFileSync:t,appendFileSync:i,existsSync:l}=await import("node:fs");if(l(o)&&t(o,"utf8").includes("shieldly completion")){console.log("shieldly completion already installed in ~/.zshrc");return}i(o,n),console.log("Installed shieldly completion in ~/.zshrc"),console.log("Run: source ~/.zshrc")}catch(t){console.error(`Failed to install: ${t.message}`),process.exit(1)}}else if(e.includes("bash")){let o=`${s}/.bashrc`,n=`
|
|
222
246
|
# shieldly CLI completion
|
|
223
|
-
source /dev/stdin <<< "$(shieldly completion bash)" 2>/dev/null`;try{let{readFileSync:
|
|
224
|
-
${
|
|
247
|
+
source /dev/stdin <<< "$(shieldly completion bash)" 2>/dev/null`;try{let{readFileSync:t,appendFileSync:i,existsSync:l}=await import("node:fs");if(l(o)&&t(o,"utf8").includes("shieldly completion")){console.log("shieldly completion already installed in ~/.bashrc");return}i(o,n),console.log("Installed shieldly completion in ~/.bashrc"),console.log("Run: source ~/.bashrc")}catch(t){console.error(`Failed to install: ${t.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 b="\x1B[1m",L="\x1B[36m",v="\x1B[2m",m="\x1B[0m",_e="1.0.3",Ce=`
|
|
248
|
+
${b}shieldly${m} \u2014 AI-Powered Security Analysis for AWS
|
|
225
249
|
|
|
226
|
-
${
|
|
250
|
+
${b}Usage:${m}
|
|
227
251
|
shieldly <command> [args] [options]
|
|
228
252
|
|
|
229
|
-
${
|
|
230
|
-
${
|
|
231
|
-
${
|
|
232
|
-
${
|
|
233
|
-
${
|
|
253
|
+
${b}Commands:${m}
|
|
254
|
+
${L}analyze-iam${m} <policy-file> Analyze an IAM policy for security issues
|
|
255
|
+
${L}analyze-cf${m} <template-file-or-dir> Analyze a CloudFormation template or CDK output directory
|
|
256
|
+
${L}api-keys${m} list|create|revoke Manage API keys
|
|
257
|
+
${L}completion${m} bash|zsh|install Generate shell completion
|
|
234
258
|
|
|
235
|
-
${
|
|
259
|
+
${b}Global Options:${m}
|
|
236
260
|
--api-key <key> API key (or set SHIELDLY_API_KEY env var)
|
|
237
261
|
--version Show version
|
|
238
262
|
-h, --help Show this help
|
|
239
263
|
|
|
240
|
-
${
|
|
264
|
+
${b}Authentication:${m}
|
|
241
265
|
Set your API key via env var: export SHIELDLY_API_KEY=sk_...
|
|
242
|
-
Get a free API key at: ${
|
|
266
|
+
Get a free API key at: ${L}https://www.shieldly.io/app/api${m}
|
|
243
267
|
|
|
244
|
-
${
|
|
245
|
-
${
|
|
268
|
+
${b}Examples:${m}
|
|
269
|
+
${v}# Analyze an IAM policy or CF template (no API key needed \u2014 demo mode)${m}
|
|
246
270
|
shieldly analyze-iam policy.json
|
|
247
271
|
|
|
248
|
-
${
|
|
249
|
-
shieldly analyze-cf template.
|
|
272
|
+
${v}# Analyze a single CloudFormation template${m}
|
|
273
|
+
shieldly analyze-cf template.json
|
|
274
|
+
|
|
275
|
+
${v}# Scan all CDK stacks after synthesis (reads manifest.json \u2014 current stacks only)${m}
|
|
276
|
+
cdk synth && shieldly analyze-cf cdk.out/
|
|
250
277
|
|
|
251
|
-
${
|
|
278
|
+
${v}# List API keys${m}
|
|
252
279
|
shieldly api-keys list
|
|
253
280
|
|
|
254
|
-
${
|
|
281
|
+
${v}# Use in CI${m}
|
|
255
282
|
SHIELDLY_API_KEY=\${{ secrets.SHIELDLY_API_KEY }} shieldly analyze-iam policy.json
|
|
256
|
-
`;async function
|
|
283
|
+
`;async function Pe(){let[,,e,...s]=process.argv;switch((!e||e==="-h"||e==="--help")&&(console.log(Ce),process.exit(0)),(e==="--version"||e==="-v")&&(console.log(_e),process.exit(0)),e){case"analyze-iam":await Q(s);break;case"analyze-cf":await Z(s);break;case"api-keys":await te(s);break;case"completion":await se(s);break;default:console.error(`Unknown command: ${e}`),console.log(`Run ${b}shieldly --help${m} for usage`),process.exit(1)}}Pe().catch(e=>{console.error("Fatal error:",e.message),process.exit(1)});
|