biofirewall 1.0.0 → 1.0.1

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/index.js CHANGED
@@ -8,4 +8,4 @@ module.exports = BioFirewall;
8
8
  module.exports.solve = challenge.solve;
9
9
 
10
10
  // Export middleware directly if preferred
11
- module.exports.middleware = (options) => new BioFirewall(options).middleware();
11
+ module.exports.middleware = (options) => new BioFirewall(options).middleware();
@@ -1,19 +1,27 @@
1
+ const crypto = require('crypto');
1
2
  const rules = require('./rules');
2
- const challenge = require('./challenge');
3
3
 
4
+ /**
5
+ * BioFirewall - Proof of Work Challenge for Silicon Verification
6
+ * Ensures only agents (silicon) can access, not browsers (carbon)
7
+ */
4
8
  class BioFirewall {
5
9
  constructor(options = {}) {
6
10
  this.options = {
7
11
  blockBrowsers: true,
8
12
  enforceChallenge: true,
9
- challengeDifficulty: 3, // Number of leading zeros required
13
+ challengeDifficulty: options.difficulty || options.challengeDifficulty || 4,
14
+ sessionTimeout: 60000,
10
15
  ...options
11
16
  };
12
17
  }
13
18
 
19
+ /**
20
+ * Express middleware
21
+ */
14
22
  middleware() {
15
23
  return (req, res, next) => {
16
- // 1. Passive Filtering (The Silicon Curtain)
24
+ // 1. Passive Filtering (Check User-Agent)
17
25
  if (this.options.blockBrowsers) {
18
26
  if (rules.isHuman(req)) {
19
27
  return res.status(406).type('application/json').json({
@@ -24,37 +32,43 @@ class BioFirewall {
24
32
  }
25
33
  }
26
34
 
27
- // 2. Active Filtering (The Computational Toll)
35
+ // 2. Active Filtering (PoW Challenge)
28
36
  if (this.options.enforceChallenge) {
29
- const solution = req.headers['x-bio-solution'];
37
+ // Get proof headers (both formats supported for compatibility)
38
+ const nonce = req.headers['x-bio-solution'] || req.headers['x-proof-nonce'];
39
+ const seed = req.headers['x-bio-challenge-seed'] || req.headers['x-proof-seed'];
30
40
 
31
- if (!solution) {
32
- return this.sendChallenge(res);
41
+ // If both are present, validate
42
+ if (nonce && seed) {
43
+ const isValid = this._validateProof(seed, nonce);
44
+
45
+ if (isValid) {
46
+ res.set('X-Silicon-Verified', 'true');
47
+ return next();
48
+ } else {
49
+ return res.status(403).json({
50
+ error: 'INVALID_PROOF',
51
+ message: 'Proof of Work validation failed',
52
+ challenge: this._generateChallenge()
53
+ });
54
+ }
33
55
  }
34
56
 
35
- // Verify specific challenge solution
36
- // In a real/stateless app, we might sign the seed.
37
- // For this demo, we verify the solution satisfies the generic work requirement.
38
- // To be fully secure, the 'seed' should be verified as one we issued recently.
39
- const originalSeed = req.headers['x-bio-challenge-seed']; // Client passes back the seed they solved for
40
-
41
- if (!originalSeed || !challenge.verify(originalSeed, solution, this.options.challengeDifficulty)) {
42
- return res.status(403).json({
43
- error: "INVALID_COMPUTATION",
44
- message: "Proof of work failed or insufficient difficulty."
45
- });
46
- }
57
+ // No proof, send challenge
58
+ return this.sendChallenge(res);
47
59
  }
48
60
 
49
61
  next();
50
62
  };
51
63
  }
52
64
 
65
+ /**
66
+ * Send a PoW challenge to the client
67
+ */
53
68
  sendChallenge(res) {
54
- const seed = challenge.generateSeed();
69
+ const seed = crypto.randomBytes(16).toString('hex');
55
70
  const difficulty = this.options.challengeDifficulty;
56
71
 
57
- // 428 Precondition Required
58
72
  res.status(428).set({
59
73
  'X-Bio-Challenge-Algo': 'sha256',
60
74
  'X-Bio-Challenge-Difficulty': difficulty,
@@ -70,6 +84,28 @@ class BioFirewall {
70
84
  }
71
85
  });
72
86
  }
87
+
88
+ /**
89
+ * Generate a new challenge
90
+ */
91
+ _generateChallenge() {
92
+ return {
93
+ algo: 'sha256',
94
+ seed: crypto.randomBytes(16).toString('hex'),
95
+ difficulty: this.options.challengeDifficulty
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Validate a nonce against a seed
101
+ */
102
+ _validateProof(seed, nonce) {
103
+ const candidate = seed + nonce;
104
+ const hash = crypto.createHash('sha256').update(candidate).digest('hex');
105
+ const target = '0'.repeat(this.options.challengeDifficulty);
106
+
107
+ return hash.startsWith(target);
108
+ }
73
109
  }
74
110
 
75
- module.exports = BioFirewall;
111
+ module.exports = BioFirewall;
package/lib/challenge.js CHANGED
@@ -1,28 +1,31 @@
1
- const crypto = require('crypto');
2
-
3
1
  module.exports = {
4
- generateSeed: () => {
5
- return crypto.randomBytes(16).toString('hex');
6
- },
2
+ isHuman: (req) => {
3
+ const userAgent = req.get('User-Agent') || '';
4
+ const accept = req.get('Accept') || '';
5
+
6
+ // Rule 1: Reject common Browser User-Agents
7
+ const browserKeywords = ['Mozilla', 'Chrome', 'Safari', 'Edge', 'Firefox', 'AppleWebKit'];
8
+ const isBrowserAgent = browserKeywords.some(keyword => userAgent.includes(keyword));
9
+
10
+ // Rule 2: Humans usually ask for HTML
11
+ const asksForHtml = accept.includes('text/html');
7
12
 
8
- verify: (seed, nonce, difficulty) => {
9
- const hash = crypto.createHash('sha256').update(seed + nonce).digest('hex');
10
- const prefix = '0'.repeat(difficulty);
11
- return hash.startsWith(prefix);
12
- },
13
+ // Rule 3: Missing User-Agent is suspicious for humans?
14
+ // Actually bots often have no UA or proper UA.
15
+ // Humans almost ALWAYS have a sophisticated UA string.
13
16
 
14
- // Helper for bots (exported so bots can use this alg)
15
- solve: (seed, difficulty) => {
16
- const prefix = '0'.repeat(difficulty);
17
- let nonce = 0;
18
- while (true) {
19
- const hash = crypto.createHash('sha256').update(seed + String(nonce)).digest('hex');
20
- if (hash.startsWith(prefix)) {
21
- return String(nonce);
17
+ if (isBrowserAgent) {
18
+ // Allow override if they explicitly identify as a bot in UA?
19
+ // For this specific 'anti-human' firewall, if it looks like a browser, kill it.
20
+ if (!userAgent.toLowerCase().includes('bot') && !userAgent.toLowerCase().includes('openclaw')) {
21
+ return true;
22
22
  }
23
- nonce++;
24
- // Safety break for testing to avoid infinite loop if difficulty is crazy high
25
- if (nonce > 10000000) return null;
26
23
  }
24
+
25
+ if (asksForHtml) {
26
+ return true;
27
+ }
28
+
29
+ return false;
27
30
  }
28
- };
31
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "biofirewall",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Anti-Human Firewall: Block browsers, allow bots via Proof-of-Work to verify silicon heritage.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -20,7 +20,6 @@
20
20
  "author": "OpenClaw Community",
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
- "axios": "^1.13.4",
24
- "express": "^4.21.2"
23
+ "axios": "^1.13.4"
25
24
  }
26
- }
25
+ }