xkat-cli 1.1.6 → 1.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/README.md CHANGED
@@ -55,6 +55,10 @@ xKat Agent includes the following safeguards:
55
55
  domains are accepted.
56
56
  3. **Audit log**: Every connection attempt and file-change event is recorded
57
57
  locally with a timestamp.
58
+ 4. **Binary Integrity Check**: During installation, the postinstall script downloads
59
+ the platform-specific prebuilt Rust binary over HTTPS and cryptographically
60
+ verifies its hash (SHA-256) against a build-time checksum manifest to prevent
61
+ MITM, tampering, or supply chain injection.
58
62
 
59
63
  ## Releasing (maintainers)
60
64
 
@@ -0,0 +1,7 @@
1
+ {
2
+ "xkat-agent-macos-aarch64": "1b34b5fa60c2097d034f5ae45ca5dcb67bfc1ad9ee67c4b84dd2be0c33debb0e",
3
+ "xkat-agent-macos-x64": "ffe1da9a5fe4c04976c9cbd527795d17647b6c7cc6597f5dd72bd23931301b6b",
4
+ "xkat-agent-linux-x64": "0963c85e9e92e36d157c69f7bd1a51285743ec3a41be71908f543b064e697e93",
5
+ "xkat-agent-linux-aarch64": "30e2c06adb61ad5e1e0ef77584a72a525201222e68049d2568084340c5f7bbc1",
6
+ "xkat-agent-win-x64.exe": "ec9a5b4c0b08db139fd7861df7de900b94d5f41577621780e99b8ce4ea40e9d3"
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xkat-cli",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "xKat Agent - an AI agent that securely connects your browser to your machine",
5
5
  "main": "bin/xkat-cli.js",
6
6
  "bin": {
@@ -0,0 +1,42 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const crypto = require('crypto');
4
+
5
+ // The artifacts directory is created by actions/download-artifact at the repository root
6
+ const artifactsDir = path.resolve(__dirname, '../../..', 'artifacts');
7
+ const destFile = path.resolve(__dirname, '..', 'bin', 'checksums.json');
8
+
9
+ const targets = [
10
+ 'xkat-agent-macos-aarch64',
11
+ 'xkat-agent-macos-x64',
12
+ 'xkat-agent-linux-x64',
13
+ 'xkat-agent-linux-aarch64',
14
+ 'xkat-agent-win-x64.exe',
15
+ ];
16
+
17
+ const checksums = {};
18
+
19
+ console.log(`🔍 Scanning for artifacts in: ${artifactsDir}`);
20
+
21
+ for (const target of targets) {
22
+ // In upload-r2/download-artifact, each artifact is downloaded into its own folder:
23
+ // artifacts/<artifact-name>/<artifact-name>
24
+ const filePath = path.join(artifactsDir, target, target);
25
+ if (!fs.existsSync(filePath)) {
26
+ console.error(`❌ Missing artifact for target ${target} at ${filePath}`);
27
+ process.exit(1);
28
+ }
29
+ const fileBuffer = fs.readFileSync(filePath);
30
+ const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
31
+ checksums[target] = hash;
32
+ console.log(`✅ Calculated hash for ${target}: ${hash}`);
33
+ }
34
+
35
+ // Make sure the destination directory exists
36
+ const destDir = path.dirname(destFile);
37
+ if (!fs.existsSync(destDir)) {
38
+ fs.mkdirSync(destDir, { recursive: true });
39
+ }
40
+
41
+ fs.writeFileSync(destFile, JSON.stringify(checksums, null, 2));
42
+ console.log(`✅ Generated checksums manifest: ${destFile}`);
@@ -7,6 +7,7 @@ const os = require('os');
7
7
  const fs = require('fs');
8
8
  const path = require('path');
9
9
  const https = require('https');
10
+ const crypto = require('crypto');
10
11
 
11
12
  // Version is the single source of truth from package.json, so a release bump
12
13
  // (npm version patch) automatically points install at the matching R2 folder.
@@ -28,6 +29,16 @@ function getPlatformTarget() {
28
29
  return map[`${platform}-${arch}`] || null;
29
30
  }
30
31
 
32
+ function calculateSha256(filePath) {
33
+ return new Promise((resolve, reject) => {
34
+ const hash = crypto.createHash('sha256');
35
+ const stream = fs.createReadStream(filePath);
36
+ stream.on('data', (data) => hash.update(data));
37
+ stream.on('end', () => resolve(hash.digest('hex')));
38
+ stream.on('error', (err) => reject(err));
39
+ });
40
+ }
41
+
31
42
  async function main() {
32
43
  const target = getPlatformTarget();
33
44
 
@@ -40,10 +51,35 @@ async function main() {
40
51
  const binDir = path.join(__dirname, '..', 'binaries');
41
52
  const destPath = path.join(binDir, target);
42
53
 
43
- // Skip if already downloaded
54
+ // Load checksums manifest if it exists
55
+ const checksumsPath = path.join(__dirname, '..', 'bin', 'checksums.json');
56
+ let expectedHash = null;
57
+ if (fs.existsSync(checksumsPath)) {
58
+ try {
59
+ const checksums = JSON.parse(fs.readFileSync(checksumsPath, 'utf8'));
60
+ expectedHash = checksums[target] || null;
61
+ } catch (e) {
62
+ console.warn(`⚠️ Failed to parse checksums manifest: ${e.message}`);
63
+ }
64
+ }
65
+
66
+ // Skip if already downloaded and verified
44
67
  if (fs.existsSync(destPath)) {
45
- console.log(`✅ xkat-agent binary already present: ${target}`);
46
- return;
68
+ if (expectedHash) {
69
+ try {
70
+ const currentHash = await calculateSha256(destPath);
71
+ if (currentHash === expectedHash) {
72
+ console.log(`✅ xkat-agent binary already present and verified: ${target}`);
73
+ return;
74
+ }
75
+ console.log(`⚠️ Hash mismatch for existing binary. Re-downloading...`);
76
+ } catch (err) {
77
+ console.log(`⚠️ Failed to verify existing binary hash: ${err.message}. Re-downloading...`);
78
+ }
79
+ } else {
80
+ console.log(`✅ xkat-agent binary already present: ${target}`);
81
+ return;
82
+ }
47
83
  }
48
84
 
49
85
  console.log(`📥 Downloading xkat-agent... (${target})`);
@@ -57,6 +93,18 @@ async function main() {
57
93
 
58
94
  try {
59
95
  await downloadFile(url, destPath);
96
+
97
+ // Verify integrity after download
98
+ if (expectedHash) {
99
+ const downloadedHash = await calculateSha256(destPath);
100
+ if (downloadedHash !== expectedHash) {
101
+ // Remove corrupted/tampered file
102
+ try { fs.unlinkSync(destPath); } catch (_) {}
103
+ throw new Error(`Integrity check failed: expected ${expectedHash}, got ${downloadedHash}`);
104
+ }
105
+ console.log(`✅ Integrity verified: SHA-256 match`);
106
+ }
107
+
60
108
  // Grant execute permission (Unix only)
61
109
  if (os.platform() !== 'win32') {
62
110
  fs.chmodSync(destPath, 0o755);