xtra-cli 1.0.4 → 1.0.6
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 +2 -294
- 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/service-account.js +7 -0
- 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 +2 -2
package/dist/commands/login.js
CHANGED
|
@@ -1,198 +1,16 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.loginCommand = void 0;
|
|
40
|
-
const commander_1 = require("commander");
|
|
41
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
42
|
-
const api_1 = require("../lib/api");
|
|
43
|
-
const config_1 = require("../lib/config");
|
|
44
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
45
|
-
const ora_1 = __importDefault(require("ora"));
|
|
46
|
-
exports.loginCommand = new commander_1.Command("login")
|
|
47
|
-
.description("Authenticate with XtraSecurity")
|
|
48
|
-
.option("-k, --key <key>", "Login using an Access Key")
|
|
49
|
-
.option("-e, --email <email>", "Login using Email")
|
|
50
|
-
.option("--sso", "Login via Web (SSO)")
|
|
51
|
-
.action(async (options) => {
|
|
52
|
-
try {
|
|
53
|
-
// Interactive Login Choice
|
|
54
|
-
if (!options.key && !options.email && !options.sso) {
|
|
55
|
-
const { method } = await inquirer_1.default.prompt([
|
|
56
|
-
{
|
|
57
|
-
type: "list",
|
|
58
|
-
name: "method",
|
|
59
|
-
message: "How would you like to login?",
|
|
60
|
-
choices: [
|
|
61
|
-
{ name: "Browser Login (SSO)", value: "sso" },
|
|
62
|
-
{ name: "Access Key", value: "key" },
|
|
63
|
-
{ name: "Email & Password", value: "email" }
|
|
64
|
-
],
|
|
65
|
-
},
|
|
66
|
-
]);
|
|
67
|
-
if (method === "sso") {
|
|
68
|
-
await handleSSOLogin();
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
else if (method === "key") {
|
|
72
|
-
const { key } = await inquirer_1.default.prompt([{ type: "password", name: "key", message: "Enter Access Key:" }]);
|
|
73
|
-
await handleKeyLogin(key);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
// Fall through to Email
|
|
77
|
-
}
|
|
78
|
-
if (options.sso) {
|
|
79
|
-
await handleSSOLogin();
|
|
80
|
-
}
|
|
81
|
-
else if (options.key) {
|
|
82
|
-
await handleKeyLogin(options.key);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
// Abstract Email Login
|
|
86
|
-
let { email } = options;
|
|
87
|
-
if (!email) {
|
|
88
|
-
const answers = await inquirer_1.default.prompt([
|
|
89
|
-
{ type: "input", name: "email", message: "Email:" },
|
|
90
|
-
]);
|
|
91
|
-
email = answers.email;
|
|
92
|
-
}
|
|
93
|
-
const answers = await inquirer_1.default.prompt([
|
|
94
|
-
{ type: "password", name: "password", message: "Password:" },
|
|
95
|
-
]);
|
|
96
|
-
const password = answers.password;
|
|
97
|
-
const spinner = (0, ora_1.default)("Authenticating...").start();
|
|
98
|
-
const data = await api_1.api.login(email, password);
|
|
99
|
-
(0, config_1.setConfig)("token", data.token);
|
|
100
|
-
spinner.succeed("Logged in successfully!");
|
|
101
|
-
// Audit Log
|
|
102
|
-
try {
|
|
103
|
-
const { logAudit } = await Promise.resolve().then(() => __importStar(require("../lib/audit")));
|
|
104
|
-
logAudit("USER_LOGIN", null, null, { method: "email", email });
|
|
105
|
-
}
|
|
106
|
-
catch (e) { }
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
if (error.response) {
|
|
111
|
-
console.error(chalk_1.default.red(`\nError: ${error.response.data.error || 'Login failed'}`));
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
console.error(chalk_1.default.red(`\nError: ${error.message}`));
|
|
115
|
-
}
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
async function handleKeyLogin(key) {
|
|
120
|
-
const spinner = (0, ora_1.default)("Authenticating with Access Key...").start();
|
|
121
|
-
const data = await api_1.api.login(undefined, undefined, key);
|
|
122
|
-
(0, config_1.setConfig)("token", data.token);
|
|
123
|
-
if (data.user) {
|
|
124
|
-
console.log(chalk_1.default.green(`\nLogged in as ${data.user.email}`));
|
|
125
|
-
}
|
|
126
|
-
spinner.succeed("Logged in successfully!");
|
|
127
|
-
// Audit Log
|
|
128
|
-
try {
|
|
129
|
-
const { logAudit } = await Promise.resolve().then(() => __importStar(require("../lib/audit")));
|
|
130
|
-
logAudit("USER_LOGIN", null, null, { method: "api_key", email: data.user?.email });
|
|
131
|
-
}
|
|
132
|
-
catch (e) { }
|
|
133
|
-
}
|
|
134
|
-
async function handleSSOLogin() {
|
|
135
|
-
const http = require("http");
|
|
136
|
-
// 'open' is strictly ESM, so we must use dynamic import
|
|
137
|
-
const { default: open } = await Promise.resolve().then(() => __importStar(require("open")));
|
|
138
|
-
return new Promise((resolve) => {
|
|
139
|
-
const server = http.createServer(async (req, res) => {
|
|
140
|
-
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
141
|
-
const token = url.searchParams.get("token");
|
|
142
|
-
const email = url.searchParams.get("email");
|
|
143
|
-
const workspaceId = url.searchParams.get("workspaceId");
|
|
144
|
-
const workspaceName = url.searchParams.get("workspaceName");
|
|
145
|
-
if (token) {
|
|
146
|
-
(0, config_1.setConfig)("token", token);
|
|
147
|
-
if (workspaceId)
|
|
148
|
-
(0, config_1.setConfig)("workspace", workspaceId);
|
|
149
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
150
|
-
res.end(`
|
|
151
|
-
<html>
|
|
152
|
-
<body style="background:#111;color:#eee;font-family:sans-serif;text-align:center;padding:50px;">
|
|
153
|
-
<h1>Login Successful</h1>
|
|
154
|
-
<p>You have successfully logged in to XtraSync.</p>
|
|
155
|
-
<p>You can now close this tab and return to your terminal.</p>
|
|
156
|
-
<script>
|
|
157
|
-
setTimeout(function() { window.close(); }, 1000);
|
|
158
|
-
</script>
|
|
159
|
-
</body>
|
|
160
|
-
</html>
|
|
161
|
-
`);
|
|
162
|
-
console.log(chalk_1.default.green(`\n✔ SS0 Login Successful! Logged in as ${email}`));
|
|
163
|
-
if (workspaceId) {
|
|
164
|
-
console.log(chalk_1.default.dim(`Selected Workspace: ${workspaceName} (${workspaceId})`));
|
|
165
|
-
}
|
|
166
|
-
// Audit Log
|
|
167
|
-
try {
|
|
168
|
-
const { logAudit } = await Promise.resolve().then(() => __importStar(require("../lib/audit")));
|
|
169
|
-
logAudit("USER_LOGIN", null, null, { method: "sso", email, workspaceId, workspaceName });
|
|
170
|
-
}
|
|
171
|
-
catch (e) { }
|
|
172
|
-
res.end(() => {
|
|
173
|
-
server.close();
|
|
174
|
-
setTimeout(() => {
|
|
175
|
-
process.exit(0);
|
|
176
|
-
}, 1000);
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
res.writeHead(400);
|
|
181
|
-
res.end("Missing token");
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
server.listen(0, async () => {
|
|
185
|
-
const address = server.address();
|
|
186
|
-
const port = typeof address === 'string' ? 0 : address?.port;
|
|
187
|
-
const callbackUrl = `http://localhost:${port}`;
|
|
188
|
-
let { apiUrl } = require("../lib/config").getConfig();
|
|
189
|
-
// Hard fallback if getConfig somehow returned the old cached localhost
|
|
190
|
-
if (apiUrl.includes("localhost")) {
|
|
191
|
-
apiUrl = "https://www.xtrasecurity.in/api";
|
|
192
|
-
}
|
|
193
|
-
const ssoUrl = `${apiUrl}/auth/cli/sso?callbackUrl=${encodeURIComponent(callbackUrl)}`;
|
|
194
|
-
console.log(chalk_1.default.blue(`Waiting for browser login... (Opening ${ssoUrl})`));
|
|
195
|
-
await open(ssoUrl);
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
}
|
|
1
|
+
var ae=Object.create;var $=Object.defineProperty;var ie=Object.getOwnPropertyDescriptor;var ce=Object.getOwnPropertyNames;var pe=Object.getPrototypeOf,ge=Object.prototype.hasOwnProperty;var x=(e,t)=>()=>(e&&(t=e(e=0)),t);var L=(e,t)=>{for(var s in t)$(e,s,{get:t[s],enumerable:!0})},D=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ce(t))!ge.call(e,r)&&r!==s&&$(e,r,{get:()=>t[r],enumerable:!(n=ie(t,r))||n.enumerable});return e};var c=(e,t,s)=>(s=e!=null?ae(pe(e)):{},D(t||!e||!e.__esModule?$(s,"default",{value:e,enumerable:!0}):s,e)),G=e=>D($({},"__esModule",{value:!0}),e);var q={};L(q,{clearConfig:()=>de,deleteConfig:()=>le,getAuthToken:()=>R,getBreakGlassSession:()=>ye,getConfig:()=>y,getConfigValue:()=>ue,getProjectConfig:()=>fe,getRcConfig:()=>he,setBreakGlassSession:()=>me,setConfig:()=>f});var J,l,O,U,i,C,y,ue,f,de,le,R,ye,me,fe,he,S=x(()=>{J=c(require("conf")),l=c(require("fs")),O=c(require("path")),U="https://www.xtrasecurity.in/api",i=new J.default({projectName:"xtra-cli",defaults:{apiUrl:U}}),C=i.get("apiUrl");C&&!process.env.XTRA_API_URL&&(C.includes("localhost")||C.includes("xtra-security.vercel.app"))&&i.set("apiUrl",U);y=()=>({...i.store,apiUrl:process.env.XTRA_API_URL||i.get("apiUrl")||U}),ue=e=>i.get(e),f=(e,t)=>{if(i.set(e,t),e==="token"&&process.platform!=="win32"&&l.default.existsSync(i.path))try{l.default.chmodSync(i.path,384)}catch{}},de=()=>i.clear(),le=e=>i.delete(e),R=()=>i.get("token"),ye=()=>i.get("breakGlassSessionId"),me=e=>{e?i.set("breakGlassSessionId",e):i.delete("breakGlassSessionId")},fe=async()=>{try{let e=O.default.join(process.cwd(),"xtra.json");if(l.default.existsSync(e)){let t=l.default.readFileSync(e,"utf-8");return JSON.parse(t)}return null}catch{return null}},he=()=>{let e={};try{let t=O.default.join(process.cwd(),".xtrarc");l.default.existsSync(t)&&(e=JSON.parse(l.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"}}});var F,H,V,we,o,k,E=x(()=>{F=c(require("axios"));S();H=c(require("fs")),V=c(require("path")),we=JSON.parse(H.readFileSync(V.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:e}=y(),t=R();return F.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":we.version,...t?{Authorization:`Bearer ${t}`}:{}}})},k={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}=y(),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}=y(),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,getServiceAccounts:async e=>(await o().get(`/projects/${e}/service-accounts`)).data,createServiceAccount:async(e,t)=>(await o().post(`/projects/${e}/service-accounts`,t)).data,deleteServiceAccount:async(e,t)=>(await o().delete(`/projects/${e}/service-accounts/${t}`)).data,getServiceAccountKeys:async(e,t)=>(await o().get(`/projects/${e}/service-accounts/${t}/keys`)).data,createServiceAccountKey:async(e,t,s,n)=>(await o().post(`/projects/${e}/service-accounts/${t}/keys`,{label:s,expiresAt:n})).data,deleteServiceAccountKey:async(e,t,s)=>(await o().delete(`/projects/${e}/service-accounts/${t}/keys/${s}`)).data,post:async(e,t)=>(await o().post(e,t)).data}});function Y(){let e=X.get("masterKey");e||(e=h.default.randomBytes(32).toString("hex"),X.set("masterKey",e));let t=(0,M.machineIdSync)();return h.default.createHash("sha256").update(e+t).digest()}var h,z,M,W,X,Q,Z,ee=x(()=>{h=c(require("crypto")),z=c(require("conf")),M=require("node-machine-id"),W="aes-256-gcm",X=new z.default({projectName:"xtra-cli-crypto"});Q=e=>{let t=h.default.randomBytes(16),s=Y(),n=h.default.createCipheriv(W,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")}},Z=e=>{let t=Y(),s=Buffer.from(e.iv,"hex"),n=Buffer.from(e.tag,"hex"),r=h.default.createDecipheriv(W,t,s);r.setAuthTag(n);let a=r.update(e.content,"hex","utf8");return a+=r.final("utf8"),a}});var A={};L(A,{loadAuditLogs:()=>j,logAudit:()=>ke,saveAuditLogs:()=>b,syncAuditLogs:()=>ve});function Se(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 se(e){try{if(!y().token)return!1;let s={action:e.action,entity:"cli-project",entityId:e.projectId||"global",details:Se(e.details),timestamp:e.timestamp,projectId:e.projectId};return await k.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 ke(e,t,s,n){let r={id:(0,te.v4)(),timestamp:new Date().toISOString(),action:e,projectId:t,environment:s,details:n,synced:!1},a=j();a.push(r),b(a),se(r).then(p=>{if(p){r.synced=!0;let g=j(),u=g.findIndex(d=>d.id===r.id);u!==-1&&(g[u].synced=!0,b(g))}}).catch(()=>{})}function j(){if(!w.default.existsSync(N))return[];try{let e=w.default.readFileSync(N,"utf-8"),t=JSON.parse(e),s=Z(t);return JSON.parse(s)}catch{return[]}}function b(e){w.default.existsSync(P)||w.default.mkdirSync(P,{recursive:!0});let t=JSON.stringify(e),s=Q(t);w.default.writeFileSync(N,JSON.stringify(s),"utf-8")}async function ve(){let e=j(),t=e.filter(n=>!n.synced);if(t.length===0)return 0;let s=0;for(let n of t)await se(n)&&(n.synced=!0,s++);return s>0&&b(e),s}var w,T,te,P,N,I=x(()=>{w=c(require("fs")),T=c(require("path")),te=require("uuid");ee();S();E();P=T.default.join(process.cwd(),".xtra"),N=T.default.join(P,"audit.enc")});var xe={};L(xe,{loginCommand:()=>$e});module.exports=G(xe);var oe=require("commander"),v=c(require("inquirer"));E();S();var m=c(require("chalk")),_=c(require("ora")),$e=new oe.Command("login").description("Authenticate with XtraSecurity").option("-k, --key <key>","Login using an Access Key").option("-e, --email <email>","Login using Email").option("--sso","Login via Web (SSO)").action(async e=>{try{if(!e.key&&!e.email&&!e.sso){let{method:t}=await v.default.prompt([{type:"list",name:"method",message:"How would you like to login?",choices:[{name:"Browser Login (SSO)",value:"sso"},{name:"Access Key",value:"key"},{name:"Email & Password",value:"email"}]}]);if(t==="sso"){await re();return}else if(t==="key"){let{key:s}=await v.default.prompt([{type:"password",name:"key",message:"Enter Access Key:"}]);await ne(s);return}}if(e.sso)await re();else if(e.key)await ne(e.key);else{let{email:t}=e;t||(t=(await v.default.prompt([{type:"input",name:"email",message:"Email:"}])).email);let n=(await v.default.prompt([{type:"password",name:"password",message:"Password:"}])).password,r=(0,_.default)("Authenticating...").start(),a=await k.login(t,n);f("token",a.token),r.succeed("Logged in successfully!");try{let{logAudit:p}=await Promise.resolve().then(()=>(I(),A));p("USER_LOGIN",null,null,{method:"email",email:t})}catch{}}}catch(t){t.response?console.error(m.default.red(`
|
|
2
|
+
Error: ${t.response.data.error||"Login failed"}`)):console.error(m.default.red(`
|
|
3
|
+
Error: ${t?.response?.data?.error||"An unexpected error occurred"}`)),process.exit(1)}});async function ne(e){let t=(0,_.default)("Authenticating with Access Key...").start(),s=await k.login(void 0,void 0,e);f("token",s.token),s.user&&console.log(m.default.green(`
|
|
4
|
+
Logged in as ${s.user.email}`)),t.succeed("Logged in successfully!");try{let{logAudit:n}=await Promise.resolve().then(()=>(I(),A));n("USER_LOGIN",null,null,{method:"api_key",email:s.user?.email})}catch{}}async function re(){let e=require("http"),{default:t}=await import("open");return new Promise(s=>{let n=e.createServer(async(r,a)=>{let p=new URL(r.url,`http://${r.headers.host}`),g=p.searchParams.get("token"),u=p.searchParams.get("email"),d=p.searchParams.get("workspaceId"),B=p.searchParams.get("workspaceName");if(g){f("token",g),d&&f("workspace",d),a.writeHead(200,{"Content-Type":"text/html"}),a.end(`
|
|
5
|
+
<html>
|
|
6
|
+
<body style="background:#111;color:#eee;font-family:sans-serif;text-align:center;padding:50px;">
|
|
7
|
+
<h1>Login Successful</h1>
|
|
8
|
+
<p>You have successfully logged in to XtraSync.</p>
|
|
9
|
+
<p>You can now close this tab and return to your terminal.</p>
|
|
10
|
+
<script>
|
|
11
|
+
setTimeout(function() { window.close(); }, 1000);
|
|
12
|
+
</script>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
15
|
+
`),console.log(m.default.green(`
|
|
16
|
+
\u2714 SS0 Login Successful! Logged in as ${u}`)),d&&console.log(m.default.dim(`Selected Workspace: ${B} (${d})`));try{let{logAudit:K}=await Promise.resolve().then(()=>(I(),A));K("USER_LOGIN",null,null,{method:"sso",email:u,workspaceId:d,workspaceName:B})}catch{}a.end(()=>{n.close(),setTimeout(()=>{process.exit(0)},1e3)})}else a.writeHead(400),a.end("Missing token")});n.listen(0,async()=>{let r=n.address(),p=`http://localhost:${typeof r=="string"?0:r?.port}`,{apiUrl:g}=(S(),G(q)).getConfig();g.includes("localhost")&&(g="https://www.xtrasecurity.in/api");let u=`${g}/auth/cli/sso?callbackUrl=${encodeURIComponent(p)}`;console.log(m.default.blue(`Waiting for browser login... (Opening ${u})`)),await t(u)})})}0&&(module.exports={loginCommand});
|
|
@@ -1,51 +1,16 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
succeed: jest.fn().mockReturnThis(),
|
|
18
|
-
fail: jest.fn().mockReturnThis(),
|
|
19
|
-
}));
|
|
20
|
-
});
|
|
21
|
-
describe('Login Command', () => {
|
|
22
|
-
const mockApi = api_1.api;
|
|
23
|
-
const mockInquirer = inquirer_1.default;
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
jest.clearAllMocks();
|
|
26
|
-
});
|
|
27
|
-
it('should login with API key option', async () => {
|
|
28
|
-
mockApi.login.mockResolvedValue({ token: 'test-token-from-key' });
|
|
29
|
-
// Simulate command execution with --key option
|
|
30
|
-
const mockOptions = { key: 'xtra_key_abc123' };
|
|
31
|
-
// Execute the action handler directly
|
|
32
|
-
await login_1.loginCommand._actionHandler(mockOptions);
|
|
33
|
-
expect(mockApi.login).toHaveBeenCalledWith(undefined, undefined, 'xtra_key_abc123');
|
|
34
|
-
expect(config_1.setConfig).toHaveBeenCalledWith('token', 'test-token-from-key');
|
|
35
|
-
});
|
|
36
|
-
it('should prompt for email and password if no options provided', async () => {
|
|
37
|
-
mockInquirer.prompt
|
|
38
|
-
.mockResolvedValueOnce({ method: 'email' })
|
|
39
|
-
.mockResolvedValueOnce({ email: 'test@example.com' })
|
|
40
|
-
.mockResolvedValueOnce({ password: 'password123' });
|
|
41
|
-
mockApi.login.mockResolvedValue({ token: 'email-token' });
|
|
42
|
-
await login_1.loginCommand._actionHandler({});
|
|
43
|
-
expect(mockInquirer.prompt).toHaveBeenCalledTimes(3);
|
|
44
|
-
expect(mockApi.login).toHaveBeenCalledWith('test@example.com', 'password123', undefined);
|
|
45
|
-
});
|
|
46
|
-
it('should handle login failure gracefully', async () => {
|
|
47
|
-
mockApi.login.mockRejectedValue(new Error('Invalid credentials'));
|
|
48
|
-
const mockOptions = { key: 'bad-key' };
|
|
49
|
-
await expect(login_1.loginCommand._actionHandler(mockOptions)).rejects.toThrow('Invalid credentials');
|
|
50
|
-
});
|
|
51
|
-
});
|
|
1
|
+
var ie=Object.create;var j=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var pe=Object.getOwnPropertyNames;var ge=Object.getPrototypeOf,de=Object.prototype.hasOwnProperty;var x=(e,t)=>()=>(e&&(t=e(e=0)),t);var K=(e,t)=>{for(var s in t)j(e,s,{get:t[s],enumerable:!0})},q=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of pe(t))!de.call(e,r)&&r!==s&&j(e,r,{get:()=>t[r],enumerable:!(n=ce(t,r))||n.enumerable});return e};var c=(e,t,s)=>(s=e!=null?ie(ge(e)):{},q(t||!e||!e.__esModule?j(s,"default",{value:e,enumerable:!0}):s,e)),le=e=>q(j({},"__esModule",{value:!0}),e);var G={};K(G,{clearConfig:()=>me,deleteConfig:()=>ye,getAuthToken:()=>O,getBreakGlassSession:()=>fe,getConfig:()=>y,getConfigValue:()=>ue,getProjectConfig:()=>we,getRcConfig:()=>ke,setBreakGlassSession:()=>he,setConfig:()=>l});var D,m,_,U,i,R,y,ue,l,me,ye,O,fe,he,we,ke,w=x(()=>{D=c(require("conf")),m=c(require("fs")),_=c(require("path")),U="https://www.xtrasecurity.in/api",i=new D.default({projectName:"xtra-cli",defaults:{apiUrl:U}}),R=i.get("apiUrl");R&&!process.env.XTRA_API_URL&&(R.includes("localhost")||R.includes("xtra-security.vercel.app"))&&i.set("apiUrl",U);y=()=>({...i.store,apiUrl:process.env.XTRA_API_URL||i.get("apiUrl")||U}),ue=e=>i.get(e),l=(e,t)=>{if(i.set(e,t),e==="token"&&process.platform!=="win32"&&m.default.existsSync(i.path))try{m.default.chmodSync(i.path,384)}catch{}},me=()=>i.clear(),ye=e=>i.delete(e),O=()=>i.get("token"),fe=()=>i.get("breakGlassSessionId"),he=e=>{e?i.set("breakGlassSessionId",e):i.delete("breakGlassSessionId")},we=async()=>{try{let e=_.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}},ke=()=>{let e={};try{let t=_.default.join(process.cwd(),".xtrarc");m.default.existsSync(t)&&(e=JSON.parse(m.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"}}});var V,J,F,ve,o,f,$=x(()=>{V=c(require("axios"));w();J=c(require("fs")),F=c(require("path")),ve=JSON.parse(J.readFileSync(F.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:e}=y(),t=O();return V.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":ve.version,...t?{Authorization:`Bearer ${t}`}:{}}})},f={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}=y(),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}=y(),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,getServiceAccounts:async e=>(await o().get(`/projects/${e}/service-accounts`)).data,createServiceAccount:async(e,t)=>(await o().post(`/projects/${e}/service-accounts`,t)).data,deleteServiceAccount:async(e,t)=>(await o().delete(`/projects/${e}/service-accounts/${t}`)).data,getServiceAccountKeys:async(e,t)=>(await o().get(`/projects/${e}/service-accounts/${t}/keys`)).data,createServiceAccountKey:async(e,t,s,n)=>(await o().post(`/projects/${e}/service-accounts/${t}/keys`,{label:s,expiresAt:n})).data,deleteServiceAccountKey:async(e,t,s)=>(await o().delete(`/projects/${e}/service-accounts/${t}/keys/${s}`)).data,post:async(e,t)=>(await o().post(e,t)).data}});function Y(){let e=M.get("masterKey");e||(e=k.default.randomBytes(32).toString("hex"),M.set("masterKey",e));let t=(0,X.machineIdSync)();return k.default.createHash("sha256").update(e+t).digest()}var k,W,X,z,M,Q,Z,ee=x(()=>{k=c(require("crypto")),W=c(require("conf")),X=require("node-machine-id"),z="aes-256-gcm",M=new W.default({projectName:"xtra-cli-crypto"});Q=e=>{let t=k.default.randomBytes(16),s=Y(),n=k.default.createCipheriv(z,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")}},Z=e=>{let t=Y(),s=Buffer.from(e.iv,"hex"),n=Buffer.from(e.tag,"hex"),r=k.default.createDecipheriv(z,t,s);r.setAuthTag(n);let a=r.update(e.content,"hex","utf8");return a+=r.final("utf8"),a}});var I={};K(I,{loadAuditLogs:()=>b,logAudit:()=>je,saveAuditLogs:()=>A,syncAuditLogs:()=>xe});function Se(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 se(e){try{if(!y().token)return!1;let s={action:e.action,entity:"cli-project",entityId:e.projectId||"global",details:Se(e.details),timestamp:e.timestamp,projectId:e.projectId};return await f.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 je(e,t,s,n){let r={id:(0,te.v4)(),timestamp:new Date().toISOString(),action:e,projectId:t,environment:s,details:n,synced:!1},a=b();a.push(r),A(a),se(r).then(p=>{if(p){r.synced=!0;let g=b(),d=g.findIndex(u=>u.id===r.id);d!==-1&&(g[d].synced=!0,A(g))}}).catch(()=>{})}function b(){if(!v.default.existsSync(T))return[];try{let e=v.default.readFileSync(T,"utf-8"),t=JSON.parse(e),s=Z(t);return JSON.parse(s)}catch{return[]}}function A(e){v.default.existsSync(E)||v.default.mkdirSync(E,{recursive:!0});let t=JSON.stringify(e),s=Q(t);v.default.writeFileSync(T,JSON.stringify(s),"utf-8")}async function xe(){let e=b(),t=e.filter(n=>!n.synced);if(t.length===0)return 0;let s=0;for(let n of t)await se(n)&&(n.synced=!0,s++);return s>0&&A(e),s}var v,P,te,E,T,C=x(()=>{v=c(require("fs")),P=c(require("path")),te=require("uuid");ee();w();$();E=P.default.join(process.cwd(),".xtra"),T=P.default.join(E,"audit.enc")});var oe=require("commander"),S=c(require("inquirer"));$();w();var h=c(require("chalk")),B=c(require("ora")),L=new oe.Command("login").description("Authenticate with XtraSecurity").option("-k, --key <key>","Login using an Access Key").option("-e, --email <email>","Login using Email").option("--sso","Login via Web (SSO)").action(async e=>{try{if(!e.key&&!e.email&&!e.sso){let{method:t}=await S.default.prompt([{type:"list",name:"method",message:"How would you like to login?",choices:[{name:"Browser Login (SSO)",value:"sso"},{name:"Access Key",value:"key"},{name:"Email & Password",value:"email"}]}]);if(t==="sso"){await re();return}else if(t==="key"){let{key:s}=await S.default.prompt([{type:"password",name:"key",message:"Enter Access Key:"}]);await ne(s);return}}if(e.sso)await re();else if(e.key)await ne(e.key);else{let{email:t}=e;t||(t=(await S.default.prompt([{type:"input",name:"email",message:"Email:"}])).email);let n=(await S.default.prompt([{type:"password",name:"password",message:"Password:"}])).password,r=(0,B.default)("Authenticating...").start(),a=await f.login(t,n);l("token",a.token),r.succeed("Logged in successfully!");try{let{logAudit:p}=await Promise.resolve().then(()=>(C(),I));p("USER_LOGIN",null,null,{method:"email",email:t})}catch{}}}catch(t){t.response?console.error(h.default.red(`
|
|
2
|
+
Error: ${t.response.data.error||"Login failed"}`)):console.error(h.default.red(`
|
|
3
|
+
Error: ${t?.response?.data?.error||"An unexpected error occurred"}`)),process.exit(1)}});async function ne(e){let t=(0,B.default)("Authenticating with Access Key...").start(),s=await f.login(void 0,void 0,e);l("token",s.token),s.user&&console.log(h.default.green(`
|
|
4
|
+
Logged in as ${s.user.email}`)),t.succeed("Logged in successfully!");try{let{logAudit:n}=await Promise.resolve().then(()=>(C(),I));n("USER_LOGIN",null,null,{method:"api_key",email:s.user?.email})}catch{}}async function re(){let e=require("http"),{default:t}=await import("open");return new Promise(s=>{let n=e.createServer(async(r,a)=>{let p=new URL(r.url,`http://${r.headers.host}`),g=p.searchParams.get("token"),d=p.searchParams.get("email"),u=p.searchParams.get("workspaceId"),N=p.searchParams.get("workspaceName");if(g){l("token",g),u&&l("workspace",u),a.writeHead(200,{"Content-Type":"text/html"}),a.end(`
|
|
5
|
+
<html>
|
|
6
|
+
<body style="background:#111;color:#eee;font-family:sans-serif;text-align:center;padding:50px;">
|
|
7
|
+
<h1>Login Successful</h1>
|
|
8
|
+
<p>You have successfully logged in to XtraSync.</p>
|
|
9
|
+
<p>You can now close this tab and return to your terminal.</p>
|
|
10
|
+
<script>
|
|
11
|
+
setTimeout(function() { window.close(); }, 1000);
|
|
12
|
+
</script>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
15
|
+
`),console.log(h.default.green(`
|
|
16
|
+
\u2714 SS0 Login Successful! Logged in as ${d}`)),u&&console.log(h.default.dim(`Selected Workspace: ${N} (${u})`));try{let{logAudit:H}=await Promise.resolve().then(()=>(C(),I));H("USER_LOGIN",null,null,{method:"sso",email:d,workspaceId:u,workspaceName:N})}catch{}a.end(()=>{n.close(),setTimeout(()=>{process.exit(0)},1e3)})}else a.writeHead(400),a.end("Missing token")});n.listen(0,async()=>{let r=n.address(),p=`http://localhost:${typeof r=="string"?0:r?.port}`,{apiUrl:g}=(w(),le(G)).getConfig();g.includes("localhost")&&(g="https://www.xtrasecurity.in/api");let d=`${g}/auth/cli/sso?callbackUrl=${encodeURIComponent(p)}`;console.log(h.default.blue(`Waiting for browser login... (Opening ${d})`)),await t(d)})})}$();w();var ae=c(require("inquirer"));jest.mock("../lib/api");jest.mock("../lib/config");jest.mock("inquirer");jest.mock("ora",()=>jest.fn(()=>({start:jest.fn().mockReturnThis(),succeed:jest.fn().mockReturnThis(),fail:jest.fn().mockReturnThis()})));describe("Login Command",()=>{let e=f,t=ae.default;beforeEach(()=>{jest.clearAllMocks()}),it("should login with API key option",async()=>{e.login.mockResolvedValue({token:"test-token-from-key"});let s={key:"xtra_key_abc123"};await L._actionHandler(s),expect(e.login).toHaveBeenCalledWith(void 0,void 0,"xtra_key_abc123"),expect(l).toHaveBeenCalledWith("token","test-token-from-key")}),it("should prompt for email and password if no options provided",async()=>{t.prompt.mockResolvedValueOnce({method:"email"}).mockResolvedValueOnce({email:"test@example.com"}).mockResolvedValueOnce({password:"password123"}),e.login.mockResolvedValue({token:"email-token"}),await L._actionHandler({}),expect(t.prompt).toHaveBeenCalledTimes(3),expect(e.login).toHaveBeenCalledWith("test@example.com","password123",void 0)}),it("should handle login failure gracefully",async()=>{e.login.mockRejectedValue(new Error("Invalid credentials"));let s={key:"bad-key"};await expect(L._actionHandler(s)).rejects.toThrow("Invalid credentials")})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var y=Object.create;var c=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,C=Object.prototype.hasOwnProperty;var b=(e,t)=>{for(var o in t)c(e,o,{get:t[o],enumerable:!0})},f=(e,t,o,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of k(t))!C.call(e,r)&&r!==o&&c(e,r,{get:()=>t[r],enumerable:!(s=x(t,r))||s.enumerable});return e};var i=(e,t,o)=>(o=e!=null?y(S(e)):{},f(t||!e||!e.__esModule?c(o,"default",{value:e,enumerable:!0}):o,e)),j=e=>f(c({},"__esModule",{value:!0}),e);var v={};b(v,{logoutCommand:()=>U});module.exports=j(v);var u=require("commander"),a=i(require("chalk")),m=i(require("ora"));var d=i(require("conf"));var g="https://www.xtrasecurity.in/api",n=new d.default({projectName:"xtra-cli",defaults:{apiUrl:g}}),l=n.get("apiUrl");l&&!process.env.XTRA_API_URL&&(l.includes("localhost")||l.includes("xtra-security.vercel.app"))&&n.set("apiUrl",g);var h=()=>({...n.store,apiUrl:process.env.XTRA_API_URL||n.get("apiUrl")||g});var p=e=>n.delete(e);var U=new u.Command("logout").description("Logout and clear all local credentials and caches").action(async()=>{let e=(0,m.default)("Logging out...").start();try{p("token");let t=h(),s=Object.keys(t).filter(r=>r.startsWith("cache."));for(let r of s)p(r);e.succeed(a.default.green("\u2714 Successfully logged out.")),console.log(a.default.gray("Local authentication token and secret caches have been purged."))}catch(t){e.fail(a.default.red(`Logout failed: ${t?.response?.data?.error||"An unexpected error occurred"}`))}});0&&(module.exports={logoutCommand});
|
package/dist/commands/logs.js
CHANGED
|
@@ -1,121 +1,3 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.logsCommand = void 0;
|
|
7
|
-
const commander_1 = require("commander");
|
|
8
|
-
const audit_1 = require("../lib/audit");
|
|
9
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
const table_1 = require("table");
|
|
11
|
-
exports.logsCommand = new commander_1.Command("logs")
|
|
12
|
-
.description("View local audit logs")
|
|
13
|
-
.option("-n, --limit <number>", "Number of logs to show", "20")
|
|
14
|
-
.option("--sync", "Sync logs to cloud", false)
|
|
15
|
-
.option("--event <type>", "Filter by event type (e.g. SECRET_UPDATE, SECRET_ROTATE)")
|
|
16
|
-
.option("--project <projectId>", "Filter by project ID")
|
|
17
|
-
.option("--since <duration>", "Show logs since (e.g. 1h, 24h, 7d, 30d)", "")
|
|
18
|
-
.option("--json", "Output raw JSON instead of table", false)
|
|
19
|
-
.action(async (options) => {
|
|
20
|
-
const { limit, sync, event: eventFilter, project: projectFilter, since, json: jsonOut } = options;
|
|
21
|
-
try {
|
|
22
|
-
let logs = (0, audit_1.loadAuditLogs)();
|
|
23
|
-
if (sync) {
|
|
24
|
-
const unsynced = logs.filter(l => !l.synced);
|
|
25
|
-
if (unsynced.length === 0) {
|
|
26
|
-
console.log(chalk_1.default.green("All logs are already synced."));
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
const spinner = require("ora")(`Syncing ${unsynced.length} logs to cloud...`).start();
|
|
30
|
-
try {
|
|
31
|
-
const { api } = require("../lib/api");
|
|
32
|
-
const { saveAuditLogs } = require("../lib/audit");
|
|
33
|
-
await api.syncLogs(unsynced);
|
|
34
|
-
unsynced.forEach(l => l.synced = true);
|
|
35
|
-
saveAuditLogs(logs);
|
|
36
|
-
spinner.succeed(`Successfully synced ${unsynced.length} logs.`);
|
|
37
|
-
}
|
|
38
|
-
catch (err) {
|
|
39
|
-
spinner.fail("Sync failed.");
|
|
40
|
-
console.error(chalk_1.default.red(err.message));
|
|
41
|
-
}
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
if (logs.length === 0) {
|
|
45
|
-
console.log(chalk_1.default.yellow("No local audit logs found."));
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
// ── Filtering ─────────────────────────────────────────────────────────
|
|
49
|
-
// Filter by event type
|
|
50
|
-
if (eventFilter) {
|
|
51
|
-
const upper = eventFilter.toUpperCase();
|
|
52
|
-
logs = logs.filter(l => l.action?.toUpperCase().includes(upper));
|
|
53
|
-
}
|
|
54
|
-
// Filter by project
|
|
55
|
-
if (projectFilter) {
|
|
56
|
-
logs = logs.filter(l => l.projectId === projectFilter);
|
|
57
|
-
}
|
|
58
|
-
// Filter by --since duration (1h, 6h, 24h, 7d, 30d)
|
|
59
|
-
if (since) {
|
|
60
|
-
const match = since.match(/^(\d+)(h|d|m)$/i);
|
|
61
|
-
if (!match) {
|
|
62
|
-
console.error(chalk_1.default.red(`Invalid --since format: '${since}'. Use e.g. 1h, 24h, 7d, 30d`));
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
const amount = parseInt(match[1]);
|
|
66
|
-
const unit = match[2].toLowerCase();
|
|
67
|
-
const msMap = { h: 3600000, d: 86400000, m: 60000 };
|
|
68
|
-
const cutoff = new Date(Date.now() - amount * msMap[unit]);
|
|
69
|
-
logs = logs.filter(l => new Date(l.timestamp) >= cutoff);
|
|
70
|
-
}
|
|
71
|
-
// Sort newest first
|
|
72
|
-
logs.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
73
|
-
const limitedLogs = logs.slice(0, parseInt(limit));
|
|
74
|
-
if (limitedLogs.length === 0) {
|
|
75
|
-
console.log(chalk_1.default.yellow("No logs matched your filters."));
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
// ── Output ────────────────────────────────────────────────────────────
|
|
79
|
-
if (jsonOut) {
|
|
80
|
-
process.stdout.write(JSON.stringify(limitedLogs, null, 2) + "\n");
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
const data = [
|
|
84
|
-
[chalk_1.default.bold("Timestamp"), chalk_1.default.bold("Action"), chalk_1.default.bold("Project"), chalk_1.default.bold("Sync"), chalk_1.default.bold("Details")]
|
|
85
|
-
];
|
|
86
|
-
limitedLogs.forEach(l => {
|
|
87
|
-
let details = "";
|
|
88
|
-
if (l.details) {
|
|
89
|
-
if (l.details.keys)
|
|
90
|
-
details = `Keys: ${l.details.keys.join(", ")}`;
|
|
91
|
-
else if (l.details.key)
|
|
92
|
-
details = `Key: ${l.details.key}`;
|
|
93
|
-
else
|
|
94
|
-
details = JSON.stringify(l.details);
|
|
95
|
-
}
|
|
96
|
-
const proj = l.projectId ? l.projectId.substring(0, 8) + "..." : "-";
|
|
97
|
-
data.push([
|
|
98
|
-
new Date(l.timestamp).toLocaleString(),
|
|
99
|
-
l.action,
|
|
100
|
-
proj,
|
|
101
|
-
l.synced ? chalk_1.default.green("✔") : chalk_1.default.yellow("✖"),
|
|
102
|
-
details.substring(0, 50) + (details.length > 50 ? "..." : "")
|
|
103
|
-
]);
|
|
104
|
-
});
|
|
105
|
-
// Show active filters in header
|
|
106
|
-
const filterParts = [];
|
|
107
|
-
if (eventFilter)
|
|
108
|
-
filterParts.push(`event: ${eventFilter.toUpperCase()}`);
|
|
109
|
-
if (projectFilter)
|
|
110
|
-
filterParts.push(`project: ${projectFilter}`);
|
|
111
|
-
if (since)
|
|
112
|
-
filterParts.push(`since: ${since}`);
|
|
113
|
-
const filterHint = filterParts.length > 0 ? chalk_1.default.gray(` [${filterParts.join(" | ")}]`) : "";
|
|
114
|
-
console.log(chalk_1.default.bold(`\nLocal Audit Logs${filterHint}`));
|
|
115
|
-
console.log((0, table_1.table)(data));
|
|
116
|
-
console.log(chalk_1.default.gray(` Showing ${limitedLogs.length} of ${logs.length} total logs.`));
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
console.error(chalk_1.default.red("Failed to load logs: " + error.message));
|
|
120
|
-
}
|
|
121
|
-
});
|
|
1
|
+
var it=Object.create;var $=Object.defineProperty;var at=Object.getOwnPropertyDescriptor;var ct=Object.getOwnPropertyNames;var pt=Object.getPrototypeOf,gt=Object.prototype.hasOwnProperty;var b=(t,e)=>()=>(t&&(e=t(t=0)),e);var I=(t,e)=>{for(var s in e)$(t,s,{get:e[s],enumerable:!0})},B=(t,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of ct(e))!gt.call(t,r)&&r!==s&&$(t,r,{get:()=>e[r],enumerable:!(n=at(e,r))||n.enumerable});return t};var u=(t,e,s)=>(s=t!=null?it(pt(t)):{},B(e||!t||!t.__esModule?$(s,"default",{value:t,enumerable:!0}):s,t)),C=t=>B($({},"__esModule",{value:!0}),t);function q(){let t=_.get("masterKey");t||(t=y.default.randomBytes(32).toString("hex"),_.set("masterKey",t));let e=(0,J.machineIdSync)();return y.default.createHash("sha256").update(t+e).digest()}var y,F,J,K,_,G,V,H=b(()=>{y=u(require("crypto")),F=u(require("conf")),J=require("node-machine-id"),K="aes-256-gcm",_=new F.default({projectName:"xtra-cli-crypto"});G=t=>{let e=y.default.randomBytes(16),s=q(),n=y.default.createCipheriv(K,s,e),r=n.update(t,"utf8","hex");r+=n.final("hex");let a=n.getAuthTag();return{iv:e.toString("hex"),content:r,tag:a.toString("hex")}},V=t=>{let e=q(),s=Buffer.from(t.iv,"hex"),n=Buffer.from(t.tag,"hex"),r=y.default.createDecipheriv(K,e,s);r.setAuthTag(n);let a=r.update(t.content,"hex","utf8");return a+=r.final("utf8"),a}});var z,U,j,L,f,M,R=b(()=>{z=u(require("conf")),U="https://www.xtrasecurity.in/api",j=new z.default({projectName:"xtra-cli",defaults:{apiUrl:U}}),L=j.get("apiUrl");L&&!process.env.XTRA_API_URL&&(L.includes("localhost")||L.includes("xtra-security.vercel.app"))&&j.set("apiUrl",U);f=()=>({...j.store,apiUrl:process.env.XTRA_API_URL||j.get("apiUrl")||U}),M=()=>j.get("token")});var Y={};I(Y,{api:()=>T});var X,Q,W,dt,o,T,D=b(()=>{X=u(require("axios"));R();Q=u(require("fs")),W=u(require("path")),dt=JSON.parse(Q.readFileSync(W.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:t}=f(),e=M();return X.default.create({baseURL:t,headers:{"Content-Type":"application/json","X-CLI-Version":dt.version,...e?{Authorization:`Bearer ${e}`}:{}}})},T={login:async(t,e,s)=>(await o().post("/auth/cli-login",{email:t,password:e,apiKey:s})).data,getSecrets:async(t,e,s="main")=>(await o().get(`/projects/${t}/envs/${e}/secrets?branch=${s}`)).data,getSecretVersions:async(t,e,s="main")=>(await o().get(`/projects/${t}/envs/${e}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(t,e,s,n,r="main")=>(await o().post(`/projects/${t}/envs/${e}/secrets`,{secrets:s,expectedVersions:n,branch:r})).data,getSecretDetails:async(t,e,s,n="main")=>(await o().get(`/projects/${t}/envs/${e}/secrets/${s}?branch=${n}`)).data,syncLogs:async t=>(await o().post("/audit/cli-logs",{logs:t})).data,linkSecret:async(t,e,s,n,r,a)=>(await o().post(`/projects/${t}/envs/${e}/secrets/link`,{key:s,sourceProjectId:n,sourceEnv:r,sourceKey:a})).data,rotateSecret:async(t,e,s,n,r)=>(await o().post(`/projects/${t}/envs/${e}/secrets/${s}/rotate`,{strategy:n,parsedNewValue:r})).data,promoteSecret:async(t,e,s)=>(await o().post(`/projects/${t}/envs/${e}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await o().get("/audit/verify")).data,exportAuditLogs:async(t,e,s,n)=>{let r={format:t};return e&&(r.startDate=e),s&&(r.endDate=s),n&&(r.projectId=n),(await o().post("/audit/export",r,{responseType:t==="csv"?"text":"json"})).data},requestAccess:async(t,e,s,n)=>(await o().post("/access/request",{projectId:t,secretId:n,reason:e,duration:s})).data,approveAccess:async(t,e)=>(await o().post("/access/approve",{requestId:t,decision:e})).data,listAccessRequests:async t=>{let{workspace:e}=f(),s=e?`&workspaceId=${e}`:"";return(await o().get(`/access/list?mode=${t}${s}`)).data},getAccessRequestStatus:async t=>(await o().get(`/access/request?id=${t}`)).data,getProjects:async()=>{let{workspace:t}=f(),e=t?`/project?workspaceId=${t}`:"/project";return(await o().get(e)).data},getBranches:async t=>(await o().get(`/branch?projectId=${t}`)).data,createBranch:async(t,e,s)=>(await o().post("/branch",{projectId:t,name:e,description:s})).data,deleteBranch:async t=>(await o().delete(`/branch/${t}`)).data,updateBranch:async(t,e)=>(await o().put("/branch",{id:t,...e})).data,getRoles:async()=>(await o().get("/admin/roles")).data,getUsers:async t=>{let e=t?`?teamId=${t}`:"";return(await o().get(`/admin/users${e}`)).data},setUserRole:async(t,e,s)=>(await o().put("/admin/users/role",{email:t,role:e,teamId:s})).data,getIntegrationStatus:async t=>(await o().get(`/integrations/${t}`)).data,getIntegrationRepos:async t=>(await o().get(`/integrations/${t}/sync`)).data.repos,syncSecretsToGithub:async t=>(await o().post("/integrations/github/sync",t)).data,exportKubernetesSecret:async(t,e)=>{let s=new URLSearchParams(e).toString();return(await o().get(`/projects/${t}/kubernetes?${s}`)).data},getSecretHistory:async(t,e,s)=>(await o().get(`/projects/${t}/envs/${e}/secrets/${s}/history`)).data,rollbackSecret:async(t,e,s,n)=>(await o().post(`/projects/${t}/envs/${e}/secrets/${s}/history`,{version:n})).data,cloneEnvironment:async(t,e,s,n,r)=>(await o().post(`/projects/${t}/envs/clone`,{fromEnv:e,toEnv:s,overwrite:n,branch:r})).data,generateJitLink:async t=>(await o().post("/jit/generate",t)).data,claimJitLink:async t=>(await o().post("/jit/claim",{token:t})).data,getJitInfo:async t=>(await o().get(`/jit/${t}`)).data,getServiceAccounts:async t=>(await o().get(`/projects/${t}/service-accounts`)).data,createServiceAccount:async(t,e)=>(await o().post(`/projects/${t}/service-accounts`,e)).data,deleteServiceAccount:async(t,e)=>(await o().delete(`/projects/${t}/service-accounts/${e}`)).data,getServiceAccountKeys:async(t,e)=>(await o().get(`/projects/${t}/service-accounts/${e}/keys`)).data,createServiceAccountKey:async(t,e,s,n)=>(await o().post(`/projects/${t}/service-accounts/${e}/keys`,{label:s,expiresAt:n})).data,deleteServiceAccountKey:async(t,e,s)=>(await o().delete(`/projects/${t}/service-accounts/${e}/keys/${s}`)).data,post:async(t,e)=>(await o().post(t,e)).data}});var et={};I(et,{loadAuditLogs:()=>h,logAudit:()=>lt,saveAuditLogs:()=>x,syncAuditLogs:()=>yt});function ut(t){if(!t||typeof t!="object")return t;let e={...t},s=["password","token","secret","value","apiKey","secretValue"];for(let n of Object.keys(e))s.some(r=>n.toLowerCase().includes(r))&&(e[n]="[REDACTED]");return e}async function tt(t){try{if(!f().token)return!1;let s={action:t.action,entity:"cli-project",entityId:t.projectId||"global",details:ut(t.details),timestamp:t.timestamp,projectId:t.projectId};return await T.post("/audit/cli-logs",{logs:[s]}),!0}catch(e){return process.env.DEBUG&&console.error("Failed to send audit log to backend:",e),!1}}function lt(t,e,s,n){let r={id:(0,Z.v4)(),timestamp:new Date().toISOString(),action:t,projectId:e,environment:s,details:n,synced:!1},a=h();a.push(r),x(a),tt(r).then(S=>{if(S){r.synced=!0;let c=h(),l=c.findIndex(v=>v.id===r.id);l!==-1&&(c[l].synced=!0,x(c))}}).catch(()=>{})}function h(){if(!m.default.existsSync(E))return[];try{let t=m.default.readFileSync(E,"utf-8"),e=JSON.parse(t),s=V(e);return JSON.parse(s)}catch{return[]}}function x(t){m.default.existsSync(O)||m.default.mkdirSync(O,{recursive:!0});let e=JSON.stringify(t),s=G(e);m.default.writeFileSync(E,JSON.stringify(s),"utf-8")}async function yt(){let t=h(),e=t.filter(n=>!n.synced);if(e.length===0)return 0;let s=0;for(let n of e)await tt(n)&&(n.synced=!0,s++);return s>0&&x(t),s}var m,N,Z,O,E,P=b(()=>{m=u(require("fs")),N=u(require("path")),Z=require("uuid");H();R();D();O=N.default.join(process.cwd(),".xtra"),E=N.default.join(O,"audit.enc")});var mt={};I(mt,{logsCommand:()=>ft});module.exports=C(mt);var st=require("commander");P();var p=u(require("chalk")),nt=require("table"),ft=new st.Command("logs").description("View local audit logs").option("-n, --limit <number>","Number of logs to show","20").option("--sync","Sync logs to cloud",!1).option("--event <type>","Filter by event type (e.g. SECRET_UPDATE, SECRET_ROTATE)").option("--project <projectId>","Filter by project ID").option("--since <duration>","Show logs since (e.g. 1h, 24h, 7d, 30d)","").option("--json","Output raw JSON instead of table",!1).action(async t=>{let{limit:e,sync:s,event:n,project:r,since:a,json:S}=t;try{let c=h();if(s){let i=c.filter(d=>!d.synced);if(i.length===0){console.log(p.default.green("All logs are already synced."));return}let g=require("ora")(`Syncing ${i.length} logs to cloud...`).start();try{let{api:d}=(D(),C(Y)),{saveAuditLogs:k}=(P(),C(et));await d.syncLogs(i),i.forEach(A=>A.synced=!0),k(c),g.succeed(`Successfully synced ${i.length} logs.`)}catch(d){g.fail("Sync failed."),console.error(p.default.red(d.message))}return}if(c.length===0){console.log(p.default.yellow("No local audit logs found."));return}if(n){let i=n.toUpperCase();c=c.filter(g=>g.action?.toUpperCase().includes(i))}if(r&&(c=c.filter(i=>i.projectId===r)),a){let i=a.match(/^(\d+)(h|d|m)$/i);i||(console.error(p.default.red(`Invalid --since format: '${a}'. Use e.g. 1h, 24h, 7d, 30d`)),process.exit(1));let g=parseInt(i[1]),d=i[2].toLowerCase(),k={h:36e5,d:864e5,m:6e4},A=new Date(Date.now()-g*k[d]);c=c.filter(ot=>new Date(ot.timestamp)>=A)}c.sort((i,g)=>new Date(g.timestamp).getTime()-new Date(i.timestamp).getTime());let l=c.slice(0,parseInt(e));if(l.length===0){console.log(p.default.yellow("No logs matched your filters."));return}if(S){process.stdout.write(JSON.stringify(l,null,2)+`
|
|
2
|
+
`);return}let v=[[p.default.bold("Timestamp"),p.default.bold("Action"),p.default.bold("Project"),p.default.bold("Sync"),p.default.bold("Details")]];l.forEach(i=>{let g="";i.details&&(i.details.keys?g=`Keys: ${i.details.keys.join(", ")}`:i.details.key?g=`Key: ${i.details.key}`:g=JSON.stringify(i.details));let d=i.projectId?i.projectId.substring(0,8)+"...":"-";v.push([new Date(i.timestamp).toLocaleString(),i.action,d,i.synced?p.default.green("\u2714"):p.default.yellow("\u2716"),g.substring(0,50)+(g.length>50?"...":"")])});let w=[];n&&w.push(`event: ${n.toUpperCase()}`),r&&w.push(`project: ${r}`),a&&w.push(`since: ${a}`);let rt=w.length>0?p.default.gray(` [${w.join(" | ")}]`):"";console.log(p.default.bold(`
|
|
3
|
+
Local Audit Logs${rt}`)),console.log((0,nt.table)(v)),console.log(p.default.gray(` Showing ${l.length} of ${c.length} total logs.`))}catch(c){console.error(p.default.red("Failed to load logs: "+c?.response?.data?.error||"An unexpected error occurred"))}});0&&(module.exports={logsCommand});
|