veracarto 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -42963,7 +42963,82 @@ var init_build5 = __esm({
42963
42963
  });
42964
42964
 
42965
42965
  // src/cli/ui/Detail.tsx
42966
- function Detail({ item, slackTeamId, onBack, onApprove, onDismiss, onTierChange, onNoise }) {
42966
+ function getFixCommands(item, gcpProjectId) {
42967
+ const meta = typeof item.metadata === "string" ? (() => {
42968
+ try {
42969
+ return JSON.parse(item.metadata);
42970
+ } catch {
42971
+ return {};
42972
+ }
42973
+ })() : item.metadata || {};
42974
+ const resource = item.affected_resource || "";
42975
+ const projectId = gcpProjectId || meta.projectId || meta.gcp_project_id || "YOUR_PROJECT_ID";
42976
+ switch (item.playbook_id) {
42977
+ case "gcp-firewall-broad":
42978
+ return {
42979
+ fix: [
42980
+ `gcloud compute firewall-rules update ${resource} \\`,
42981
+ ` --project=${projectId} \\`,
42982
+ ` --source-ranges=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16`
42983
+ ],
42984
+ rollback: [
42985
+ `gcloud compute firewall-rules update ${resource} \\`,
42986
+ ` --project=${projectId} \\`,
42987
+ ` --source-ranges=0.0.0.0/0`
42988
+ ]
42989
+ };
42990
+ case "gcp-public-bucket":
42991
+ return {
42992
+ fix: [
42993
+ `gcloud storage buckets update gs://${resource} --uniform-bucket-level-access`,
42994
+ `gcloud storage buckets remove-iam-policy-binding gs://${resource} \\`,
42995
+ ` --member=allUsers --role=roles/storage.objectViewer`
42996
+ ],
42997
+ rollback: [
42998
+ `gcloud storage buckets add-iam-policy-binding gs://${resource} \\`,
42999
+ ` --member=allUsers --role=roles/storage.objectViewer`
43000
+ ]
43001
+ };
43002
+ case "gcp-overpermissioned-iam": {
43003
+ const member = meta.member || `serviceAccount:${resource}`;
43004
+ const role = meta.role || "roles/editor";
43005
+ return {
43006
+ fix: [
43007
+ `gcloud projects remove-iam-policy-binding ${projectId} \\`,
43008
+ ` --member=${member} \\`,
43009
+ ` --role=${role}`
43010
+ ],
43011
+ rollback: [
43012
+ `gcloud projects add-iam-policy-binding ${projectId} \\`,
43013
+ ` --member=${member} \\`,
43014
+ ` --role=${role}`
43015
+ ]
43016
+ };
43017
+ }
43018
+ case "gcp-audit-logging-disabled":
43019
+ return {
43020
+ fix: [
43021
+ `gcloud projects get-iam-policy ${resource || projectId} --format=json > /tmp/policy.json`,
43022
+ `# Add audit config to policy.json`,
43023
+ `gcloud projects set-iam-policy ${resource || projectId} /tmp/policy.json`
43024
+ ],
43025
+ rollback: [
43026
+ `gcloud projects set-iam-policy ${resource || projectId} /tmp/policy-backup.json`
43027
+ ]
43028
+ };
43029
+ case "github-dependabot":
43030
+ return {
43031
+ fix: [
43032
+ `# Draft PR: bump ${meta.dependency || "package"} ${meta.fromVersion || ""} \u2192 ${meta.toVersion || "latest"}`,
43033
+ `# PR opened automatically by Veracarto`
43034
+ ],
43035
+ rollback: []
43036
+ };
43037
+ default:
43038
+ return null;
43039
+ }
43040
+ }
43041
+ function Detail({ item, slackTeamId, gcpProjectId, onBack, onApprove, onDismiss, onTierChange, onNoise }) {
42967
43042
  const [loading, setLoading] = (0, import_react31.useState)(false);
42968
43043
  const [dismissMode, setDismissMode] = (0, import_react31.useState)(false);
42969
43044
  const [dismissReason, setDismissReason] = (0, import_react31.useState)("");
@@ -42975,6 +43050,8 @@ function Detail({ item, slackTeamId, onBack, onApprove, onDismiss, onTierChange,
42975
43050
  const [noiseReason, setNoiseReason] = (0, import_react31.useState)("");
42976
43051
  const confidencePct = Math.round((item.confidence || 0) * 100);
42977
43052
  const tierColor = TIER_COLORS2[item.tier] || "#888888";
43053
+ const fixCommands = getFixCommands(item, gcpProjectId);
43054
+ const [copied, setCopied] = (0, import_react31.useState)(false);
42978
43055
  function getSuggestedRule() {
42979
43056
  const resource = item.affected_resource || "unknown";
42980
43057
  const category = (item.affected_resource_type || "").toUpperCase();
@@ -43005,6 +43082,11 @@ function Detail({ item, slackTeamId, onBack, onApprove, onDismiss, onTierChange,
43005
43082
  if (input === "d" && !loading) {
43006
43083
  setDismissMode(true);
43007
43084
  }
43085
+ if (input === "c" && !loading && fixCommands) {
43086
+ clipboardy_default.writeSync(fixCommands.fix.join("\n"));
43087
+ setCopied(true);
43088
+ setTimeout(() => setCopied(false), 2e3);
43089
+ }
43008
43090
  if (input === "t" && !loading) {
43009
43091
  setTierSelectMode(true);
43010
43092
  }
@@ -43065,7 +43147,7 @@ function Detail({ item, slackTeamId, onBack, onApprove, onDismiss, onTierChange,
43065
43147
  setLoading(false);
43066
43148
  }
43067
43149
  }
43068
- return /* @__PURE__ */ import_react31.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react31.default.createElement(Box_default, { borderStyle: "single", borderBottom: false, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, "\u2190 back"), /* @__PURE__ */ import_react31.default.createElement(Box_default, null, /* @__PURE__ */ import_react31.default.createElement(Text, { color: tierColor, bold: true }, "T", item.tier), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, " \xB7 ", confidencePct, "%"))), /* @__PURE__ */ import_react31.default.createElement(Box_default, { flexDirection: "column", borderStyle: "single", borderTop: false, borderBottom: false, paddingX: 2, paddingY: 1 }, /* @__PURE__ */ import_react31.default.createElement(Text, { bold: true, color: "white" }, item.title), /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Text, { bold: true, color: "#888888" }, "RESOURCE"), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#cccccc" }, item.affected_resource_type || "unknown", " \xB7 ", item.affected_resource), /* @__PURE__ */ import_react31.default.createElement(Box_default, null, item.is_production ? /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#f59e0b" }, "production") : /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, "non-production"), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#444444" }, " \xB7 "), item.is_internet_facing ? /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#f59e0b" }, "internet-facing") : /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, "internal")), /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Text, { bold: true, color: "#888888" }, "AGENT REASONING"), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#cccccc" }, item.agent_reasoning || item.description || "No reasoning available."), /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Text, { bold: true, color: "#888888" }, "RECOMMENDED ACTION"), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#cccccc" }, item.recommended_action || "No recommendation."), /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Text, { bold: true, color: "#888888" }, "BLAST RADIUS"), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#cccccc" }, item.blast_radius || "Unknown"), loading && /* @__PURE__ */ import_react31.default.createElement(import_react31.default.Fragment, null, /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Box_default, null, /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#22c55e" }, /* @__PURE__ */ import_react31.default.createElement(build_default2, { type: "dots" })), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, " executing..."))), resultMessage && /* @__PURE__ */ import_react31.default.createElement(import_react31.default.Fragment, null, /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Text, { color: resultMessage.startsWith("\u2713") ? "#22c55e" : "#ef4444" }, resultMessage)), dismissMode && /* @__PURE__ */ import_react31.default.createElement(import_react31.default.Fragment, null, /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Box_default, null, /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, "Dismiss reason: "), /* @__PURE__ */ import_react31.default.createElement(
43150
+ return /* @__PURE__ */ import_react31.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react31.default.createElement(Box_default, { borderStyle: "single", borderBottom: false, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, "\u2190 back"), /* @__PURE__ */ import_react31.default.createElement(Box_default, null, /* @__PURE__ */ import_react31.default.createElement(Text, { color: tierColor, bold: true }, "T", item.tier), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, " \xB7 ", confidencePct, "%"))), /* @__PURE__ */ import_react31.default.createElement(Box_default, { flexDirection: "column", borderStyle: "single", borderTop: false, borderBottom: false, paddingX: 2, paddingY: 1 }, /* @__PURE__ */ import_react31.default.createElement(Text, { bold: true, color: "white" }, item.title), /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Text, { bold: true, color: "#888888" }, "RESOURCE"), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#cccccc" }, item.affected_resource_type || "unknown", " \xB7 ", item.affected_resource), /* @__PURE__ */ import_react31.default.createElement(Box_default, null, item.is_production ? /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#f59e0b" }, "production") : /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, "non-production"), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#444444" }, " \xB7 "), item.is_internet_facing ? /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#f59e0b" }, "internet-facing") : /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, "internal")), /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Text, { bold: true, color: "#888888" }, "AGENT REASONING"), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#cccccc" }, item.agent_reasoning || item.description || "No reasoning available."), /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Text, { bold: true, color: "#888888" }, "RECOMMENDED ACTION"), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#cccccc" }, item.recommended_action || "No recommendation."), /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Text, { bold: true, color: "#888888" }, "BLAST RADIUS"), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#cccccc" }, item.blast_radius || "Unknown"), fixCommands && /* @__PURE__ */ import_react31.default.createElement(import_react31.default.Fragment, null, /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Box_default, null, /* @__PURE__ */ import_react31.default.createElement(Text, { bold: true, color: "#888888" }, "FIX COMMAND"), copied ? /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#22c55e" }, " copied") : /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#444444" }, " [c] copy")), /* @__PURE__ */ import_react31.default.createElement(Box_default, { flexDirection: "column", borderStyle: "single", paddingX: 1 }, fixCommands.fix.map((line, i) => /* @__PURE__ */ import_react31.default.createElement(Text, { key: `fix-${i}`, color: "#22c55e" }, line)))), fixCommands && fixCommands.rollback.length > 0 && /* @__PURE__ */ import_react31.default.createElement(import_react31.default.Fragment, null, /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Text, { bold: true, color: "#888888" }, "ROLLBACK"), /* @__PURE__ */ import_react31.default.createElement(Box_default, { flexDirection: "column", borderStyle: "single", paddingX: 1 }, fixCommands.rollback.map((line, i) => /* @__PURE__ */ import_react31.default.createElement(Text, { key: `rb-${i}`, color: "#888888" }, line)))), loading && /* @__PURE__ */ import_react31.default.createElement(import_react31.default.Fragment, null, /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Box_default, null, /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#22c55e" }, /* @__PURE__ */ import_react31.default.createElement(build_default2, { type: "dots" })), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, " executing..."))), resultMessage && /* @__PURE__ */ import_react31.default.createElement(import_react31.default.Fragment, null, /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Text, { color: resultMessage.startsWith("\u2713") ? "#22c55e" : "#ef4444" }, resultMessage)), dismissMode && /* @__PURE__ */ import_react31.default.createElement(import_react31.default.Fragment, null, /* @__PURE__ */ import_react31.default.createElement(Text, null, " "), /* @__PURE__ */ import_react31.default.createElement(Box_default, null, /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, "Dismiss reason: "), /* @__PURE__ */ import_react31.default.createElement(
43069
43151
  build_default,
43070
43152
  {
43071
43153
  value: dismissReason,
@@ -43115,7 +43197,7 @@ function Detail({ item, slackTeamId, onBack, onApprove, onDismiss, onTierChange,
43115
43197
  }
43116
43198
  }
43117
43199
  }
43118
- )), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#444444" }, "[enter] submit [esc] cancel"))), /* @__PURE__ */ import_react31.default.createElement(Box_default, { borderStyle: "single", borderTop: false, paddingX: 1 }, /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, "[a] mark fixed [d] dismiss [n] noise [e] exception [t] tier [esc] back")));
43200
+ )), /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#444444" }, "[enter] submit [esc] cancel"))), /* @__PURE__ */ import_react31.default.createElement(Box_default, { borderStyle: "single", borderTop: false, paddingX: 1 }, /* @__PURE__ */ import_react31.default.createElement(Text, { color: "#888888" }, "[a] mark fixed [c] copy fix [d] dismiss [n] noise [e] exception [t] tier [esc] back")));
43119
43201
  }
