skillscokac 1.5.6 → 1.5.9

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/bin/skillscokac.js +82 -53
  2. package/package.json +1 -1
@@ -121,22 +121,34 @@ function preprocessFrontmatter(frontmatterText) {
121
121
  return processedLines.join('\n')
122
122
  }
123
123
 
124
+ function isWithIn(checkPath, base) {
125
+ return checkPath.startsWith(base + path.sep) || checkPath === base;
126
+ }
127
+
128
+ function isWithInClaudeSkill(resolvedBase) {
129
+ const expectedPersonalPath = path.resolve(os.homedir(), '.claude', 'skills');// + path.sep;
130
+ const expectedProjectPath = path.resolve(process.cwd(), '.claude', 'skills');// + path.sep;
131
+ return isWithIn(resolvedBase, expectedPersonalPath) || isWithIn(resolvedBase, expectedProjectPath);
132
+ }
133
+
124
134
  function validatePathWithinBase(targetPath, baseDir, enforceClaudeSkills = true) {
125
135
  try {
126
136
  const resolvedTarget = path.resolve(targetPath)
127
137
  const resolvedBase = path.resolve(baseDir)
128
- const isWithinBase = resolvedTarget.startsWith(resolvedBase + path.sep) || resolvedTarget === resolvedBase
129
- if (!isWithinBase || (enforceClaudeSkills && !resolvedTarget.includes(path.join('.claude', 'skills')))) {
130
- debugLog('PATH_REJECTED', targetPath, { resolvedTarget, resolvedBase, enforceClaudeSkills })
131
- return false
132
- }
133
- return true
138
+ const isWithinBase = isWithIn(resolvedTarget, resolvedBase);
139
+ if (!isWithinBase) return false;
140
+ if (!enforceClaudeSkills) return true;
141
+ if (!isWithInClaudeSkill(resolvedBase)) return false;
142
+ return true;
134
143
  } catch (error) {
135
144
  return false
136
145
  }
137
146
  }
138
147
 
