31 KiB
31 KiB
Changelog - Develop
This file documents changes on the develop branch of this project.
2026-05-28 — Authentication: SPA gate
Added
- App boot calls
/api/auth/me; 401 redirects to/login.html(or/setup.html). - Header user-badge with sign-out button.
- Users nav link (hidden for non-admin).
Changed
requestJsonwrapper now redirects on any 401 response.
2026-05-28 — Authentication: setup page
Added
setup.htmlfor first-run admin creation, reachable only while theuserstable is empty.
2026-05-28 — Authentication: login page
Added
login.htmlwith username / password / remember-me.- Shared
auth.jshelpers (postJson, getJson). - CSS for auth pages and the header user-badge.
2026-05-28 — Authentication: API gating
Changed
- Tenants, Jobs, and Onboarding routers now require an authenticated session.
- Auth and Users routers wired into the FastAPI app.
2026-05-28 — Authentication: users + audit endpoints
Added
- Admin endpoints: list/create/update/delete users, reset password, view audit log.
- Self-delete protection; deactivating or resetting password revokes existing sessions.
2026-05-28 — Authentication: auth router
Added
/api/auth/setup-required,/api/auth/setup,/api/auth/login,/api/auth/logout,/api/auth/me.- HttpOnly session cookie with SameSite=Lax; Secure flag controlled by
COOKIE_SECUREenv.
2026-05-28 — Authentication: FastAPI dependencies
Added
require_user/require_admincookie-based session loading.- 401 for missing/expired/inactive; 403 for non-admin on admin routes.
2026-05-28 — Authentication: session lifecycle
Added
auth.sessionswith 8h sliding / 30d remember TTLs, lookup-and-refresh, revoke, purge.UTCDateTimetype decorator inauth.modelsto keep UTC-aware datetimes across SQLite roundtrips.
2026-05-28 — Authentication: audit helper
Added
auth.audit.record_event()for one-line writes toauth_audit.
2026-05-28 — Authentication: database migration
Added
- Alembic migration
0003_auth_tablescreatingusers,user_sessions,auth_audit.
2026-05-28 — Authentication: hashing + password policy
Added
- Argon2id password hashing (
hash_password,verify_password). - Server-side password policy (min 12, letter + digit).
- Opaque hex session-id generator.
2026-05-28 — Authentication: data models
Added
User,UserSession,AuthAuditSQLAlchemy models.- Model-level tests using SQLite in-memory engine.
2026-05-28 — Authentication: scaffold
Added
argon2-cffi,pytest,httpxdependencies.- New
clearview_app/auth/package skeleton. tests/directory with SQLite-backed pytest fixtures.
2026-05-26 — UI/UX: dead CSS removal, a11y, distinct risk colours, richer dashboard
Added
- Dashboard enrichment — a fourth KPI card With errors (
#statErrors, counts jobs that arecompleted_with_errorsor havefailed_targets > 0) and a Recent jobs panel (#dashRecentJobs, last 5 jobs, each row clickable to jump to its details). Populated from the existing/api/scan-jobslist inrefreshJobs()via a newrenderDashRecent(); all interpolated fields run throughescHtml().
Changed
- Removed dead CSS — the pre-sidebar
.topbar,.topbar-actions, and.layoutrules (and their now-orphaned references inside the 930px/640px media queries) were deleted; the layout has used.app-shell/.sidebar/.contentsince the sidebar refactor. - Accessibility — focus outline strengthened from
rgba(14,165,233,0.38)to a solidvar(--cv-accent)(meets WCAG non-text 3:1) and now also coversa:focus-visible. On route changes (applyRoute), focus now moves to the new page's first heading (h1/h2,tabindex=-1) anddocument.titleupdates, so screen-reader/keyboard users land in the freshly shown content. - Distinct risk colours — the
risk.warnbadge changed from accent-blue (indistinguishable frominfo/low) to amber (#854d0eonrgba(234,179,8,.18)), giving a real low→high colour gradient. - Consistent XSS escaping —
job.idandjob.source_typein the Scan Jobs table are now passed throughescHtml()(previously interpolated raw), matching the rest of the table.
2026-05-26 — Split monolithic main.py into route modules
Changed
main.pyreduced from 1152 to 64 lines — now a composition root that only wires the FastAPI app, scan-worker lifecycle,/healthz,/api/version, the/index + static mount, andinclude_routerfor the new route modules. All endpoint logic moved out verbatim (behaviour-preserving).- New route modules (flat modules at package level so existing single-dot relative imports stay unchanged — lower risk than a
routers/subpackage):api_tenants.py(tenant profiles + certificate),api_jobs.py(all scan-job routes incl. CSV import, cancel/delete, resolve-sharing-links, resolve-groups, test-connection, Excel export, detail),api_onboarding.py(Microsoft connect/callback/scan-app). Shared helpers (_resolve_credentials,_create_job_from_targets,_enumerate_all_*,_to_job_summary,_to_tenant_item,_build_export_filename,_sharing_link_risk_label,_extract_sharing_link_group_and_type) extracted toapi_helpers.py. - Verified behaviour-preserving — captured the OpenAPI route set before/after; both expose the identical 22 endpoints (
diffempty). Built the image, booted against a fresh DB:/healthz,/api/version,/api/tenants,/api/scan-jobsall respond, invalidscan_typestill returns 422, no startup errors.
2026-05-26 — Correctness P1: token cache, atomic job claim, timezone-aware datetimes, scan_type validation
Changed
- Token cache now has TTL + thread lock + MSAL app reuse (
scanners/sharepoint.py) —_TOKEN_CACHEpreviously stored access tokens as plain strings forever, so long scans started failing with 401s once the ~1h token expired. It now stores(token, expires_at)and refreshes 60s before expiry, guarded by a new_TOKEN_LOCK(the worker fetches tokens from multiple threads). New_get_msal_app()caches oneConfidentialClientApplicationper(tenant, client, auth_method)so MSAL's own token cache is reused instead of building a fresh app on every call. - Atomic job claim (
worker.py) — the queued-job selection now uses.with_for_update(skip_locked=True)(SELECT … FOR UPDATE SKIP LOCKED), so multiple worker threads/replicas can never claim the same job. Behaviour is unchanged for the current single worker but is now replica-safe. - Timezone-aware datetimes everywhere — replaced all 24
datetime.utcnow()(naive, deprecated) withdatetime.now(timezone.utc)acrossmodels.py,worker.py,main.py, andcert.py. SQLAlchemy datetime columns are nowDateTime(timezone=True); model defaults use a new_utcnow()helper. New Alembic migration0002_timestamptzconverts existingtimestamp without time zonecolumns totimestamptz(reinterpreting stored values as UTC), guarded per-column so it is a no-op on databases already timestamptz. Behaviour note: API datetimes now carry a UTC offset, so the frontend renders them correctly in local time (previously stored UTC was shown as if local). scan_typerequest validation (schemas.py) —CreateScanJobRequest.scan_typeis nowLiteral["sharepoint","sharepoint_root","mailbox","entra_groups"]instead of freestr; invalid values return HTTP 422. The response model keepsstrso legacy rows never trigger a serialization error. Verified:scan_type=bogus→ 422, valid type passes schema validation.
2026-05-26 — Alembic migrations replace startup create_all + raw ALTERs
Added
- Alembic introduced (
alembic==1.14.0) — schema is now version-controlled instead of being patched at every startup. Newclearview_app/migrations/package (env.pyreuses the app's SQLAlchemy engine andBase.metadata;versions/0001_baseline.pybaseline) and dev-onlycontainers/clearview/alembic.inifor manual CLI use. The app builds the AlembicConfigprogrammatically, soalembic.iniis not shipped in the image. - Baseline migration
0001_baseline— creates the full current schema viaBase.metadata.create_all, guaranteed identical to the models (the same DDL the app emitted before). Future schema changes become explicit Alembic revisions. - Startup bootstrap
clearview_app/db_migrate.run_migrations()— idempotent, three cases: fresh DB →upgrade head; existing pre-Alembic DB (tables present, noalembic_version) →stamp head(adopt baseline without re-creating); already under Alembic →upgrade head. Verified end-to-end against throwaway databases (fresh upgrade, existing-DB stamp, re-run no-op) and a local image boot test (/healthzOK, schema +alembic_version=0001_baseline).
Changed
main.pystartup —on_startup()now callsrun_migrations()instead ofBase.metadata.create_all(bind=engine)+_ensure_schema_columns(). The 18-statement rawALTER TABLE ... ADD COLUMN IF NOT EXISTSblock (_ensure_schema_columns) is removed; unusedBase/engineimports dropped. The existing dev/prod database is adopted automatically (stamped to baseline) on first start of the new build — no manual migration step required.
2026-05-26 — Build/version number in the UI (Dropkeep-style)
Added
- Version metadata module
clearview_app/version.py— single source of truth mirroring Dropkeep:VERSION = "v0.1.0"(release) +BUILD = 0(explicit dev/test build segment, source state, not git-derived).display_version()returnsvX.Y.Z.NwhenBUILD > 0, elsevX.Y.Z;cache_version()strips the leadingv. GET /api/versionendpoint — returns{"version": display_version()}. The FastAPI appversion=is also sourced fromversion.py(was hardcoded"0.1.0").- Version shown in the UI — the sidebar footer version (previously a hardcoded
v0.1.0inindex.html) is now populated at load time from/api/versionvia a newloadVersion()inapp.js(spanid="appVersion"). Operators see exactly which image build is running, e.g.v0.1.0.3. - Build wrapper
build.sh+scripts/—./build.sh trunsscripts/bump-dev-build.py(incrementsBUILD) then./build-and-push.sh t;./build.sh rrunsscripts/check-release-version.py(assertsBUILD == 0and thatversion.pymatches the topdocs/changelog.mdrelease heading) then./build-and-push.sh r.scripts/set-release-version.py vX.Y.Zsets a new release version and resetsBUILD = 0. Build numbers are committed in source so the image carries the exact build with no Docker build args.
2026-05-26 — Root report: expand Entra/M365 groups & readable direct users
Added
- Entra/AAD & M365 group expansion at site root — the "Resolve groups" action now also expands Azure AD security groups and Microsoft 365 groups that are assigned directly at the site root, not just classic SharePoint site groups. Previously these claim-encoded principals (
c:0t.c|tenant|<guid>,c:0o.c|federateddirectoryclaimprovider|<guid>) were skipped byis_sharepoint_group_principal, so the root report showed only the group name and never the people inside — making the inventory incomplete. New helpers inscanners/sharepoint.py:_extract_aad_group_object_id(parses the Entra object id out of the claim, incl. the_oowners suffix),is_aad_group_principal,resolve_aad_group_members, and_expand_aad_group_by_id(extracted from_expand_aad_group_via_graphso both mail-based and id-based lookups share the/groups/{id}/members+/ownersGraph path, depth-limited to 3 with a per-resolveseenset).POST /api/scan-jobs/{id}/resolve-groupsnow routes AAD/M365 group principals to the Graph resolver and SharePoint groups to the existinggetbynameresolver. RequiresGroupMember.Read.All(orGroup.Read.All) on Microsoft Graph; without it the group stays visible by name and counts as "skipped" — no crash.
Changed
- Readable principals for directly-assigned users — individual users granted rights directly on the site root now render as their UPN/email (e.g.
jan@contoso.com) instead of the raw claim stringi:0#.f|membership|jan@contoso.com. New helpers_extract_user_upnand_display_principalinscanners/sharepoint.py, applied in_get_role_assignments(so both the root scan and the deviation scan benefit, consistently on both sides of the root-vs-child set comparison). Only users with an@-shaped UPN are rewritten; groups, on-prem (i:0#.w|domain\\user) and built-in/system accounts keep their original LoginName so claim object ids stay resolvable and the site-root noise filter (SHAREPOINT\\system,NT AUTHORITY\\*, etc.) keeps matching.
[2026-04-28]
Changed
- Excel export sheet name + columns adapt to scan type — second sheet is now named
Mailbox Permissionsfor mailbox jobs,Group Membershipsfor Entra-group jobs,Root Permissionsfor SharePoint-root jobs, andDeviationsfor the original SharePoint deviation scan. Column sets are tailored per type so headers like "Object URL" / "Link Risk" / "Delta" no longer appear on exports where they don't apply. Targets sheet first column label switches between Site URL / Mailbox / Group based on the job.
Added
- Entra Group Scan — new scan type
entra_groupsdedicated to enumerating Microsoft 365 / Azure AD group memberships. Newscanners/entra.pyresolves a target (Object ID, mail, or display name) via Microsoft Graph and stores one deviation per user with roleMemberorOwner(with(via group > nested-group)chain when expanded recursively). Group classification (Microsoft 365 / Security / Mail-enabled Security / Distribution) is stored inpermission_type. New helperentra.list_all_groupsfor the "All groups in tenant" option. New CSV parserparse_entra_groups_csvreads theObject IDcolumn from the Entra portal Groups export. New sidebar route#/scan/entrawith three forms (manual IDs, CSV import, all-tenant). New filter option in the Scan Jobs type dropdown. Job Details renders Group / Group Type / User / Role columns for these jobs. RequiresGroup.Read.Allon Microsoft Graph. - Recursive group expansion via Microsoft Graph — when a SharePoint group member is itself a Microsoft 365 / Azure AD group, the resolver now expands it transitively. New helpers
_expand_aad_group_via_graphand_graph_collectinscanners/sharepoint.pycall/groups?$filter=mail eq …to look up the group, then/groups/{id}/membersand/groups/{id}/ownersto enumerate users. Owners are tagged with(owner)in the output. Recursion is depth-limited to 3 with a per-resolveseenset to break cycles. Output format puts nested members in square brackets after the group name, e.g.Pharmacology@contoso.onmicrosoft.com [alice@contoso.com, bob@contoso.com (owner)]. Requires the newGroup.Read.AllApplication permission on Microsoft Graph (added to the onboarding instructions). Without it, group lines remain collapsed and labelled(group, no readable members). - Resolve SharePoint groups — new "Resolve groups" action on the Job Details panel for SharePoint and SharePoint-root jobs. Expands every SharePoint group principal (Owners / Members / Visitors / custom site groups) to its underlying user list via
/_api/web/sitegroups/getbyname/<group>/usersand writes the comma-separated members topermission_deviations.resolved_members. Members are rendered below the principal in the Deviations table and included in the Excel export. Azure AD security groups and federated claims (principals starting withc:0…/i:0…or containing|) are skipped — those would needGroup.Read.Allon Microsoft Graph. New endpointPOST /api/scan-jobs/{id}/resolve-groups, helpersharepoint.is_sharepoint_group_principal(). - SharePoint root-permissions scan mode — new
scan_type='sharepoint_root'that lists role assignments on the site root only, without traversing libraries/folders/files. Much faster (~1 HTTP call per target) and useful for an inventory of who has site-level access. New scanner functionsharepoint.scan_site_root_permissions. Records are stored withdelta_type='root'andobject_type='Site'. Selectable on the New SharePoint Scan page via a "Scan mode" dropdown that controls both the manual-URL and CSV-import forms. New filter option in the Scan Jobs type filter. Noise filter_is_noise_principalexcludes SharingLinks groups,SHAREPOINT\system/NT AUTHORITY\*accounts, and "Limited Access System Group" entries — these are SharePoint plumbing surfaced at site-root by spotted-item shares and are not part of a meaningful root inventory. - Tenant
primary_domainfield — new column ontenant_profiles, exposed in the Add Tenant form (e.g.contoso.onmicrosoft.com). When set, the Mailbox scan page auto-fills the Organization field on tenant selection, and the API falls back to it whenorganizationis omitted on ascan_all_mailboxesrequest. SharePoint scans are unaffected. - Expanded mailbox-scan onboarding instructions — new "Enable mailbox scanning" section in the Add Tenant form covers adding the
Exchange.ManageAsAppAPI permission, granting admin consent, assigning the Exchange Administrator Entra role to the service principal, certificate generation/upload, and primary-domain entry. Always visible (independent of automated/manual onboarding mode). - Scan all mailboxes in a tenant — third option on the Mailbox scan page next to manual UPNs and CSV import. Clearview enumerates every mailbox via
Get-EXOMailbox -ResultSize Unlimitedand queues one target per mailbox. Requires the tenant's primary domain (e.g.contoso.onmicrosoft.com) and a tenant certificate. New PowerShell scriptexo_scripts/list-mailboxes.ps1, new Python helpermailbox.list_mailboxes(), new request fieldsscan_all_mailboxesandorganization. Job source type is recorded astenant_all.
Changed
- Sidebar logo — replaced with a dark-background variant (
assets/clearview-logo-dark.svg) so the "view" wordmark stays legible on the dark sidebar (previously rendered in#141413and was invisible). - English-only UI — replaced remaining Dutch labels in the application with English equivalents: probe status
Nog niet getest/Mislukt→Not tested yet/Failed, button labelTesten…→Testing…, error toastTest mislukt:→Test failed:, and probe hints inscanners/sharepoint.py+scanners/mailbox.py. The Dutch→English role-name mapping table insharepoint.pyis unchanged (it normalizes incoming SharePoint role names). - Mailbox permission scanning — Clearview can now scan Exchange Online mailboxes for delegated access alongside SharePoint sites.
- Permission categories collected: Full Access (
Get-MailboxPermission), Send As (Get-RecipientPermission), Send on Behalf (GrantSendOnBehalfTomailbox property), and folder delegations on Calendar and Inbox (Get-MailboxFolderPermission). - Implementation:
pwshsubprocess invoking theExchangeOnlineManagementmodule with certificate-based app-only authentication (same tenant profile cert as SharePoint scans). - Default principals (
NT AUTHORITY\SELF,S-1-5-*, folderDefault/Anonymous=None) are filtered out at scan time; only non-default permissions become deviations. - Mailbox scans require a tenant certificate plus the
Office 365 Exchange Online → Exchange.ManageAsAppAPI permission and the Exchange Administrator Entra role on the scan app's service principal. Client-secret auth is not supported by Exchange Online.
- Permission categories collected: Full Access (
- Frontend sidebar layout — single-page UI replaced with a fixed left sidebar (200px, dark) and routed pages, mirroring the AlertHub layout convention.
- Routes via hash-based router:
#/dashboard,#/jobs,#/scan/sharepoint,#/scan/mailbox,#/tenants,#/settings. Implementation stays vanilla HTML/JS/CSS (no React introduction). - Job Details panel adapts column labels and headers based on
scan_type: SharePoint shows Site/Object/Type/Principal/Role/Delta; Mailbox shows Mailbox/Object/Permission Type/Principal/Access Rights. SharingLinks resolution is hidden for mailbox jobs. - Jobs list gets a Type column (SharePoint / Mailbox) and a type filter.
- Routes via hash-based router:
- Scanners package —
clearview_app/scanner.pysplit intoclearview_app/scanners/{__init__.py, common.py, sharepoint.py, mailbox.py, exo_scripts/}. Public dispatcherscanners.scan(scan_type, target, auth, progress)andscanners.probe(scan_type, target, auth). The originalscanner.pyremains as a thin compatibility shim re-exporting the SharePoint API. - Datamodel changes (auto-migrated on startup):
scan_jobs.scan_type VARCHAR(32) NOT NULL DEFAULT 'sharepoint'permission_deviations.permission_type VARCHAR(32)— populated by mailbox scans (FullAccess,SendAs,SendOnBehalf,Folder:Calendar,Folder:Inbox)tenant_profiles.cert_public_pem TEXT— public PEM is now stored alongside the private key so the mailbox scanner can build a.pfxforConnect-ExchangeOnline -CertificateFilePath. Existing tenants need to regenerate the certificate before mailbox scanning is available; SharePoint scans keep working with the existing key.
- Mailbox CSV import —
parse_mailboxes_csvacceptsUserPrincipalName/UPN/Email/Mailbox/Primary SMTP Addresscolumns with case-insensitive matching, dedup, and email-shape validation. - API additions:
POST /api/scan-jobspayload extended withscan_typeandmailboxes[]next to the existingsite_urls[].POST /api/scan-jobs/import-csvaccepts ascan_typeform field (sharepoint|mailbox).GET /api/scan-jobs?scan_type=…filter.ScanJobSummary.scan_typeandPermissionDeviationItem.permission_typereturned.
- Dockerfile now installs Microsoft PowerShell 7 from the official Microsoft repository plus the
ExchangeOnlineManagementPowerShell module from PSGallery. Adds ~150 MB to the image. - Build script migration — replaced the local
build-and-push.shwith the shared version from/docker/develop/shared-integrations/tooling/docker-build-and-push/. Reads the version fromdocs/changelog.md(release-summary file) instead ofversion.txt. docs/changelog.md— new release-summary changelog file used by the new build script. The development log (changelog-develop.md) remains the append-only source of truth for individual changes.
[2026-04-23]
Added
- Connection preflight per scan target — before a target is scanned, a lightweight probe validates that the configured credentials can reach the site and read role assignments (
/_api/web+/_api/web/roleassignments?$top=1). Targets that fail preflight are markedfailedwith a clear reason (401/403/404 hints) instead of attempting the full scan. Fixes the previous silent-failure behaviour when admin consent or the certificate upload was missing in Azure. - Manual "Test" button — new button in the Targets table in Job Details that re-runs the probe on demand. New endpoint:
POST /api/scan-jobs/{id}/targets/{target_id}/test-connection. Blocked while the job is still queued or running. - Probe status in UI — each target row shows the last probe result (OK / Mislukt / Nog niet getest) with timestamp and error message. Fields persist until the next test, so "last known status" remains visible even after permissions are later revoked.
scan_targetstable extended withlast_probe_at,last_probe_ok,last_probe_message(auto-migrated on startup).
[2026-04-13]
Added
- Site filter in Job Details — dropdown in the Selected Job Details panel to filter Targets and Deviations tables by site URL (client-side, no extra API call).
- Excel export —
GET /api/scan-jobs/{id}/exportendpoint (optional?site_url=filter) returns a.xlsxfile with two sheets:- Targets: URL, status, attempts, error, timestamps.
- Deviations: Site URL, relative Object URL, Object Type, Principal, Link Risk (colour-coded), Resolved Members, Role, Delta — sorted by Site URL → Object URL → Principal.
- Hierarchical deduplication — after scanning a target, deviations are post-processed to suppress child-level entries already covered by a parent (library/folder). Prevents result explosion on large sites with deeply inherited permissions. No additional API calls.
- SharingLinks classification and colour coding — SharePoint sharing-link principals are parsed and displayed with a risk badge in the Deviations table:
Anonymous*→ Critical (red)Flexible→ High (orange)Organization*→ Low (blue)Direct*→ Low (green)
- Resolve Sharing Links — post-scan action in the Job Details panel. Fetches the actual member list of sharing-link groups via
/_api/web/sitegroups/getbyname/users. Stored in newpermission_deviations.resolved_memberscolumn. Anonymous links produce an empty member list (shown as(public link)). New endpoint:POST /api/scan-jobs/{id}/resolve-sharing-links. - Role name normalisation — common Dutch SharePoint role names (e.g. "Volledig beheer", "Bijdragen") are translated to their English equivalents at scan time before being stored.
openpyxldependency added torequirements.txt.- Favicon replaced with a dedicated icon (blue rounded square with eye/keyhole symbol) instead of the concept design SVG.
Changed
SCAN_TARGET_TIMEOUT_SECdefault raised from 180 s to 3600 s (1 hour) to accommodate large sites with tens of thousands of files.permission_deviationstable extended withresolved_members TEXTcolumn (auto-migrated on startup).- Object URL in the Deviations table and Excel export is now shown relative to the site URL (site URL prefix stripped).
- Principal display in the Deviations table strips the SharePoint claim prefix (e.g.
i:0#.f|membership|) and shows only the email/name; full value visible on hover. - Site URL in the Deviations table is abbreviated to the last path segment with full URL on hover.
- Deviations table uses
table-layout: fixedwith column widths sized to fit on a 1080p display. docs/TECHNICAL.mdandREADME.mdupdated to reflect all new functionality.
[2026-04-13]
Added
- Certificate-based authentication for SharePoint app-only access:
- Clearview generates a self-signed RSA-2048 certificate per tenant (no external CA required).
- New endpoint
POST /api/tenants/{id}/generate-certificatestores the private key and returns the public cert. - Public certificate downloadable as a
.cerfile from the UI, named after the tenant. - Scanner uses MSAL with certificate when available; client secret remains as fallback.
- Resolves SharePoint error "Unsupported app only token" when using client secret authentication.
TenantProfileextended withcert_private_key,cert_thumbprint, andcert_expires_at.- Tenant table shows auth method (cert with expiry date or secret).
- Client secret is now optional when creating a tenant profile (can be omitted when a certificate will be used).
- Job deletion:
DELETE /api/scan-jobs/{id}endpoint added (not allowed for queued or running jobs).- Delete button per job in the UI; cascades to targets and deviations.
Fixed
- SharePoint REST API error when fetching list items: removed
$filter=HasUniqueRoleAssignments eq trueas SharePoint does not support this field as an OData filter. The check is now performed client-side.
[2026-04-13]
Added
- Multi-tenant support: Clearview now manages multiple customer tenants from a single instance.
- New
TenantProfiledata model (tenant_profilestable) for storing customer credentials. ScanJoblinked to a tenant profile viatenant_profile_idFK.- API endpoints for tenant profile management:
GET/POST /api/tenants,DELETE /api/tenants/{id}. GET /api/scan-jobssupports filtering bytenant_profile_id.
- New
- UI fully redesigned for multi-tenant use:
- New Tenants panel with a table of configured customers, Add/Delete actions, and a Scan shortcut per tenant.
- Onboarding flow (Connect Microsoft / manual instructions) moved into the Add Tenant form.
- Scan form uses a tenant profile dropdown; manual credentials only shown as a fallback option.
- Jobs table extended with a Tenant column and a tenant filter dropdown.
- Hero stats now show: Tenants / Jobs / Active Jobs.
- XSS escaping added for all user-supplied data rendered in the jobs and deviations tables.
Changed
TECHNICAL.mdupdated with multi-tenant model documentation, tenant profile API, and redesigned onboarding flow.
[2026-04-13]
Added
- Initial repository structure.
containers/directory added with theclearviewstarter service.build-and-push.shadded for container build and push.docs/TECHNICAL.mdadded.docs/changelog-develop.mdadded.version.txtadded with initial version..last-branchadded for branch tracking in the build script.
[2026-04-13]
Added
- FastAPI backend integrated into the
clearviewcontainer (single-container app runtime). - PostgreSQL-backed scan job model (
scan_jobs,scan_targets,permission_deviations). - Background scan worker with queue processing, retries, and per-target timeout controls.
- API endpoints for manual URL scan creation, CSV import, job listing, and job detail retrieval.
- CSV parsing support for Microsoft Sites export format with URL normalization and de-duplication.
- Default-site skip rules for tenant root and app catalog paths.
- Frontend replaced with production-oriented scan UI:
- Manual URL submission
- CSV upload
- Job status overview
- Target-level result view
- Deviation table view
- Stack configuration extended with scan worker runtime environment settings.
Changed
containers/clearview/Dockerfileswitched from static nginx hosting to Python FastAPI runtime.
Added
- Real SharePoint scan implementation for app-only authentication mode (
SHAREPOINT_SCAN_MODE=sharepoint_app_only):- OAuth2 client credentials token acquisition via Microsoft Entra ID.
- Site root permission baseline loading through SharePoint REST
roleassignments. - Document library, folder, and file traversal with unique-permission detection (
HasUniqueRoleAssignments). - Deviation persistence only for rights not present on site root (
delta_type=added). - HTTP retry/backoff and throttle handling (429/503), plus list-level scan caps.
- Scanner HTTP retry/backoff and list-limit controls added in backend configuration.
Changed
- Authentication flow updated to universal multi-tenant style:
- Azure credentials are now supplied per scan job from the web UI/API payload.
- No Azure tenant/client/secret dependency in stack
.env.
- Added UI and technical documentation guidance for one-time Entra app setup and required SharePoint permission (
Sites.FullControl.All+ admin consent).
Added
- Automated onboarding endpoint
POST /api/onboarding/create-scan-app:- Creates a dedicated scan app in Entra for the connected tenant.
- Configures SharePoint app permission
Sites.FullControl.All. - Creates service principal and assigns app role consent.
- Generates and returns a new client secret.
- Microsoft connect/admin-consent flow endpoints:
GET /api/onboarding/microsoft/connect-urlGET /api/onboarding/microsoft/callback
- UI onboarding flow updated:
Connect Microsoftbutton for admin consent redirect- Callback handling to capture tenant id
- Automatic scan-app creation without manual bootstrap app input