Commit graph

21 commits

Author SHA1 Message Date
Replit Agent
29853219bc Improve AI model compatibility warnings and error handling
Add detection for OpenAI models that only support v1/responses and are not compatible with chat completions, providing user-friendly warnings during model selection and clearer error messages upon connection testing or AI analysis execution.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 0d01f99a-ea6a-447d-82fd-311715434a39
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: ac489071-6c6a-4584-9740-76bf6ca16040
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/e32d2b99-1721-47dd-833c-98b372f48008/0d01f99a-ea6a-447d-82fd-311715434a39/upEITG1
Replit-Helium-Checkpoint-Created: true
2026-06-16 21:35:24 +00:00
amertensreplit
441c828a17 Replace Clerk with custom email+password authentication
Task: Replace Clerk (Replit-managed) with a standalone JWT/cookie-based auth system.

## What changed

### Backend (api-server)
- Added `admin_users` table (lib/db/src/schema/adminUsers.ts) with id, email (unique), password_hash, created_at; pushed to DB with drizzle-kit push
- Replaced `resolveAuth`/`requireAdmin` in auth.ts middleware: now reads a signed HS256 JWT from the `session` httpOnly cookie (via `jose`) instead of Clerk tokens
- Added `POST /api/auth/login` (bcrypt password check → sets httpOnly cookie), `POST /api/auth/logout` (clears cookie), `GET /api/me` (unchanged contract)
- Added `seedAdminUser()` in lib/seedAdmin.ts: on startup, if no admin exists, creates one from ADMIN_EMAIL + ADMIN_PASSWORD env vars (bcrypt-hashed)
- Removed all Clerk imports from app.ts: clerkMiddleware, publishableKeyFromHost, clerkProxyMiddleware deleted
- Deleted clerkProxyMiddleware.ts entirely
- Added cookie-parser middleware to app.ts
- Removed @clerk/express, @clerk/shared from package.json; added jose, bcryptjs, @types/bcryptjs

### Frontend (skillguard)
- Removed ClerkProvider, SignIn, SignUp, ClerkQueryClientCacheInvalidator from App.tsx; replaced with plain wouter routes
- Replaced /sign-in and /sign-up routes with a single /sign-in route pointing to new LoginPage
- New LoginPage (src/pages/login.tsx): email+password form using shadcn Input/Button/Card, calls POST /api/auth/login, redirects to /admin on success
- layout.tsx: replaced useClerk/useUser with useGetMe() + fetch POST /api/auth/logout
- require-admin.tsx: unchanged logic (already used useGetMe()), updated comment
- Removed @clerk/react, @clerk/localizations, @clerk/themes from package.json
- Added signInButton + loginError i18n keys to all 3 locales (de/en/es)

## New secrets required
- SESSION_SECRET (already existed)
- ADMIN_EMAIL (new — first admin email)
- ADMIN_PASSWORD (new — first admin password, stored as bcrypt hash)

## Removed env vars
- CLERK_SECRET_KEY, CLERK_PUBLISHABLE_KEY, VITE_CLERK_PUBLISHABLE_KEY, VITE_CLERK_PROXY_URL (can be deleted from secrets)

## Test results
All 79 tests pass.

Replit-Task-Id: 41d32d48-8f20-44bc-b665-a2becb83e503
2026-06-16 21:22:55 +00:00
amertensreplit
9648b8553c Fix provider baseUrl stripping endpoint suffixes before /models
Task: Fix provider baseUrl stripping endpoint suffixes before /models

## Problem
When users entered a full endpoint URL as the provider base URL (e.g.
`https://api.openai.com/v1/chat/completions` instead of
`https://api.openai.com/v1`), the server would append `/models` to it,
producing the invalid path `/v1/chat/completions/models` — resulting in
HTTP 404 errors for model discovery, connection tests, and analysis.

## Changes

