xtra-cli 1.0.3 → 1.0.5
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/bin/xtra.js +3 -137
- package/dist/commands/access.js +1 -184
- package/dist/commands/admin.js +5 -118
- package/dist/commands/audit.js +1 -67
- package/dist/commands/branch.js +1 -212
- package/dist/commands/checkout.js +1 -80
- package/dist/commands/ci.js +31 -341
- package/dist/commands/completion.js +85 -224
- package/dist/commands/diff.js +6 -162
- package/dist/commands/doctor.js +2 -164
- package/dist/commands/env.js +2 -70
- package/dist/commands/export.js +2 -83
- package/dist/commands/generate.js +3 -179
- package/dist/commands/history.js +4 -77
- package/dist/commands/import.js +1 -121
- package/dist/commands/init.js +7 -205
- package/dist/commands/integration.js +11 -188
- package/dist/commands/jit-run.js +5 -0
- package/dist/commands/local.js +16 -198
- package/dist/commands/login.js +16 -198
- package/dist/commands/login.test.js +16 -51
- package/dist/commands/logout.js +1 -0
- package/dist/commands/logs.js +3 -121
- package/dist/commands/profile.js +14 -184
- package/dist/commands/project.js +1 -165
- package/dist/commands/rollback.js +1 -95
- package/dist/commands/rotate.js +3 -93
- package/dist/commands/run.js +11 -215
- package/dist/commands/scan.js +25 -127
- package/dist/commands/secrets.js +14 -305
- package/dist/commands/simulate.js +6 -109
- package/dist/commands/status.js +2 -93
- package/dist/commands/template.js +22 -276
- package/dist/commands/ui.js +2 -289
- package/dist/commands/watch.js +17 -123
- package/dist/lib/api.js +1 -187
- package/dist/lib/api.test.js +1 -89
- package/dist/lib/audit.js +1 -136
- package/dist/lib/config.js +1 -72
- package/dist/lib/config.test.js +1 -47
- package/dist/lib/crypto.js +1 -50
- package/dist/lib/manifest.js +1 -52
- package/dist/lib/profiles.js +1 -103
- package/package.json +7 -2
package/dist/bin/xtra.js
CHANGED
|
@@ -1,138 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
-
}
|
|
9
|
-
Object.defineProperty(o, k2, desc);
|
|
10
|
-
}) : (function(o, m, k, k2) {
|
|
11
|
-
if (k2 === undefined) k2 = k;
|
|
12
|
-
o[k2] = m[k];
|
|
13
|
-
}));
|
|
14
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
-
}) : function(o, v) {
|
|
17
|
-
o["default"] = v;
|
|
18
|
-
});
|
|
19
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
-
var ownKeys = function(o) {
|
|
21
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
-
var ar = [];
|
|
23
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
-
return ar;
|
|
25
|
-
};
|
|
26
|
-
return ownKeys(o);
|
|
27
|
-
};
|
|
28
|
-
return function (mod) {
|
|
29
|
-
if (mod && mod.__esModule) return mod;
|
|
30
|
-
var result = {};
|
|
31
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
-
__setModuleDefault(result, mod);
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
|
-
})();
|
|
36
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
const commander_1 = require("commander");
|
|
38
|
-
const fs = __importStar(require("fs"));
|
|
39
|
-
const path = __importStar(require("path"));
|
|
40
|
-
const login_1 = require("../commands/login");
|
|
41
|
-
const run_1 = require("../commands/run");
|
|
42
|
-
const secrets_1 = require("../commands/secrets");
|
|
43
|
-
const generate_1 = require("../commands/generate");
|
|
44
|
-
const status_1 = require("../commands/status");
|
|
45
|
-
const diff_1 = require("../commands/diff");
|
|
46
|
-
const rollback_1 = require("../commands/rollback");
|
|
47
|
-
const scan_1 = require("../commands/scan");
|
|
48
|
-
const logs_1 = require("../commands/logs");
|
|
49
|
-
const export_1 = require("../commands/export");
|
|
50
|
-
const import_1 = require("../commands/import");
|
|
51
|
-
const rotate_1 = require("../commands/rotate");
|
|
52
|
-
const audit_1 = require("../commands/audit");
|
|
53
|
-
const access_1 = require("../commands/access");
|
|
54
|
-
const branch_1 = require("../commands/branch");
|
|
55
|
-
const checkout_1 = require("../commands/checkout");
|
|
56
|
-
const project_1 = require("../commands/project");
|
|
57
|
-
const admin_1 = require("../commands/admin");
|
|
58
|
-
const integration_1 = require("../commands/integration");
|
|
59
|
-
const history_1 = require("../commands/history");
|
|
60
|
-
const env_1 = require("../commands/env");
|
|
61
|
-
const ui_1 = require("../commands/ui");
|
|
62
|
-
const completion_1 = require("../commands/completion");
|
|
63
|
-
const profile_1 = require("../commands/profile");
|
|
64
|
-
const profiles_1 = require("../lib/profiles");
|
|
65
|
-
const ci_1 = require("../commands/ci");
|
|
66
|
-
const template_1 = require("../commands/template");
|
|
67
|
-
const doctor_1 = require("../commands/doctor");
|
|
68
|
-
const init_1 = require("../commands/init");
|
|
69
|
-
const watch_1 = require("../commands/watch");
|
|
70
|
-
const simulate_1 = require("../commands/simulate");
|
|
71
|
-
const local_1 = require("../commands/local");
|
|
72
|
-
const program = new commander_1.Command();
|
|
73
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"), "utf-8"));
|
|
74
|
-
program
|
|
75
|
-
.name("xtra")
|
|
76
|
-
.description("XtraSync CLI - Secure Runtime Secret Injection")
|
|
77
|
-
.version(pkg.version)
|
|
78
|
-
.option("--profile <name>", "Use a specific named profile for this command");
|
|
79
|
-
// Commands that do NOT require authentication
|
|
80
|
-
const PUBLIC_COMMANDS = new Set(["login", "completion", "doctor", "help"]);
|
|
81
|
-
// Apply profile + auth guard before any subcommand runs
|
|
82
|
-
program.hook("preSubcommand", (thisCommand, subCommand) => {
|
|
83
|
-
// ── Profile override ──────────────────────────────────────────
|
|
84
|
-
const profileName = thisCommand.opts().profile;
|
|
85
|
-
if (profileName) {
|
|
86
|
-
try {
|
|
87
|
-
(0, profiles_1.useProfile)(profileName);
|
|
88
|
-
}
|
|
89
|
-
catch (e) {
|
|
90
|
-
console.error(`\x1b[31mError: ${e.message}\x1b[0m`);
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
// ── Auth guard ────────────────────────────────────────────────
|
|
95
|
-
const cmdName = subCommand.name();
|
|
96
|
-
if (PUBLIC_COMMANDS.has(cmdName))
|
|
97
|
-
return; // allow unauthenticated
|
|
98
|
-
const { getAuthToken } = require("../lib/config");
|
|
99
|
-
const token = getAuthToken();
|
|
100
|
-
if (!token) {
|
|
101
|
-
console.error("\x1b[31m✖ You are not logged in.\x1b[0m\n" +
|
|
102
|
-
" Run \x1b[36mxtra login\x1b[0m to authenticate before using this command.\n");
|
|
103
|
-
process.exit(1);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
program.addCommand(login_1.loginCommand);
|
|
107
|
-
program.addCommand(run_1.runCommand);
|
|
108
|
-
program.addCommand(secrets_1.secretsCommand);
|
|
109
|
-
program.addCommand(generate_1.generateCommand);
|
|
110
|
-
program.addCommand(status_1.statusCommand);
|
|
111
|
-
program.addCommand(diff_1.diffCommand);
|
|
112
|
-
program.addCommand(rollback_1.rollbackCommand);
|
|
113
|
-
program.addCommand(scan_1.scanCommand);
|
|
114
|
-
program.addCommand(logs_1.logsCommand);
|
|
115
|
-
program.addCommand(export_1.exportCommand);
|
|
116
|
-
program.addCommand(import_1.importCommand);
|
|
117
|
-
program.addCommand(rotate_1.rotateCommand);
|
|
118
|
-
program.addCommand(audit_1.auditCommand);
|
|
119
|
-
program.addCommand(access_1.accessCommand);
|
|
120
|
-
program.addCommand(branch_1.branchCommand);
|
|
121
|
-
program.addCommand(checkout_1.checkoutCommand);
|
|
122
|
-
program.addCommand(project_1.projectCommand);
|
|
123
|
-
program.addCommand(admin_1.adminCommand);
|
|
124
|
-
program.addCommand(integration_1.integrationCommand);
|
|
125
|
-
program.addCommand(integration_1.kubernetesCommand);
|
|
126
|
-
program.addCommand(history_1.historyCommand);
|
|
127
|
-
program.addCommand(env_1.envCommand);
|
|
128
|
-
program.addCommand(ui_1.uiCommand);
|
|
129
|
-
program.addCommand(completion_1.completionCommand);
|
|
130
|
-
program.addCommand(profile_1.profileCommand);
|
|
131
|
-
program.addCommand(ci_1.ciCommand);
|
|
132
|
-
program.addCommand(template_1.templateCommand);
|
|
133
|
-
program.addCommand(doctor_1.doctorCommand);
|
|
134
|
-
program.addCommand(init_1.initCommand);
|
|
135
|
-
program.addCommand(watch_1.watchCommand);
|
|
136
|
-
program.addCommand(simulate_1.simulateCommand);
|
|
137
|
-
program.addCommand(local_1.localCommand);
|
|
138
|
-
program.parse(process.argv);
|
|
2
|
+
var ge=Object.create;var C=Object.defineProperty;var le=Object.getOwnPropertyDescriptor;var ue=Object.getOwnPropertyNames;var fe=Object.getPrototypeOf,ye=Object.prototype.hasOwnProperty;var x=(e,t)=>()=>(e&&(t=e(e=0)),t);var k=(e,t)=>{for(var s in t)C(e,s,{get:t[s],enumerable:!0})},N=(e,t,s,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ue(t))!ye.call(e,n)&&n!==s&&C(e,n,{get:()=>t[n],enumerable:!(r=le(t,n))||r.enumerable});return e};var c=(e,t,s)=>(s=e!=null?ge(fe(e)):{},N(t||!e||!e.__esModule?C(s,"default",{value:e,enumerable:!0}):s,e)),S=e=>N(C({},"__esModule",{value:!0}),e);var M={};k(M,{createProfile:()=>xe,deleteProfile:()=>we,getActiveProfile:()=>B,getActiveProfileName:()=>I,getProfileValue:()=>Ce,listProfiles:()=>m,setActiveProfile:()=>he,setProfileValue:()=>ve,useProfile:()=>Se});function m(){return d.get("profiles")||{}}function I(){return process.env.XTRA_PROFILE||d.get("activeProfile")||"default"}function B(){let e=I();return m()[e]||{}}function he(e){if(!m()[e])throw new Error(`Profile '${e}' does not exist. Create it first with 'xtra profile create ${e}'.`);d.set("activeProfile",e)}function xe(e,t={}){let s=m();if(s[e])throw new Error(`Profile '${e}' already exists. Use 'xtra profile set' to edit it.`);s[e]={apiUrl:process.env.XTRA_API_URL||"https://www.xtrasecurity.in/api",...t},d.set("profiles",s)}function we(e){if(e==="default")throw new Error("Cannot delete the 'default' profile.");let t=m();if(!t[e])throw new Error(`Profile '${e}' not found.`);delete t[e],d.set("profiles",t),I()===e&&d.set("activeProfile","default")}function ve(e,t,s){let r=m();if(!r[e])throw new Error(`Profile '${e}' not found.`);r[e][t]=s,d.set("profiles",r)}function Ce(e){return B()[e]}function Se(e){if(!m()[e])throw new Error(`Profile '${e}' does not exist. Run 'xtra profile create ${e}' first.`);process.env.XTRA_PROFILE=e}var O,d,J=x(()=>{O=c(require("conf")),d=new O.default({projectName:"xtra-cli",defaults:{activeProfile:"default",profiles:{default:{apiUrl:process.env.XTRA_API_URL||"https://www.xtrasecurity.in/api"}}}})});var L={};k(L,{clearConfig:()=>ke,deleteConfig:()=>Ie,getAuthToken:()=>P,getBreakGlassSession:()=>$e,getConfig:()=>l,getConfigValue:()=>be,getProjectConfig:()=>Pe,getRcConfig:()=>Re,setBreakGlassSession:()=>Ae,setConfig:()=>je});var q,g,R,A,i,$,l,be,je,ke,Ie,P,$e,Ae,Pe,Re,w=x(()=>{q=c(require("conf")),g=c(require("fs")),R=c(require("path")),A="https://www.xtrasecurity.in/api",i=new q.default({projectName:"xtra-cli",defaults:{apiUrl:A}}),$=i.get("apiUrl");$&&!process.env.XTRA_API_URL&&($.includes("localhost")||$.includes("xtra-security.vercel.app"))&&i.set("apiUrl",A);l=()=>({...i.store,apiUrl:process.env.XTRA_API_URL||i.get("apiUrl")||A}),be=e=>i.get(e),je=(e,t)=>{if(i.set(e,t),e==="token"&&process.platform!=="win32"&&g.default.existsSync(i.path))try{g.default.chmodSync(i.path,384)}catch{}},ke=()=>i.clear(),Ie=e=>i.delete(e),P=()=>i.get("token"),$e=()=>i.get("breakGlassSessionId"),Ae=e=>{e?i.set("breakGlassSessionId",e):i.delete("breakGlassSessionId")},Pe=async()=>{try{let e=R.default.join(process.cwd(),"xtra.json");if(g.default.existsSync(e)){let t=g.default.readFileSync(e,"utf-8");return JSON.parse(t)}return null}catch{return null}},Re=()=>{let e={};try{let t=R.default.join(process.cwd(),".xtrarc");g.default.existsSync(t)&&(e=JSON.parse(g.default.readFileSync(t,"utf-8")))}catch{}return{project:e.project||i.get("project")||"",env:e.env||i.get("env")||"development",branch:e.branch||i.get("branch")||"main"}}});function K(){let e=G.get("masterKey");e||(e=u.default.randomBytes(32).toString("hex"),G.set("masterKey",e));let t=(0,V.machineIdSync)();return u.default.createHash("sha256").update(e+t).digest()}var u,F,V,X,G,z,H,W=x(()=>{u=c(require("crypto")),F=c(require("conf")),V=require("node-machine-id"),X="aes-256-gcm",G=new F.default({projectName:"xtra-cli-crypto"});z=e=>{let t=u.default.randomBytes(16),s=K(),r=u.default.createCipheriv(X,s,t),n=r.update(e,"utf8","hex");n+=r.final("hex");let a=r.getAuthTag();return{iv:t.toString("hex"),content:n,tag:a.toString("hex")}},H=e=>{let t=K(),s=Buffer.from(e.iv,"hex"),r=Buffer.from(e.tag,"hex"),n=u.default.createDecipheriv(X,t,s);n.setAuthTag(r);let a=n.update(e.content,"hex","utf8");return a+=n.final("utf8"),a}});var Y,Q,Z,Le,o,ee,te=x(()=>{Y=c(require("axios"));w();Q=c(require("fs")),Z=c(require("path")),Le=JSON.parse(Q.readFileSync(Z.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:e}=l(),t=P();return Y.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":Le.version,...t?{Authorization:`Bearer ${t}`}:{}}})},ee={login:async(e,t,s)=>(await o().post("/auth/cli-login",{email:e,password:t,apiKey:s})).data,getSecrets:async(e,t,s="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets?branch=${s}`)).data,getSecretVersions:async(e,t,s="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(e,t,s,r,n="main")=>(await o().post(`/projects/${e}/envs/${t}/secrets`,{secrets:s,expectedVersions:r,branch:n})).data,getSecretDetails:async(e,t,s,r="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets/${s}?branch=${r}`)).data,syncLogs:async e=>(await o().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,s,r,n,a)=>(await o().post(`/projects/${e}/envs/${t}/secrets/link`,{key:s,sourceProjectId:r,sourceEnv:n,sourceKey:a})).data,rotateSecret:async(e,t,s,r,n)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/rotate`,{strategy:r,parsedNewValue:n})).data,promoteSecret:async(e,t,s)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await o().get("/audit/verify")).data,exportAuditLogs:async(e,t,s,r)=>{let n={format:e};return t&&(n.startDate=t),s&&(n.endDate=s),r&&(n.projectId=r),(await o().post("/audit/export",n,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,s,r)=>(await o().post("/access/request",{projectId:e,secretId:r,reason:t,duration:s})).data,approveAccess:async(e,t)=>(await o().post("/access/approve",{requestId:e,decision:t})).data,listAccessRequests:async e=>{let{workspace:t}=l(),s=t?`&workspaceId=${t}`:"";return(await o().get(`/access/list?mode=${e}${s}`)).data},getAccessRequestStatus:async e=>(await o().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=l(),t=e?`/project?workspaceId=${e}`:"/project";return(await o().get(t)).data},getBranches:async e=>(await o().get(`/branch?projectId=${e}`)).data,createBranch:async(e,t,s)=>(await o().post("/branch",{projectId:e,name:t,description:s})).data,deleteBranch:async e=>(await o().delete(`/branch/${e}`)).data,updateBranch:async(e,t)=>(await o().put("/branch",{id:e,...t})).data,getRoles:async()=>(await o().get("/admin/roles")).data,getUsers:async e=>{let t=e?`?teamId=${e}`:"";return(await o().get(`/admin/users${t}`)).data},setUserRole:async(e,t,s)=>(await o().put("/admin/users/role",{email:e,role:t,teamId:s})).data,getIntegrationStatus:async e=>(await o().get(`/integrations/${e}`)).data,getIntegrationRepos:async e=>(await o().get(`/integrations/${e}/sync`)).data.repos,syncSecretsToGithub:async e=>(await o().post("/integrations/github/sync",e)).data,exportKubernetesSecret:async(e,t)=>{let s=new URLSearchParams(t).toString();return(await o().get(`/projects/${e}/kubernetes?${s}`)).data},getSecretHistory:async(e,t,s)=>(await o().get(`/projects/${e}/envs/${t}/secrets/${s}/history`)).data,rollbackSecret:async(e,t,s,r)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/history`,{version:r})).data,cloneEnvironment:async(e,t,s,r,n)=>(await o().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:s,overwrite:r,branch:n})).data,generateJitLink:async e=>(await o().post("/jit/generate",e)).data,claimJitLink:async e=>(await o().post("/jit/claim",{token:e})).data,getJitInfo:async e=>(await o().get(`/jit/${e}`)).data,post:async(e,t)=>(await o().post(e,t)).data}});var ne={};k(ne,{loadAuditLogs:()=>b,logAudit:()=>Te,saveAuditLogs:()=>j,syncAuditLogs:()=>_e});function Ue(e){if(!e||typeof e!="object")return e;let t={...e},s=["password","token","secret","value","apiKey","secretValue"];for(let r of Object.keys(t))s.some(n=>r.toLowerCase().includes(n))&&(t[r]="[REDACTED]");return t}async function re(e){try{if(!l().token)return!1;let s={action:e.action,entity:"cli-project",entityId:e.projectId||"global",details:Ue(e.details),timestamp:e.timestamp,projectId:e.projectId};return await ee.post("/audit/cli-logs",{logs:[s]}),!0}catch(t){return process.env.DEBUG&&console.error("Failed to send audit log to backend:",t),!1}}function Te(e,t,s,r){let n={id:(0,se.v4)(),timestamp:new Date().toISOString(),action:e,projectId:t,environment:s,details:r,synced:!1},a=b();a.push(n),j(a),re(n).then(v=>{if(v){n.synced=!0;let h=b(),E=h.findIndex(me=>me.id===n.id);E!==-1&&(h[E].synced=!0,j(h))}}).catch(()=>{})}function b(){if(!f.default.existsSync(T))return[];try{let e=f.default.readFileSync(T,"utf-8"),t=JSON.parse(e),s=H(t);return JSON.parse(s)}catch{return[]}}function j(e){f.default.existsSync(U)||f.default.mkdirSync(U,{recursive:!0});let t=JSON.stringify(e),s=z(t);f.default.writeFileSync(T,JSON.stringify(s),"utf-8")}async function _e(){let e=b(),t=e.filter(r=>!r.synced);if(t.length===0)return 0;let s=0;for(let r of t)await re(r)&&(r.synced=!0,s++);return s>0&&j(e),s}var f,_,se,U,T,oe=x(()=>{f=c(require("fs")),_=c(require("path")),se=require("uuid");W();w();te();U=_.default.join(process.cwd(),".xtra"),T=_.default.join(U,"audit.enc")});var ie=require("commander"),ae=c(require("fs")),ce=c(require("path")),pe=c(require("update-notifier")),p=new ie.Command,de=JSON.parse(ae.readFileSync(ce.join(__dirname,"../../package.json"),"utf-8"));(0,pe.default)({pkg:de}).notify();p.name("xtra").description("XtraSync CLI - Secure Runtime Secret Injection").version(de.version).option("--profile <name>","Use a specific named profile for this command");var D={login:{module:"../commands/login",export:"loginCommand",desc:"Authenticate with XtraSecurity"},logout:{module:"../commands/logout",export:"logoutCommand",desc:"Logout and clear local token"},run:{module:"../commands/run",export:"runCommand",desc:"Run a command with injected secrets"},"jit-run":{module:"../commands/jit-run",export:"jitRunCommand",desc:"Run with temporary JIT access"},secrets:{module:"../commands/secrets",export:"secretsCommand",desc:"Manage secrets (List, Set, Delete)"},generate:{module:"../commands/generate",export:"generateCommand",desc:"Generate secret values"},status:{module:"../commands/status",export:"statusCommand",desc:"Check synchronization status"},diff:{module:"../commands/diff",export:"diffCommand",desc:"Show differences between environments"},rollback:{module:"../commands/rollback",export:"rollbackCommand",desc:"Rollback secrets to a previous version"},scan:{module:"../commands/scan",export:"scanCommand",desc:"Scan codebase for hardcoded secrets"},logs:{module:"../commands/logs",export:"logsCommand",desc:"View activity and audit logs"},export:{module:"../commands/export",export:"exportCommand",desc:"Export secrets to various formats"},import:{module:"../commands/import",export:"importCommand",desc:"Import secrets from files"},rotate:{module:"../commands/rotate",export:"rotateCommand",desc:"Trigger secret rotation"},audit:{module:"../commands/audit",export:"auditCommand",desc:"Manage audit configurations"},access:{module:"../commands/access",export:"accessCommand",desc:"Manage Just-in-Time access requests"},branch:{module:"../commands/branch",export:"branchCommand",desc:"Manage secret branches"},checkout:{module:"../commands/checkout",export:"checkoutCommand",desc:"Switch between branches"},project:{module:"../commands/project",export:"projectCommand",desc:"Manage project configuration"},admin:{module:"../commands/admin",export:"adminCommand",desc:"Administrative tasks (Roles, Teams)"},integration:{module:"../commands/integration",export:"integrationCommand",desc:"Sync secrets to external platforms"},kubernetes:{module:"../commands/integration",export:"kubernetesCommand",desc:"Kubernetes specific integrations"},history:{module:"../commands/history",export:"historyCommand",desc:"View secret version history"},env:{module:"../commands/env",export:"envCommand",desc:"Manage environments"},ui:{module:"../commands/ui",export:"uiCommand",desc:"Launch interactive TUI dashboard"},completion:{module:"../commands/completion",export:"completionCommand",desc:"Generate shell completion"},profile:{module:"../commands/profile",export:"profileCommand",desc:"Manage CLI profiles"},ci:{module:"../commands/ci",export:"ciCommand",desc:"CI/CD integration helpers"},template:{module:"../commands/template",export:"templateCommand",desc:"Manage secret templates"},doctor:{module:"../commands/doctor",export:"doctorCommand",desc:"Diagnose CLI configuration issues"},init:{module:"../commands/init",export:"initCommand",desc:"Initialize XtraSecurity in current directory"},watch:{module:"../commands/watch",export:"watchCommand",desc:"Watch for secret changes in real-time"},simulate:{module:"../commands/simulate",export:"simulateCommand",desc:"Simulate secret access behavior"},local:{module:"../commands/local",export:"localCommand",desc:"Manage local secret overrides"}},De=new Set(["login","logout","completion","doctor","help","jit-run"]);p.hook("preSubcommand",(e,t)=>{let s=e.opts().profile;if(s){let{useProfile:v}=(J(),S(M));try{v(s)}catch(h){console.error(`\x1B[31mError: ${h.message}\x1B[0m`),process.exit(1)}}let r=t.name();if(De.has(r))return;let{getAuthToken:n}=(w(),S(L));n()||(console.error(`\x1B[31m\u2716 You are not logged in.\x1B[0m
|
|
3
|
+
Run \x1B[36mxtra login\x1B[0m to authenticate before using this command.
|
|
4
|
+
`),process.exit(1))});p.hook("postAction",(e,t)=>{let{getBreakGlassSession:s}=(w(),S(L)),r=s();if(r){let{logAudit:n}=(oe(),S(ne));n("BREAK_GLASS_CLI_ACTIVITY",null,null,{command:t.name(),args:t.args,options:t.opts(),sessionId:r})}});var Ee=process.argv.slice(2),y=Ee.find(e=>!e.startsWith("-"));if(y&&D[y]){let e=D[y];try{let s=require(e.module)[e.export||`${y}Command`];s?p.addCommand(s):p.command(y).description(e.desc||"")}catch{p.command(y).description(e.desc||"")}}else Object.entries(D).forEach(([e,t])=>{p.command(e).description(t.desc||"")});p.parse(process.argv);
|
package/dist/commands/access.js
CHANGED
|
@@ -1,184 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.accessCommand = void 0;
|
|
7
|
-
const commander_1 = require("commander");
|
|
8
|
-
const api_1 = require("../lib/api");
|
|
9
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
const ora_1 = __importDefault(require("ora"));
|
|
11
|
-
const table_1 = require("table");
|
|
12
|
-
const config_1 = require("../lib/config");
|
|
13
|
-
const logAuditSafe = (event, projectId, meta) => {
|
|
14
|
-
try {
|
|
15
|
-
const { logAudit } = require("../lib/audit");
|
|
16
|
-
logAudit(event, projectId, null, meta);
|
|
17
|
-
}
|
|
18
|
-
catch (e) { }
|
|
19
|
-
};
|
|
20
|
-
const safeError = (error) => error?.response?.data?.error || error?.message || "Unknown error";
|
|
21
|
-
exports.accessCommand = new commander_1.Command("access")
|
|
22
|
-
.description("Manage Just-in-Time access requests")
|
|
23
|
-
.addCommand(new commander_1.Command("request")
|
|
24
|
-
.description("Request access to a secret or project")
|
|
25
|
-
.option("-p, --project <projectId>", "Project ID")
|
|
26
|
-
.option("-s, --secret <secretId>", "Specific Secret ID (optional)")
|
|
27
|
-
.requiredOption("-d, --duration <minutes>", "Duration in minutes")
|
|
28
|
-
.requiredOption("-r, --reason <text>", "Reason for access")
|
|
29
|
-
.action(async (options) => {
|
|
30
|
-
let { project, secret, duration, reason } = options;
|
|
31
|
-
if (!project) {
|
|
32
|
-
project = (0, config_1.getRcConfig)().project;
|
|
33
|
-
}
|
|
34
|
-
if (!project) {
|
|
35
|
-
console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
const spinner = (0, ora_1.default)("Submitting access request...").start();
|
|
39
|
-
try {
|
|
40
|
-
const result = await api_1.api.requestAccess(project, reason, parseInt(duration), secret);
|
|
41
|
-
spinner.succeed(chalk_1.default.green(`Access request submitted! (ID: ${result.requestId})`));
|
|
42
|
-
console.log(chalk_1.default.yellow(`Status: ${result.status}`));
|
|
43
|
-
// Audit log
|
|
44
|
-
logAuditSafe("ACCESS_REQUESTED", project, { requestId: result.requestId, secretId: secret, duration, reason });
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
spinner.fail("Request failed");
|
|
48
|
-
console.error(chalk_1.default.red(safeError(error)));
|
|
49
|
-
}
|
|
50
|
-
}))
|
|
51
|
-
.addCommand(new commander_1.Command("list")
|
|
52
|
-
.description("List access requests")
|
|
53
|
-
.option("--pending", "Show pending approvals (for admins)", false)
|
|
54
|
-
.action(async (options) => {
|
|
55
|
-
try {
|
|
56
|
-
const mode = options.pending ? "pending" : "my";
|
|
57
|
-
const requests = await api_1.api.listAccessRequests(mode);
|
|
58
|
-
if (requests.length === 0) {
|
|
59
|
-
console.log(chalk_1.default.yellow("No requests found."));
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
const data = [
|
|
63
|
-
[chalk_1.default.bold("ID"), chalk_1.default.bold("User"), chalk_1.default.bold("Target"), chalk_1.default.bold("Duration"), chalk_1.default.bold("Reason"), chalk_1.default.bold("Status")]
|
|
64
|
-
];
|
|
65
|
-
requests.forEach((r) => {
|
|
66
|
-
const target = r.secret ? `Secret: ${r.secret.key}` : `Project: ${r.projectId}`;
|
|
67
|
-
data.push([
|
|
68
|
-
r.id,
|
|
69
|
-
r.user.email,
|
|
70
|
-
target,
|
|
71
|
-
`${r.duration}m`,
|
|
72
|
-
r.reason,
|
|
73
|
-
r.status === "pending" ? chalk_1.default.yellow(r.status) : r.status === "approved" ? chalk_1.default.green(r.status) : chalk_1.default.red(r.status)
|
|
74
|
-
]);
|
|
75
|
-
});
|
|
76
|
-
console.log((0, table_1.table)(data));
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
console.error(chalk_1.default.red("Failed to list requests: " + safeError(error)));
|
|
80
|
-
}
|
|
81
|
-
}))
|
|
82
|
-
.addCommand(new commander_1.Command("approve")
|
|
83
|
-
.description("Approve or reject a request")
|
|
84
|
-
.argument("<requestId>", "Request ID")
|
|
85
|
-
.requiredOption("--decision <decision>", "approved or rejected")
|
|
86
|
-
.action(async (requestId, options) => {
|
|
87
|
-
// Validate decision value
|
|
88
|
-
const validDecisions = ["approved", "rejected"];
|
|
89
|
-
if (!validDecisions.includes(options.decision.toLowerCase())) {
|
|
90
|
-
console.error(chalk_1.default.red(`Invalid decision: '${options.decision}'. Must be 'approved' or 'rejected'.`));
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
const spinner = (0, ora_1.default)("Processing decision...").start();
|
|
94
|
-
try {
|
|
95
|
-
const result = await api_1.api.approveAccess(requestId, options.decision);
|
|
96
|
-
spinner.succeed(chalk_1.default.green(`Request ${options.decision}!`));
|
|
97
|
-
if (result.expiresAt) {
|
|
98
|
-
console.log(chalk_1.default.blue(`Access granted until: ${new Date(result.expiresAt).toLocaleString()}`));
|
|
99
|
-
}
|
|
100
|
-
// Audit log — privileged action
|
|
101
|
-
logAuditSafe(options.decision === "approved" ? "ACCESS_APPROVED" : "ACCESS_REJECTED", null, { requestId, decision: options.decision, expiresAt: result.expiresAt });
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
spinner.fail("Operation failed");
|
|
105
|
-
console.error(chalk_1.default.red(safeError(error)));
|
|
106
|
-
}
|
|
107
|
-
}))
|
|
108
|
-
.addCommand(new commander_1.Command("jit-generate")
|
|
109
|
-
.description("Generate a shareable JIT access link for a project")
|
|
110
|
-
.option("-p, --project <projectId>", "Project ID")
|
|
111
|
-
.requiredOption("-d, --duration <minutes>", "Access duration in minutes")
|
|
112
|
-
.option("-b, --branch <branchId>", "Scope to branch ID")
|
|
113
|
-
.option("-e, --env <environment>", "Scope to environment (production, staging, development)")
|
|
114
|
-
.option("-s, --secrets <ids>", "Comma-separated secret IDs to scope access")
|
|
115
|
-
.option("--label <text>", "Label for the JIT link")
|
|
116
|
-
.option("--max-uses <count>", "Maximum number of uses", "1")
|
|
117
|
-
.option("--expires <hours>", "Link expiry in hours", "24")
|
|
118
|
-
.action(async (options) => {
|
|
119
|
-
let { project, duration, branch, env, secrets, label, maxUses, expires } = options;
|
|
120
|
-
if (!project) {
|
|
121
|
-
project = (0, config_1.getRcConfig)().project;
|
|
122
|
-
}
|
|
123
|
-
if (!project) {
|
|
124
|
-
console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
|
|
125
|
-
process.exit(1);
|
|
126
|
-
}
|
|
127
|
-
const spinner = (0, ora_1.default)("Generating JIT link...").start();
|
|
128
|
-
try {
|
|
129
|
-
const opts = {
|
|
130
|
-
projectId: project,
|
|
131
|
-
duration: parseInt(duration),
|
|
132
|
-
maxUses: parseInt(maxUses),
|
|
133
|
-
expiresInHours: parseInt(expires),
|
|
134
|
-
};
|
|
135
|
-
if (branch)
|
|
136
|
-
opts.branchId = branch;
|
|
137
|
-
if (env)
|
|
138
|
-
opts.environment = env;
|
|
139
|
-
if (secrets)
|
|
140
|
-
opts.secretIds = secrets.split(",").map((s) => s.trim());
|
|
141
|
-
if (label)
|
|
142
|
-
opts.label = label;
|
|
143
|
-
const result = await api_1.api.generateJitLink(opts);
|
|
144
|
-
spinner.succeed(chalk_1.default.green("JIT link generated!"));
|
|
145
|
-
console.log();
|
|
146
|
-
console.log(chalk_1.default.bold(" Shareable URL:"));
|
|
147
|
-
console.log(chalk_1.default.cyan(` ${result.url}`));
|
|
148
|
-
console.log();
|
|
149
|
-
console.log(chalk_1.default.bold(" CLI Command:"));
|
|
150
|
-
console.log(chalk_1.default.yellow(` ${result.cliCommand}`));
|
|
151
|
-
console.log();
|
|
152
|
-
console.log(chalk_1.default.dim(` Expires: ${new Date(result.expiresAt).toLocaleString()}`));
|
|
153
|
-
console.log(chalk_1.default.dim(` Duration: ${result.duration} minutes | Access: ${result.accessLevel}-only`));
|
|
154
|
-
logAuditSafe("JIT_LINK_GENERATED", project, { token: result.token, duration, env, branch });
|
|
155
|
-
}
|
|
156
|
-
catch (error) {
|
|
157
|
-
spinner.fail("Failed to generate JIT link");
|
|
158
|
-
console.error(chalk_1.default.red(safeError(error)));
|
|
159
|
-
}
|
|
160
|
-
}))
|
|
161
|
-
.addCommand(new commander_1.Command("jit")
|
|
162
|
-
.description("Claim a JIT access link (request read-only access)")
|
|
163
|
-
.argument("<token>", "JIT link token")
|
|
164
|
-
.action(async (token) => {
|
|
165
|
-
const spinner = (0, ora_1.default)("Claiming JIT link...").start();
|
|
166
|
-
try {
|
|
167
|
-
// First, show link info
|
|
168
|
-
const info = await api_1.api.getJitInfo(token);
|
|
169
|
-
spinner.text = `Requesting access to "${info.projectName}"...`;
|
|
170
|
-
const result = await api_1.api.claimJitLink(token);
|
|
171
|
-
spinner.succeed(chalk_1.default.green("Access request submitted!"));
|
|
172
|
-
console.log();
|
|
173
|
-
console.log(chalk_1.default.yellow(" Status: Pending admin/owner approval"));
|
|
174
|
-
console.log(chalk_1.default.dim(` Request ID: ${result.requestId}`));
|
|
175
|
-
console.log(chalk_1.default.dim(` Access: ${result.accessLevel}-only for ${result.duration} minutes`));
|
|
176
|
-
console.log();
|
|
177
|
-
console.log(chalk_1.default.dim(" Run 'xtra access list' to check request status."));
|
|
178
|
-
logAuditSafe("JIT_LINK_CLAIMED", null, { token, requestId: result.requestId });
|
|
179
|
-
}
|
|
180
|
-
catch (error) {
|
|
181
|
-
spinner.fail("Failed to claim JIT link");
|
|
182
|
-
console.error(chalk_1.default.red(safeError(error)));
|
|
183
|
-
}
|
|
184
|
-
}));
|
|
1
|
+
var ce=Object.create;var A=Object.defineProperty;var pe=Object.getOwnPropertyDescriptor;var de=Object.getOwnPropertyNames;var ge=Object.getPrototypeOf,le=Object.prototype.hasOwnProperty;var C=(e,t)=>()=>(e&&(t=e(e=0)),t);var D=(e,t)=>{for(var s in t)A(e,s,{get:t[s],enumerable:!0})},G=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of de(t))!le.call(e,r)&&r!==s&&A(e,r,{get:()=>t[r],enumerable:!(n=pe(t,r))||n.enumerable});return e};var p=(e,t,s)=>(s=e!=null?ce(ge(e)):{},G(t||!e||!e.__esModule?A(s,"default",{value:e,enumerable:!0}):s,e)),R=e=>G(A({},"__esModule",{value:!0}),e);var V={};D(V,{clearConfig:()=>ye,deleteConfig:()=>fe,getAuthToken:()=>J,getBreakGlassSession:()=>he,getConfig:()=>y,getConfigValue:()=>ue,getProjectConfig:()=>we,getRcConfig:()=>w,setBreakGlassSession:()=>b,setConfig:()=>me});var F,m,P,U,c,T,y,ue,me,ye,fe,J,he,b,we,w,x=C(()=>{F=p(require("conf")),m=p(require("fs")),P=p(require("path")),U="https://www.xtrasecurity.in/api",c=new F.default({projectName:"xtra-cli",defaults:{apiUrl:U}}),T=c.get("apiUrl");T&&!process.env.XTRA_API_URL&&(T.includes("localhost")||T.includes("xtra-security.vercel.app"))&&c.set("apiUrl",U);y=()=>({...c.store,apiUrl:process.env.XTRA_API_URL||c.get("apiUrl")||U}),ue=e=>c.get(e),me=(e,t)=>{if(c.set(e,t),e==="token"&&process.platform!=="win32"&&m.default.existsSync(c.path))try{m.default.chmodSync(c.path,384)}catch{}},ye=()=>c.clear(),fe=e=>c.delete(e),J=()=>c.get("token"),he=()=>c.get("breakGlassSessionId"),b=e=>{e?c.set("breakGlassSessionId",e):c.delete("breakGlassSessionId")},we=async()=>{try{let e=P.default.join(process.cwd(),"xtra.json");if(m.default.existsSync(e)){let t=m.default.readFileSync(e,"utf-8");return JSON.parse(t)}return null}catch{return null}},w=()=>{let e={};try{let t=P.default.join(process.cwd(),".xtrarc");m.default.existsSync(t)&&(e=JSON.parse(m.default.readFileSync(t,"utf-8")))}catch{}return{project:e.project||c.get("project")||"",env:e.env||c.get("env")||"development",branch:e.branch||c.get("branch")||"main"}}});var K,M,H,Ie,i,g,O=C(()=>{K=p(require("axios"));x();M=p(require("fs")),H=p(require("path")),Ie=JSON.parse(M.readFileSync(H.join(__dirname,"../../package.json"),"utf-8")),i=()=>{let{apiUrl:e}=y(),t=J();return K.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":Ie.version,...t?{Authorization:`Bearer ${t}`}:{}}})},g={login:async(e,t,s)=>(await i().post("/auth/cli-login",{email:e,password:t,apiKey:s})).data,getSecrets:async(e,t,s="main")=>(await i().get(`/projects/${e}/envs/${t}/secrets?branch=${s}`)).data,getSecretVersions:async(e,t,s="main")=>(await i().get(`/projects/${e}/envs/${t}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(e,t,s,n,r="main")=>(await i().post(`/projects/${e}/envs/${t}/secrets`,{secrets:s,expectedVersions:n,branch:r})).data,getSecretDetails:async(e,t,s,n="main")=>(await i().get(`/projects/${e}/envs/${t}/secrets/${s}?branch=${n}`)).data,syncLogs:async e=>(await i().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,s,n,r,a)=>(await i().post(`/projects/${e}/envs/${t}/secrets/link`,{key:s,sourceProjectId:n,sourceEnv:r,sourceKey:a})).data,rotateSecret:async(e,t,s,n,r)=>(await i().post(`/projects/${e}/envs/${t}/secrets/${s}/rotate`,{strategy:n,parsedNewValue:r})).data,promoteSecret:async(e,t,s)=>(await i().post(`/projects/${e}/envs/${t}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await i().get("/audit/verify")).data,exportAuditLogs:async(e,t,s,n)=>{let r={format:e};return t&&(r.startDate=t),s&&(r.endDate=s),n&&(r.projectId=n),(await i().post("/audit/export",r,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,s,n)=>(await i().post("/access/request",{projectId:e,secretId:n,reason:t,duration:s})).data,approveAccess:async(e,t)=>(await i().post("/access/approve",{requestId:e,decision:t})).data,listAccessRequests:async e=>{let{workspace:t}=y(),s=t?`&workspaceId=${t}`:"";return(await i().get(`/access/list?mode=${e}${s}`)).data},getAccessRequestStatus:async e=>(await i().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=y(),t=e?`/project?workspaceId=${e}`:"/project";return(await i().get(t)).data},getBranches:async e=>(await i().get(`/branch?projectId=${e}`)).data,createBranch:async(e,t,s)=>(await i().post("/branch",{projectId:e,name:t,description:s})).data,deleteBranch:async e=>(await i().delete(`/branch/${e}`)).data,updateBranch:async(e,t)=>(await i().put("/branch",{id:e,...t})).data,getRoles:async()=>(await i().get("/admin/roles")).data,getUsers:async e=>{let t=e?`?teamId=${e}`:"";return(await i().get(`/admin/users${t}`)).data},setUserRole:async(e,t,s)=>(await i().put("/admin/users/role",{email:e,role:t,teamId:s})).data,getIntegrationStatus:async e=>(await i().get(`/integrations/${e}`)).data,getIntegrationRepos:async e=>(await i().get(`/integrations/${e}/sync`)).data.repos,syncSecretsToGithub:async e=>(await i().post("/integrations/github/sync",e)).data,exportKubernetesSecret:async(e,t)=>{let s=new URLSearchParams(t).toString();return(await i().get(`/projects/${e}/kubernetes?${s}`)).data},getSecretHistory:async(e,t,s)=>(await i().get(`/projects/${e}/envs/${t}/secrets/${s}/history`)).data,rollbackSecret:async(e,t,s,n)=>(await i().post(`/projects/${e}/envs/${t}/secrets/${s}/history`,{version:n})).data,cloneEnvironment:async(e,t,s,n,r)=>(await i().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:s,overwrite:n,branch:r})).data,generateJitLink:async e=>(await i().post("/jit/generate",e)).data,claimJitLink:async e=>(await i().post("/jit/claim",{token:e})).data,getJitInfo:async e=>(await i().get(`/jit/${e}`)).data,post:async(e,t)=>(await i().post(e,t)).data}});function W(){let e=z.get("masterKey");e||(e=I.default.randomBytes(32).toString("hex"),z.set("masterKey",e));let t=(0,Y.machineIdSync)();return I.default.createHash("sha256").update(e+t).digest()}var I,X,Y,Q,z,Z,ee,te=C(()=>{I=p(require("crypto")),X=p(require("conf")),Y=require("node-machine-id"),Q="aes-256-gcm",z=new X.default({projectName:"xtra-cli-crypto"});Z=e=>{let t=I.default.randomBytes(16),s=W(),n=I.default.createCipheriv(Q,s,t),r=n.update(e,"utf8","hex");r+=n.final("hex");let a=n.getAuthTag();return{iv:t.toString("hex"),content:r,tag:a.toString("hex")}},ee=e=>{let t=W(),s=Buffer.from(e.iv,"hex"),n=Buffer.from(e.tag,"hex"),r=I.default.createDecipheriv(Q,t,s);r.setAuthTag(n);let a=r.update(e.content,"hex","utf8");return a+=r.final("utf8"),a}});var re={};D(re,{loadAuditLogs:()=>L,logAudit:()=>Se,saveAuditLogs:()=>E,syncAuditLogs:()=>ve});function je(e){if(!e||typeof e!="object")return e;let t={...e},s=["password","token","secret","value","apiKey","secretValue"];for(let n of Object.keys(t))s.some(r=>n.toLowerCase().includes(r))&&(t[n]="[REDACTED]");return t}async function ne(e){try{if(!y().token)return!1;let s={action:e.action,entity:"cli-project",entityId:e.projectId||"global",details:je(e.details),timestamp:e.timestamp,projectId:e.projectId};return await g.post("/audit/cli-logs",{logs:[s]}),!0}catch(t){return process.env.DEBUG&&console.error("Failed to send audit log to backend:",t),!1}}function Se(e,t,s,n){let r={id:(0,se.v4)(),timestamp:new Date().toISOString(),action:e,projectId:t,environment:s,details:n,synced:!1},a=L();a.push(r),E(a),ne(r).then(d=>{if(d){r.synced=!0;let v=L(),k=v.findIndex($=>$.id===r.id);k!==-1&&(v[k].synced=!0,E(v))}}).catch(()=>{})}function L(){if(!j.default.existsSync(B))return[];try{let e=j.default.readFileSync(B,"utf-8"),t=JSON.parse(e),s=ee(t);return JSON.parse(s)}catch{return[]}}function E(e){j.default.existsSync(N)||j.default.mkdirSync(N,{recursive:!0});let t=JSON.stringify(e),s=Z(t);j.default.writeFileSync(B,JSON.stringify(s),"utf-8")}async function ve(){let e=L(),t=e.filter(n=>!n.synced);if(t.length===0)return 0;let s=0;for(let n of t)await ne(n)&&(n.synced=!0,s++);return s>0&&E(e),s}var j,_,se,N,B,oe=C(()=>{j=p(require("fs")),_=p(require("path")),se=require("uuid");te();x();O();N=_.default.join(process.cwd(),".xtra"),B=_.default.join(N,"audit.enc")});var xe={};D(xe,{accessCommand:()=>be});module.exports=R(xe);var l=require("commander");O();var o=p(require("chalk")),f=p(require("ora")),ie=require("table");x();var q=(e,t,s)=>{try{let{logAudit:n}=(oe(),R(re));n(e,t,null,s)}catch{}},S=e=>e?.response?.data?.error||e?.message||"Unknown error",be=new l.Command("access").description("Manage Just-in-Time access requests").addCommand(new l.Command("request").description("Request access to a secret or project").option("-p, --project <projectId>","Project ID").option("-s, --secret <secretId>","Specific Secret ID (optional)").requiredOption("-d, --duration <minutes>","Duration in minutes").requiredOption("-r, --reason <text>","Reason for access").action(async e=>{let{project:t,secret:s,duration:n,reason:r}=e;t||(t=w().project),t||(console.error(o.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let a=(0,f.default)("Submitting access request...").start();try{let d=await g.requestAccess(t,r,parseInt(n),s);a.succeed(o.default.green(`Access request submitted! (ID: ${d.requestId})`)),console.log(o.default.yellow(`Status: ${d.status}`)),q("ACCESS_REQUESTED",t,{requestId:d.requestId,secretId:s,duration:n,reason:r})}catch(d){a.fail("Request failed"),console.error(o.default.red(S(d)))}})).addCommand(new l.Command("list").description("List access requests").option("--pending","Show pending approvals (for admins)",!1).action(async e=>{try{let t=e.pending?"pending":"my",s=await g.listAccessRequests(t);if(s.length===0){console.log(o.default.yellow("No requests found."));return}let n=[[o.default.bold("ID"),o.default.bold("User"),o.default.bold("Target"),o.default.bold("Duration"),o.default.bold("Reason"),o.default.bold("Status")]];s.forEach(r=>{let a=r.secret?`Secret: ${r.secret.key}`:`Project: ${r.projectId}`;n.push([r.id,r.user.email,a,`${r.duration}m`,r.reason,r.status==="pending"?o.default.yellow(r.status):r.status==="approved"?o.default.green(r.status):o.default.red(r.status)])}),console.log((0,ie.table)(n))}catch(t){console.error(o.default.red("Failed to list requests: "+S(t)))}})).addCommand(new l.Command("approve").description("Approve or reject a request").argument("<requestId>","Request ID").requiredOption("--decision <decision>","approved or rejected").action(async(e,t)=>{["approved","rejected"].includes(t.decision.toLowerCase())||(console.error(o.default.red(`Invalid decision: '${t.decision}'. Must be 'approved' or 'rejected'.`)),process.exit(1));let n=(0,f.default)("Processing decision...").start();try{let r=await g.approveAccess(e,t.decision);n.succeed(o.default.green(`Request ${t.decision}!`)),r.expiresAt&&console.log(o.default.blue(`Access granted until: ${new Date(r.expiresAt).toLocaleString()}`)),q(t.decision==="approved"?"ACCESS_APPROVED":"ACCESS_REJECTED",null,{requestId:e,decision:t.decision,expiresAt:r.expiresAt})}catch(r){n.fail("Operation failed"),console.error(o.default.red(S(r)))}})).addCommand(new l.Command("jit-generate").description("Generate a shareable JIT access link for a project").option("-p, --project <projectId>","Project ID").requiredOption("-d, --duration <minutes>","Access duration in minutes").option("-b, --branch <branchId>","Scope to branch ID").option("-e, --env <environment>","Scope to environment (production, staging, development)").option("-s, --secrets <ids>","Comma-separated secret IDs to scope access").option("--label <text>","Label for the JIT link").option("--max-uses <count>","Maximum number of uses","1").option("--expires <hours>","Link expiry in hours","24").action(async e=>{let{project:t,duration:s,branch:n,env:r,secrets:a,label:d,maxUses:v,expires:k}=e;t||(t=w().project),t||(console.error(o.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let $=(0,f.default)("Generating JIT link...").start();try{let u={projectId:t,duration:parseInt(s),maxUses:parseInt(v),expiresInHours:parseInt(k)};n&&(u.branchId=n),r&&(u.environment=r),a&&(u.secretIds=a.split(",").map(ae=>ae.trim())),d&&(u.label=d);let h=await g.generateJitLink(u);$.succeed(o.default.green("JIT link generated!")),console.log(),console.log(o.default.bold(" Shareable URL:")),console.log(o.default.cyan(` ${h.url}`)),console.log(),console.log(o.default.bold(" CLI Command:")),console.log(o.default.yellow(` ${h.cliCommand}`)),console.log(),console.log(o.default.dim(` Expires: ${new Date(h.expiresAt).toLocaleString()}`)),console.log(o.default.dim(` Duration: ${h.duration} minutes | Access: ${h.accessLevel}-only`)),q("JIT_LINK_GENERATED",t,{token:h.token,duration:s,env:r,branch:n})}catch(u){$.fail("Failed to generate JIT link"),console.error(o.default.red(S(u)))}})).addCommand(new l.Command("jit").description("Claim a JIT access link (request read-only access)").argument("<token>","JIT link token").action(async e=>{let t=(0,f.default)("Claiming JIT link...").start();try{let s=await g.getJitInfo(e);t.text=`Requesting access to "${s.projectName}"...`;let n=await g.claimJitLink(e);t.succeed(o.default.green("Access request submitted!")),console.log(),console.log(o.default.yellow(" Status: Pending admin/owner approval")),console.log(o.default.dim(` Request ID: ${n.requestId}`)),console.log(o.default.dim(` Access: ${n.accessLevel}-only for ${n.duration} minutes`)),console.log(),console.log(o.default.dim(" Run 'xtra access list' to check request status.")),q("JIT_LINK_CLAIMED",null,{token:e,requestId:n.requestId})}catch(s){t.fail("Failed to claim JIT link"),console.error(o.default.red(S(s)))}})).addCommand(new l.Command("break-glass").description("Emergency break-glass access management").addCommand(new l.Command("link").description("Link an active break-glass session from the dashboard to this CLI").requiredOption("--id <sessionId>","Active Break Glass Session ID").action(async e=>{let{id:t}=e,s=(0,f.default)("Verifying session...").start();try{let n=await g.post("/access/break-glass",{projectId:w().project,reason:"CLI Link Verification"});(n.session||n.success)&&(b(t),s.succeed(o.default.red.bold("\u{1F6A8} EMERGENCY ACCESS LINKED")),console.log(),console.log(o.default.yellow(` Session ID: ${t}`)),console.log(o.default.dim(" Permissions: Full Administrative (Root)")),console.log(o.default.dim(" Audit Status: FULL VERBOSE LOGGING ENABLED")),console.log(),console.log(o.default.cyan(" You can now run any command with elevated permissions.")),console.log(o.default.cyan(" Example: 'xtra secrets list' will now show all secrets in the project.")))}catch(n){s.fail("Link failed"),console.error(o.default.red(S(n)))}})).addCommand(new l.Command("deactivate").description("End the current break-glass session").action(async()=>{let{getBreakGlassSession:e}=(x(),R(V));if(!e()){console.log(o.default.blue("No active break-glass session found locally."));return}let s=(0,f.default)("Deactivating session...").start();try{await g.post("/access/break-glass",{projectId:w().project,deactivate:!0}),b(null),s.succeed(o.default.green("Break-glass session ended."))}catch{b(null),s.warn("Local session cleared. (API might have already expired it)")}})));0&&(module.exports={accessCommand});
|
package/dist/commands/admin.js
CHANGED
|
@@ -1,118 +1,5 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
exports.adminCommand = void 0;
|
|
7
|
-
const commander_1 = require("commander");
|
|
8
|
-
const api_1 = require("../lib/api");
|
|
9
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
const ora_1 = __importDefault(require("ora"));
|
|
11
|
-
const table_1 = require("table");
|
|
12
|
-
exports.adminCommand = new commander_1.Command("admin")
|
|
13
|
-
.description("Admin commands for user and role management");
|
|
14
|
-
// Role Management
|
|
15
|
-
const roleCommand = new commander_1.Command("role")
|
|
16
|
-
.description("Manage user roles and permissions");
|
|
17
|
-
const listRoles = async () => {
|
|
18
|
-
const spinner = (0, ora_1.default)("Fetching roles from server...").start();
|
|
19
|
-
try {
|
|
20
|
-
const roles = await api_1.api.getRoles();
|
|
21
|
-
spinner.stop();
|
|
22
|
-
console.log(chalk_1.default.bold("\nAvailable Roles:\n"));
|
|
23
|
-
const tableData = [
|
|
24
|
-
[chalk_1.default.bold("Role"), chalk_1.default.bold("Description"), chalk_1.default.bold("Permissions")]
|
|
25
|
-
];
|
|
26
|
-
roles.forEach((role) => {
|
|
27
|
-
tableData.push([
|
|
28
|
-
chalk_1.default.cyan(role.name),
|
|
29
|
-
role.description || "-",
|
|
30
|
-
Array.isArray(role.permissions) ? role.permissions.join(", ") : role.permissions || "-",
|
|
31
|
-
]);
|
|
32
|
-
});
|
|
33
|
-
console.log((0, table_1.table)(tableData));
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
spinner.fail("Failed to fetch roles");
|
|
37
|
-
const safeErr = error.response?.data?.error || error.message || "Unknown error";
|
|
38
|
-
console.error(chalk_1.default.red(safeErr));
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
roleCommand
|
|
42
|
-
.command("list")
|
|
43
|
-
.description("List all available roles")
|
|
44
|
-
.alias("ls")
|
|
45
|
-
.action(listRoles);
|
|
46
|
-
exports.adminCommand.addCommand(roleCommand);
|
|
47
|
-
// Legacy/Short support: `xtra admin roles` -> alias to `xtra admin role list`
|
|
48
|
-
exports.adminCommand
|
|
49
|
-
.command("roles")
|
|
50
|
-
.description("Alias for 'role list'")
|
|
51
|
-
.action(listRoles);
|
|
52
|
-
// List all users (admin only)
|
|
53
|
-
exports.adminCommand
|
|
54
|
-
.command("users")
|
|
55
|
-
.description("List all users with their roles")
|
|
56
|
-
.option("-t, --team <teamId>", "Filter by team ID")
|
|
57
|
-
.action(async (options) => {
|
|
58
|
-
const spinner = (0, ora_1.default)("Fetching users...").start();
|
|
59
|
-
try {
|
|
60
|
-
const users = await api_1.api.getUsers(options.team);
|
|
61
|
-
spinner.stop();
|
|
62
|
-
if (!users || users.length === 0) {
|
|
63
|
-
console.log(chalk_1.default.yellow("No users found."));
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
console.log(chalk_1.default.bold("\nUsers:\n"));
|
|
67
|
-
const tableData = [
|
|
68
|
-
[chalk_1.default.bold("Email"), chalk_1.default.bold("Name"), chalk_1.default.bold("Role"), chalk_1.default.bold("Status")]
|
|
69
|
-
];
|
|
70
|
-
users.forEach((user) => {
|
|
71
|
-
const roleColor = user.role === "owner" || user.role === "admin"
|
|
72
|
-
? chalk_1.default.magenta
|
|
73
|
-
: user.role === "viewer"
|
|
74
|
-
? chalk_1.default.gray
|
|
75
|
-
: chalk_1.default.cyan;
|
|
76
|
-
tableData.push([
|
|
77
|
-
user.email,
|
|
78
|
-
user.name || "-",
|
|
79
|
-
roleColor(user.role),
|
|
80
|
-
user.status || "active"
|
|
81
|
-
]);
|
|
82
|
-
});
|
|
83
|
-
console.log((0, table_1.table)(tableData));
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
spinner.fail("Failed to fetch users");
|
|
87
|
-
console.error(chalk_1.default.red(error.message));
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
// Set user role
|
|
91
|
-
exports.adminCommand
|
|
92
|
-
.command("set-role <email> <role>")
|
|
93
|
-
.description("Change a user's role (owner, admin, developer, viewer, guest)")
|
|
94
|
-
.option("-t, --team <teamId>", "Team ID (for team-specific role)")
|
|
95
|
-
.action(async (email, role, options) => {
|
|
96
|
-
const validRoles = ["owner", "admin", "developer", "viewer", "guest"];
|
|
97
|
-
if (!validRoles.includes(role.toLowerCase())) {
|
|
98
|
-
console.error(chalk_1.default.red(`Invalid role: ${role}`));
|
|
99
|
-
console.log(chalk_1.default.yellow(`Valid roles: ${validRoles.join(", ")}`));
|
|
100
|
-
process.exit(1);
|
|
101
|
-
}
|
|
102
|
-
const spinner = (0, ora_1.default)(`Updating role for ${email}...`).start();
|
|
103
|
-
try {
|
|
104
|
-
await api_1.api.setUserRole(email, role.toLowerCase(), options.team);
|
|
105
|
-
spinner.succeed(chalk_1.default.green(`Role updated: ${email} → ${role}`));
|
|
106
|
-
// Audit log this privileged action
|
|
107
|
-
try {
|
|
108
|
-
const { logAudit } = require("../lib/audit");
|
|
109
|
-
logAudit("ADMIN_ROLE_CHANGE", null, null, { email, newRole: role, teamId: options.team });
|
|
110
|
-
}
|
|
111
|
-
catch (e) { }
|
|
112
|
-
}
|
|
113
|
-
catch (error) {
|
|
114
|
-
spinner.fail("Failed to update role");
|
|
115
|
-
const safeErr = error.response?.data?.error || error.message || "Unknown error";
|
|
116
|
-
console.error(chalk_1.default.red(safeErr));
|
|
117
|
-
}
|
|
118
|
-
});
|
|
1
|
+
var te=Object.create;var f=Object.defineProperty;var se=Object.getOwnPropertyDescriptor;var ne=Object.getOwnPropertyNames;var re=Object.getPrototypeOf,oe=Object.prototype.hasOwnProperty;var h=(e,t)=>()=>(e&&(t=e(e=0)),t);var D=(e,t)=>{for(var s in t)f(e,s,{get:t[s],enumerable:!0})},E=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ne(t))!oe.call(e,r)&&r!==s&&f(e,r,{get:()=>t[r],enumerable:!(n=se(t,r))||n.enumerable});return e};var c=(e,t,s)=>(s=e!=null?te(re(e)):{},E(t||!e||!e.__esModule?f(s,"default",{value:e,enumerable:!0}):s,e)),N=e=>E(f({},"__esModule",{value:!0}),e);var B,j,u,S,p,O,$=h(()=>{B=c(require("conf")),j="https://www.xtrasecurity.in/api",u=new B.default({projectName:"xtra-cli",defaults:{apiUrl:j}}),S=u.get("apiUrl");S&&!process.env.XTRA_API_URL&&(S.includes("localhost")||S.includes("xtra-security.vercel.app"))&&u.set("apiUrl",j);p=()=>({...u.store,apiUrl:process.env.XTRA_API_URL||u.get("apiUrl")||j}),O=()=>u.get("token")});var T,_,F,ae,o,g,k=h(()=>{T=c(require("axios"));$();_=c(require("fs")),F=c(require("path")),ae=JSON.parse(_.readFileSync(F.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:e}=p(),t=O();return T.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":ae.version,...t?{Authorization:`Bearer ${t}`}:{}}})},g={login:async(e,t,s)=>(await o().post("/auth/cli-login",{email:e,password:t,apiKey:s})).data,getSecrets:async(e,t,s="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets?branch=${s}`)).data,getSecretVersions:async(e,t,s="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(e,t,s,n,r="main")=>(await o().post(`/projects/${e}/envs/${t}/secrets`,{secrets:s,expectedVersions:n,branch:r})).data,getSecretDetails:async(e,t,s,n="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets/${s}?branch=${n}`)).data,syncLogs:async e=>(await o().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,s,n,r,a)=>(await o().post(`/projects/${e}/envs/${t}/secrets/link`,{key:s,sourceProjectId:n,sourceEnv:r,sourceKey:a})).data,rotateSecret:async(e,t,s,n,r)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/rotate`,{strategy:n,parsedNewValue:r})).data,promoteSecret:async(e,t,s)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await o().get("/audit/verify")).data,exportAuditLogs:async(e,t,s,n)=>{let r={format:e};return t&&(r.startDate=t),s&&(r.endDate=s),n&&(r.projectId=n),(await o().post("/audit/export",r,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,s,n)=>(await o().post("/access/request",{projectId:e,secretId:n,reason:t,duration:s})).data,approveAccess:async(e,t)=>(await o().post("/access/approve",{requestId:e,decision:t})).data,listAccessRequests:async e=>{let{workspace:t}=p(),s=t?`&workspaceId=${t}`:"";return(await o().get(`/access/list?mode=${e}${s}`)).data},getAccessRequestStatus:async e=>(await o().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=p(),t=e?`/project?workspaceId=${e}`:"/project";return(await o().get(t)).data},getBranches:async e=>(await o().get(`/branch?projectId=${e}`)).data,createBranch:async(e,t,s)=>(await o().post("/branch",{projectId:e,name:t,description:s})).data,deleteBranch:async e=>(await o().delete(`/branch/${e}`)).data,updateBranch:async(e,t)=>(await o().put("/branch",{id:e,...t})).data,getRoles:async()=>(await o().get("/admin/roles")).data,getUsers:async e=>{let t=e?`?teamId=${e}`:"";return(await o().get(`/admin/users${t}`)).data},setUserRole:async(e,t,s)=>(await o().put("/admin/users/role",{email:e,role:t,teamId:s})).data,getIntegrationStatus:async e=>(await o().get(`/integrations/${e}`)).data,getIntegrationRepos:async e=>(await o().get(`/integrations/${e}/sync`)).data.repos,syncSecretsToGithub:async e=>(await o().post("/integrations/github/sync",e)).data,exportKubernetesSecret:async(e,t)=>{let s=new URLSearchParams(t).toString();return(await o().get(`/projects/${e}/kubernetes?${s}`)).data},getSecretHistory:async(e,t,s)=>(await o().get(`/projects/${e}/envs/${t}/secrets/${s}/history`)).data,rollbackSecret:async(e,t,s,n)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/history`,{version:n})).data,cloneEnvironment:async(e,t,s,n,r)=>(await o().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:s,overwrite:n,branch:r})).data,generateJitLink:async e=>(await o().post("/jit/generate",e)).data,claimJitLink:async e=>(await o().post("/jit/claim",{token:e})).data,getJitInfo:async e=>(await o().get(`/jit/${e}`)).data,post:async(e,t)=>(await o().post(e,t)).data}});function K(){let e=P.get("masterKey");e||(e=d.default.randomBytes(32).toString("hex"),P.set("masterKey",e));let t=(0,G.machineIdSync)();return d.default.createHash("sha256").update(e+t).digest()}var d,J,G,q,P,V,H,z=h(()=>{d=c(require("crypto")),J=c(require("conf")),G=require("node-machine-id"),q="aes-256-gcm",P=new J.default({projectName:"xtra-cli-crypto"});V=e=>{let t=d.default.randomBytes(16),s=K(),n=d.default.createCipheriv(q,s,t),r=n.update(e,"utf8","hex");r+=n.final("hex");let a=n.getAuthTag();return{iv:t.toString("hex"),content:r,tag:a.toString("hex")}},H=e=>{let t=K(),s=Buffer.from(e.iv,"hex"),n=Buffer.from(e.tag,"hex"),r=d.default.createDecipheriv(q,t,s);r.setAuthTag(n);let a=r.update(e.content,"hex","utf8");return a+=r.final("utf8"),a}});var Q={};D(Q,{loadAuditLogs:()=>w,logAudit:()=>ce,saveAuditLogs:()=>v,syncAuditLogs:()=>pe});function ie(e){if(!e||typeof e!="object")return e;let t={...e},s=["password","token","secret","value","apiKey","secretValue"];for(let n of Object.keys(t))s.some(r=>n.toLowerCase().includes(r))&&(t[n]="[REDACTED]");return t}async function X(e){try{if(!p().token)return!1;let s={action:e.action,entity:"cli-project",entityId:e.projectId||"global",details:ie(e.details),timestamp:e.timestamp,projectId:e.projectId};return await g.post("/audit/cli-logs",{logs:[s]}),!0}catch(t){return process.env.DEBUG&&console.error("Failed to send audit log to backend:",t),!1}}function ce(e,t,s,n){let r={id:(0,M.v4)(),timestamp:new Date().toISOString(),action:e,projectId:t,environment:s,details:n,synced:!1},a=w();a.push(r),v(a),X(r).then(y=>{if(y){r.synced=!0;let x=w(),L=x.findIndex(ee=>ee.id===r.id);L!==-1&&(x[L].synced=!0,v(x))}}).catch(()=>{})}function w(){if(!l.default.existsSync(A))return[];try{let e=l.default.readFileSync(A,"utf-8"),t=JSON.parse(e),s=H(t);return JSON.parse(s)}catch{return[]}}function v(e){l.default.existsSync(I)||l.default.mkdirSync(I,{recursive:!0});let t=JSON.stringify(e),s=V(t);l.default.writeFileSync(A,JSON.stringify(s),"utf-8")}async function pe(){let e=w(),t=e.filter(n=>!n.synced);if(t.length===0)return 0;let s=0;for(let n of t)await X(n)&&(n.synced=!0,s++);return s>0&&v(e),s}var l,C,M,I,A,W=h(()=>{l=c(require("fs")),C=c(require("path")),M=require("uuid");z();$();k();I=C.default.join(process.cwd(),".xtra"),A=C.default.join(I,"audit.enc")});var ge={};D(ge,{adminCommand:()=>m});module.exports=N(ge);var R=require("commander");k();var i=c(require("chalk")),b=c(require("ora")),U=require("table"),m=new R.Command("admin").description("Admin commands for user and role management"),Y=new R.Command("role").description("Manage user roles and permissions"),Z=async()=>{let e=(0,b.default)("Fetching roles from server...").start();try{let t=await g.getRoles();e.stop(),console.log(i.default.bold(`
|
|
2
|
+
Available Roles:
|
|
3
|
+
`));let s=[[i.default.bold("Role"),i.default.bold("Description"),i.default.bold("Permissions")]];t.forEach(n=>{s.push([i.default.cyan(n.name),n.description||"-",Array.isArray(n.permissions)?n.permissions.join(", "):n.permissions||"-"])}),console.log((0,U.table)(s))}catch(t){e.fail("Failed to fetch roles");let s=t.response?.data?.error||t.message||"Unknown error";console.error(i.default.red(s))}};Y.command("list").description("List all available roles").alias("ls").action(Z);m.addCommand(Y);m.command("roles").description("Alias for 'role list'").action(Z);m.command("users").description("List all users with their roles").option("-t, --team <teamId>","Filter by team ID").action(async e=>{let t=(0,b.default)("Fetching users...").start();try{let s=await g.getUsers(e.team);if(t.stop(),!s||s.length===0){console.log(i.default.yellow("No users found."));return}console.log(i.default.bold(`
|
|
4
|
+
Users:
|
|
5
|
+
`));let n=[[i.default.bold("Email"),i.default.bold("Name"),i.default.bold("Role"),i.default.bold("Status")]];s.forEach(r=>{let a=r.role==="owner"||r.role==="admin"?i.default.magenta:r.role==="viewer"?i.default.gray:i.default.cyan;n.push([r.email,r.name||"-",a(r.role),r.status||"active"])}),console.log((0,U.table)(n))}catch(s){t.fail("Failed to fetch users"),console.error(i.default.red(s.message))}});m.command("set-role <email> <role>").description("Change a user's role (owner, admin, developer, viewer, guest)").option("-t, --team <teamId>","Team ID (for team-specific role)").action(async(e,t,s)=>{let n=["owner","admin","developer","viewer","guest"];n.includes(t.toLowerCase())||(console.error(i.default.red(`Invalid role: ${t}`)),console.log(i.default.yellow(`Valid roles: ${n.join(", ")}`)),process.exit(1));let r=(0,b.default)(`Updating role for ${e}...`).start();try{await g.setUserRole(e,t.toLowerCase(),s.team),r.succeed(i.default.green(`Role updated: ${e} \u2192 ${t}`));try{let{logAudit:a}=(W(),N(Q));a("ADMIN_ROLE_CHANGE",null,null,{email:e,newRole:t,teamId:s.team})}catch{}}catch(a){r.fail("Failed to update role");let y=a.response?.data?.error||a.message||"Unknown error";console.error(i.default.red(y))}});0&&(module.exports={adminCommand});
|
package/dist/commands/audit.js
CHANGED
|
@@ -1,67 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.auditCommand = void 0;
|
|
7
|
-
const commander_1 = require("commander");
|
|
8
|
-
const api_1 = require("../lib/api");
|
|
9
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
const ora_1 = __importDefault(require("ora"));
|
|
11
|
-
const fs_1 = __importDefault(require("fs"));
|
|
12
|
-
exports.auditCommand = new commander_1.Command("audit")
|
|
13
|
-
.description("Manage and verify audit logs")
|
|
14
|
-
// Subcommand: Verify
|
|
15
|
-
.addCommand(new commander_1.Command("verify")
|
|
16
|
-
.description("Verify the integrity of the audit log chain (Tamper-Evident)")
|
|
17
|
-
.action(async () => {
|
|
18
|
-
const spinner = (0, ora_1.default)("Verifying audit chain integrity...").start();
|
|
19
|
-
try {
|
|
20
|
-
const result = await api_1.api.verifyAuditLogs();
|
|
21
|
-
if (result.valid) {
|
|
22
|
-
spinner.succeed(chalk_1.default.green(result.message));
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
spinner.fail(chalk_1.default.red("Verification Failed!"));
|
|
26
|
-
console.error(chalk_1.default.red(`Broken at Log ID: ${result.details?.brokenAtId}`));
|
|
27
|
-
console.error(chalk_1.default.red(`Reason: ${result.details?.reason}`));
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
spinner.fail("Verification failed");
|
|
32
|
-
if (error.response?.data?.details) {
|
|
33
|
-
console.error(chalk_1.default.red(`Chain Broken: ${error.response.data.message}`));
|
|
34
|
-
console.error(chalk_1.default.yellow(`Details: ${JSON.stringify(error.response.data.details, null, 2)}`));
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
console.error(chalk_1.default.red(error.message));
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}))
|
|
41
|
-
// Subcommand: Export
|
|
42
|
-
.addCommand(new commander_1.Command("export")
|
|
43
|
-
.description("Export audit logs for compliance (SOC2/ISO)")
|
|
44
|
-
.option("-f, --format <format>", "Output format (json, csv)", "json")
|
|
45
|
-
.option("--start <date>", "Start date (YYYY-MM-DD)")
|
|
46
|
-
.option("--end <date>", "End date (YYYY-MM-DD)")
|
|
47
|
-
.option("-o, --output <file>", "Output file path")
|
|
48
|
-
.action(async (options) => {
|
|
49
|
-
const { format, start, end, output } = options;
|
|
50
|
-
const spinner = (0, ora_1.default)(`Exporting audit logs (${format})...`).start();
|
|
51
|
-
try {
|
|
52
|
-
const data = await api_1.api.exportAuditLogs(format, start, end);
|
|
53
|
-
const content = typeof data === 'object' ? JSON.stringify(data, null, 2) : data;
|
|
54
|
-
if (output) {
|
|
55
|
-
fs_1.default.writeFileSync(output, content);
|
|
56
|
-
spinner.succeed(chalk_1.default.green(`Export saved to ${output}`));
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
spinner.stop();
|
|
60
|
-
console.log(content);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
spinner.fail("Export failed");
|
|
65
|
-
console.error(chalk_1.default.red(error.message));
|
|
66
|
-
}
|
|
67
|
-
}));
|
|
1
|
+
var I=Object.create;var d=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var R=Object.getPrototypeOf,U=Object.prototype.hasOwnProperty;var L=(e,t)=>{for(var s in t)d(e,s,{get:t[s],enumerable:!0})},$=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of A(t))!U.call(e,o)&&o!==s&&d(e,o,{get:()=>t[o],enumerable:!(n=C(t,o))||n.enumerable});return e};var c=(e,t,s)=>(s=e!=null?I(R(e)):{},$(t||!e||!e.__esModule?d(s,"default",{value:e,enumerable:!0}):s,e)),O=e=>$(d({},"__esModule",{value:!0}),e);var D={};L(D,{auditCommand:()=>_});module.exports=O(D);var u=require("commander");var v=c(require("axios"));var S=c(require("conf"));var m="https://www.xtrasecurity.in/api",g=new S.default({projectName:"xtra-cli",defaults:{apiUrl:m}}),y=g.get("apiUrl");y&&!process.env.XTRA_API_URL&&(y.includes("localhost")||y.includes("xtra-security.vercel.app"))&&g.set("apiUrl",m);var l=()=>({...g.store,apiUrl:process.env.XTRA_API_URL||g.get("apiUrl")||m});var j=()=>g.get("token");var x=c(require("fs")),b=c(require("path")),P=JSON.parse(x.readFileSync(b.join(__dirname,"../../package.json"),"utf-8")),r=()=>{let{apiUrl:e}=l(),t=j();return v.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":P.version,...t?{Authorization:`Bearer ${t}`}:{}}})},f={login:async(e,t,s)=>(await r().post("/auth/cli-login",{email:e,password:t,apiKey:s})).data,getSecrets:async(e,t,s="main")=>(await r().get(`/projects/${e}/envs/${t}/secrets?branch=${s}`)).data,getSecretVersions:async(e,t,s="main")=>(await r().get(`/projects/${e}/envs/${t}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(e,t,s,n,o="main")=>(await r().post(`/projects/${e}/envs/${t}/secrets`,{secrets:s,expectedVersions:n,branch:o})).data,getSecretDetails:async(e,t,s,n="main")=>(await r().get(`/projects/${e}/envs/${t}/secrets/${s}?branch=${n}`)).data,syncLogs:async e=>(await r().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,s,n,o,i)=>(await r().post(`/projects/${e}/envs/${t}/secrets/link`,{key:s,sourceProjectId:n,sourceEnv:o,sourceKey:i})).data,rotateSecret:async(e,t,s,n,o)=>(await r().post(`/projects/${e}/envs/${t}/secrets/${s}/rotate`,{strategy:n,parsedNewValue:o})).data,promoteSecret:async(e,t,s)=>(await r().post(`/projects/${e}/envs/${t}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await r().get("/audit/verify")).data,exportAuditLogs:async(e,t,s,n)=>{let o={format:e};return t&&(o.startDate=t),s&&(o.endDate=s),n&&(o.projectId=n),(await r().post("/audit/export",o,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,s,n)=>(await r().post("/access/request",{projectId:e,secretId:n,reason:t,duration:s})).data,approveAccess:async(e,t)=>(await r().post("/access/approve",{requestId:e,decision:t})).data,listAccessRequests:async e=>{let{workspace:t}=l(),s=t?`&workspaceId=${t}`:"";return(await r().get(`/access/list?mode=${e}${s}`)).data},getAccessRequestStatus:async e=>(await r().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=l(),t=e?`/project?workspaceId=${e}`:"/project";return(await r().get(t)).data},getBranches:async e=>(await r().get(`/branch?projectId=${e}`)).data,createBranch:async(e,t,s)=>(await r().post("/branch",{projectId:e,name:t,description:s})).data,deleteBranch:async e=>(await r().delete(`/branch/${e}`)).data,updateBranch:async(e,t)=>(await r().put("/branch",{id:e,...t})).data,getRoles:async()=>(await r().get("/admin/roles")).data,getUsers:async e=>{let t=e?`?teamId=${e}`:"";return(await r().get(`/admin/users${t}`)).data},setUserRole:async(e,t,s)=>(await r().put("/admin/users/role",{email:e,role:t,teamId:s})).data,getIntegrationStatus:async e=>(await r().get(`/integrations/${e}`)).data,getIntegrationRepos:async e=>(await r().get(`/integrations/${e}/sync`)).data.repos,syncSecretsToGithub:async e=>(await r().post("/integrations/github/sync",e)).data,exportKubernetesSecret:async(e,t)=>{let s=new URLSearchParams(t).toString();return(await r().get(`/projects/${e}/kubernetes?${s}`)).data},getSecretHistory:async(e,t,s)=>(await r().get(`/projects/${e}/envs/${t}/secrets/${s}/history`)).data,rollbackSecret:async(e,t,s,n)=>(await r().post(`/projects/${e}/envs/${t}/secrets/${s}/history`,{version:n})).data,cloneEnvironment:async(e,t,s,n,o)=>(await r().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:s,overwrite:n,branch:o})).data,generateJitLink:async e=>(await r().post("/jit/generate",e)).data,claimJitLink:async e=>(await r().post("/jit/claim",{token:e})).data,getJitInfo:async e=>(await r().get(`/jit/${e}`)).data,post:async(e,t)=>(await r().post(e,t)).data};var a=c(require("chalk")),w=c(require("ora")),k=c(require("fs")),_=new u.Command("audit").description("Manage and verify audit logs").addCommand(new u.Command("verify").description("Verify the integrity of the audit log chain (Tamper-Evident)").action(async()=>{let e=(0,w.default)("Verifying audit chain integrity...").start();try{let t=await f.verifyAuditLogs();t.valid?e.succeed(a.default.green(t.message)):(e.fail(a.default.red("Verification Failed!")),console.error(a.default.red(`Broken at Log ID: ${t.details?.brokenAtId}`)),console.error(a.default.red(`Reason: ${t.details?.reason}`)))}catch(t){e.fail("Verification failed"),t.response?.data?.details?(console.error(a.default.red(`Chain Broken: ${t.response.data.message}`)),console.error(a.default.yellow(`Details: ${JSON.stringify(t.response.data.details,null,2)}`))):console.error(a.default.red(t.message))}})).addCommand(new u.Command("export").description("Export audit logs for compliance (SOC2/ISO)").option("-f, --format <format>","Output format (json, csv)","json").option("--start <date>","Start date (YYYY-MM-DD)").option("--end <date>","End date (YYYY-MM-DD)").option("-o, --output <file>","Output file path").action(async e=>{let{format:t,start:s,end:n,output:o}=e,i=(0,w.default)(`Exporting audit logs (${t})...`).start();try{let p=await f.exportAuditLogs(t,s,n),h=typeof p=="object"?JSON.stringify(p,null,2):p;o?(k.default.writeFileSync(o,h),i.succeed(a.default.green(`Export saved to ${o}`))):(i.stop(),console.log(h))}catch(p){i.fail("Export failed"),console.error(a.default.red(p.message))}}));0&&(module.exports={auditCommand});
|