Skip to content

v1.3.0 — Two-factor, audit log and security policy

Choose a tag to compare

@ibuilder ibuilder released this 07 Aug 16:16
· 20 commits to main since this release

Track B: what it takes to survive a customer's security questionnaire.

Important

This repository is now ibuilder/scopemaker. GitHub redirects the old URLs. Documentation has moved to https://ibuilder.github.io/scopemaker/. The product is ScopeMaker — the scope engine is the point, and the Procore connector is one optional integration among others, documented in docs/integrations.md. The integration itself is unchanged and still fully supported.

Two-factor authentication

TOTP with single-use recovery codes. The enrolment QR is rendered as an inline SVG rather than fetched from a chart API — a surprising number of tutorials hand the shared secret to a third party, and it would also break the strict default-src 'self' policy.

The property the tests lean on hardest: a correct password alone does not authenticate anyone who has a second factor. Login parks a pending challenge in the session instead of signing you in. That marker expires, carries the session epoch so a password change mid-challenge invalidates it, and its failures count against the same account lockout — so the second factor cannot be brute-forced independently of the first.

Turning MFA off requires re-entering your password, because that is exactly what a hijacked session would try first.

Organization security policy

Admin → Security lets you require two-factor, or require single sign-on.

Enforced on every request, not just at sign-in. A policy that only applies to the next login leaves every currently-open session untouched — which is precisely the window an administrator turns it on to close. The enrolment pages are exempt from the redirect, or a user has nowhere to land.

sso_only refuses a correct password outright, and cannot be enabled when no identity provider is configured — that would lock everyone out.

Audit log

Append-only record of sign-ins and failures, lockouts, password resets, session revocation, role changes, member removal, invitations, token issue and revocation, scope issue/revise/archive, MFA changes, and integration activity.

Entries outlive their actor: the foreign key is nulled when a member is deleted, but their email is preserved on the row — removing somebody does not erase what they did. Filterable, with a security-events-only view and CSV export.

Supply chain and code quality

  • SECURITY.md with a private disclosure route, the full security posture, an honest section on what this does not protect against, and a hardening checklist
  • Dependabot for pip, GitHub Actions and Docker
  • CI now runs pip-audit --strict and publishes a CycloneDX SBOM artifact
  • mypy blocks the build. It was continue-on-error, which meant nobody read it. The seven type errors it was hiding are fixed — including two ScopeSection | None dereferences that would have been 500s.

Fixed

  • API tokens bypassed the organization's MFA requirement. The request hook that enforces policy keys off Flask-Login, and a bearer token is not a session — so a token issued before the policy was enabled kept working indefinitely. Enforcement now also runs where the bearer identity resolves.
  • Alembic renders JSONB columns as JSONB(astext_type=Text()) without importing Text, a NameError the moment the migration runs. Fixed in the affected migrations and in script.py.mako so it cannot recur.

330 tests, green on Python 3.11 and 3.12 with the PDF stack installed. ruff and mypy clean, pip-audit reports no known vulnerabilities.