lazaret 0.0.1__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.
Files changed (33) hide show
  1. lazaret-0.0.1/LICENSE +13 -0
  2. lazaret-0.0.1/PKG-INFO +44 -0
  3. lazaret-0.0.1/README.md +23 -0
  4. lazaret-0.0.1/_build/lazaret_build.py +238 -0
  5. lazaret-0.0.1/pyproject.toml +9 -0
  6. lazaret-0.0.1/src/lazaret/__init__.py +11 -0
  7. lazaret-0.0.1/src/lazaret/__main__.py +3 -0
  8. lazaret-0.0.1/src/lazaret/mcp/__init__.py +1 -0
  9. lazaret-0.0.1/src/lazaret/mcp/__main__.py +3 -0
  10. lazaret-0.0.1/src/lazaret/mcp/server.py +536 -0
  11. lazaret-0.0.1/src/lazaret/pg/__init__.py +40 -0
  12. lazaret-0.0.1/src/lazaret/pg/_dsn.py +277 -0
  13. lazaret-0.0.1/src/lazaret/pg/_scram.py +202 -0
  14. lazaret-0.0.1/src/lazaret/pg/_types.py +374 -0
  15. lazaret-0.0.1/src/lazaret/pg/connection.py +858 -0
  16. lazaret-0.0.1/src/lazaret/pg/errors.py +115 -0
  17. lazaret-0.0.1/src/lazaret/registry/__init__.py +1 -0
  18. lazaret-0.0.1/src/lazaret/registry/__main__.py +3 -0
  19. lazaret-0.0.1/src/lazaret/registry/repo.py +1199 -0
  20. lazaret-0.0.1/src/lazaret/registry/schema.sql +53 -0
  21. lazaret-0.0.1/src/lazaret/safexml/ElementTree.py +244 -0
  22. lazaret-0.0.1/src/lazaret/safexml/__init__.py +46 -0
  23. lazaret-0.0.1/src/lazaret/safexml/_common.py +150 -0
  24. lazaret-0.0.1/src/lazaret/safexml/minidom.py +86 -0
  25. lazaret-0.0.1/src/lazaret/safexml/pulldom.py +26 -0
  26. lazaret-0.0.1/src/lazaret/safexml/sax.py +95 -0
  27. lazaret-0.0.1/src/lazaret/safexml/xmlrpc.py +140 -0
  28. lazaret-0.0.1/src/lazaret/scanner/__init__.py +2 -0
  29. lazaret-0.0.1/src/lazaret/scanner/core.py +2558 -0
  30. lazaret-0.0.1/src/lazaret/scanner/flow.py +995 -0
  31. lazaret-0.0.1/src/lazaret/scanner/reports.py +384 -0
  32. lazaret-0.0.1/src/lazaret/scanner/sca.py +859 -0
  33. lazaret-0.0.1/src/lazaret/web/lazaret.html +1293 -0