43120
43202
  var import_react31, TIER_COLORS2, TIER_ITEMS;
43121
43203
  var init_Detail = __esm({
@@ -43127,6 +43209,7 @@ var init_Detail = __esm({
43127
43209
  await init_build5();
43128
43210
  await init_build4();
43129
43211
  init_open();
43212
+ init_clipboardy();
43130
43213
  init_api();
43131
43214
  TIER_COLORS2 = {
43132
43215
  1: "#888888",
@@ -44447,6 +44530,7 @@ function Dashboard() {
44447
44530
  {
44448
44531
  item: selectedItem,
44449
44532
  slackTeamId,
44533
+ gcpProjectId: status?.gcpProjectId,
44450
44534
  onBack: () => {
44451
44535
  setView("dashboard");
44452
44536
  setSelectedItem(null);
@@ -45058,7 +45142,7 @@ var {
45058
45142
  // src/cli/index.ts
45059
45143
  init_config();
45060
45144
  var program2 = new Command();
45061
- program2.name("veracarto").description("Security findings, down to what matters").version("0.1.5");
45145
+ program2.name("veracarto").description("Security findings, down to what matters").version("0.1.7");
45062
45146
  program2.command("init").description("Interactive onboarding wizard").option("--step <step>", "Jump to a specific step (gcp, github, slack)").action(async (opts) => {
45063
45147
  const { runInit: runInit2 } = await init_init().then(() => init_exports);
45064
45148
  await runInit2(opts.step);