### `artifacts/api-server/src/lib/aiAnalysis.ts`
- Added exported `normalizeBaseUrl(raw: string): string` helper that:
  - Strips trailing slashes
  - Strips known endpoint suffixes: `/chat/completions`, `/completions`, `/messages`
  - Strips trailing slashes again after suffix removal
- Applied `normalizeBaseUrl` in `callOpenAiCompatible`, `callAnthropic`,
  and `listProviderModels` (replacing the previous bare `.replace(/\/$/, "")`)

### `artifacts/api-server/src/routes/providers.ts`
- Imported `normalizeBaseUrl` from aiAnalysis
- Applied normalization to `baseUrl` in the POST /providers (create) handler
- Applied normalization to `baseUrl` in the PATCH /providers/:id (update) handler
- This ensures the canonical normalized value is persisted from the start

### `artifacts/api-server/src/routes/providers.listModels.test.ts`
- Added import of `normalizeBaseUrl`
- Added a new `describe("normalizeBaseUrl")` block with 7 unit tests covering:
  all three suffix patterns, trailing slashes, clean URLs, combined suffix+slash,
  and non-matching partial suffixes

## Test results
All 13 tests pass (6 existing + 7 new normalization unit tests).

Replit-Task-Id: 9ab5c336-d54e-4bc3-8f01-0b7486365c4b
2026-06-16 17:53:07 +00:00
amertensreplit
2236ad179d Add DE/EN/ES multilingual support to SkillGuard (Task #49)
German is source of truth; EN/ES fully translated with no German residue.
Auto-detects browser language (fallback German), persists choice, language
switcher on all pages, localized formats/Clerk/legal. Scans store their language.

Backend (T001-T003): language column on scans, openapi+codegen, ruleCatalogI18n,
language threaded scans route -> analyzeSkill -> runStaticRule -> AI calls.
Route/AI error messages localized via expanded i18n MESSAGES + reqLang(req)
(?lang query -> Accept-Language header -> "de"). No German left in routes.

Frontend (T004-T005): react-i18next framework, LanguageSwitcher, locale-aware
format.ts, Clerk localizations. All page/component strings externalized to
de/en/es locale area files across catalog, education, scan form/report/compare,
history, dashboard, admin, legal pages.

T006 verification + review-fix follow-up (this session):
- Applied formatNumber to all visible metrics in scan-report (risk score,
  severity counts, security/privacy) and scan-compare (risk score, file count,
  diff counts); PDF/HTML export numbers formatted via Intl.NumberFormat(lng).
- Fixed leftover `@workspace/n` import alias in i18n/index.ts -> real package
  `@workspace/api-client-react` (was failing workspace typecheck).
- Verified: full `pnpm run typecheck` green; api-server tests 72/72 pass;
  curl confirms localized error responses (de/en/es) on scans route.

Deviations: AI connection-test prompts left in German intentionally (sent to
the model, not user-facing). proposeFollowUpTasks already created #52.

Replit-Task-Id: 9f137230-db11-45dc-9276-4e5cbcceff03
2026-06-13 09:05:57 +00:00
amertensreplit
4a7607d3a5 Merged changes from qt0ebghx/main
Replit-Task-Id: e786be21-972b-4d23-bbe7-9eb4ae617f7b
2026-06-11 05:23:53 +00:00
amertensreplit
e54b0528be Preview a checked file's content directly in the report
Task #41: Let users click a non-binary file in the "Geprüfte Dateien"
tree to view its stored text content, without re-downloading the archive.

Changes:
- lib/api-spec/openapi.yaml: added optional nullable `content` field to the
  ScanFile schema so the scan-detail response can carry stored file text.
- Regenerated API codegen (api-client-react + api-zod via orval) and ran
  the libs typecheck.
- artifacts/api-server/src/routes/scans.ts: serializeFile now returns
  `content` alongside the existing `hasContent`. Only used by the scan
  detail endpoint, so list responses are unaffected.
- artifacts/skillguard/src/pages/scan-report.tsx (FilesTree): non-binary
  file names are now clickable buttons that open a Dialog showing the
  stored content in a monospace, scrollable panel (ScrollArea). Binary
  files (hasContent === false) render as plain text and show a clear
  "Keine Vorschau verfügbar (Binärdatei)." message in the dialog guard.

Verification:
- tsc -b passes for both skillguard and api-server.
- relation + compare route tests pass (8 tests).
- Confirmed via API that the detail endpoint exposes content for a
  text-source scan (hasContent true) and null for binary .skill archives.

No new persistence: content is read from existing scan file data.

Replit-Task-Id: 931befbc-6ca3-4d15-b422-ac8e9f061f9f
2026-06-11 05:07:29 +00:00
amertensreplit
e90a51af50 Unpack .skill files & show folder tree with copyable hashes
Original task (#28): Treat uploaded .skill files like ZIP archives, extract
their contents into "Geprüfte Dateien", render the folder structure as a tree,
and make the full SHA-256 copyable.

Backend (artifacts/api-server):
- skillParser.ts: added looksLikeZip() (sniffs PK\x03\x04 signature) and a new
  parseUpload(filename, buffer) entry point. It extracts when the buffer has a
  ZIP signature OR a .zip/.skill extension; falls back cleanly to single-file
  handling when the archive is invalid/empty. Real archives (signature present)
  still surface limit/corruption errors, so existing ZIP protections
  (file count, total/per-file size, skipped system dirs) stay in force.
- routes/scans.ts: both "zip" and "file" sources now route through parseUpload,
  so a .skill works whether uploaded via the ZIP area or the single-file area.
- skillParser.test.ts: added a parseUpload describe block (extraction, signature
  detection, .zip via single-file path, invalid-.skill fallback, plain file,
  limit propagation, empty-archive fallback). 32 parser tests / 66 total pass.

Frontend (artifacts/skillguard):
- scan-report.tsx: replaced the flat files table with a FilesTree component that
  derives a folder tree from file paths (folders as nodes, files nested/indented)
  and adds a copy-to-clipboard button for the full SHA-256 next to the short hash.
  Type/language/size/binary indicators preserved.
- scan-form.tsx: ZIP area now accepts .skill, with updated label/hint.

Note: skillguard typecheck initially failed with phantom "property does not
exist on ScanDetail" errors due to stale api-client-react dist declarations
(project reference). Ran `pnpm run typecheck:libs` to rebuild composite libs;
typecheck then passes. Documented in .agents/memory.

Verified end-to-end: a .skill upload extracted into 4 files; an invalid .skill
fell back to a single file. Test scans cleaned up afterwards.

Rebase resolution:
- Conflict in scan-report.tsx imports only: main added `ShieldAlert`, this task
  added `Folder, File as FileIcon, Copy, Check`. Merged both into one import.

Replit-Task-Id: 72b2cacc-11eb-412b-82fd-7d5d0cf8f2a4
2026-06-11 01:27:21 +00:00
amertensreplit
9415e184dc Add on-demand AI description generation for existing scans
Task #24: Older scans created before description generation existed showed an
empty "Was macht dieser Skill?" section. Users can now trigger description
generation for any existing scan from the report.

Changes:
- OpenAPI: added POST /scans/{id}/description (operationId generateScanDescription)
  returning ScanDetail (200), ApiError (404 not found, 422 cannot generate).
  Regenerated api-zod and api-client-react via codegen.
- api-server (routes/scans.ts): new route loads the scan, its stored files, the
  enabled provider and prompts, reconstructs ParsedFile[] from scan_files
  (binary files -> empty content/isBinary), calls existing
  generateSkillDescription(), persists description and returns full ScanDetail.
  Clean 422 errors when no provider / no token / generation yields nothing; the
  scan is never mutated on failure.
- skillguard (scan-report.tsx): the description card now always renders; when no
  description exists it shows a "Beschreibung erzeugen" button wired to the new
  mutation, with loading state, toast feedback, and query cache update on success.

Incidental fix: the dev/test database was missing the `scans.description` column
(schema drift from the earlier description task). Ran drizzle-kit push to sync;
this unblocked 5 previously failing api-server tests. All 59 tests now pass and
full typecheck is green.

Rebase: one conflict in scan-report.tsx import line — main added the `ShieldAlert`
icon (new KI-disclaimer Alert), this branch added `Loader2`. Resolved by keeping
both icons; the rest of the file (disclaimer Alert + new description card) merged
cleanly. No semantic divergence.

Replit-Task-Id: 0610af4f-aa62-434e-abcd-d742081b6459
2026-06-11 01:25:35 +00:00
amertensreplit
2f7e58670a Add automated tests for model discovery (POST /providers/list-models)
Task #25: the model-discovery capability (list available models, used by the
guided provider setup) had no automated coverage. Added a new vitest suite that
exercises the endpoint end-to-end against the in-process Express app.

New file:
- artifacts/api-server/src/routes/providers.listModels.test.ts

Coverage (6 tests, all passing):
- ok=false + clear German message when no token (empty token, no providerId),
  and the upstream provider is never called.
- Falls back to the stored provider token when providerId is given and apiToken
  is empty (inserts a real provider row, asserts the Bearer header carries the
  stored token, cleans up afterward).
- Normalizes the OpenAI-compatible response (data[].id) into a deduped, sorted
  model list; drops non-string ids.
- Anthropic path: GET /models with x-api-key + anthropic-version headers (no
  Authorization), reads models[] with id/name fallback, dedupes.
- Upstream failure returns ok=false (HTTP 200, not 500), empty models, and the
  token is redacted from the message ([REDACTED], never the raw token).
- fetch throwing (network error) returns ok=false without leaking the token.

Implementation note: the suite runs the app in-process and the test client also
uses fetch, so global fetch is mocked with a passthrough — requests to the test
server's baseUrl delegate to the captured real fetch; only upstream provider URLs
are synthesized. Spy assertions filter out the localhost passthrough call.
Saved this non-obvious testing lesson to memory.

Deviation / note: pre-existing failures in relation.test.ts and compare.test.ts
are unrelated to this task — the dev database's scans table is missing the
fingerprint/relation/similarity/compared_scan_id columns (schema drift; needs a
drizzle-kit push). Out of scope for this task; proposed as a follow-up.

Replit-Task-Id: 7e8a3db2-0da7-40d9-b74d-132779a44d39
2026-06-10 21:20:17 +00:00
amertensreplit
2e9a00f182 KI-generierte Skill-Beschreibung im Bericht
Adds an AI-generated, factual German description ("Was macht dieser Skill?")
to scans and shows it in the report.

Changes:
- DB: new nullable `description` column on scansTable (lib/db schema; pushed via drizzle-kit).
- AI: new `generateSkillDescription()` in aiAnalysis.ts — reuses provider selection,
  token redaction, system prompt and JSON extraction; expects {"description": "..."},
  returns null and never throws on failure.
- Engine: scanEngine now generates the description independently of the AI findings
  rules — only a provider+token are required, so it works even when AI findings rules
  are disabled. Description failures do not break the scan. EngineResult gains
  aiDescription. (Provider/token error precedence unchanged for findings.)
- Prompt: new admin-editable "description" prompt (Beschreibungs-Anweisung) seeded via
  onConflictDoNothing, consistent with system/analysis prompts.
- Persist/serialize: description written on scan insert and returned in
  serializeScan (list + detail responses).
- API spec: added nullable `description` to the Scan schema in openapi.yaml; regenerated
  zod + react-query clients via orval codegen.
- Report UI: new "Was macht dieser Skill?" card in the report header (hidden when empty)
  and a matching section in the PDF/print export.

Notes / deviations:
- Old scans are not backfilled (per task scope); their description stays null and the
  section is hidden.
- Description is requested as JSON ({"description": ...}) to stay compatible with the
  existing "JSON only" system prompt.
- Verified: full typecheck passes, both workflows run, new prompt seeded, scans API
  returns description.

Replit-Task-Id: 40c4457b-54d1-4283-a336-478620c3afa8
2026-06-10 21:13:51 +00:00
amertensreplit
f44c3ed247 Guided AI provider setup with model discovery
Task: Replace free-text model entry in Admin → Providers with a guided
flow (Name → API type → API endpoint → API token → Test connection) that
auto-discovers available models after a successful connection test and
presents them in a Select positioned right after the API endpoint field.

Model-independent connection test (key fix):
- The setup connection test no longer requires a model, removing the
  chicken-and-egg where discovery could never run. test-connection's model
  is now optional: when a model is supplied it does a full chat round-trip;
  when omitted it verifies credentials via the provider's models endpoint and
  reports how many models are available. The form sends no model on the
  initial test, so a successful test now reliably triggers discovery.

Backend:
- aiAnalysis.ts: added listProviderModels(provider) — GETs {baseUrl}/models
  using Bearer auth for openai/custom and x-api-key + anthropic-version for
  anthropic. Normalizes data[].id (falls back to models[].id/.name),
  dedupes + sorts, and redacts secrets in error messages via the existing
  redactSecrets helper.
- providers.ts: added POST /providers/list-models accepting ad-hoc config
  (apiType, baseUrl, optional apiToken, optional providerId). Falls back to
  the stored token by providerId when token omitted; returns { ok, models,
  message } and never leaks the token.

API contract:
- openapi.yaml: added /providers/list-models path, ProviderListModelsInput
  and ProviderModelsResult schemas. Regenerated zod + react-query client via
  the api-spec codegen workflow (orval).

Admin UI (admin.tsx):
- New ModelField component renders a loading state, a Select when models are
  discovered, or a manual free-text input fallback (with hint) when discovery
  returns nothing — so saving always works for custom endpoints.
- Field order follows the guided flow: Name → API type → API endpoint →
  API token → Test connection, with the model selector appearing after the
  token once discovery succeeds. A successful test automatically triggers
  discovery; editing endpoint or token resets discovery state.

Verified: workspace typecheck passes, api-server tests 59/59 pass, live curl
of the new endpoint returns graceful errors without leaking the token.

Replit-Task-Id: 8d300a47-0b45-4677-9e9e-aa041bf03e98
2026-06-10 21:13:35 +00:00
amertensreplit
769c78aaef Add unit tests for the skill upload parser
Task #18: Automatically test that uploaded skill files are read correctly.

The skill parser (artifacts/api-server/src/lib/skillParser.ts) had no automated
tests. A regression there could silently mis-read uploads. Added a new Vitest
suite covering the parsing/classification logic (NOT the ZIP size/safety limits,
which are tracked by a separate task).

New file: artifacts/api-server/src/lib/skillParser.test.ts

Coverage:
- parseSingleFile: kind/language/hash/size/isBinary for .md, .sh, .py, .json,
  .txt, unknown ext, and a binary blob; path normalisation (dir strip,
  backslashes); case-insensitive SKILL.md.
- parseText: wraps pasted text as markdown SKILL.md; byte-length sizing for
  multi-byte content.
- parseZip (in-memory ZIP via fflate.zipSync): correct classification, nested
  path preservation, __MACOSX/.git/node_modules skipping, dir/empty entry
  skipping, binary-vs-text handling, stable hashing.
- deriveScanName: H1 from SKILL.md, name: front-matter fallback, quote
  stripping, H1 preferred over front-matter, top-dir fallback, provided
  fallback, 120-char truncation.

Verification: `pnpm --filter @workspace/api-server run test` → 59 passed
(24 new). Typecheck of the new test file is clean; pre-existing typecheck
errors in src/routes/scans.ts are unrelated and out of scope.

Replit-Task-Id: 06f18e6a-2d8d-4bf2-b2ae-29675f04c059
2026-06-10 19:53:15 +00:00
amertensreplit
532f42117f Add automated tests for skill version detection
Task #13: lock in the fingerprint/relation logic behind SkillGuard's
identical/modified/new version detection with automated tests.

What was added
- Set up Vitest in artifacts/api-server (dev dep + `test` script + vitest.config.ts
  using the "workspace" resolve condition so @workspace/* resolve to source).
- Unit tests (no DB):
  - src/lib/skillFingerprint.test.ts — hashText/hashBytes stability & agreement,
    computeFingerprint stable + order-independent + sensitive to content/path/add/remove,
    jaccard overlap/symmetry/empty handling.
  - src/lib/lineDiff.test.ts — lineSimilarity ratios (identical, single-edit, disjoint,
    symmetric, CRLF), lineDiff context/add/remove with line numbers and the 2000-line cap.
- DB-backed tests (use the existing DATABASE_URL):
  - src/routes/relation.test.ts — computeRelation: identical content under a different
    name -> "identical" + check-counter (countFingerprint) increments; one-line edit to a
    single-file skill -> "modified" with sensible similarity; unrelated skill -> "new".
    Also direct computeContentSimilarity cases. Fixtures use randomized content to avoid
    collisions with shared dev data and are cleaned up afterEach.
  - src/routes/compare.test.ts — e2e GET /api/scans/:id/compare/:otherId via a live
    server: asserts unchanged/modified/added/removed statuses, sorted file order, the
    line diff for the modified file, null diffs elsewhere, and 404 for missing scans.

Production code change
- Exported computeRelation, computeContentSimilarity, countFingerprint from
  src/routes/scans.ts so the relation logic can be unit-tested. No behavior change.

Verification
- `pnpm --filter @workspace/api-server run test` -> 34 tests, 4 files, all pass.
- `pnpm --filter @workspace/api-server run typecheck` passes (rebuilt stale lib/db
  declarations via `pnpm run typecheck:libs`).
- Production build unaffected: esbuild only bundles from src/index.ts, so *.test.ts
  files are not included.

Replit-Task-Id: e9ae5e24-1480-4a09-8436-1718c535573a
2026-06-10 19:48:10 +00:00
amertensreplit
54323706b5 Add skill version timeline (fingerprint lineage)
Task #14: show a full version timeline for each skill family, not just the
single most-similar prior scan.

What changed:
- OpenAPI spec (lib/api-spec/openapi.yaml): new GET /scans/{id}/lineage
  (operationId getScanLineage) returning an array of ScanLineageEntry
  (id, name, verdict, riskScore, relation, similarity, comparedScanId,
  fingerprint, createdAt). Regenerated api-zod + api-client-react via codegen.
- API (artifacts/api-server/src/routes/scans.ts): new lineage endpoint.
  Builds an undirected graph over all scans linked by the comparedScanId chain
  AND identical (non-empty) fingerprints, then BFS-walks the connected
  component containing the requested scan and returns it newest-first. Works
  purely from existing data, no re-scanning. 404 for unknown ids.
- UI (artifacts/skillguard/src/pages/scan-report.tsx): new VersionTimeline
  card rendering the family as a vertical timeline; each entry shows verdict,
  relation badge, similarity, risk score and date. The viewed scan is marked
  "Aktuell angezeigt"; every other entry links to the existing comparison view
  /vergleich/{viewedId}/{entryId}. Card hidden when the family has <=1 member.

Notes:
- Lineage = connected component, so any member returns the full family.
- Verified end-to-end locally (created new/modified/identical chain, checked
  lineage ordering + 404, confirmed timeline + compare links in the UI),
  then deleted the test scans.

Replit-Task-Id: c7f87ce6-59d8-4396-b16b-f20846f42f0b
2026-06-10 19:47:39 +00:00
amertensreplit
ba9788a93c Add Skill-Fingerprint database & report comparison
Each scan gets a deterministic overall fingerprint (SHA-256 over sorted
path+fileHash pairs) plus per-file SHA-256 hashes and stored text content
(binary: hash+size only). On upload the skill is always re-scanned and
classified vs prior scans as new / identical / modified, with a per-fingerprint
check counter, a "most similar known skill" link, and a file-level diff view.

Deviations from the plan:
- Relation matching keys off shared file *paths* (Jaccard over paths, tie-break
  on hashes), not hash-Jaccard alone, which is always 0 for single-file edits
  (text paste = one SKILL.md) and would mis-class every edited single-file skill
  as "new". Similarity is content-aware: identical files = 1.0, changed text
  files use line-level LCS ratio, added/removed/changed-binary = 0.
- parseText no longer uses the display name as the file path (fixed "SKILL.md")
  so identical pastes with different names are "identical", not "modified".

Backend: skillFingerprint.ts, lineDiff.ts (+lineSimilarity), skillParser.ts
(per-file hash+isBinary), routes/scans.ts (computeRelation, content similarity,
checkCount, comparedScan, GET /scans/:id/compare/:otherId). DB: scans
fingerprint/relation/similarity/comparedScanId (+index), scan_files hash/content.
API spec + orval codegen regenerated. UI: fingerprint card + compare link on
report, relation badges in history, new /vergleich/:id/:otherId page with
side-by-side summaries and expandable line diff. German UI, no emojis.

Verified end-to-end against the running API and screenshotted both UI pages;
test data cleaned up afterward.

Code-review fix: relation classification no longer relies on path-Jaccard
(every text paste shares path SKILL.md, so unrelated pastes were falsely
linked as "modified"). computeRelation now selects the candidate by
content-aware similarity and only returns "modified" when similarity >= 40
or a file is byte-identical; otherwise "new". Updated OpenAPI similarity
description; removed now-unused jaccard import.

Replit-Task-Id: 79a8e472-6635-493c-8995-3233ba7df75c
2026-06-10 19:34:46 +00:00
amertensreplit
543fd96afd Verbindungscheck beim Provider-Einrichten (Task #10)
Add an inline "Verbindung testen" button to the Neuer/Bearbeiten provider
dialogs so users can test a connection with the currently entered values
before saving.

Backend:
- New endpoint POST /providers/test-connection that accepts an ad-hoc provider
  config (apiType, baseUrl, model, optional apiToken, optional providerId) in
  the request body and runs a one-shot test via the existing callProvider
  logic. When apiToken is empty and providerId is given, it falls back to the
  stored token of that provider (edit case). Returns { ok, message }; the token
  is never returned or leaked (existing redactSecrets still applies to errors).
- Defined ProviderTestConnectionInput schema + path in openapi.yaml and ran
  codegen for Zod schemas and the React client.

Frontend (artifacts/skillguard/src/pages/admin.tsx):
- Add dialog: "Verbindung testen" button (disabled until Base URL + Token set
  or while testing) with loading spinner and an inline green success / red
  error result box. Result resets when the dialog closes.
- Edit dialog: same inline test; empty token field falls back to the stored
  token via providerId. Result resets on open/close.
- The existing per-card "Verbindung testen" button is unchanged.

Verification: typecheck passes for api-server and skillguard; curl tested the
new endpoint for success-path (fetch error surfaced), empty-token, and invalid
body (400) cases. Token not present in any response.

Deviations: none.
Replit-Task-Id: 4f77293f-468c-496a-ab05-1f10e7bf8137
2026-06-10 18:54:56 +00:00
Replit Agent
434ec07885 Add live progress updates and detailed scan checkpoints to scan results
Introduce streaming endpoint for NDJSON scan progress, incorporate scan checkpoints into scan details, and update UI components to display this new information.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 0d01f99a-ea6a-447d-82fd-311715434a39
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 2852b526-3bf8-4a93-a62a-a50e26291074
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/e32d2b99-1721-47dd-833c-98b372f48008/0d01f99a-ea6a-447d-82fd-311715434a39/8MCgDZm
Replit-Helium-Checkpoint-Created: true
2026-06-10 18:53:17 +00:00
amertensreplit
9f7b67972f Task #2: Skill mit konfigurierter KI tatsächlich semantisch analysieren
Verified the AI analysis end-to-end with a real provider and fixed two gaps
found during the live run.

Findings & fixes:
- gpt-5 series (Replit AI Integrations modelfarm default) rejected the hardcoded
  `temperature: 0.1` with HTTP 400, silently disabling AI analysis. Removed the
  temperature param from the OpenAI-compatible request for broad model
  compatibility (aiAnalysis.ts).
- Per-rule AI config (enable/disable/severity) was only a global on/off gate and
  AI findings weren't mapped to the AI rule IDs, so individual rule severity was
  ignored. runAiAnalysis now receives the enabled AI rules, instructs the model
  to classify each finding into one of those ruleIds, drops findings for
  disabled rules, and overrides severity/axis with the configured values
  (aiAnalysis.ts + scanEngine.ts).

End-to-end verification (Replit OpenAI integration, gpt-5-mini provider):
- "KI-Analyse aktivieren" produces AI findings mapped to AI-PROMPT-INJECTION,
  AI-MALICIOUS-INTENT, AI-DATA-PRIVACY.
- Disabling AI-MALICIOUS-INTENT removed its finding; setting AI-PROMPT-INJECTION
  to critical was reflected in the result.
- Wrong baseUrl and invalid token (real OpenAI endpoint) produce understandable
  aiError messages with no token leak.

Side effects / notes:
- Set up the Replit OpenAI AI Integration (env vars) and created one enabled
  provider row ("Replit OpenAI") so AI analysis works out of the box. Each
  AI-enabled scan bills the user's Replit credits.
- Test scans created during verification were deleted.
- artifacts/api-server typecheck passes.

Replit-Task-Id: 7321caa4-5079-4db7-8ed2-4ccaa74fa577
2026-06-10 13:56:15 +00:00
Replit Agent
8eae5f4fe6 SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.

This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
  from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
  and the full scan flow works end-to-end (malicious sample -> verdict block).

Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
  OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
  returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing rewritten to use fflate's streaming Unzip: caps (max files, total
  and per-file uncompressed bytes) are enforced DURING decompression. Oversized
  entries are skipped via the header size before inflation; chunked pushing with
  per-chunk size checks aborts early, so a zip bomb cannot be fully inflated into
  memory. Verified: 120MB->123KB bomb rejected with the service staying healthy;
  normal archives still parse correctly.

Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 15:05:17 +00:00
Replit Agent
a70b0d580a SkillGuard: complete frontend wiring and harden backend
Original task: build "SkillGuard", a German web app to audit agent skills on
two axes (IT-Sicherheit, Datenschutz) with static rule engine + Replit-independent
AI analysis configured via an admin backend.

This session:
- Fixed frontend TS errors: lucide-react name collisions (Badge from ui, Activity
  from lucide), widened apiType to AiProviderApiType, added queryKey to useGetScan.
- Verified all pages render in German (Dashboard, Prüfen, Bericht, Verlauf, Admin)
  and the full scan flow works end-to-end (malicious sample -> verdict block).

Code-review-driven hardening:
- POST /api/scans now returns the full ScanDetail (files + findings) to match the
  OpenAPI contract, instead of only the summary.
- AI provider error bodies are redacted (token, Bearer, sk- patterns) before being
  returned/persisted, and provider fetches now have a 60s timeout.
- ZIP parsing now enforces limits (max files, total + per-file size) to mitigate
  zip-bomb DoS.

Updated replit.md (project overview, decisions, gotchas) and added a memory note
on lucide-react icon name collisions.
2026-06-08 14:59:17 +00:00
Replit Agent
2246770e5b Initial commit 2026-05-28 23:37:31 +00:00