lazaret-0.0.1/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2026 The Lazaret authors
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
lazaret-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,44 @@
1
+ Metadata-Version: 2.1
2
+ Name: lazaret
3
+ Version: 0.0.1
4
+ Summary: Static security, supply-chain and quality analysis for Python, JavaScript and SQL
5
+ Requires-Python: >=3.10
6
+ License: Apache-2.0
7
+ Keywords: security,supply-chain,sast,taint-analysis,sca,pypi,npm
8
+ Project-URL: Homepage, https://lazaret.dev
9
+ Project-URL: Source, https://github.com/lazaret-dev/lazaret
10
+ Project-URL: Issues, https://github.com/lazaret-dev/lazaret/issues
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Topic :: Security
19
+ Classifier: Topic :: Software Development :: Quality Assurance
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Lazaret
23
+
24
+ Static security, supply-chain and quality analysis for Python, JavaScript and SQL, with **no dependencies**: Lazaret runs on the Python standard library alone, and even building it downloads nothing.
25
+
26
+ ```bash
27
+ pip install lazaret
28
+ lazaret path/to/project # scan; writes lazaret-report.{html,json}
29
+ lazaret . --ci --sarif out.sarif # quality gate for CI, SARIF for code scanning
30
+ lazaret-registry scan npm:left-pad # audit a published npm / PyPI package
31
+ lazaret-sca . --bundle cve-bundle.json # match installed dependencies against CVEs
32
+ lazaret-mcp # MCP server, so an AI assistant can scan code
33
+ ```
34
+
35
+ What it finds:
36
+
37
+ - **Security:** SQL/command/code injection, SSTI, XXE, unsafe deserialization, XSS sinks, weak crypto, disabled TLS verification, hardcoded secrets (provider signatures and entropy), and more, across about 58 rules.
38
+ - **Taint analysis:** follows untrusted input through assignments, function calls, and across files into sinks, with category-aware sanitizers and a configurable source/sink/sanitizer spec.
39
+ - **Supply chain:** decode-then-execute patterns, packed and obfuscated JavaScript, suspicious install hooks, smuggled binaries and nested archives, both in your tree and in published npm/PyPI packages.
40
+ - **Quality:** bugs, code smells, complexity, duplication, with a quality gate and ratings.
41
+
42
+ Two of its building blocks are usable on their own (provisional APIs until 1.0): `lazaret.pg`, a PostgreSQL client in pure Python with SCRAM-SHA-256, channel binding and TLS; and `lazaret.safexml`, a layer that makes the stdlib XML parsers safe for untrusted input.
43
+
44
+ Licensed under Apache-2.0. Documentation, source and issue tracker: https://github.com/lazaret-dev/lazaret · https://lazaret.dev
@@ -0,0 +1,23 @@
1
+ # Lazaret
2
+
3
+ Static security, supply-chain and quality analysis for Python, JavaScript and SQL, with **no dependencies**: Lazaret runs on the Python standard library alone, and even building it downloads nothing.
4
+
5
+ ```bash
6
+ pip install lazaret
7
+ lazaret path/to/project # scan; writes lazaret-report.{html,json}
8
+ lazaret . --ci --sarif out.sarif # quality gate for CI, SARIF for code scanning
9
+ lazaret-registry scan npm:left-pad # audit a published npm / PyPI package
10
+ lazaret-sca . --bundle cve-bundle.json # match installed dependencies against CVEs
11
+ lazaret-mcp # MCP server, so an AI assistant can scan code
12
+ ```
13
+
14
+ What it finds:
15
+
16
+ - **Security:** SQL/command/code injection, SSTI, XXE, unsafe deserialization, XSS sinks, weak crypto, disabled TLS verification, hardcoded secrets (provider signatures and entropy), and more, across about 58 rules.
17
+ - **Taint analysis:** follows untrusted input through assignments, function calls, and across files into sinks, with category-aware sanitizers and a configurable source/sink/sanitizer spec.
18
+ - **Supply chain:** decode-then-execute patterns, packed and obfuscated JavaScript, suspicious install hooks, smuggled binaries and nested archives, both in your tree and in published npm/PyPI packages.
19
+ - **Quality:** bugs, code smells, complexity, duplication, with a quality gate and ratings.
20
+
21
+ Two of its building blocks are usable on their own (provisional APIs until 1.0): `lazaret.pg`, a PostgreSQL client in pure Python with SCRAM-SHA-256, channel binding and TLS; and `lazaret.safexml`, a layer that makes the stdlib XML parsers safe for untrusted input.
22
+
23
+ Licensed under Apache-2.0. Documentation, source and issue tracker: https://github.com/lazaret-dev/lazaret · https://lazaret.dev
@@ -0,0 +1,238 @@
1
+ """Lazaret's build backend (PEP 517 / PEP 660), standard library only.
2
+
3
+ Building Lazaret downloads nothing: pyproject.toml declares no build
4
+ requirements and points here via backend-path. pip uses these hooks for
5
+ `pip install .` and `pip install -e .`; release CI calls them directly:
6
+
7
+ python _build/lazaret_build.py [output-dir] # writes the wheel and sdist
8
+
9
+ Package metadata lives in this module (METADATA below) rather than in a
10
+ [project] table: a backend must honor [project] if it exists, and reading TOML
11
+ on Python 3.10 would need a third-party parser. The version has one source of
12
+ truth: __version__ in src/lazaret/__init__.py.
13
+
14
+ Outputs are reproducible: file order, timestamps, and permissions are fixed,
15
+ so building the same tree twice gives byte-identical artifacts (set
16
+ SOURCE_DATE_EPOCH to stamp a specific time instead of 1980-01-01).
17
+
18
+ The wheel contains only the package. Tests and fixtures never ship, in the
19
+ wheel or the sdist (see STRUCTURE.md, "What ships to the registries").
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import base64
25
+ import hashlib
26
+ import io
27
+ import os
28
+ import pathlib
29
+ import re
30
+ import sys
31
+ import tarfile
32
+ import zipfile
33
+
34
+ ROOT = pathlib.Path(__file__).resolve().parent.parent # python/
35
+ SRC = ROOT / "src"
36
+ PKG = SRC / "lazaret"
37
+
38
+ NAME = "lazaret"
39
+ METADATA = {
40
+ "Summary": "Static security, supply-chain and quality analysis for Python, JavaScript and SQL",
41
+ "Requires-Python": ">=3.10",
42
+ "License": "Apache-2.0",
43
+ "Keywords": "security,supply-chain,sast,taint-analysis,sca,pypi,npm",
44
+ "Project-URL": [
45
+ "Homepage, https://lazaret.dev",
46
+ "Source, https://github.com/lazaret-dev/lazaret",
47
+ "Issues, https://github.com/lazaret-dev/lazaret/issues",
48
+ ],
49
+ "Classifier": [
50
+ "Development Status :: 4 - Beta",
51
+ "Environment :: Console",
52
+ "Intended Audience :: Developers",
53
+ "License :: OSI Approved :: Apache Software License",
54
+ "Operating System :: OS Independent",
55
+ "Programming Language :: Python :: 3",
56
+ "Programming Language :: Python :: 3 :: Only",
57
+ "Topic :: Security",
58
+ "Topic :: Software Development :: Quality Assurance",
59
+ ],
60
+ }
61
+ # Deliberately empty: Lazaret has no runtime dependencies. Tested.
62
+ REQUIRES_DIST: list[str] = []
63
+
64
+ CONSOLE_SCRIPTS = {
65
+ "lazaret": "lazaret.scanner.core:main",
66
+ "lazaret-registry": "lazaret.registry.repo:main",
67
+ "lazaret-mcp": "lazaret.mcp.server:main",
68
+ "lazaret-sca": "lazaret.scanner.sca:main",
69
+ }
70
+
71
+ SDIST_TOP_FILES = ["pyproject.toml", "README.md", "LICENSE"]
72
+ _SKIP_DIRS = {"__pycache__"}
73
+ _SKIP_SUFFIXES = (".pyc", ".pyo")
74
+
75
+
76
+ # --- helpers -------------------------------------------------------------------
77
+
78
+ def version() -> str:
79
+ text = (PKG / "__init__.py").read_text(encoding="utf-8")
80
+ match = re.search(r'^__version__\s*=\s*["\']([^"\']+)["\']', text, re.M)
81
+ if not match:
82
+ raise RuntimeError("__version__ not found in src/lazaret/__init__.py")
83
+ return match.group(1)
84
+
85
+
86
+ def _epoch_date_time() -> tuple[int, int, int, int, int, int]:
87
+ import time
88
+ epoch = os.environ.get("SOURCE_DATE_EPOCH")
89
+ if not epoch:
90
+ return (1980, 1, 1, 0, 0, 0)
91
+ t = time.gmtime(max(int(epoch), 315532800)) # zip can't go before 1980
92
+ return (t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec)
93
+
94
+
95
+ def _package_files() -> list[pathlib.Path]:
96
+ out = []
97
+ for path in sorted(PKG.rglob("*")):
98
+ if not path.is_file():
99
+ continue
100
+ if _SKIP_DIRS & set(path.relative_to(PKG).parts) or path.suffix in _SKIP_SUFFIXES:
101
+ continue
102
+ out.append(path)
103
+ return out
104
+
105
+
106
+ def metadata_text() -> str:
107
+ lines = ["Metadata-Version: 2.1", f"Name: {NAME}", f"Version: {version()}"]
108
+ for key, value in METADATA.items():
109
+ for item in (value if isinstance(value, list) else [value]):
110
+ lines.append(f"{key}: {item}")
111
+ lines += [f"Requires-Dist: {req}" for req in REQUIRES_DIST]
112
+ readme = ROOT / "README.md"
113
+ if readme.exists():
114
+ lines.append("Description-Content-Type: text/markdown")
115
+ return "\n".join(lines) + "\n\n" + readme.read_text(encoding="utf-8")
116
+ return "\n".join(lines) + "\n"
117
+
118
+
119
+ def _entry_points_text() -> str:
120
+ body = "".join(f"{name} = {target}\n" for name, target in CONSOLE_SCRIPTS.items())
121
+ return "[console_scripts]\n" + body
122
+
123
+
124
+ def _record_hash(data: bytes) -> str:
125
+ digest = base64.urlsafe_b64encode(hashlib.sha256(data).digest()).rstrip(b"=").decode("ascii")
126
+ return f"sha256={digest}"
127
+
128
+
129
+ class _WheelWriter:
130
+ def __init__(self, path: pathlib.Path):
131
+ self._zip = zipfile.ZipFile(path, "w", compression=zipfile.ZIP_DEFLATED)
132
+ self._records: list[str] = []
133
+ self._date_time = _epoch_date_time()
134
+
135
+ def add(self, arcname: str, data: bytes) -> None:
136
+ info = zipfile.ZipInfo(arcname, date_time=self._date_time)
137
+ info.external_attr = 0o644 << 16
138
+ info.compress_type = zipfile.ZIP_DEFLATED
139
+ self._zip.writestr(info, data)
140
+ self._records.append(f"{arcname},{_record_hash(data)},{len(data)}")
141
+
142
+ def close(self, dist_info: str) -> None:
143
+ record = f"{dist_info}/RECORD"
144
+ body = "\n".join(self._records + [f"{record},,"]) + "\n"
145
+ info = zipfile.ZipInfo(record, date_time=self._date_time)
146
+ info.external_attr = 0o644 << 16
147
+ self._zip.writestr(info, body.encode("utf-8"))
148
+ self._zip.close()
149
+
150
+
151
+ def _write_wheel(directory: str, payload: dict[str, bytes]) -> str:
152
+ ver = version()
153
+ dist_info = f"{NAME}-{ver}.dist-info"
154
+ filename = f"{NAME}-{ver}-py3-none-any.whl"
155
+ writer = _WheelWriter(pathlib.Path(directory) / filename)
156
+ for arcname in sorted(payload):
157
+ writer.add(arcname, payload[arcname])
158
+ writer.add(f"{dist_info}/METADATA", metadata_text().encode("utf-8"))
159
+ writer.add(f"{dist_info}/WHEEL", (
160
+ "Wheel-Version: 1.0\nGenerator: lazaret_build\nRoot-Is-Purelib: true\nTag: py3-none-any\n"
161
+ ).encode("utf-8"))
162
+ writer.add(f"{dist_info}/entry_points.txt", _entry_points_text().encode("utf-8"))
163
+ license_file = ROOT / "LICENSE"
164
+ if license_file.exists():
165
+ writer.add(f"{dist_info}/licenses/LICENSE", license_file.read_bytes())
166
+ writer.close(dist_info)
167
+ return filename
168
+
169
+
170
+ # --- PEP 517 hooks ---------------------------------------------------------------
171
+
172
+ def get_requires_for_build_wheel(config_settings=None):
173
+ return []
174
+
175
+
176
+ def get_requires_for_build_sdist(config_settings=None):
177
+ return []
178
+
179
+
180
+ def get_requires_for_build_editable(config_settings=None):
181
+ return []
182
+
183
+
184
+ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
185
+ payload = {path.relative_to(SRC).as_posix(): path.read_bytes() for path in _package_files()}
186
+ return _write_wheel(wheel_directory, payload)
187
+
188
+
189
+ def build_editable(wheel_directory, config_settings=None, metadata_directory=None):
190
+ """PEP 660: a wheel whose only payload is a .pth file pointing at src/."""
191
+ pth = f"__editable__.{NAME}-{version()}.pth"
192
+ return _write_wheel(wheel_directory, {pth: (str(SRC) + "\n").encode("utf-8")})
193
+
194
+
195
+ def build_sdist(sdist_directory, config_settings=None):
196
+ ver = version()
197
+ base = f"{NAME}-{ver}"
198
+ filename = f"{base}.tar.gz"
199
+ mtime = int(os.environ.get("SOURCE_DATE_EPOCH", "315532800"))
200
+
201
+ members: list[tuple[str, bytes]] = []
202
+ for name in SDIST_TOP_FILES:
203
+ path = ROOT / name
204
+ if path.exists():
205
+ members.append((name, path.read_bytes()))
206
+ members.append(("_build/lazaret_build.py", pathlib.Path(__file__).read_bytes()))
207
+ for path in _package_files():
208
+ members.append((path.relative_to(ROOT).as_posix(), path.read_bytes()))
209
+ members.append(("PKG-INFO", metadata_text().encode("utf-8")))
210
+
211
+ # gzip header carries a timestamp too; pin it for reproducible output
212
+ raw = io.BytesIO()
213
+ import gzip
214
+ with gzip.GzipFile(fileobj=raw, mode="wb", mtime=mtime) as gz:
215
+ with tarfile.open(fileobj=gz, mode="w", format=tarfile.PAX_FORMAT) as tar:
216
+ for arcname, data in sorted(members):
217
+ info = tarfile.TarInfo(f"{base}/{arcname}")
218
+ info.size = len(data)
219
+ info.mtime = mtime
220
+ info.mode = 0o644
221
+ info.uid = info.gid = 0
222
+ info.uname = info.gname = ""
223
+ tar.addfile(info, io.BytesIO(data))
224
+ (pathlib.Path(sdist_directory) / filename).write_bytes(raw.getvalue())
225
+ return filename
226
+
227
+
228
+ def main(argv=None) -> int:
229
+ argv = sys.argv[1:] if argv is None else argv
230
+ out = pathlib.Path(argv[0] if argv else ROOT / "dist")
231
+ out.mkdir(parents=True, exist_ok=True)
232
+ for build in (build_sdist, build_wheel):
233
+ print(out / build(str(out)))
234
+ return 0
235
+
236
+
237
+ if __name__ == "__main__":
238
+ sys.exit(main())
@@ -0,0 +1,9 @@
1
+ # Lazaret builds with its own standard-library backend (_build/lazaret_build.py):
2
+ # no build requirements, nothing downloaded. Package metadata and console
3
+ # scripts are defined in that module; the version is __version__ in
4
+ # src/lazaret/__init__.py. Lazaret has no runtime dependencies.
5
+
6
+ [build-system]
7
+ requires = []
8
+ build-backend = "lazaret_build"
9
+ backend-path = ["_build"]
@@ -0,0 +1,11 @@
1
+ """Lazaret: static security, supply-chain and quality analysis for Python,
2
+ JavaScript and SQL. Standard library only.
3
+
4
+ lazaret.scanner rule engine, taint analysis, CLI (`lazaret`)
5
+ lazaret.registry npm / PyPI package auditing (`lazaret-registry`)
6
+ lazaret.mcp MCP server (`lazaret-mcp`)
7
+ lazaret.pg PostgreSQL client (provisional API until 1.0)
8
+ lazaret.safexml safe XML parsing (provisional API until 1.0)
9
+ """
10
+
11
+ __version__ = "0.0.1"
@@ -0,0 +1,3 @@
1
+ from lazaret.scanner.core import main
2
+
3
+ main()
@@ -0,0 +1 @@
1
+ """MCP server exposing the Lazaret scanner as tools (stdio transport)."""
@@ -0,0 +1,3 @@
1
+ from lazaret.mcp.server import main
2
+
3
+ main()