Files
shopdb-flask/CLAUDE.md
cproudlock 794bb4668f
Some checks failed
CI / naming (push) Successful in 3s
CI / frontend (push) Successful in 13s
CI / migrations-mysql (push) Successful in 41s
CI / backend (push) Has been cancelled
Add vulnerability scanning on top of the SBOM
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.
2026-08-29 17:18:58 -04:00

5.2 KiB

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:

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 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:

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

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.