@kata.dev/challenge-cli 1.0.0 → 1.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kata.dev/challenge-cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "CLI for authoring, packing, validating, and publishing Eval Engine challenges",
6
6
  "bin": {
@@ -7,7 +7,6 @@ export function registerLoginCommand(program) {
7
7
  .description('Save API credentials to ~/.challengerc.json')
8
8
  .requiredOption('--api <url>', 'Eval Engine API base URL')
9
9
  .requiredOption('--token <token>', 'Authentication token (JWT)')
10
- .option('--signing-secret <secret>', 'Challenge artifact signing secret (optional, for local validation)')
11
10
  .action(async (opts) => {
12
11
  const existing = loadConfig();
13
12
 
@@ -17,17 +16,10 @@ export function registerLoginCommand(program) {
17
16
  token: opts.token,
18
17
  };
19
18
 
20
- if (opts.signingSecret) {
21
- config.signingSecret = opts.signingSecret;
22
- }
23
-
24
19
  const configPath = saveConfig(config);
25
20
 
26
21
  console.log(chalk.green('✔') + ' Credentials saved to ' + chalk.dim(configPath));
27
22
  console.log(' API: ' + chalk.cyan(config.apiUrl));
28
23
  console.log(' Token: ' + chalk.dim(config.token.slice(0, 20) + '…'));
29
- if (config.signingSecret) {
30
- console.log(' Signing secret: ' + chalk.dim('configured'));
31
- }
32
24
  });
33
25
  }
@@ -11,7 +11,6 @@ import {
11
11
  resolveRuntimeMode,
12
12
  } from '../lib/helpers.js';
13
13
  import { resolveRuntimeDepsSourceDir } from '../lib/runtime-deps.js';
14
- import { resolveSigningSecret } from '../lib/config.js';
15
14
 
16
15
  export function registerPackCommand(program) {
17
16
  program
@@ -20,19 +19,11 @@ export function registerPackCommand(program) {
20
19
  .requiredOption('--dir <challengeDir>', 'Path to the challenge directory')
21
20
  .option('--out <outDir>', 'Output directory (defaults to <challengeDir>/dist)')
22
21
  .option('--runtime-mode <mode>', 'Runtime deps mode: auto (default) or manual', 'auto')
23
- .option('--signing-secret <secret>', 'Artifact signing secret (falls back to config)')
24
22
  .action(async (opts) => {
25
23
  const challengeDir = path.resolve(opts.dir);
26
24
  const outDir = path.resolve(opts.out || path.join(challengeDir, 'dist'));
27
25
  const runtimeMode = resolveRuntimeMode(opts.runtimeMode);
28
26
 
29
- const signingSecret = resolveSigningSecret(opts.signingSecret);
30
- if (!signingSecret) {
31
- throw new Error(
32
- 'Signing secret is required. Set it via --signing-secret, CHALLENGE_ARTIFACT_SIGNING_SECRET env var, or run "challenge login --signing-secret <secret>".'
33
- );
34
- }
35
-
36
27
  const challengeJsonPath = path.join(challengeDir, 'challenge.json');
37
28
  if (!fs.existsSync(challengeJsonPath)) {
38
29
  throw new Error(`challenge.json not found: ${challengeJsonPath}`);
@@ -63,11 +54,10 @@ export function registerPackCommand(program) {
63
54
  }
64
55
 
65
56
  const outputFile = path.join(outDir, `${type}.tar.gz`);
66
- const packed = await packArtifact(sourceDir, outputFile, signingSecret);
57
+ const packed = await packArtifact(sourceDir, outputFile);
67
58
 
68
59
  artifacts[type] = {
69
60
  sha256: packed.sha256,
70
- signature: packed.signature,
71
61
  sizeBytes: packed.sizeBytes,
72
62
  file: path.basename(outputFile),
73
63
  };
@@ -94,7 +84,7 @@ export function registerPackCommand(program) {
94
84
  console.log(chalk.green('✔') + ` Manifest: ${chalk.cyan(manifestPath)}`);
95
85
  console.log();
96
86
  console.log(' Next steps:');
97
- console.log(` ${chalk.cyan(`npx @kata.dev/challenge-cli validate --manifest ${path.relative(process.cwd(), manifestPath)}`)}`);
98
- console.log(` ${chalk.cyan(`npx @kata.dev/challenge-cli publish --manifest ${path.relative(process.cwd(), manifestPath)}`)}`);
87
+ console.log(` ${chalk.cyan(`npx "@kata.dev/challenge-cli" validate --manifest ${path.relative(process.cwd(), manifestPath)}`)}`);
88
+ console.log(` ${chalk.cyan(`npx "@kata.dev/challenge-cli" publish --manifest ${path.relative(process.cwd(), manifestPath)}`)}`);
99
89
  });
100
90
  }
@@ -5,27 +5,17 @@ import ora from 'ora';
5
5
  import {
6
6
  REQUIRED_ARTIFACT_TYPES,
7
7
  computeSha256Hex,
8
- signSha256Hex,
9
8
  validateTarGzBuffer,
10
9
  } from '../lib/artifacts.js';
11
10
  import { readJson } from '../lib/helpers.js';
12
- import { resolveSigningSecret } from '../lib/config.js';
13
11
 
14
12
  export function registerValidateCommand(program) {
15
13
  program
16
14
  .command('validate')
17
- .description('Validate packed artifacts locally')
15
+ .description('Validate packed artifacts locally (SHA-256 + tar safety)')
18
16
  .requiredOption('--manifest <path>', 'Path to cks-manifest.json')
19
- .option('--signing-secret <secret>', 'Artifact signing secret (falls back to config)')
20
17
  .action(async (opts) => {
21
18
  const manifestPath = path.resolve(opts.manifest);
22
- const signingSecret = resolveSigningSecret(opts.signingSecret);
23
-
24
- if (!signingSecret) {
25
- throw new Error(
26
- 'Signing secret is required for validation. Set it via --signing-secret, CHALLENGE_ARTIFACT_SIGNING_SECRET env var, or run "challenge login --signing-secret <secret>".'
27
- );
28
- }
29
19
 
30
20
  const manifest = readJson(manifestPath);
31
21
  const slug = manifest.metadata?.slug || 'unknown';
@@ -56,14 +46,7 @@ export function registerValidateCommand(program) {
56
46
  throw new Error(`SHA mismatch for ${type}. expected=${artifact.sha256} actual=${actualSha}`);
57
47
  }
58
48
 
59
- // Verify HMAC signature
60
- const actualSig = signSha256Hex(actualSha, signingSecret);
61
- if (actualSig !== artifact.signature) {
62
- spinner.fail(`${type}: signature mismatch`);
63
- throw new Error(`Signature mismatch for ${type}. expected=${artifact.signature} actual=${actualSig}`);
64
- }
65
-
66
- // Validate tar contents
49
+ // Validate tar contents (path safety, symlinks, size limits)
67
50
  spinner.text = `Validating ${type} tar contents…`;
68
51
  const { entryCount, totalSize } = await validateTarGzBuffer({ buffer });
69
52
 
@@ -73,5 +56,6 @@ export function registerValidateCommand(program) {
73
56
 
74
57
  console.log();
75
58
  console.log(chalk.green('✔') + ' All artifacts validated successfully');
59
+ console.log(chalk.dim(' Note: HMAC signature verification happens server-side during publish.'));
76
60
  });
77
61
  }
@@ -14,15 +14,7 @@ export function computeSha256Hex(buffer) {
14
14
  return crypto.createHash('sha256').update(buffer).digest('hex');
15
15
  }
16
16
 
17
- export function signSha256Hex(sha256Hex, secret) {
18
- if (!sha256Hex || typeof sha256Hex !== 'string') {
19
- throw new Error('sha256Hex is required to compute signature.');
20
- }
21
- if (!secret || typeof secret !== 'string') {
22
- throw new Error('Signing secret is required to compute signature.');
23
- }
24
- return crypto.createHmac('sha256', secret).update(sha256Hex).digest('hex');
25
- }
17
+
26
18
 
27
19
  export function assertSafeTarPath(entryPath) {
28
20
  const normalized = String(entryPath || '').replace(/\\/g, '/');
@@ -96,7 +88,7 @@ export async function validateTarGzBuffer({ buffer, maxEntries = 2000, maxExtrac
96
88
  return { entryCount, totalSize };
97
89
  }
98
90
 
99
- export async function packArtifact(sourceDir, outputFile, signingSecret) {
91
+ export async function packArtifact(sourceDir, outputFile) {
100
92
  fs.mkdirSync(path.dirname(outputFile), { recursive: true });
101
93
 
102
94
  await tarCreate(
@@ -112,12 +104,10 @@ export async function packArtifact(sourceDir, outputFile, signingSecret) {
112
104
 
113
105
  const buffer = fs.readFileSync(outputFile);
114
106
  const sha256 = computeSha256Hex(buffer);
115
- const signature = signSha256Hex(sha256, signingSecret);
116
107
 
117
108
  return {
118
109
  file: outputFile,
119
110
  sha256,
120
- signature,
121
111
  sizeBytes: buffer.length,
122
112
  };
123
113
  }
package/src/lib/config.js CHANGED
@@ -39,11 +39,4 @@ export function resolveToken(flagValue) {
39
39
  return flagValue || process.env.CKS_API_TOKEN || loadConfig().token || null;
40
40
  }
41
41
 
42
- export function resolveSigningSecret(flagValue) {
43
- return (
44
- flagValue ||
45
- process.env.CHALLENGE_ARTIFACT_SIGNING_SECRET ||
46
- loadConfig().signingSecret ||
47
- null
48
- );
49
- }
42
+