picosentry 0.16.0__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.
- picosentry-0.16.0/.editorconfig +21 -0
- picosentry-0.16.0/.pre-commit-hooks.yaml +60 -0
- picosentry-0.16.0/CHANGELOG.md +193 -0
- picosentry-0.16.0/CITATION.cff +35 -0
- picosentry-0.16.0/COMMERCIAL-LICENSE.md +20 -0
- picosentry-0.16.0/CONTRIBUTING.md +168 -0
- picosentry-0.16.0/Dockerfile +79 -0
- picosentry-0.16.0/LICENSE +91 -0
- picosentry-0.16.0/LICENSE-SUMMARY.md +29 -0
- picosentry-0.16.0/MANIFEST.in +25 -0
- picosentry-0.16.0/PKG-INFO +392 -0
- picosentry-0.16.0/README.md +330 -0
- picosentry-0.16.0/SECURITY.md +105 -0
- picosentry-0.16.0/mypy.ini +34 -0
- picosentry-0.16.0/pyproject.toml +158 -0
- picosentry-0.16.0/schemas/picosentry-scan-result.schema.json +101 -0
- picosentry-0.16.0/scripts/bundle-advisories.py +205 -0
- picosentry-0.16.0/scripts/ci.sh +411 -0
- picosentry-0.16.0/scripts/download-advisories.sh +51 -0
- picosentry-0.16.0/scripts/generate_release_evidence.py +194 -0
- picosentry-0.16.0/scripts/generate_sbom.py +130 -0
- picosentry-0.16.0/scripts/load_test.py +257 -0
- picosentry-0.16.0/scripts/management-server.py +136 -0
- picosentry-0.16.0/scripts/schema_diff_check.py +157 -0
- picosentry-0.16.0/scripts/sign-advisories.sh +62 -0
- picosentry-0.16.0/scripts/verify-docker-digest.sh +108 -0
- picosentry-0.16.0/scripts/verify-slsa.sh +141 -0
- picosentry-0.16.0/scripts/verify_release.sh +91 -0
- picosentry-0.16.0/setup.cfg +4 -0
- picosentry-0.16.0/src/picosentry/__init__.py +47 -0
- picosentry-0.16.0/src/picosentry/__main__.py +8 -0
- picosentry-0.16.0/src/picosentry/_network.py +77 -0
- picosentry-0.16.0/src/picosentry/advisory.py +368 -0
- picosentry-0.16.0/src/picosentry/audit.py +329 -0
- picosentry-0.16.0/src/picosentry/auth.py +617 -0
- picosentry-0.16.0/src/picosentry/cache.py +370 -0
- picosentry-0.16.0/src/picosentry/cli.py +1802 -0
- picosentry-0.16.0/src/picosentry/config.py +525 -0
- picosentry-0.16.0/src/picosentry/corpus/advisories/npm-critical-advisories.json +2196 -0
- picosentry-0.16.0/src/picosentry/corpus/generate_npm_top.py +311 -0
- picosentry-0.16.0/src/picosentry/corpus/ioc/colors_js.json +13 -0
- picosentry-0.16.0/src/picosentry/corpus/ioc/crossenv.json +15 -0
- picosentry-0.16.0/src/picosentry/corpus/ioc/event_stream_3.3.6.json +24 -0
- picosentry-0.16.0/src/picosentry/corpus/ioc/left_pad.json +14 -0
- picosentry-0.16.0/src/picosentry/corpus/ioc/nx_typosquat.json +20 -0
- picosentry-0.16.0/src/picosentry/corpus/ioc/shai_hulud.json +21 -0
- picosentry-0.16.0/src/picosentry/corpus/ioc/ua_parser_js.json +13 -0
- picosentry-0.16.0/src/picosentry/corpus/npm_top_packages.json +329 -0
- picosentry-0.16.0/src/picosentry/corpus_governance.py +515 -0
- picosentry-0.16.0/src/picosentry/corpus_share.py +462 -0
- picosentry-0.16.0/src/picosentry/crypto.py +527 -0
- picosentry-0.16.0/src/picosentry/daemon.py +784 -0
- picosentry-0.16.0/src/picosentry/detection_quality.py +495 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-ADV-001.md +46 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-BUND-001.md +46 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-CRED-001.md +46 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-DEPC-001.md +37 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-ENGIN-001.md +55 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-FORK-001.md +40 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-IOC-001.md +65 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-LICENSE-001.md +61 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-LOCK-001.md +45 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-MAINT-001.md +50 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-MANI-001.md +45 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-MANI-002.md +37 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-OBFS-001.md +36 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-OBFS-002.md +35 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-OBFS-003.md +36 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-OBFS-004.md +35 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-PNPM-001.md +52 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-POST-001.md +47 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-PROV-001.md +46 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-SIDELOAD-001.md +59 -0
- picosentry-0.16.0/src/picosentry/docs/rules/L2-TYPO-001.md +57 -0
- picosentry-0.16.0/src/picosentry/docs/rules/README.md +92 -0
- picosentry-0.16.0/src/picosentry/engine.py +412 -0
- picosentry-0.16.0/src/picosentry/enterprise.py +178 -0
- picosentry-0.16.0/src/picosentry/fleet.py +566 -0
- picosentry-0.16.0/src/picosentry/formatters/__init__.py +10 -0
- picosentry-0.16.0/src/picosentry/formatters/cyclonedx.py +208 -0
- picosentry-0.16.0/src/picosentry/formatters/github.py +98 -0
- picosentry-0.16.0/src/picosentry/formatters/json_fmt.py +17 -0
- picosentry-0.16.0/src/picosentry/formatters/ml_context.py +18 -0
- picosentry-0.16.0/src/picosentry/formatters/sarif.py +116 -0
- picosentry-0.16.0/src/picosentry/formatters/table.py +95 -0
- picosentry-0.16.0/src/picosentry/guards.py +288 -0
- picosentry-0.16.0/src/picosentry/ioc_registry.py +214 -0
- picosentry-0.16.0/src/picosentry/logging.py +219 -0
- picosentry-0.16.0/src/picosentry/management.py +414 -0
- picosentry-0.16.0/src/picosentry/metrics.py +222 -0
- picosentry-0.16.0/src/picosentry/models.py +382 -0
- picosentry-0.16.0/src/picosentry/policy.py +814 -0
- picosentry-0.16.0/src/picosentry/policy_lifecycle.py +387 -0
- picosentry-0.16.0/src/picosentry/py.typed +0 -0
- picosentry-0.16.0/src/picosentry/rules/__init__.py +196 -0
- picosentry-0.16.0/src/picosentry/rules/advisory_check.py +150 -0
- picosentry-0.16.0/src/picosentry/rules/bundled_shadow.py +151 -0
- picosentry-0.16.0/src/picosentry/rules/credential_read.py +334 -0
- picosentry-0.16.0/src/picosentry/rules/dep_confusion.py +166 -0
- picosentry-0.16.0/src/picosentry/rules/engine.py +208 -0
- picosentry-0.16.0/src/picosentry/rules/fork_drift.py +295 -0
- picosentry-0.16.0/src/picosentry/rules/ioc_detection.py +199 -0
- picosentry-0.16.0/src/picosentry/rules/license.py +248 -0
- picosentry-0.16.0/src/picosentry/rules/lockfile_drift.py +397 -0
- picosentry-0.16.0/src/picosentry/rules/maintainer_change.py +287 -0
- picosentry-0.16.0/src/picosentry/rules/manifest.py +151 -0
- picosentry-0.16.0/src/picosentry/rules/obfuscation.py +218 -0
- picosentry-0.16.0/src/picosentry/rules/pnpm_config.py +149 -0
- picosentry-0.16.0/src/picosentry/rules/pnpm_lock_parser.py +243 -0
- picosentry-0.16.0/src/picosentry/rules/post_install.py +156 -0
- picosentry-0.16.0/src/picosentry/rules/provenance.py +188 -0
- picosentry-0.16.0/src/picosentry/rules/sideloading.py +134 -0
- picosentry-0.16.0/src/picosentry/rules/typosquat.py +331 -0
- picosentry-0.16.0/src/picosentry/rules/utils.py +100 -0
- picosentry-0.16.0/src/picosentry/tenant.py +433 -0
- picosentry-0.16.0/src/picosentry/workspace.py +371 -0
- picosentry-0.16.0/src/picosentry.egg-info/PKG-INFO +392 -0
- picosentry-0.16.0/src/picosentry.egg-info/SOURCES.txt +168 -0
- picosentry-0.16.0/src/picosentry.egg-info/dependency_links.txt +1 -0
- picosentry-0.16.0/src/picosentry.egg-info/entry_points.txt +2 -0
- picosentry-0.16.0/src/picosentry.egg-info/requires.txt +26 -0
- picosentry-0.16.0/src/picosentry.egg-info/top_level.txt +1 -0
- picosentry-0.16.0/tests/test_action_exit_code.py +282 -0
- picosentry-0.16.0/tests/test_advisory_extended.py +793 -0
- picosentry-0.16.0/tests/test_audit.py +163 -0
- picosentry-0.16.0/tests/test_auth.py +301 -0
- picosentry-0.16.0/tests/test_auth_rbac_tls.py +450 -0
- picosentry-0.16.0/tests/test_baseline.py +325 -0
- picosentry-0.16.0/tests/test_benchmark.py +239 -0
- picosentry-0.16.0/tests/test_cache_governance.py +148 -0
- picosentry-0.16.0/tests/test_cli.py +1164 -0
- picosentry-0.16.0/tests/test_cli_extended.py +675 -0
- picosentry-0.16.0/tests/test_cli_unit.py +1004 -0
- picosentry-0.16.0/tests/test_config.py +376 -0
- picosentry-0.16.0/tests/test_config_integration.py +300 -0
- picosentry-0.16.0/tests/test_corpus_governance.py +184 -0
- picosentry-0.16.0/tests/test_corpus_share_extended.py +966 -0
- picosentry-0.16.0/tests/test_crypto.py +197 -0
- picosentry-0.16.0/tests/test_crypto_integration.py +222 -0
- picosentry-0.16.0/tests/test_cyclonedx_extended.py +245 -0
- picosentry-0.16.0/tests/test_daemon.py +143 -0
- picosentry-0.16.0/tests/test_daemon_extended.py +1079 -0
- picosentry-0.16.0/tests/test_dashboard_rbac.py +143 -0
- picosentry-0.16.0/tests/test_detection_quality.py +118 -0
- picosentry-0.16.0/tests/test_deterministic_output.py +169 -0
- picosentry-0.16.0/tests/test_docs.py +45 -0
- picosentry-0.16.0/tests/test_engine.py +191 -0
- picosentry-0.16.0/tests/test_enterprise.py +140 -0
- picosentry-0.16.0/tests/test_fleet.py +232 -0
- picosentry-0.16.0/tests/test_github.py +225 -0
- picosentry-0.16.0/tests/test_guards.py +517 -0
- picosentry-0.16.0/tests/test_init_and_sarif.py +260 -0
- picosentry-0.16.0/tests/test_ioc_detection_extended.py +270 -0
- picosentry-0.16.0/tests/test_ioc_registry_extended.py +586 -0
- picosentry-0.16.0/tests/test_license.py +258 -0
- picosentry-0.16.0/tests/test_logging_and_audit.py +176 -0
- picosentry-0.16.0/tests/test_management.py +928 -0
- picosentry-0.16.0/tests/test_obfuscation_extended.py +173 -0
- picosentry-0.16.0/tests/test_pnpm_lock_parser.py +296 -0
- picosentry-0.16.0/tests/test_policy_extended.py +1059 -0
- picosentry-0.16.0/tests/test_policy_lifecycle.py +159 -0
- picosentry-0.16.0/tests/test_realistic_fixtures.py +217 -0
- picosentry-0.16.0/tests/test_scanner.py +1486 -0
- picosentry-0.16.0/tests/test_sideloading.py +349 -0
- picosentry-0.16.0/tests/test_tenant.py +231 -0
- picosentry-0.16.0/tests/test_tenant_isolation.py +232 -0
- picosentry-0.16.0/tests/test_timeout_plugin.py +14 -0
- picosentry-0.16.0/tests/test_v0130_fixes.py +305 -0
- picosentry-0.16.0/tests/test_v091_fixes.py +244 -0
- picosentry-0.16.0/tests/test_workspace.py +227 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# EditorConfig for PicoSentry
|
|
2
|
+
# https://editorconfig.org
|
|
3
|
+
|
|
4
|
+
root = true
|
|
5
|
+
|
|
6
|
+
[*]
|
|
7
|
+
indent_style = space
|
|
8
|
+
indent_size = 4
|
|
9
|
+
end_of_line = lf
|
|
10
|
+
charset = utf-8
|
|
11
|
+
trim_trailing_whitespace = true
|
|
12
|
+
insert_final_newline = true
|
|
13
|
+
|
|
14
|
+
[*.{yml,yaml}]
|
|
15
|
+
indent_size = 2
|
|
16
|
+
|
|
17
|
+
[*.md]
|
|
18
|
+
trim_trailing_whitespace = false
|
|
19
|
+
|
|
20
|
+
[Makefile]
|
|
21
|
+
indent_style = tab
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# PicoSentry pre-commit hooks
|
|
2
|
+
# Registered at: https://pre-commit.com/hooks.html
|
|
3
|
+
#
|
|
4
|
+
# Usage in your .pre-commit-config.yaml:
|
|
5
|
+
# repos:
|
|
6
|
+
# - repo: https://github.com/KirkForge/PicoSentry
|
|
7
|
+
# rev: v0.15.0
|
|
8
|
+
# hooks:
|
|
9
|
+
# - id: picosentry-scan
|
|
10
|
+
# - id: picosentry-check
|
|
11
|
+
# - id: picosentry-workspace
|
|
12
|
+
#
|
|
13
|
+
# All hooks are deterministic — same inputs = same output, every time.
|
|
14
|
+
# No network calls at scan time. Safe for offline/air-gapped environments.
|
|
15
|
+
# Requires: Python 3.10+
|
|
16
|
+
|
|
17
|
+
# ── Full supply-chain scan (all 21 rules) ──
|
|
18
|
+
- id: picosentry-scan
|
|
19
|
+
name: "PicoSentry — full scan"
|
|
20
|
+
description: "Deterministic npm/pnpm supply-chain scan (21 rules, fail on MEDIUM+)"
|
|
21
|
+
entry: picosentry scan . --format json --quiet --exit-code --fail-on medium --fail-on-rule-error
|
|
22
|
+
language: python
|
|
23
|
+
language_version: python3.10
|
|
24
|
+
additional_dependencies:
|
|
25
|
+
- pyyaml>=6.0
|
|
26
|
+
minimum_pre_commit_version: 2.9.0
|
|
27
|
+
types: [json, yaml]
|
|
28
|
+
files: ^(package\.json|pnpm-lock\.yaml|yarn\.lock|package-lock\.json|pnpm-workspace\.yaml|\.picosentry\.yml)$
|
|
29
|
+
pass_filenames: false
|
|
30
|
+
always_run: true
|
|
31
|
+
|
|
32
|
+
# ── CI-optimized fast check ──
|
|
33
|
+
- id: picosentry-check
|
|
34
|
+
name: "PicoSentry — CI check"
|
|
35
|
+
description: "Quick health check (fail only on HIGH or CRITICAL findings, fail-closed on rule errors)"
|
|
36
|
+
entry: picosentry check --fail-on high --fail-on-rule-error
|
|
37
|
+
language: python
|
|
38
|
+
language_version: python3.10
|
|
39
|
+
additional_dependencies:
|
|
40
|
+
- pyyaml>=6.0
|
|
41
|
+
minimum_pre_commit_version: 2.9.0
|
|
42
|
+
types: [json, yaml]
|
|
43
|
+
files: ^(package\.json|pnpm-lock\.yaml|yarn\.lock|package-lock\.json)$
|
|
44
|
+
pass_filenames: false
|
|
45
|
+
always_run: true
|
|
46
|
+
|
|
47
|
+
# ── Monorepo workspace scan ──
|
|
48
|
+
- id: picosentry-workspace
|
|
49
|
+
name: "PicoSentry — workspace scan"
|
|
50
|
+
description: "Scan entire monorepo workspace (all npm/pnpm projects, fail-closed)"
|
|
51
|
+
entry: picosentry workspace --format summary --fail-on medium
|
|
52
|
+
language: python
|
|
53
|
+
language_version: python3.10
|
|
54
|
+
additional_dependencies:
|
|
55
|
+
- pyyaml>=6.0
|
|
56
|
+
minimum_pre_commit_version: 2.9.0
|
|
57
|
+
types: [json, yaml]
|
|
58
|
+
files: ^(pnpm-workspace\.yaml|nx\.json|lerna\.json|turbo\.json)$
|
|
59
|
+
pass_filenames: false
|
|
60
|
+
always_run: true
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.15.1] - 2026-05-21
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **action.yml: exit-code enforcement bug** — composite action caught PicoSentry's nonzero exit but never propagated it. `exit-code: true` with findings now correctly fails the step with `::error::` annotation
|
|
7
|
+
- **release.yml: merge-conflict markers** — removed stray `>>>>>>> origin/main` lines that broke YAML parsing
|
|
8
|
+
- **Determinism claim corrected** — default JSON output includes audit timestamps and timing; `--deterministic-output` flag added for byte-identical output across runs. README updated to reflect this honestly
|
|
9
|
+
- **CI security theater** — removed `|| true` from pip-audit and self-scan steps in ci.yml; pip-audit no longer silently passes on vulnerability findings
|
|
10
|
+
- **ScanResult.to_dict() key ordering** — top-level keys now always sorted, matching the "sorted keys" guarantee
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **`--deterministic-output` flag** — omits audit timestamps, `duration_ms`, `rule_timings_ms`, and per-rule `duration_ms` from JSON output for byte-stable reproducible CI artifacts. `--verify-determinism` implies this flag
|
|
14
|
+
- **`.github/workflows/test-action.yml`** — CI workflow testing the GitHub Action exit-code contract, deterministic output byte-identity, and `--verify-determinism`
|
|
15
|
+
- **Release workflow: test installed wheel** — `release.yml` now runs `pytest` against the built wheel before publishing
|
|
16
|
+
- **`tests/test_action_exit_code.py`** — 19 tests proving action enforcement, shell logic replication, and exit-code behavior
|
|
17
|
+
- **`tests/test_deterministic_output.py`** — 11 tests proving byte-identical JSON, audit/timing omission, config loading, and key ordering
|
|
18
|
+
- **`tests/test_realistic_fixtures.py`** — 33 tests: realistic npm project (lockfile + node_modules), all-fixture smoke tests
|
|
19
|
+
- **`tests/fixtures/realistic_npm/`** — realistic fixture with `package.json`, `package-lock.json`, and `node_modules/` (express, axios)
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- **STATE.md** — rewritten from inflated self-scoring to honest maturity assessment
|
|
23
|
+
- **CONTRIBUTING.md** — documented `pip install -e ".[dev]"` requirement; updated determinism verification to show `--deterministic-output`
|
|
24
|
+
- **`ScanResult.to_dict()`** and **`RuleExecution.to_dict()`** — accept `deterministic_output` parameter; when true, timing fields are omitted
|
|
25
|
+
- **`format_json()`** — passes `deterministic_output` through to `ScanResult.to_json()`
|
|
26
|
+
|
|
27
|
+
## [0.14.0] - Unreleased (enterprise-ready)
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- **Workspace/monorepo scanning** — `picosentry workspace` discovers and scans all npm/pnpm projects in a monorepo (Nx, Turborepo, Lerna, pnpm workspaces)
|
|
31
|
+
- **Custom IoC registry** — register organization-specific indicators of compromise (`picosentry ioc register`)
|
|
32
|
+
- **Enterprise Docker image** — multi-stage build with non-root user, health check, venv isolation
|
|
33
|
+
- **Full CycloneDX SBOM** — walks node_modules for complete component inventory with purl and hashes
|
|
34
|
+
- **Structured JSON logging** — `--log-format json` for SIEM integration (Splunk, ELK, Datadog)
|
|
35
|
+
- **CI type checking** — mypy --strict enforced on every push
|
|
36
|
+
- **CI coverage reporting** — pytest-cov with XML report upload
|
|
37
|
+
- **CI linting** — ruff check + format enforcement in CI
|
|
38
|
+
- **Python 3.13** in CI test matrix
|
|
39
|
+
- **Release workflow** — automated PyPI publish + GitHub Release on tag push
|
|
40
|
+
- **Dependabot** configuration for pip and GitHub Actions
|
|
41
|
+
- **CODEOWNERS** file for code review routing
|
|
42
|
+
- **.editorconfig** for consistent formatting across contributors
|
|
43
|
+
- **Pre-commit hooks** — 3 hooks: full scan, fast CI check, workspace scan
|
|
44
|
+
- **Performance benchmark suite** — cold start, rule timing, format throughput targets
|
|
45
|
+
- **Self-SBOM generation** — scripts/generate_sbom.py for dogfooding
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
- CycloneDX timestamp is now deterministic (derived from scan_id, not wall-clock time)
|
|
49
|
+
- CycloneDX vulnerability IDs use descriptive `PICOSENTRY-` prefix
|
|
50
|
+
- Credential scanner excludes `dist/`, `build/`, `out/` directories and `.min.*` files
|
|
51
|
+
- CI pre-push script rewritten for Python (was Node.js-centric)
|
|
52
|
+
- CI scan-self SARIF upload gated to public repos only
|
|
53
|
+
- Rules documentation: corrected "15" → "19" detector rules
|
|
54
|
+
- Dockerfile: multi-stage build with non-root user and health check
|
|
55
|
+
- State.md: enterprise readiness score updated to 94/100
|
|
56
|
+
|
|
57
|
+
### Fixed
|
|
58
|
+
- LICENSE copyright holder: fixed to "KirkForge" (was "55N10E")
|
|
59
|
+
- Rules documentation rule count now matches actual RULE_INFO (19)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
- **CycloneDX 1.5 SBOM output** (`--format cyclonedx`) — enterprise standard SBOM compatible with OWASP Dependency-Track
|
|
64
|
+
- **`picosentry check` command** — CI-optimized health gate with exit-code only
|
|
65
|
+
- **Symlink traversal guards** — `rglob` calls now skip symlinks to prevent scanning outside project boundaries
|
|
66
|
+
- **`__all__` exports** on all 15 rule modules for explicit public API
|
|
67
|
+
- **`py.typed` marker** (PEP 561) for type-checker compatibility
|
|
68
|
+
- **`mypy.ini`** with strict type checking configuration
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
- Lockfile v1 parser: strips version suffix from keys for correct package name matching
|
|
72
|
+
- Provenance path detection: uses `Path.parts` instead of fragile `str()` comparison
|
|
73
|
+
- Config merge: uses `getattr` with safe defaults instead of direct attribute access
|
|
74
|
+
- Engine version detection: prefers `importlib.metadata` over regex source parsing
|
|
75
|
+
- CLI eliminates double config load — config loaded once and passed through
|
|
76
|
+
- Typosquat optimized with length and first-char pruning (~90% fewer computations)
|
|
77
|
+
|
|
78
|
+
### Fixed
|
|
79
|
+
- `merge_cli()` no longer crashes on minimal test Namespaces
|
|
80
|
+
- Symlink traversal prevented in obfuscation, credential_read, and engine scans
|
|
81
|
+
- All references migrated from `55N10E/SecDev_kimi` to `KirkForge/PicoSentry`
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
All notable changes to PicoSentry are documented here.
|
|
85
|
+
|
|
86
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
87
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
88
|
+
|
|
89
|
+
## [0.12.0] - 2026-05-16
|
|
90
|
+
|
|
91
|
+
### Added
|
|
92
|
+
- **All 19 rule IDs now registered in engine** (was 15): sub-rules L2-OBFS-002/003/004 and L2-MANI-002 are individually registered
|
|
93
|
+
- **Sub-rule filtering**: `--rules L2-OBFS-002` now returns only OBFS-002 findings (engine deduplicates shared functions, filters findings by requested rule_ids)
|
|
94
|
+
- 3 new tests for sub-rule filtering behavior
|
|
95
|
+
|
|
96
|
+
### Changed
|
|
97
|
+
- `picosentry rules` now correctly shows 19 rules (was 15)
|
|
98
|
+
- `RULE_COUNT` = 19 (was 15)
|
|
99
|
+
- Engine scan loop deduplicates function calls when multiple rule_ids share the same detector
|
|
100
|
+
- Documented immutability contract for `apply_severity_overrides` and `apply_overrides`
|
|
101
|
+
|
|
102
|
+
### Fixed
|
|
103
|
+
- Removed stray `finding missing file` line in `DeterministicGuard.check()` stats verification block
|
|
104
|
+
- Rule count inconsistency resolved: RULE_INFO (19) matches engine registration (19)
|
|
105
|
+
|
|
106
|
+
## [0.11.0] - 2026-05-16
|
|
107
|
+
|
|
108
|
+
### Added
|
|
109
|
+
- **Deterministic guard stack** (`guards.py`): `DeterministicGuard`, `deterministic_hash()`, `fingerprint_scan()`, `verify_determinism()`, `diff_scans()`, `DeterminismViolation`
|
|
110
|
+
- **`--verify-determinism` CLI flag**: Runs scan twice, compares SHA-256 of deterministic fields. Exit 0 if match, 4 if violation
|
|
111
|
+
- **SECURITY.md**: Vulnerability disclosure policy (90-day coordinated disclosure)
|
|
112
|
+
- **CI determinism gate**: GitHub Actions step that verifies determinism on every push
|
|
113
|
+
- Stats consistency guard: `DeterministicGuard.check()` now verifies `findings_by_severity` and `findings_by_rule` match actual findings
|
|
114
|
+
- **`ScanResult.apply_overrides()`**: Clean API for replacing findings + recomputing stats
|
|
115
|
+
- **`user_corpus_dir()`**: XDG-aware corpus directory (`~/.local/share/picosentry/corpus/` or `$PICOCORPUS_DIR`)
|
|
116
|
+
- **Response size limit** on `update` command (10MB per page) — prevents OOM from oversized responses
|
|
117
|
+
- **Response format validation** on `update` command — rejects non-JSON and missing `objects` key
|
|
118
|
+
- Typosquat corpus fallback logging: warns on corrupt file, info on missing file
|
|
119
|
+
- Python 3.13 classifier in pyproject.toml
|
|
120
|
+
|
|
121
|
+
### Changed
|
|
122
|
+
- `picosentry update` now writes to user corpus dir instead of package install dir (fixes PEP 668 compatibility)
|
|
123
|
+
- `ScanEngine` resolves corpus: explicit `--corpus` > user dir (`~/.local/share/picosentry/corpus/`) > built-in
|
|
124
|
+
- `picosentry rules` now shows all 19 rule IDs from `RULE_INFO` (not just 15 engine-registered functions)
|
|
125
|
+
- `picosentry version` shows "19 (15 detector functions)"
|
|
126
|
+
- `files_scanned` now counts only relevant file types and skips `.git`, `__pycache__`, `.cache`
|
|
127
|
+
- `packages_scanned` now counts scoped packages (`@scope/pkg`) individually
|
|
128
|
+
- Removed `Typing :: Typed` classifier (no `.pyi` stub files yet)
|
|
129
|
+
|
|
130
|
+
### Fixed
|
|
131
|
+
- `update` command no longer writes inside installed package directory
|
|
132
|
+
- `update` command validates response format before processing
|
|
133
|
+
- Rule count inconsistency between `RULE_INFO` (19) and engine (15) resolved
|
|
134
|
+
|
|
135
|
+
## [0.10.0] - 2026-05-16
|
|
136
|
+
|
|
137
|
+
### Added
|
|
138
|
+
- **`--verify-determinism` flag**: Runs scan twice, compares SHA-256 of deterministic fields
|
|
139
|
+
- **SECURITY.md**: Vulnerability disclosure policy
|
|
140
|
+
- **CI determinism gate**: GitHub Actions step for determinism verification
|
|
141
|
+
- 4 new tests for determinism verification
|
|
142
|
+
|
|
143
|
+
### Fixed
|
|
144
|
+
- `format_json` included `duration_ms`/`rule_timings_ms` in determinism hash — now excluded
|
|
145
|
+
- Missing `tempfile` import in violation path
|
|
146
|
+
|
|
147
|
+
## [0.9.1] - 2026-05-15
|
|
148
|
+
|
|
149
|
+
### Added
|
|
150
|
+
- `--version` flag
|
|
151
|
+
- Baseline update optimization
|
|
152
|
+
- CONTRIBUTING.md
|
|
153
|
+
|
|
154
|
+
### Changed
|
|
155
|
+
- 19 rules, 291 tests, 5 output formats
|
|
156
|
+
|
|
157
|
+
## [0.1.0] - 2026-05-14
|
|
158
|
+
|
|
159
|
+
### Added
|
|
160
|
+
- Initial release: 10 detector rules, 27 tests
|
|
161
|
+
- Scanner module extracted from Iron Dome
|
|
162
|
+
- 4 output formats: json, sarif, table, ml-context
|
|
163
|
+
- Determinism enforced: `sha256(findings_a) == sha256(findings_b)` on identical inputs
|
|
164
|
+
|
|
165
|
+
[0.12.0]: https://github.com/KirkForge/PicoSentry/compare/v0.11.0...v0.12.0
|
|
166
|
+
[0.11.0]: https://github.com/KirkForge/PicoSentry/compare/v0.10.0...v0.11.0
|
|
167
|
+
[0.10.0]: https://github.com/KirkForge/PicoSentry/compare/v0.9.1...v0.10.0
|
|
168
|
+
[0.9.1]: https://github.com/KirkForge/PicoSentry/releases/tag/v0.9.1
|
|
169
|
+
[0.1.0]: https://github.com/KirkForge/PicoSentry/releases/tag/v0.1.0
|
|
170
|
+
## [0.16.1] - 2026-05-29
|
|
171
|
+
|
|
172
|
+
### Security
|
|
173
|
+
|
|
174
|
+
- **§1.1 Typosquat FP rate** — short package names (≤4 chars) no longer emit HIGH-severity typosquat findings. Distance-2 short names → LOW, distance-1 short → MEDIUM. Normal-length names use length-ratio scoring for appropriate severity. Prevents CI breakage from common packages like `swr`, `ky`, `clsx`
|
|
175
|
+
- **§1.4 Arbitrary scan paths** — `/scan` endpoint now rejects absolute paths and `..`, resolves targets against `PICOSENTRY_SCAN_ROOT` (or CWD), and verifies the resolved path stays within the workspace root. Prefix-confusion attack (e.g. `/app-sekrit` passing `/app` check) is also blocked
|
|
176
|
+
- **§1.5 Missing authz on /metrics and /dashboard** — `check_authorization()` is now enforced on `/metrics`, `/metrics/json`, and `/dashboard`. Previously only `_check_auth()` (authentication) was called, allowing any authenticated low-privilege token to read cross-tenant data
|
|
177
|
+
- **§2.1 Default bind address** — `DEFAULT_HOST` changed from `0.0.0.0` to `127.0.0.1`. Non-loopback bind with `auth=off` now prints a CRITICAL warning. Prevents accidental exposure of admin access on network interfaces
|
|
178
|
+
- **§2.2 Token scope dead code** — scope lookup in `check_token_auth` now checks `identity in config.scopes` (preserving empty scope lists = no permissions) before falling through to `token_default` → `default_scopes`. Previously `or` chain treated empty lists as falsy, silently escalating permissions
|
|
179
|
+
- **§2.5 Fail-open verify** — `verify_content()` now returns `False` for unsigned bundles (fail-closed). Callers that need to accept unsigned bundles should pass `allow_unsigned=True`. `corpus_require_signature` defaults to `True` across both `Config` and `Policy` classes
|
|
180
|
+
- **§3.4 Silent symlink fallback** — `engine.py` now logs a warning when symlinked corpus files are skipped and the version hash changes, instead of silently degrading to the builtin top-100 list
|
|
181
|
+
|
|
182
|
+
### Changed
|
|
183
|
+
|
|
184
|
+
- Typosquat severity now reflects match quality: short-name matches at LOW/MEDIUM, distance-1 matches with ≥0.8 length ratio at HIGH, distance-2 with low ratio at LOW
|
|
185
|
+
- `Policy` dataclass `corpus_require_signature` default changed from `False` to `True` (fail-closed)
|
|
186
|
+
|
|
187
|
+
### Documentation
|
|
188
|
+
|
|
189
|
+
- Updated `docs/security/threat-model.md` — added attack surfaces for scan path traversal, default bind exposure, authz enforcement, corpus signature verification, typosquat FP noise
|
|
190
|
+
- Updated `docs/security/access-control-policy.md` — added token-mode scope resolution, authz enforcement on all endpoints, scan path restrictions, fail-closed defaults table
|
|
191
|
+
- Updated `docs/runbooks/daemon-auth-failures.md` — added scope resolution troubleshooting, authz enforcement note, bind address change, scan path restrictions
|
|
192
|
+
- Updated `docs/ENTERPRISE_DEPLOYMENT.md` — added v0.16.1 security defaults section, dashboard endpoints to scope-enforced table, scan path restriction note, token scope resolution warning
|
|
193
|
+
- Updated `SECURITY.md` — added fail-closed defaults table, typosquat detection section, supported versions bump
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use PicoSentry in your research or security pipeline, please cite it as below."
|
|
3
|
+
title: "PicoSentry: Deterministic Supply-Chain Scanner for npm/pnpm"
|
|
4
|
+
authors:
|
|
5
|
+
- given-names: Henrik
|
|
6
|
+
family-names: Kirk
|
|
7
|
+
name: "Henrik Kirk"
|
|
8
|
+
- given-names: GLM
|
|
9
|
+
family-names: "5.1"
|
|
10
|
+
name: "GLM-5.1"
|
|
11
|
+
- given-names: PicoClaw
|
|
12
|
+
family-names: "🦞"
|
|
13
|
+
name: "PicoClaw"
|
|
14
|
+
url: "https://github.com/KirkForge/PicoSentry"
|
|
15
|
+
repository-code: "https://github.com/KirkForge/PicoSentry"
|
|
16
|
+
version: "0.16.0"
|
|
17
|
+
date-released: "2026-05-28"
|
|
18
|
+
license: PolyForm-Noncommercial-1.0.0
|
|
19
|
+
abstract: >-
|
|
20
|
+
PicoSentry is a deterministic, offline supply-chain scanner for npm
|
|
21
|
+
and pnpm ecosystems, designed to be safe for ML pipelines and
|
|
22
|
+
enterprise CI/CD. Same inputs + same corpus version = same output,
|
|
23
|
+
every time. 21 detector rules, 6 output formats (JSON, SARIF, table,
|
|
24
|
+
ml-context, GitHub, CycloneDX SBOM), and a 4-layer determinism guard
|
|
25
|
+
stack.
|
|
26
|
+
keywords:
|
|
27
|
+
- supply-chain-security
|
|
28
|
+
- npm
|
|
29
|
+
- pnpm
|
|
30
|
+
- deterministic-scanner
|
|
31
|
+
- sbom
|
|
32
|
+
- cyclonedx
|
|
33
|
+
- sarif
|
|
34
|
+
- devsecops
|
|
35
|
+
type: software
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Commercial License
|
|
2
|
+
|
|
3
|
+
Commercial use that competes with KirkForge's paid offerings requires a separate commercial license from KirkForge.
|
|
4
|
+
|
|
5
|
+
Under the Business Source License 1.1 (BUSL-1.1), you may use the software for non-production purposes and internal production use without a commercial license. However, offering the software to third parties on a hosted or embedded basis as a competitive offering requires a commercial license.
|
|
6
|
+
|
|
7
|
+
Examples of use requiring a commercial license:
|
|
8
|
+
|
|
9
|
+
- offering the software as a hosted/managed service that competes with KirkForge's products
|
|
10
|
+
- embedding the software in a paid product that competes with KirkForge's products
|
|
11
|
+
- packaging the software so it must be accessed or downloaded for your competitive offering to operate
|
|
12
|
+
|
|
13
|
+
Examples of use **not** requiring a commercial license:
|
|
14
|
+
|
|
15
|
+
- internal use within your organization
|
|
16
|
+
- non-production use (development, testing, evaluation)
|
|
17
|
+
- academic or educational use
|
|
18
|
+
- personal/hobby projects
|
|
19
|
+
|
|
20
|
+
For commercial licensing, contact: kirk@kirkforge.dev
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Contributing to PicoSentry
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in PicoSentry! This guide covers how to contribute effectively.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/KirkForge/PicoSentry.git
|
|
9
|
+
cd PicoSentry
|
|
10
|
+
python3 -m pip install -e ".[dev]" # Required: CLI tests need the package importable
|
|
11
|
+
python3 -m pytest
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
> **Important:** You must install the package in editable mode before running tests.
|
|
15
|
+
> CLI integration tests use `subprocess` to invoke `picosentry`, which requires the
|
|
16
|
+
> package to be importable. Raw `pytest` from a clean checkout will fail at CLI tests
|
|
17
|
+
> without `pip install -e ".[dev]"`.
|
|
18
|
+
|
|
19
|
+
## Development
|
|
20
|
+
|
|
21
|
+
### Running Tests
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python3 -m pytest # All tests
|
|
25
|
+
python3 -m pytest tests/test_cli.py # CLI tests only
|
|
26
|
+
python3 -m pytest -x # Stop on first failure
|
|
27
|
+
python3 -m pytest -m "not slow" # Skip benchmark tests
|
|
28
|
+
python3 -m pytest tests/test_benchmark.py # Performance benchmarks
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Linting & Type Checking
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
ruff check src/ tests/ # Lint
|
|
35
|
+
ruff format --check src/ tests/ # Format check
|
|
36
|
+
mypy src/picosentry --strict # Type check
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Pre-push CI
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bash scripts/ci.sh # Run all checks: mypy, ruff, pytest, determinism
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Adding a New Rule
|
|
46
|
+
|
|
47
|
+
1. Create detector in `src/picosentry/rules/` (e.g., `my_rule.py`)
|
|
48
|
+
2. Register in `src/picosentry/rules/__init__.py` → `RULE_INFO`
|
|
49
|
+
3. Register in `src/picosentry/engine.py` → `create_default_engine()`
|
|
50
|
+
4. Add fixture in `tests/fixtures/`
|
|
51
|
+
5. Write tests in `tests/test_my_rule.py`
|
|
52
|
+
6. Create rule doc in `src/picosentry/docs/rules/`
|
|
53
|
+
7. Update `SCAAT.md` with the new attack vector coverage
|
|
54
|
+
8. Run: `python3 -m pytest`
|
|
55
|
+
|
|
56
|
+
### Rule Requirements
|
|
57
|
+
|
|
58
|
+
Every rule **must** be deterministic:
|
|
59
|
+
- Same target + same corpus = same findings, every time
|
|
60
|
+
- No network calls at scan time
|
|
61
|
+
- No `uuid4()`, `random()`, or timestamps in findings
|
|
62
|
+
- Output sorted by `(rule_id, package, file, line)`
|
|
63
|
+
|
|
64
|
+
### Adding a Custom IoC
|
|
65
|
+
|
|
66
|
+
Register known-bad packages in the built-in corpus:
|
|
67
|
+
|
|
68
|
+
1. Create `src/picosentry/corpus/ioc/<package>.json` following the existing format
|
|
69
|
+
2. Reference the attack vector, indicators, and expected detection rules
|
|
70
|
+
3. Add a test fixture that exercises the detection
|
|
71
|
+
|
|
72
|
+
Or use the CLI for custom IoCs (these stay local, not in the repo):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
picosentry corpus export ./my-iocs.json
|
|
76
|
+
# Edit my-iocs.json, add entries
|
|
77
|
+
picosentry corpus import ./my-iocs.json --force
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Code Style
|
|
81
|
+
|
|
82
|
+
- Type hints on all public functions
|
|
83
|
+
- Docstrings on all public functions
|
|
84
|
+
- `dataclass(frozen=True)` for immutable data (Findings, etc.)
|
|
85
|
+
- Sorted keys in all JSON output
|
|
86
|
+
- 120-char line limit (ruff enforced)
|
|
87
|
+
|
|
88
|
+
## Project Structure
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
src/picosentry/
|
|
92
|
+
├── cli.py # CLI entry point — all subcommands
|
|
93
|
+
├── engine.py # ScanEngine — orchestrates rules
|
|
94
|
+
├── models.py # Frozen dataclasses (Finding, ScanResult)
|
|
95
|
+
├── config.py # .picosentry.yml loader
|
|
96
|
+
├── guards.py # Determinism enforcement
|
|
97
|
+
├── logging.py # Structured JSON logging
|
|
98
|
+
├── workspace.py # Monorepo scanning
|
|
99
|
+
├── ioc_registry.py # Custom IoC management
|
|
100
|
+
├── corpus_share.py # Corpus pack import/export
|
|
101
|
+
├── rules/ # 19 detector rules (pure functions)
|
|
102
|
+
├── formatters/ # 6 output formatters
|
|
103
|
+
└── corpus/ # Built-in IoC database
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Determinism Verification
|
|
107
|
+
|
|
108
|
+
After any scan logic change, verify determinism (including CycloneDX output):
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Run two scans and compare (byte-identical output)
|
|
112
|
+
picosentry scan ./my-project --format json --deterministic-output -o scan_a.json
|
|
113
|
+
picosentry scan ./my-project --format json --deterministic-output -o scan_b.json
|
|
114
|
+
diff scan_a.json scan_b.json # should produce no output
|
|
115
|
+
|
|
116
|
+
# Or use built-in verification (runs twice, compares SHA-256)
|
|
117
|
+
picosentry scan ./my-project --verify-determinism
|
|
118
|
+
# Should output: "✓ DETERMINISM VERIFIED — scans are deterministic"
|
|
119
|
+
|
|
120
|
+
# Note: without --deterministic-output, JSON includes timestamps and timing.
|
|
121
|
+
# The --verify-determinism flag automatically enables deterministic output mode.
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Commit Messages
|
|
125
|
+
|
|
126
|
+
Format: `type: description`
|
|
127
|
+
|
|
128
|
+
Types: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
131
|
+
- `feat: L2-SIDELOAD-001 protocol sideloading detector`
|
|
132
|
+
- `feat: workspace scanning for monorepos`
|
|
133
|
+
- `fix: engine_version now reads from __version__ dynamically`
|
|
134
|
+
- `docs: add SCAAT attestation and corpus marketplace docs`
|
|
135
|
+
|
|
136
|
+
## Reporting Issues
|
|
137
|
+
|
|
138
|
+
- Include: PicoSentry version, Python version, OS, target project
|
|
139
|
+
- Include: `picosentry version` output
|
|
140
|
+
- Include: `--verbose` output if possible
|
|
141
|
+
|
|
142
|
+
## Security
|
|
143
|
+
|
|
144
|
+
See [SECURITY.md](SECURITY.md) for vulnerability reporting policy.
|
|
145
|
+
|
|
146
|
+
## AI-Assisted Development
|
|
147
|
+
|
|
148
|
+
PicoSentry is developed with AI assistance. All AI-generated contributions are
|
|
149
|
+
reviewed, tested, and approved by a human maintainer before merge.
|
|
150
|
+
|
|
151
|
+
### Co-Authorship
|
|
152
|
+
|
|
153
|
+
When AI tools produce substantive contributions (features, bug fixes, documentation),
|
|
154
|
+
they receive co-author credit:
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
Co-authored-by: GLM-5.1 <glm@z.ai>
|
|
158
|
+
Co-authored-by: PicoClaw <picoclaw@kirkforge.dev>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- **GLM-5.1** — Code generation, refactoring, test writing
|
|
162
|
+
- **PicoClaw** — Review, analysis, documentation, security auditing
|
|
163
|
+
|
|
164
|
+
AI co-authors are credited because they did real work. This is transparent and honest.
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
By contributing, you agree that your contributions will be licensed under the same public license as the project, currently PolyForm Noncommercial License 1.0.0, and may also be used by KirkForge under separate commercial licensing terms.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# PicoSentry — Enterprise Docker Image
|
|
2
|
+
# Multi-stage build with pinned digests for secure, reproducible CI/CD pipeline scanning.
|
|
3
|
+
#
|
|
4
|
+
# Build:
|
|
5
|
+
# docker build -t picosentry:latest .
|
|
6
|
+
# docker build --build-arg VERSION=0.15.0 -t picosentry:0.15.0 .
|
|
7
|
+
#
|
|
8
|
+
# Run:
|
|
9
|
+
# docker run --rm -v $(pwd):/scan picosentry scan /scan
|
|
10
|
+
# docker run --rm -v $(pwd):/scan picosentry workspace /scan --format json
|
|
11
|
+
# docker run --rm -v $(pwd):/scan picosentry check /scan --fail-on high --fail-on-rule-error
|
|
12
|
+
#
|
|
13
|
+
# Security: runs as non-root, no network at scan time, read-only scan dir.
|
|
14
|
+
# Base images pinned by digest for supply-chain integrity.
|
|
15
|
+
|
|
16
|
+
# ── Stage 1: Builder ──────────────────────────────────
|
|
17
|
+
# python:3.12-slim (bookworm) — digest varies; update periodically.
|
|
18
|
+
# Verify: docker pull python:3.12-slim@sha256:...
|
|
19
|
+
FROM python:3.12-slim@sha256:9d3abd9fc11d06998ccdbdd93b4dd49b5ad7d67fcbbc11c016eb0eb2c2194891 AS builder
|
|
20
|
+
|
|
21
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
22
|
+
git \
|
|
23
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
24
|
+
|
|
25
|
+
WORKDIR /build
|
|
26
|
+
|
|
27
|
+
# Copy only what's needed for install
|
|
28
|
+
COPY pyproject.toml README.md LICENSE ./
|
|
29
|
+
COPY src/ ./src/
|
|
30
|
+
|
|
31
|
+
# Install into a clean venv
|
|
32
|
+
RUN python3 -m venv /opt/venv && \
|
|
33
|
+
/opt/venv/bin/pip install --no-cache-dir -e . && \
|
|
34
|
+
# Install pyyaml for pnpm workspace support
|
|
35
|
+
/opt/venv/bin/pip install --no-cache-dir pyyaml
|
|
36
|
+
|
|
37
|
+
# Verify installation
|
|
38
|
+
RUN /opt/venv/bin/picosentry --version
|
|
39
|
+
|
|
40
|
+
# ── Stage 2: Runner ───────────────────────────────────
|
|
41
|
+
FROM python:3.12-slim@sha256:9d3abd9fc11d06998ccdbdd93b4dd49b5ad7d67fcbbc11c016eb0eb2c2194891 AS runner
|
|
42
|
+
|
|
43
|
+
LABEL org.opencontainers.image.title="PicoSentry"
|
|
44
|
+
LABEL org.opencontainers.image.description="Deterministic supply-chain scanner for npm/pnpm — enterprise CI/CD"
|
|
45
|
+
LABEL org.opencontainers.image.url="https://github.com/KirkForge/PicoSentry"
|
|
46
|
+
LABEL org.opencontainers.image.vendor="KirkForge"
|
|
47
|
+
LABEL org.opencontainers.image.licenses="MIT"
|
|
48
|
+
LABEL org.opencontainers.image.authors="KirkForge"
|
|
49
|
+
LABEL org.opencontainers.image.documentation="https://github.com/KirkForge/PicoSentry"
|
|
50
|
+
LABEL org.opencontainers.image.source="https://github.com/KirkForge/PicoSentry"
|
|
51
|
+
LABEL org.opencontainers.image.version="0.15.0"
|
|
52
|
+
|
|
53
|
+
# Create non-root user
|
|
54
|
+
RUN groupadd -r picosentry && useradd -r -g picosentry -d /home/picosentry -s /bin/bash picosentry && \
|
|
55
|
+
mkdir -p /home/picosentry/.local/share/picosentry/corpus && \
|
|
56
|
+
mkdir -p /scan && \
|
|
57
|
+
chown -R picosentry:picosentry /home/picosentry /scan
|
|
58
|
+
|
|
59
|
+
# Copy venv from builder
|
|
60
|
+
COPY --from=builder /opt/venv /opt/venv
|
|
61
|
+
|
|
62
|
+
# Set PATH to use venv binaries
|
|
63
|
+
ENV PATH="/opt/venv/bin:$PATH"
|
|
64
|
+
ENV PYTHONUNBUFFERED=1
|
|
65
|
+
ENV PYTHONDONTWRITEBYTECODE=1
|
|
66
|
+
|
|
67
|
+
# Drop to non-root user
|
|
68
|
+
USER picosentry
|
|
69
|
+
WORKDIR /home/picosentry
|
|
70
|
+
|
|
71
|
+
# Default scan directory
|
|
72
|
+
VOLUME ["/scan"]
|
|
73
|
+
|
|
74
|
+
# Health check: verify scanner works
|
|
75
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
76
|
+
CMD picosentry --version || exit 1
|
|
77
|
+
|
|
78
|
+
ENTRYPOINT ["picosentry"]
|
|
79
|
+
CMD ["--help"]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
License text copyright (c) 2020 MariaDB Corporation Ab, All Rights Reserved.
|
|
2
|
+
"Business Source License" is a trademark of MariaDB Corporation Ab.
|
|
3
|
+
|
|
4
|
+
Parameters
|
|
5
|
+
|
|
6
|
+
Licensor: KirkForge
|
|
7
|
+
Licensed Work: PicoSentry Version 0.16.0 or later. The Licensed Work is (c) 2025
|
|
8
|
+
KirkForge.
|
|
9
|
+
Additional Use Grant: You may make production use of the Licensed Work provided
|
|
10
|
+
You do not offer the Licensed Work to third parties on a
|
|
11
|
+
hosted or embedded basis as a competitive offering. For
|
|
12
|
+
purposes of this license:
|
|
13
|
+
|
|
14
|
+
A "competitive offering" is a Product that is offered to
|
|
15
|
+
third parties on a paid basis, including through paid
|
|
16
|
+
support arrangements, that significantly overlaps with the
|
|
17
|
+
capabilities of KirkForge's paid version(s) of the Licensed
|
|
18
|
+
Work. If Your Product is not a competitive offering when You
|
|
19
|
+
first make it generally available, it will not become a
|
|
20
|
+
competitive offering later due to KirkForge releasing a new
|
|
21
|
+
version of the Licensed Work with additional capabilities.
|
|
22
|
+
In addition, Products that are not provided on a paid basis
|
|
23
|
+
are not competitive.
|
|
24
|
+
|
|
25
|
+
"Product" means software that is offered to end users to
|
|
26
|
+
manage in their own environments or offered as a service on
|
|
27
|
+
a hosted basis.
|
|
28
|
+
|
|
29
|
+
"Embedded" means including the source code or executable code
|
|
30
|
+
from the Licensed Work in a competitive offering. "Embedded"
|
|
31
|
+
also means packaging the competitive offering in such a way
|
|
32
|
+
that the Licensed Work must be accessed or downloaded for
|
|
33
|
+
the competitive offering to operate.
|
|
34
|
+
|
|
35
|
+
Hosting or using the Licensed Work(s) for internal purposes
|
|
36
|
+
within an organization is not considered a competitive
|
|
37
|
+
offering. KirkForge considers your organization to include
|
|
38
|
+
all of your affiliates under common control.
|
|
39
|
+
|
|
40
|
+
For commercial licensing arrangements, contact
|
|
41
|
+
kirk@kirkforge.dev or see COMMERCIAL-LICENSE.md.
|
|
42
|
+
Change Date: Three years from the date the Licensed Work is published.
|
|
43
|
+
Change License: Apache-2.0
|
|
44
|
+
|
|
45
|
+
For information about alternative licensing arrangements for the Licensed Work,
|
|
46
|
+
please contact kirk@kirkforge.dev.
|
|
47
|
+
|
|
48
|
+
Notice
|
|
49
|
+
|
|
50
|
+
Business Source License 1.1
|
|
51
|
+
|
|
52
|
+
Terms
|
|
53
|
+
|
|
54
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
55
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
56
|
+
Licensor may make an Additional Use Grant, above, permitting limited production use.
|
|
57
|
+
|
|
58
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
59
|
+
available distribution of a specific version of the Licensed Work under this
|
|
60
|
+
License, whichever comes first, the Licensor hereby grants you rights under
|
|
61
|
+
the terms of the Change License, and the rights granted in the paragraph
|
|
62
|
+
above terminate.
|
|
63
|
+
|
|
64
|
+
If your use of the Licensed Work does not comply with the requirements
|
|
65
|
+
currently in effect as described in this License, you must purchase a
|
|
66
|
+
commercial license from the Licensor, its affiliated entities, or authorized
|
|
67
|
+
resellers, or you must refrain from using the Licensed Work.
|
|
68
|
+
|
|
69
|
+
All copies of the original and modified Licensed Work, and derivative works
|
|
70
|
+
of the Licensed Work, are subject to this License. This License applies
|
|
71
|
+
separately for each version of the Licensed Work and the Change Date may vary
|
|
72
|
+
for each version of the Licensed Work released by Licensor.
|
|
73
|
+
|
|
74
|
+
You must conspicuously display this License on each original or modified copy
|
|
75
|
+
of the Licensed Work. If you receive the Licensed Work in original or
|
|
76
|
+
modified form from a third party, the terms and conditions set forth in this
|
|
77
|
+
License apply to your use of that work.
|
|
78
|
+
|
|
79
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
80
|
+
terminate your rights under this License for the current and all other
|
|
81
|
+
versions of the Licensed Work.
|
|
82
|
+
|
|
83
|
+
This License does not grant you any right in any trademark or logo of
|
|
84
|
+
Licensor or its affiliates (provided that you may use a trademark or logo of
|
|
85
|
+
Licensor as expressly required by this License).
|
|
86
|
+
|
|
87
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
|
88
|
+
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
|
89
|
+
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
|
90
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
|
91
|
+
TITLE.
|