Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ jobs:

steps:
- name: Check out repository
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version-file: .node-version
cache: npm
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@

### Changed

- Migrated the AI integration to the extension's current versioned `postMessage` bridge and added protocol-envelope tests.
- Hardened student handoff imports with exact scope, real-date, enum, class relationship, and logical-duplicate validation.
- Prevented stale attendance loads from overwriting the active selection or reusing an attendance ID from another session.
- Hardened the local app lock against manipulated work factors and repeated unlock attempts.
- Updated compatible development dependencies, restored full-source coverage reporting, and added reusable Nginx security-header configuration.
- Standardized the first-run experience on the single term "initial setup" across global prompts, onboarding cards, dialogs, and actions.
- Unified initial-setup prompts under the primary blue visual treatment while keeping amber reserved for actionable context warnings.
- Separated the missing-group blocker from the onboarding progress coach, removing duplicated setup messaging and the redundant compact step badge.
- Reorganized the fifteen-item top navigation into five workflow areas with route-aware secondary navigation and a non-scrolling mobile layout.
- Redesigned the teacher setup prompt as a compact, responsive task card with clearer progress, next-step guidance, and accessible primary and secondary actions.
- Reports now apply date ranges to manual assessments through their explicit assessment date and exclude undated legacy rows instead of silently mixing periods.
- Attendance and reports now distinguish a measured zero percent from a course with no attendance observations.
- ACS and reinforcement measures remain visible support context but no longer increase the automatic academic risk label.
Expand Down
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ npm run build:production # Build the production deployment profile
npm run preview # Preview the production build
npm run clean # Remove generated builds, coverage, logs, and TypeScript caches
npm run test # Run Vitest
npm run test:coverage # Run Vitest and write the full source coverage report
npm run lint # Run ESLint, including React hook checks
npm run typecheck # Run TypeScript project checks
npm run audit # Audit dependencies against the reviewed advisory policy
Expand Down Expand Up @@ -109,17 +110,17 @@ Database payloads are intentionally not backward compatible. Only payloads produ

- Academic records remain local to the browser profile in IndexedDB; ProfePlus has no application backend.
- Downloaded backups and automatic pre-operation safety backups are encrypted. ProfePlus never stores their passwords.
- An optional local app lock uses a salted PBKDF2-SHA-256 verifier and automatically locks after the configured inactivity period. It protects the visible application from casual access but is not a substitute for operating-system disk and profile encryption.
- An optional local app lock uses a salted PBKDF2-SHA-256 verifier, validates its stored work factor, pauses retries after repeated failures, and automatically locks after the configured inactivity period. It protects the visible application from casual access but is not a substitute for operating-system disk and profile encryption.
- CSV exports neutralize spreadsheet formula prefixes before escaping cell values.
- Backup imports enforce a size limit and validate metadata, structure, types, relationships, scopes, uniqueness, dates, and numeric ranges before changing the database.
- The application includes a restrictive Content Security Policy, referrer policy, permission policy, clickjacking protection, and MIME-sniffing protection. `public/_headers` applies the full header set on compatible static hosts; other hosts must configure equivalent response headers.
- The application includes a restrictive Content Security Policy, referrer policy, permission policy, cross-origin isolation headers, clickjacking protection, and MIME-sniffing protection. `public/_headers` applies the full header set on compatible static hosts. Nginx deployments can include `deploy/nginx-security-headers.conf` from the HTTPS server or location block.
- Anyone with access to an unlocked browser profile can still inspect its IndexedDB data. Use an encrypted operating-system account and do not share the browser profile when handling real student data.

## AI Extension

ProfePlus does not include an in-app AI settings page and does not import WebLLM directly.

AI features use the AI Proxy Bridge Chrome extension:
AI features use the AI Proxy Bridge Chrome extension through its versioned same-origin page bridge:

- The extension injects its compact overlay into trusted pages.
- Feature-level AI requests go through `src/shared/ai/extensionRuntime.ts`.
Expand All @@ -128,8 +129,8 @@ AI features use the AI Proxy Bridge Chrome extension:

Configuration options:

- `VITE_AI_RUNTIME_EXTENSION_ID`: optional default extension ID.
- The app can also detect the extension through the content-script ready event on trusted origins.
- `VITE_AI_RUNTIME_EXTENSION_ID`: optional extension-ID pin. Use it when the deployed extension has a stable ID.
- Without a pin, the app discovers the installed bridge through a versioned `postMessage` availability exchange and keeps the discovered ID fixed for each request.
- The extension must authorize the current hostname, such as `localhost`, `127.0.0.1`, or the deployed domain.

The extension project is external to this app and should be managed separately.
Expand Down Expand Up @@ -181,14 +182,18 @@ Automated coverage includes focused Vitest suites for:
- Printable reports.
- Student follow-up helpers.
- AI-generated instrument parsing and validation.
- Versioned AI extension bridge envelopes.
- Student handoff scope, reference, date, and enum validation.

The latest full verification passed with:
Run `npm run test:coverage` to generate the complete text and HTML coverage report for `src/`.

Run the full verification with:

```bash
npm run verify
```

Expected result:
The expected result is:

- TypeScript project checks pass.
- ESLint passes without errors or warnings.
Expand Down Expand Up @@ -223,7 +228,8 @@ provider must:
- Serve `dist/` over HTTPS.
- Rewrite unknown application routes to `index.html`.
- Apply the security headers in `public/_headers`, or equivalent platform
configuration.
configuration. For Nginx, include `deploy/nginx-security-headers.conf` and
reload Nginx after validating the configuration.
- Avoid sharing browser storage or service-worker state between the two
domains. Their separate origins provide this isolation automatically.

Expand Down
10 changes: 10 additions & 0 deletions deploy/nginx-security-headers.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Include this file in the HTTPS server or location block that serves ProfePlus.
add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; worker-src 'self' blob:; manifest-src 'self'; form-action 'self'; frame-ancestors 'none'" always;
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "0" always;
add_header Permissions-Policy "camera=(), geolocation=(), microphone=(), payment=(), usb=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
Loading