The SBOM was generated and installed onto every server, and nothing read it. "Are we exposed to this advisory" was still a person going and looking, which meant it happened when somebody remembered to. scripts/scan_sbom.py queries OSV.dev by purl - no account, no API key, no agent, no vulnerability database to maintain on a box that cannot reach the internet. The SBOM is already the input OSV wants, so this is a lookup against what shipped rather than a second scan of the tree that could disagree with it. Three decisions worth recording: Shipped and build-only findings are both reported, but only shipped ones can fail a build. Two thirds of the components here are build tooling the SBOM already marks scope 'excluded', and the highest-severity match in the whole tree today is a CRITICAL against the test runner. A gate that fails a release over that trains everybody to bypass it, and then it catches nothing. Severity is taken from the advisory, never derived. Where there is only a CVSS vector the report says UNKNOWN, which is shown and never gates. A guessed score is worse than an honest gap because somebody would act on it. The allowlist is a record rather than a mute button: each entry carries the reason, the accepter, the date, and what would invalidate the reasoning. The first entry is a worked example - a PyJWT algorithm-confusion advisory that needs both mixed algorithm families and a raw JSON JWK as the key, and this application configures neither. The report ends with a per-component upgrade rollup, since that is the part somebody acts on. A target on a different major version is labelled rather than listed beside patch bumps as though the two cost the same. Current state: 8 shipped components have fixes available, none of them exploitable through a path this application opens today.
139 lines
5.2 KiB
Markdown
139 lines
5.2 KiB
Markdown
# ShopDB Flask Project
|
|
|
|
Modern rewrite of the classic-ASP shopdb. Built as a framework so sister GE
|
|
Aerospace sites can adopt it. The plugin system is the product.
|
|
|
|
**This site is not the only site.** Anything that names West Jefferson - a
|
|
hostname, a share path, a subnet, a machine-number format, a label prefix - is a
|
|
defect unless it is a setting with a neutral default, a site-namespaced
|
|
directory, or seed data. That is ADR-015, and it is the rule most often broken
|
|
in the places tooling cannot see: PowerShell, the installer, JSON, the imaging
|
|
share.
|
|
|
|
## Facts that move live in the map
|
|
|
|
`docs/PROJECT-MAP.md` is GENERATED. It carries the product and contract
|
|
versions, the plugin inventory, every Alembic chain head, the ADR index with
|
|
status, and the size of the codebase. Read it instead of asking; regenerate it
|
|
rather than correcting it by hand:
|
|
|
|
```bash
|
|
venv/bin/python scripts/gen_project_map.py # write
|
|
venv/bin/python scripts/gen_project_map.py --check # CI form
|
|
```
|
|
|
|
Hand-written version numbers in this file were wrong by two contract releases
|
|
and 400 tests before the map existed. Nothing that the generator can derive
|
|
should be typed here again.
|
|
|
|
## Where to look
|
|
|
|
| question | read |
|
|
|---|---|
|
|
| what a name may be called | `CONTRIBUTING.md` |
|
|
| why the architecture is like that | `docs/adr/` (index in the map) |
|
|
| what a plugin may touch | `docs/PLUGIN-HOOKS.md` |
|
|
| what the collector sends | `docs/COLLECTOR-INTEGRATION.md`, ADR-006 |
|
|
| how a site is deployed | `docs/INSTALL-WINDOWS.md`, `docs/OPERATE-WINDOWS.md` |
|
|
| how code reaches GitHub | `tools/export-github.sh` |
|
|
|
|
## Standards are gates, not prose
|
|
|
|
Compliance is checked, not remembered. Run these rather than re-reading the
|
|
rules:
|
|
|
|
```bash
|
|
bash scripts/check-naming-and-style.sh # naming, style, site literals (ADR-015)
|
|
venv/bin/python -m pytest -q # includes the docs publishability gate
|
|
venv/bin/python -m pytest tests/test_plugin_migrations.py # chain heads registry
|
|
venv/bin/python scripts/gen_project_map.py --check
|
|
```
|
|
|
|
A new plugin table means updating `PLUGIN_TABLE_OWNERS` and
|
|
`EXPECTED_HEAD_REVISION` in `tests/test_plugin_migrations.py`.
|
|
|
|
Dependencies have their own check. It needs the network, so it is not in the
|
|
suite - run it before a release, and see `docs/DEPENDENCY-SECURITY.md`:
|
|
|
|
```bash
|
|
venv/bin/python scripts/scan_sbom.py --gate # exit 1 on a SHIPPED finding
|
|
```
|
|
|
|
## Database
|
|
|
|
- **Active:** `shopdb_flask` (MySQL, asset-based schema)
|
|
- **Legacy:** `shopdb` (classic ASP schema; one-time import only)
|
|
- **Connection:** `.env`, see `.env.example`
|
|
|
|
Core migrations run with `flask db upgrade`; every plugin owns its own chain
|
|
(ADR-008) and runs with `flask plugin upgrade-all`. **A deploy needs both** -
|
|
skipping the second is how a new column reaches production as a 1054 error.
|
|
|
|
Tests run on SQLite, production is MySQL. Dialect drift is not caught by the
|
|
suite: `ORDER BY ... NULLS LAST` parses on SQLite and is rejected by MySQL. Sort
|
|
in Python when in doubt.
|
|
|
|
## Coding convention
|
|
|
|
`CONTRIBUTING.md` is authoritative: lowercase concatenated DB columns, snake_case
|
|
pure-code identifiers, a closed acronym list, banned shorthand. The pre-commit
|
|
hook `scripts/check-naming-and-style.sh` enforces what it can.
|
|
|
|
## Plugin structure
|
|
|
|
```
|
|
plugins/<name>/
|
|
manifest.json # metadata, single source of truth (ADR-002)
|
|
plugin.py # BasePlugin implementation
|
|
models/ # __init__.py exports every model
|
|
api/routes.py # Flask blueprint
|
|
migrations/ # this plugin's own Alembic chain
|
|
services/ schemas/ frontend/ client/ # as needed
|
|
```
|
|
|
|
No plugin imports core directly - everything goes through the `shopdb.api`
|
|
contract surface. A manifest-less directory under `plugins/` is core frontend
|
|
surface and always ships.
|
|
|
|
## Key files
|
|
|
|
- `shopdb/__init__.py` - app factory, version constants
|
|
- `shopdb/plugins/base.py` - BasePlugin, PluginMeta
|
|
- `shopdb/plugins/loader.py` - discovery and dependency-aware loading
|
|
- `shopdb/plugins/alembic_template.py` - the env every plugin chain runs through
|
|
- `frontend/src/router/index.js`, `frontend/src/components/AppSidebar.vue`
|
|
- `tools/export-github.sh` - scrub and publication gate
|
|
|
|
## State
|
|
|
|
Refactor phases 0-5 landed; phase 6 (multi-site distribution readiness) largely
|
|
complete. The last milestone before 1.0 is unchanged and still open: the
|
|
legacy-data import plus a production pilot (`scripts/site_imports/wjf/` is the
|
|
reference loader, validated end to end).
|
|
|
|
Pre-1.0 framework. Sister sites pin tight `core_version` ranges until the
|
|
contract reaches 1.0.
|
|
|
|
### Deferred
|
|
|
|
- Equipment data migration (legacy ASP -> assets). Per ADR-001 only
|
|
`category='Equipment' AND machinenumber IS NOT NULL` migrates.
|
|
- Printers retirement: legacy `PrinterData` model, coordinated with the above.
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
source venv/bin/activate
|
|
pip install -r requirements.txt
|
|
cp .env.example .env # DB credentials, JWT secrets
|
|
|
|
flask db upgrade # core chain
|
|
flask plugin upgrade-all # every plugin chain
|
|
flask seed permissions && flask seed settings && flask seed reference-data
|
|
|
|
pm2 restart shopdb-flask-api shopdb-flask-ui
|
|
```
|
|
|
|
Dev API on :5001, dev UI on :5173. `venv/bin/python tools/shot.py /route` logs
|
|
in and screenshots a page.
|