veil-proxy 0.3.1__tar.gz → 0.3.2__tar.gz
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.
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/PKG-INFO +6 -4
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/README.md +5 -3
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/pyproject.toml +1 -1
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/cli.py +2 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/mcpdb.py +1 -1
- veil_proxy-0.3.2/veil/netenv.py +62 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/proxy.py +2 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/reseed.py +2 -1
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/sources/sql.py +2 -1
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/LICENSE +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/__init__.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/adopt.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/anthropic.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/cloud.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/crypto.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/data/README.md +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/data/pg_anonymizer_identifier.csv +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/data/words.txt.gz +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/decode.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/detect.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/display.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/engine.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/filesafe.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/guard.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/hookclient.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/hooks.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/keys.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/match.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/ner.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/openai.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/quasi.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/responses.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/rotate.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/secretref.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/shell.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/sources/__init__.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/sources/core.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/sources/dynamo.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/sources/elastic.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/sources/files.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/sources/git.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/sources/kv.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/sources/mongo.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/sources/mssql.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/sources/schema.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/sources/warehouse.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/state.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/stream.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/team.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/tokens.py +0 -0
- {veil_proxy-0.3.1 → veil_proxy-0.3.2}/veil/vault.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: veil-proxy
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Privacy middleware for AI coding agents: the model sees consistent pseudonyms, your machine keeps the real values
|
|
5
5
|
Keywords: privacy,pii,llm,proxy,pseudonymization,claude-code,coding-agent
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -47,10 +47,10 @@ Privacy middleware for AI coding agents. The model sees consistent pseudonyms
|
|
|
47
47
|
and commands the agent runs locally get the real values back (shell- and SQL-safe).
|
|
48
48
|
|
|
49
49
|
```sh
|
|
50
|
-
pip install "veil-proxy[postgres]" # extras: postgres, mysql, mongo, redis, bedrock, ner, mssql, all
|
|
50
|
+
pip install "veil-proxy[postgres]" # extras: postgres, mysql, mongo, redis, bedrock, ner, mssql, snowflake, all
|
|
51
51
|
# (installs the `veil` and `veil-hook` commands; import name `veil`)
|
|
52
52
|
veil init ~/code/myapp # seed from .env DB URLs, SQLite, fixtures; wire Claude Code
|
|
53
|
-
veil service install # proxy as a LaunchAgent (macOS) / systemd user unit (Linux)
|
|
53
|
+
veil service install # proxy as a LaunchAgent (macOS) / systemd user unit (Linux) / Startup item (Windows)
|
|
54
54
|
veil status ~/code/myapp
|
|
55
55
|
```
|
|
56
56
|
|
|
@@ -63,7 +63,7 @@ https://github.com/lemonadesaltbagel/veil):
|
|
|
63
63
|
pattern matching (1,000 labelled records).
|
|
64
64
|
- **21/21** one-command bypass transforms are contained. Restored values are escaped for every
|
|
65
65
|
language layer they pass through (shell, heredocs, `python -c`, `node -e`, SQL, `bash -c`...):
|
|
66
|
-
35/35 injection tests, and
|
|
66
|
+
35/35 injection tests, and 68,000 fuzzed nested commands executed with 0 code executions.
|
|
67
67
|
- Prompt caching keeps working (74–89% cache hits); about 19 ms overhead per request at 40k seeded
|
|
68
68
|
values, and 37 + 68 ms per 234 KB request at 1.2 million seeded values (1.1 GB).
|
|
69
69
|
|
|
@@ -71,6 +71,8 @@ One local proxy (default `127.0.0.1:8787`) serves every client, routed by path:
|
|
|
71
71
|
Anthropic Messages (Claude Code, Agent SDK), Amazon Bedrock InvokeModel (binary eventstream),
|
|
72
72
|
Google Vertex rawPredict, Microsoft Foundry (`/foundry`), Claude Platform on AWS (`/anthropic-aws`),
|
|
73
73
|
OpenAI Chat Completions (Aider, Continue, OpenAI SDK) and OpenAI Responses (Codex CLI).
|
|
74
|
+
DeepSeek Harness uses the [dsh-veil](https://github.com/lemonadesaltbagel/dsh-veil) plugin. Other
|
|
75
|
+
agents' tools can get veil's restore rules with `veil proxy --tool-alias NAME=ROLE`.
|
|
74
76
|
|
|
75
77
|
Seeds from Postgres, MySQL/MariaDB, SQLite, SQL Server (native sensitivity labels), MongoDB, Redis,
|
|
76
78
|
Elasticsearch/OpenSearch and fixture files, plus personal fields declared in code (Prisma, Django,
|
|
@@ -5,10 +5,10 @@ Privacy middleware for AI coding agents. The model sees consistent pseudonyms
|
|
|
5
5
|
and commands the agent runs locally get the real values back (shell- and SQL-safe).
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
pip install "veil-proxy[postgres]" # extras: postgres, mysql, mongo, redis, bedrock, ner, mssql, all
|
|
8
|
+
pip install "veil-proxy[postgres]" # extras: postgres, mysql, mongo, redis, bedrock, ner, mssql, snowflake, all
|
|
9
9
|
# (installs the `veil` and `veil-hook` commands; import name `veil`)
|
|
10
10
|
veil init ~/code/myapp # seed from .env DB URLs, SQLite, fixtures; wire Claude Code
|
|
11
|
-
veil service install # proxy as a LaunchAgent (macOS) / systemd user unit (Linux)
|
|
11
|
+
veil service install # proxy as a LaunchAgent (macOS) / systemd user unit (Linux) / Startup item (Windows)
|
|
12
12
|
veil status ~/code/myapp
|
|
13
13
|
```
|
|
14
14
|
|
|
@@ -21,7 +21,7 @@ https://github.com/lemonadesaltbagel/veil):
|
|
|
21
21
|
pattern matching (1,000 labelled records).
|
|
22
22
|
- **21/21** one-command bypass transforms are contained. Restored values are escaped for every
|
|
23
23
|
language layer they pass through (shell, heredocs, `python -c`, `node -e`, SQL, `bash -c`...):
|
|
24
|
-
35/35 injection tests, and
|
|
24
|
+
35/35 injection tests, and 68,000 fuzzed nested commands executed with 0 code executions.
|
|
25
25
|
- Prompt caching keeps working (74–89% cache hits); about 19 ms overhead per request at 40k seeded
|
|
26
26
|
values, and 37 + 68 ms per 234 KB request at 1.2 million seeded values (1.1 GB).
|
|
27
27
|
|
|
@@ -29,6 +29,8 @@ One local proxy (default `127.0.0.1:8787`) serves every client, routed by path:
|
|
|
29
29
|
Anthropic Messages (Claude Code, Agent SDK), Amazon Bedrock InvokeModel (binary eventstream),
|
|
30
30
|
Google Vertex rawPredict, Microsoft Foundry (`/foundry`), Claude Platform on AWS (`/anthropic-aws`),
|
|
31
31
|
OpenAI Chat Completions (Aider, Continue, OpenAI SDK) and OpenAI Responses (Codex CLI).
|
|
32
|
+
DeepSeek Harness uses the [dsh-veil](https://github.com/lemonadesaltbagel/dsh-veil) plugin. Other
|
|
33
|
+
agents' tools can get veil's restore rules with `veil proxy --tool-alias NAME=ROLE`.
|
|
32
34
|
|
|
33
35
|
Seeds from Postgres, MySQL/MariaDB, SQLite, SQL Server (native sensitivity labels), MongoDB, Redis,
|
|
34
36
|
Elasticsearch/OpenSearch and fixture files, plus personal fields declared in code (Prisma, Django,
|
|
@@ -256,6 +256,8 @@ def _service(args) -> int:
|
|
|
256
256
|
|
|
257
257
|
def main(argv: list[str] | None = None) -> int:
|
|
258
258
|
argv = sys.argv[1:] if argv is None else argv
|
|
259
|
+
from .netenv import normalize_no_proxy
|
|
260
|
+
normalize_no_proxy() # before any httpx client: one unparseable NO_PROXY entry makes them all raise
|
|
259
261
|
if argv[:1] == ["audit"]:
|
|
260
262
|
try: # dev harness: only in a source checkout (prototype/veil_dev), never in the package
|
|
261
263
|
import os
|
|
@@ -154,7 +154,7 @@ class SQLSource:
|
|
|
154
154
|
self.conn = pymysql.connect(host=u.hostname or "127.0.0.1", port=u.port or 3306,
|
|
155
155
|
user=unquote(u.username or "root"), password=unquote(u.password or ""),
|
|
156
156
|
database=u.path.lstrip("/"), autocommit=True, charset="utf8mb4",
|
|
157
|
-
connect_timeout=10)
|
|
157
|
+
connect_timeout=10, read_timeout=60, write_timeout=60)
|
|
158
158
|
with self.conn.cursor() as c:
|
|
159
159
|
c.execute("SET SESSION TRANSACTION READ ONLY")
|
|
160
160
|
for limit in ("SET SESSION MAX_EXECUTION_TIME = 30000", # MySQL (ms)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""NO_PROXY entries that httpx can't parse.
|
|
2
|
+
|
|
3
|
+
httpx turns each NO_PROXY entry into a URL pattern when a client is created, and one entry it
|
|
4
|
+
can't parse makes every `httpx.Client()` raise, so veil would exit at startup. DeepSeek
|
|
5
|
+
Harness, for one, adds `[::1]` (an IPv6 address in brackets, which httpx doesn't recognise as an
|
|
6
|
+
address) for its child processes.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import ipaddress
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
import sys
|
|
15
|
+
from typing import MutableMapping
|
|
16
|
+
|
|
17
|
+
KEYS = ("NO_PROXY", "no_proxy")
|
|
18
|
+
_BRACKETED = re.compile(r"\[([0-9A-Fa-f:.%]+)\](?::\d+)?")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _pattern(host: str) -> str:
|
|
22
|
+
"""The URL pattern httpx builds for one NO_PROXY entry (httpx._utils.get_environment_proxies)."""
|
|
23
|
+
if "://" in host:
|
|
24
|
+
return host
|
|
25
|
+
for cls, fmt in ((ipaddress.IPv4Address, "all://{}"), (ipaddress.IPv6Address, "all://[{}]")):
|
|
26
|
+
try:
|
|
27
|
+
cls(host.split("/")[0])
|
|
28
|
+
return fmt.format(host)
|
|
29
|
+
except ValueError:
|
|
30
|
+
pass
|
|
31
|
+
return f"all://{host}" if host.lower() == "localhost" else f"all://*{host}"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _parses(host: str) -> bool:
|
|
35
|
+
import httpx
|
|
36
|
+
try:
|
|
37
|
+
httpx.URL(_pattern(host))
|
|
38
|
+
return True
|
|
39
|
+
except Exception:
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def normalize_no_proxy(environ: MutableMapping[str, str] = os.environ, log=sys.stderr) -> None:
|
|
44
|
+
"""Rewrite `[::1]` and `[::1]:port` to `::1`, and drop entries httpx would still reject."""
|
|
45
|
+
for key in KEYS:
|
|
46
|
+
value = environ.get(key)
|
|
47
|
+
if not value:
|
|
48
|
+
continue
|
|
49
|
+
kept, dropped = [], []
|
|
50
|
+
for host in (h.strip() for h in value.split(",")):
|
|
51
|
+
if not host:
|
|
52
|
+
continue
|
|
53
|
+
m = _BRACKETED.fullmatch(host)
|
|
54
|
+
host = m.group(1) if m else host
|
|
55
|
+
if host == "*" or _parses(host):
|
|
56
|
+
if host not in kept:
|
|
57
|
+
kept.append(host)
|
|
58
|
+
else:
|
|
59
|
+
dropped.append(host)
|
|
60
|
+
if dropped and log:
|
|
61
|
+
print(f"[veil] ignoring {key} entries that can't be parsed: {', '.join(dropped)}", file=log)
|
|
62
|
+
environ[key] = ",".join(kept)
|
|
@@ -414,6 +414,8 @@ def serve(veil: Veil, policy: Policy, port: int = 8787, upstream: str = "https:/
|
|
|
414
414
|
capture: str | None = None, openai_upstream: str | None = None, bedrock_upstream: str | None = None,
|
|
415
415
|
vertex_upstream: str | None = None, foundry_upstream: str | None = None,
|
|
416
416
|
anthropic_aws_upstream: str | None = None):
|
|
417
|
+
from .netenv import normalize_no_proxy
|
|
418
|
+
normalize_no_proxy()
|
|
417
419
|
client = httpx.Client(timeout=httpx.Timeout(600, connect=10))
|
|
418
420
|
observer = capture_observer(capture) if capture else None
|
|
419
421
|
server = LocalHTTPServer(("127.0.0.1", port), make_handler(veil, policy, upstream, client,
|
|
@@ -63,7 +63,8 @@ def fingerprint(source: str) -> str:
|
|
|
63
63
|
if u.scheme in ("mysql", "mariadb"):
|
|
64
64
|
import pymysql
|
|
65
65
|
c = pymysql.connect(host=u.hostname or "127.0.0.1", port=u.port or 3306, user=unquote(u.username or "root"),
|
|
66
|
-
password=unquote(u.password or ""), database=u.path.lstrip("/"), connect_timeout=5
|
|
66
|
+
password=unquote(u.password or ""), database=u.path.lstrip("/"), connect_timeout=5,
|
|
67
|
+
read_timeout=5, write_timeout=5) # connect_timeout doesn't cover the handshake
|
|
67
68
|
try:
|
|
68
69
|
with c.cursor() as cur:
|
|
69
70
|
try:
|
|
@@ -207,7 +207,8 @@ def seed_mysql(known: KnownValues, host: str, user: str, password: str, database
|
|
|
207
207
|
import pymysql
|
|
208
208
|
|
|
209
209
|
conn = pymysql.connect(host=host, port=port, user=user, password=password, database=database,
|
|
210
|
-
cursorclass=pymysql.cursors.DictCursor, charset="utf8mb4", connect_timeout=10
|
|
210
|
+
cursorclass=pymysql.cursors.DictCursor, charset="utf8mb4", connect_timeout=10,
|
|
211
|
+
read_timeout=300, write_timeout=300)
|
|
211
212
|
with conn.cursor() as cur:
|
|
212
213
|
cur.execute("SET SESSION TRANSACTION READ ONLY")
|
|
213
214
|
cur.execute("""SELECT TABLE_NAME t, COLUMN_NAME c, DATA_TYPE d, COLUMN_KEY k, COLUMN_COMMENT m
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|