139
148
  function safeWriteFile(filePath, content, baseDir, enforceClaudeSkills = true) {
149
+ if (enforceClaudeSkills && !isWithInClaudeSkill(filePath)) {
150
+ throw new Error(`Security: Rejected unsafe file path: ${filePath}`)
151
+ }
140
152
  if (!validatePathWithinBase(filePath, baseDir, enforceClaudeSkills)) {
141
153
  debugLog('WRITE_REJECTED', filePath, { reason: 'Path validation failed', baseDir })
142
154
  throw new Error(`Security: Rejected unsafe file path: ${filePath}`)
@@ -167,31 +179,34 @@ function safeRemoveDirectory(dirPath, skillNameOrBaseDir, isSkillDirectory = tru
167
179
  if (isSkillDirectory) {
168
180
  const skillName = skillNameOrBaseDir
169
181
  validateSkillName(skillName)
170
- if (!dirPath.endsWith(skillName)) {
171
- debugLog('DELETE_REJECTED', dirPath, { reason: 'Path does not end with skill name', skillName })
182
+ if (!isWithInClaudeSkill(resolvedPath)) {
183
+ throw new Error(`Security: Rejected unsafe file path: ${resolvedPath}`)
184
+ }
185
+ if (!resolvedPath.endsWith(skillName)) {
186
+ debugLog('DELETE_REJECTED', resolvedPath, { reason: 'Path does not end with skill name', skillName })
172
187
  throw new Error(`Security: Directory path must end with skill name: ${skillName}`)
173
188
  }
174
- if (!dirPath.includes(path.join('.claude', 'skills', skillName))) {
175
- debugLog('DELETE_REJECTED', dirPath, { reason: 'Path not in .claude/skills', skillName })
189
+ if (!resolvedPath.includes(path.join('.claude', 'skills', skillName))) {
190
+ debugLog('DELETE_REJECTED', resolvedPath, { reason: 'Path not in .claude/skills', skillName })
176
191
  throw new Error(`Security: Directory must be within .claude/skills/${skillName}`)
177
192
  }
178
193
  const expectedPersonalPath = path.resolve(os.homedir(), '.claude', 'skills', skillName)
179
194
  const expectedProjectPath = path.resolve(process.cwd(), '.claude', 'skills', skillName)
180
195
  if (resolvedPath !== expectedPersonalPath && resolvedPath !== expectedProjectPath) {
181
- debugLog('DELETE_REJECTED', dirPath, { reason: 'Unexpected path', resolvedPath, expectedPersonalPath, expectedProjectPath })
196
+ debugLog('DELETE_REJECTED', resolvedPath, { reason: 'Unexpected path', resolvedPath, expectedPersonalPath, expectedProjectPath })
182
197
  throw new Error(`Security: Unexpected directory path: ${resolvedPath}`)
183
198
  }
184
199
  } else {
185
200
  const baseDir = skillNameOrBaseDir
186
- if (!validatePathWithinBase(dirPath, baseDir, false)) {
187
- debugLog('DELETE_REJECTED', dirPath, { reason: 'Path validation failed', baseDir })
188
- throw new Error(`Security: Rejected unsafe directory path: ${dirPath}`)
201
+ if (!validatePathWithinBase(resolvedPath, baseDir, false)) {
202
+ debugLog('DELETE_REJECTED', resolvedPath, { reason: 'Path validation failed', baseDir })
203
+ throw new Error(`Security: Rejected unsafe directory path: ${resolvedPath}`)
189
204
  }
190
205
  }
191
206
  if (fs.existsSync(resolvedPath)) {
192
207
  const stats = fs.lstatSync(resolvedPath)
193
208
  if (stats.isSymbolicLink()) {
194
- debugLog('DELETE_REJECTED', dirPath, { reason: 'Symlink', resolvedPath })
209
+ debugLog('DELETE_REJECTED', resolvedPath, { reason: 'Symlink', resolvedPath })
195
210
  throw new Error(`Security: Cannot remove symlink: ${resolvedPath}`)
196
211
  }
197
212
  }
@@ -209,7 +224,7 @@ function safeRemoveDirectory(dirPath, skillNameOrBaseDir, isSkillDirectory = tru
209
224
  }
210
225
  }
211
226
  countFiles(resolvedPath)
212
- } catch (e) {}
227
+ } catch (e) { }
213
228
  debugLog('FS_RM', resolvedPath, { recursive: true, fileCount })
214
229
  fs.rmSync(resolvedPath, { recursive: true })
215
230
  debugLog('DELETE', resolvedPath, {
@@ -318,7 +333,7 @@ async function fetchSkill(skillName, options = {}) {
318
333
  throw new Error(`ZIP contains too many files (${zipEntries.length}). Maximum: ${MAX_FILES}`)
319
334
  }
320
335
  let totalSize = 0
321
- const MAX_COMPRESSION_RATIO = 10
336
+ const MAX_COMPRESSION_RATIO = 1250
322
337
  const MAX_PATH_LENGTH = 255
323
338
  for (const entry of zipEntries) {
324
339
  const entryName = entry.entryName
@@ -1141,46 +1156,60 @@ program
1141
1156
  .option('-f, --remove-skill-force <skillName>', 'Remove skill from all locations without confirmation')
1142
1157
  .option('-a, --remove-all-skills', 'Remove all installed skills')
1143
1158
  .option('-A, --remove-all-skills-force', 'Remove all installed skills without confirmation')
1159
+ .option('-t, --test', 'Test code')
1144
1160
  .option('-l, --list-installed-skills', 'List all installed skills')
1145
1161
  .parse(process.argv)
1146
1162
 
1147
1163
  const options = program.opts()
1148
1164
 
1149
- ;(async () => {
1150
- try {
1151
- if (options.installSkill) {
1152
- await installSkillCommand(options.installSkill)
1153
- } else if (options.installCollection) {
1154
- await installCollectionCommand(options.installCollection)
1155
- } else if (options.download) {
1156
- if (options.download.length < 1 || options.download.length > 2) {
1157
- console.log(chalk.red('✗ Invalid arguments for --download'))
1158
- console.log(chalk.dim('Usage: npx skillscokac --download <skillName> [path]'))
1159
- console.log()
1160
- process.exit(1)
1165
+ ; (async () => {
1166
+ try {
1167
+ if (options.installSkill) {
1168
+ await installSkillCommand(options.installSkill)
1169
+ } else if (options.installCollection) {
1170
+ await installCollectionCommand(options.installCollection)
1171
+ } else if (options.download) {
1172
+ if (options.download.length < 1 || options.download.length > 2) {
1173
+ console.log(chalk.red('✗ Invalid arguments for --download'))
1174
+ console.log(chalk.dim('Usage: npx skillscokac --download <skillName> [path]'))
1175
+ console.log()
1176
+ process.exit(1)
1177
+ }
1178
+ const [skillName, downloadPath] = options.download
1179
+ await downloadSkillCommand(skillName, downloadPath)
1180
+ } else if (options.test) {
1181
+ console.log(validatePathWithinBase('/home/ubuntu/codejogak/skillscokac/11/11', '/home/ubuntu/codejogak/skillscokac/11/', false));
1182
+ console.log(validatePathWithinBase('/home/ubuntu/codejogak/skillscokac', '/home/ubuntu/codejogak/skillscokac/ffe'));
1183
+ console.log(validatePathWithinBase('/home/ubuntu/codejogak/skillscokac', '/home/ubuntu/codejogak/skillscokac'));
1184
+ console.log(validatePathWithinBase('/home/ubuntu/codejogak/skillscokac/dfe', '/home/ubuntu/codejogak/skillscokac'));
1185
+ console.log(validatePathWithinBase('/home/ubuntu/.claude', '/home/ubuntu/.claude'));
1186
+ console.log(validatePathWithinBase('/home/ubuntu/.claude/dd', '/home/ubuntu/.claude'));
1187
+ console.log(validatePathWithinBase('/home/ubuntu/.claude/dd', '/home/ubuntu/.claude/skills'));
1188
+ console.log(validatePathWithinBase('/home/ubuntu/.claude/skills', '/home/ubuntu/.claude/skills/'));
1189
+ console.log(validatePathWithinBase('/home/ubuntu/.claude/skills/f', '/home/ubuntu/.claude/skills'));
1190
+ console.log(isWithInClaudeSkill('/home/ubuntu/.claude/skills/f'));
1191
+ console.log(isWithInClaudeSkill('/home/ubuntu/1.claude/skills/f'));
1192
+ console.log(isWithInClaudeSkill('/home/ubuntu/.claude/skills'));
1193
+ } else if (options.upload) {
1194
+ await uploadSkillCommand(options.upload, options.apikey)
1195
+ } else if (options.uploadmodify) {
1196
+ await uploadModifySkillCommand(options.uploadmodify, options.apikey)
1197
+ } else if (options.removeAllSkillsForce) {
1198
+ await removeAllSkillsCommand(true)
1199
+ } else if (options.removeAllSkills) {
1200
+ await removeAllSkillsCommand()
1201
+ } else if (options.removeSkillForce) {
1202
+ await removeSkillCommand(options.removeSkillForce, true)
1203
+ } else if (options.removeSkill) {
1204
+ await removeSkillCommand(options.removeSkill)
1205
+ } else if (options.listInstalledSkills) {
1206
+ await listInstalledSkillsCommand()
1207
+ } else {
1208
+ program.help()
1161
1209
  }
1162
- const [skillName, downloadPath] = options.download
1163
- await downloadSkillCommand(skillName, downloadPath)
1164
- } else if (options.upload) {
1165
- await uploadSkillCommand(options.upload, options.apikey)
1166
- } else if (options.uploadmodify) {
1167
- await uploadModifySkillCommand(options.uploadmodify, options.apikey)
1168
- } else if (options.removeAllSkillsForce) {
1169
- await removeAllSkillsCommand(true)
1170
- } else if (options.removeAllSkills) {
1171
- await removeAllSkillsCommand()
1172
- } else if (options.removeSkillForce) {
1173
- await removeSkillCommand(options.removeSkillForce, true)
1174
- } else if (options.removeSkill) {
1175
- await removeSkillCommand(options.removeSkill)
1176
- } else if (options.listInstalledSkills) {
1177
- await listInstalledSkillsCommand()
1178
- } else {
1179
- program.help()
1210
+ } catch (error) {
1211
+ console.error(chalk.red('\n✗ Unexpected error:'), error.message)
1212
+ if (process.env.DEBUG) console.error(error.stack)
1213
+ process.exit(1)
1180
1214
  }
1181
- } catch (error) {
1182
- console.error(chalk.red('\n✗ Unexpected error:'), error.message)
1183
- if (process.env.DEBUG) console.error(error.stack)
1184
- process.exit(1)
1185
- }
1186
- })()
1215
+ })()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillscokac",
3
- "version": "1.5.6",
3
+ "version": "1.5.9",
4
4
  "description": "CLI tool to install and manage Claude Code skills from skills.cokac.com",
5
5
  "main": "bin/skillscokac.js",
6
6
  "bin": {