@kinetixui/cli 0.5.0 → 0.6.0

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.
Files changed (2) hide show
  1. package/dist/index.js +43 -6
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -58,7 +58,11 @@ async function writeItems(cwd, config, items, overwrite) {
58
58
  for (const item of items) {
59
59
  for (const dep of item.dependencies ?? []) deps.add(dep);
60
60
  for (const file of item.files) {
61
- const dest = targetPath(cwd, config, file);
61
+ const dest = path2.resolve(targetPath(cwd, config, file));
62
+ const root = path2.resolve(cwd);
63
+ if (dest !== root && !dest.startsWith(root + path2.sep)) {
64
+ throw new Error(`Refusing to write outside the project: ${path2.relative(root, dest)}`);
65
+ }
62
66
  const rel = path2.relative(cwd, dest);
63
67
  if (existsSync2(dest) && !overwrite) {
64
68
  skipped.push(rel);
@@ -76,6 +80,37 @@ async function writeItems(cwd, config, items, overwrite) {
76
80
  import { existsSync as existsSync3 } from "fs";
77
81
  import { spawn } from "child_process";
78
82
  import path3 from "path";
83
+
84
+ // src/lib/validate.ts
85
+ function assertComponentName(name) {
86
+ if (typeof name !== "string" || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(name)) {
87
+ throw new Error(
88
+ `Invalid component name ${JSON.stringify(name)} \u2014 expected letters, digits and dashes.`
89
+ );
90
+ }
91
+ return name;
92
+ }
93
+ function assertRegistryUrl(url) {
94
+ let parsed;
95
+ try {
96
+ parsed = new URL(url);
97
+ } catch {
98
+ throw new Error(`Invalid registry URL: ${JSON.stringify(url)}`);
99
+ }
100
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
101
+ throw new Error(`Registry URL must be http(s), got "${parsed.protocol}".`);
102
+ }
103
+ return parsed.toString();
104
+ }
105
+ var PKG_SPEC = /^(?:@[a-z0-9-~][a-z0-9._-]*\/)?[a-z0-9-~][a-z0-9._-]*(?:@[a-z0-9.^~*x><=|.-]+)?$/i;
106
+ function assertPackageSpec(spec) {
107
+ if (typeof spec !== "string" || spec.startsWith("-") || !PKG_SPEC.test(spec)) {
108
+ throw new Error(`Refusing to install suspicious dependency: ${JSON.stringify(spec)}`);
109
+ }
110
+ return spec;
111
+ }
112
+
113
+ // src/lib/pm.ts
79
114
  function detectPackageManager(cwd) {
80
115
  if (existsSync3(path3.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
81
116
  if (existsSync3(path3.join(cwd, "yarn.lock"))) return "yarn";
@@ -87,8 +122,9 @@ function installArgs(pm, packages) {
87
122
  }
88
123
  function runInstall(cwd, pm, packages) {
89
124
  if (packages.length === 0) return Promise.resolve();
125
+ const safe = packages.map(assertPackageSpec);
90
126
  return new Promise((resolve, reject) => {
91
- const child = spawn(pm, installArgs(pm, packages), {
127
+ const child = spawn(pm, installArgs(pm, safe), {
92
128
  cwd,
93
129
  stdio: "inherit",
94
130
  shell: process.platform === "win32"
@@ -103,7 +139,7 @@ function runInstall(cwd, pm, packages) {
103
139
 
104
140
  // src/lib/registry.ts
105
141
  async function fetchRegistryIndex(registryUrl) {
106
- const url = `${registryUrl.replace(/\/$/, "")}/registry.json`;
142
+ const url = `${assertRegistryUrl(registryUrl).replace(/\/$/, "")}/registry.json`;
107
143
  const res = await fetch(url);
108
144
  if (!res.ok) {
109
145
  throw new Error(`Could not load the registry index (${res.status} at ${url}).`);
@@ -116,7 +152,8 @@ async function fetchComponentNames(registryUrl) {
116
152
  return items.filter((item) => item.type === "registry:ui").map((item) => item.name);
117
153
  }
118
154
  async function fetchRegistryItem(registryUrl, name) {
119
- const url = `${registryUrl.replace(/\/$/, "")}/${name}.json`;
155
+ const base = assertRegistryUrl(registryUrl).replace(/\/$/, "");
156
+ const url = `${base}/${assertComponentName(name)}.json`;
120
157
  const res = await fetch(url);
121
158
  if (!res.ok) {
122
159
  throw new Error(`Could not find "${name}" in the registry (${res.status} at ${url}).`);
@@ -129,10 +166,10 @@ async function resolveTree(registryUrl, names) {
129
166
  while (queue.length) {
130
167
  const name = queue.shift();
131
168
  if (!name || resolved.has(name)) continue;
132
- const item = await fetchRegistryItem(registryUrl, name);
169
+ const item = await fetchRegistryItem(registryUrl, assertComponentName(name));
133
170
  resolved.set(name, item);
134
171
  for (const dep of item.registryDependencies ?? []) {
135
- if (!resolved.has(dep)) queue.push(dep);
172
+ if (!resolved.has(dep)) queue.push(assertComponentName(dep));
136
173
  }
137
174
  }
138
175
  return resolved;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kinetixui/cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Add KinetixUI components and tokens to your project.",
5
5
  "type": "module",
6
6
  "license": "MIT",