dsh-search-plugin 0.2.0 → 0.2.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/lib/cidr.js ADDED
@@ -0,0 +1,36 @@
1
+ /** Schemastery serializes this function for browser forms. Keep it closure-free. */
2
+ export function validateCidr(rule) {
3
+ const [address, prefixText, ...extra] = rule.trim().split('/');
4
+ if (!address || !prefixText || extra.length || !/^(0|[1-9]\d*)$/.test(prefixText)) {
5
+ throw new Error(`Invalid CIDR: ${JSON.stringify(rule)}`);
6
+ }
7
+ let bytes;
8
+ if (address.includes(':')) {
9
+ if (!/^[\da-f:.]+$/i.test(address))
10
+ throw new Error(`Invalid IPv6 CIDR: ${JSON.stringify(rule)}`);
11
+ // URL uses the same IPv6 parser in Node and browsers, including embedded IPv4.
12
+ const normalized = new URL(`http://[${address}]/`).hostname.slice(1, -1);
13
+ const halves = normalized.split('::').map(part => part ? part.split(':') : []);
14
+ const groups = halves.length === 1 ? halves[0] : [...halves[0], ...Array(8 - halves[0].length - halves[1].length).fill('0'), ...halves[1]];
15
+ bytes = groups.flatMap(group => { const n = parseInt(group, 16); return [n >>> 8, n & 255]; });
16
+ }
17
+ else {
18
+ const parts = address.split('.');
19
+ if (parts.length !== 4 || parts.some(part => !/^(0|[1-9]\d{0,2})$/.test(part) || Number(part) > 255)) {
20
+ throw new Error(`Invalid IPv4 CIDR: ${JSON.stringify(rule)}`);
21
+ }
22
+ bytes = parts.map(Number);
23
+ }
24
+ const prefix = Number(prefixText);
25
+ if (prefix > bytes.length * 8)
26
+ throw new Error(`Invalid CIDR prefix: ${JSON.stringify(rule)}`);
27
+ if (bytes.some((byte, i) => (byte & (255 >>> Math.max(0, Math.min(8, prefix - i * 8)))) !== 0)) {
28
+ throw new Error(`CIDR must use its network base: ${JSON.stringify(rule)}`);
29
+ }
30
+ const embeddedV4 = bytes.length === 16 && bytes.slice(0, 10).every(b => b === 0)
31
+ && ((bytes[10] === 255 && bytes[11] === 255) || (bytes[10] === 0 && bytes[11] === 0));
32
+ if (embeddedV4 && bytes.some(Boolean) && !(bytes.slice(0, 15).every(b => b === 0) && bytes[15] === 1)) {
33
+ throw new Error(`Use an IPv4 CIDR for IPv4-mapped or compatible addresses: ${JSON.stringify(rule)}`);
34
+ }
35
+ return rule;
36
+ }
package/lib/fetch.js CHANGED
@@ -1,25 +1,14 @@
1
1
  import { lookup } from 'node:dns/promises';
2
2
  import { isIP } from 'node:net';
3
3
  import ipaddr from 'ipaddr.js';
4
+ import { validateCidr } from './cidr.js';
4
5
  import { WebError } from '@deepseek-ai/dsh-web';
5
6
  import { HttpFetchProvider } from '@deepseek-ai/dsh-web-fetch-http';
6
7
  const bareHost = (host) => host.startsWith('[') && host.endsWith(']') ? host.slice(1, -1) : host;
7
8
  function parseCidr(rule) {
8
- const [address, prefixText, ...extra] = rule.trim().split('/');
9
- if (!address || !prefixText || extra.length || !/^(0|[1-9]\d*)$/.test(prefixText) || !isIP(address) || address.includes('%')) {
10
- throw new Error(`Invalid CIDR: ${JSON.stringify(rule)}`);
11
- }
9
+ const [address, prefixText] = validateCidr(rule).trim().split('/');
12
10
  const bytes = ipaddr.parse(address).toByteArray();
13
11
  const prefix = Number(prefixText);
14
- if (prefix > bytes.length * 8)
15
- throw new Error(`Invalid CIDR prefix: ${JSON.stringify(rule)}`);
16
- // Requiring the network base catches accidental overly broad operator rules.
17
- if (bytes.some((byte, i) => (byte & (255 >>> Math.max(0, Math.min(8, prefix - i * 8)))) !== 0)) {
18
- throw new Error(`CIDR must use its network base: ${JSON.stringify(rule)}`);
19
- }
20
- if (bytes.length === 16 && isEmbeddedV4(bytes) && bytes.some(Boolean) && !(bytes.slice(0, 15).every(b => b === 0) && bytes[15] === 1)) {
21
- throw new Error(`Use an IPv4 CIDR for IPv4-mapped or compatible addresses: ${JSON.stringify(rule)}`);
22
- }
23
12
  return { bytes, prefix };
24
13
  }
25
14
  /** Validate explicit IPv4/IPv6 network CIDRs; no implicit private ranges are added. */
package/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import Schema from '@deepseek-ai/schemastery';
2
2
  import { ConfiguredSearchProvider } from './connection.js';
3
- import { ConfiguredFetchProvider, validateCidrs } from './fetch.js';
3
+ import { ConfiguredFetchProvider } from './fetch.js';
4
+ import { validateCidr } from './cidr.js';
4
5
  export const name = 'dsh-search-plugin';
5
6
  export const inject = ['web'];
6
7
  export const Config = Schema.object({
@@ -8,11 +9,7 @@ export const Config = Schema.object({
8
9
  deepseekEndpoint: Schema.string().pattern(/^https?:\/\/[^\s]+$/).default('https://api.deepseek.com/v1/tools').volatile(),
9
10
  deepseekCredentialRef: Schema.string().pattern(/^[A-Za-z_][A-Za-z0-9_]*$/).default('DEEPSEEK_API_KEY').volatile(),
10
11
  deepseekCount: Schema.number().step(1).min(1).max(50).default(10).volatile(),
11
- fetchAllowedCidrs: Schema.transform(Schema.array(Schema.string()), value => {
12
- if (!validateCidrs(value))
13
- throw new Error('Use valid IPv4 or IPv6 network CIDRs');
14
- return value;
15
- }, true).default([]).volatile(),
12
+ fetchAllowedCidrs: Schema.array(Schema.transform(Schema.string(), validateCidr, true)).default([]).volatile(),
16
13
  fetchTimeoutMs: Schema.number().step(1).min(1).default(30000).volatile(),
17
14
  fetchMaxResponseBytes: Schema.number().step(1).min(1).default(5000000).volatile(),
18
15
  fetchMaxContentChars: Schema.number().step(1).min(1).default(100000).volatile(),
@@ -0,0 +1,2 @@
1
+ /** Schemastery serializes this function for browser forms. Keep it closure-free. */
2
+ export declare function validateCidr(rule: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-search-plugin",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/urzz/dsh-search-plugin.git"