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.
- package/bin/skillscokac.js +82 -53
- package/package.json +1 -1
package/bin/skillscokac.js
CHANGED
|
@@ -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 =
|
|
129
|
-
if (!isWithinBase
|
|
130
|
-
|
|
131
|
-
|
|
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 (!
|
|
171
|
-
|
|
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 (!
|
|
175
|
-
debugLog('DELETE_REJECTED',
|
|
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',
|
|
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(
|
|
187
|
-
debugLog('DELETE_REJECTED',
|
|
188
|
-
throw new Error(`Security: Rejected unsafe directory path: ${
|
|
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',
|
|
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 =
|
|
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
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
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
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
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
|
-
}
|
|
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
|
+
})()
|