Repository navigation
Stream findings live, and notify the triggering user via browser push #150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
741aada
eb6f474
c032dbf
1aeea98
6a7d613
ddb43fe
ef27dac
7e1b277
441693f
9007481
73c0e81
e9d6998
f670c59
afcbe3b
0d027e5
3d1fbe2
1b86b8e
dadaad4
f62d3a4
75c9c47
d4c61a5
bd8ce5c
0f7cc1c
38034ad
9d195aa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| -- migrate:up | ||
|
|
||
| -- One row per browser/device a user has granted push permission on (a user | ||
| -- has no single device, so this is 1:many, not a column on users). | ||
| CREATE TABLE IF NOT EXISTS push_subscriptions ( | ||
| id BIGSERIAL PRIMARY KEY, | ||
| user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, | ||
| endpoint TEXT NOT NULL UNIQUE, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: warning Ensure Suggestions:
|
||
| p256dh TEXT NOT NULL, | ||
| auth_key TEXT NOT NULL, | ||
| user_agent TEXT, | ||
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | ||
| last_used_at TIMESTAMPTZ | ||
| ); | ||
|
|
||
| CREATE INDEX IF NOT EXISTS idx_push_subscriptions_user_id ON push_subscriptions(user_id); | ||
|
|
||
| -- A missing row means "both notification types enabled" (the agreed default), | ||
| -- so there is no backfill to do for existing users. | ||
| CREATE TABLE IF NOT EXISTS user_notification_preferences ( | ||
| user_id BIGINT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, | ||
| notify_first_finding BOOLEAN NOT NULL DEFAULT TRUE, | ||
| notify_completion BOOLEAN NOT NULL DEFAULT TRUE, | ||
| updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | ||
| ); | ||
|
|
||
| -- Claim-once marker so concurrent batch-completion goroutines (see | ||
| -- internal/batch/task_queue.go's worker pool) can't double-send the | ||
| -- "first finding available" push for the same review. | ||
| ALTER TABLE reviews ADD COLUMN IF NOT EXISTS first_finding_notified_at TIMESTAMPTZ; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: warning The comment claims this column prevents double-sends, but the schema alone cannot enforce that. Two concurrent senders can both read NULL, send, then update. All senders must claim the review with an atomic conditional update. Suggestions:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: warning This column only guarantees a single send if workers claim it with an atomic conditional UPDATE. A read-then-write check still allows two goroutines to send before either writes the marker. Suggestions:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: warning A nullable timestamp alone does not enforce claim-once. Concurrent workers can both see NULL and double-send. The send path must atomically claim the review via a conditional UPDATE and check rows affected. Suggestions:
|
||
|
|
||
| -- migrate:down | ||
| ALTER TABLE reviews DROP COLUMN IF EXISTS first_finding_notified_at; | ||
| DROP TABLE IF EXISTS user_notification_preferences; | ||
| DROP TABLE IF EXISTS push_subscriptions; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| -- migrate:up | ||
|
|
||
| -- Optional per-org soft preferences for review concurrency/timeout. NULL | ||
| -- means "use the system default" (resolved at review-trigger time, see | ||
| -- internal/aiselection's review-performance resolver) - these are not hard | ||
| -- caps the org can set arbitrarily high; the resolved value is always | ||
| -- clamped to the system-wide ceiling in system_settings (row | ||
| -- 'ai_review_performance'), set by a super admin / self-hosted owner. | ||
| ALTER TABLE org_review_ai_settings | ||
| ADD COLUMN IF NOT EXISTS preferred_max_concurrent_batches INTEGER, | ||
| ADD COLUMN IF NOT EXISTS preferred_review_timeout_seconds INTEGER; | ||
|
|
||
| ALTER TABLE org_review_ai_settings | ||
| ADD CONSTRAINT org_review_ai_settings_preferred_max_concurrent_batches_check | ||
| CHECK (preferred_max_concurrent_batches IS NULL OR preferred_max_concurrent_batches > 0), | ||
| ADD CONSTRAINT org_review_ai_settings_preferred_review_timeout_seconds_check | ||
| CHECK (preferred_review_timeout_seconds IS NULL OR preferred_review_timeout_seconds > 0); | ||
|
|
||
| -- migrate:down | ||
| ALTER TABLE org_review_ai_settings | ||
| DROP CONSTRAINT IF EXISTS org_review_ai_settings_preferred_max_concurrent_batches_check, | ||
| DROP CONSTRAINT IF EXISTS org_review_ai_settings_preferred_review_timeout_seconds_check; | ||
|
|
||
| ALTER TABLE org_review_ai_settings | ||
| DROP COLUMN IF EXISTS preferred_max_concurrent_batches, | ||
| DROP COLUMN IF EXISTS preferred_review_timeout_seconds; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| -- migrate:up | ||
|
|
||
| -- Commit message, captured best-effort from whichever provider API call already | ||
| -- fetched the commit SHA (GitHub/GitLab PR-commits endpoints both return it). Null | ||
| -- for older rows and for sources that never had one available (e.g. local staged | ||
| -- commits recorded before a message was ever sent). | ||
| ALTER TABLE review_commits ADD COLUMN IF NOT EXISTS message TEXT; | ||
|
|
||
| -- migrate:down | ||
| ALTER TABLE review_commits DROP COLUMN IF EXISTS message; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| -- migrate:up | ||
|
|
||
| -- riskscore.Prior() changed: every structurally-unbounded count/sum feature | ||
| -- (br_sum, br_high_count, critical_count, warning_count, security_count, and | ||
| -- the 9 taxonomy counts) is now log1p-compressed before scoring, with weights | ||
| -- retuned to match. The old br_sum weight assumed a 0-100 range, but br_sum is | ||
| -- actually a raw, unbounded SUM(combined) across every blast-radius hunk in a | ||
| -- diff (real production values ran 1700-4840), which saturated the sigmoid | ||
| -- past the point float64 can represent 1+exp(-z) as anything but exactly 1.0 | ||
| -- - confirmed in production, where an org's entire risk-score history was | ||
| -- pinned to exactly 1.0 for every review. See internal/riskscore/model.go's | ||
| -- Prior() doc comment and internal/riskscore/saturation_test.go for the full | ||
| -- investigation and regression coverage. | ||
| -- | ||
| -- EnsureSeedModel (internal/riskscore/store.go) only inserts the shared seed | ||
| -- row (org_id IS NULL, update_kind = 'seed') if one doesn't already exist - it | ||
| -- never updates one in place. At the time this migration was written, that | ||
| -- table held exactly one row in production, and no org had ever trained a | ||
| -- custom model (training_sample_count was 0 everywhere) - so there is no | ||
| -- fitted-model data to preserve or migrate. Deleting the stale row lets | ||
| -- EnsureSeedModel's existing idempotent bootstrap logic reinsert it from the | ||
| -- corrected Prior() the next time any review is scored, with no new code path | ||
| -- needed. | ||
| DELETE FROM risk_model_versions WHERE org_id IS NULL AND update_kind = 'seed'; | ||
|
|
||
| -- migrate:down | ||
|
|
||
| -- Data-only reset; the prior seed weights are not reconstructed here (see git | ||
| -- history for the exact values this migration replaced). EnsureSeedModel will | ||
| -- reinsert a seed row from whatever Prior() the running code defines, same as | ||
| -- after any fresh deploy - there is no way to "undo" this short of also | ||
| -- reverting the Prior()/ExtractFeatures code changes. | ||
| DELETE FROM risk_model_versions WHERE org_id IS NULL AND update_kind = 'seed'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| \restrict dbmate | ||
|
|
||
| -- Dumped from database version 18.6 (Debian 18.6-1.pgdg13+2) | ||
| -- Dumped by pg_dump version 18.6 (Debian 18.6-1.pgdg13+2) | ||
| -- Dumped by pg_dump version 18.6 (Ubuntu 18.6-1.pgdg24.04+2) | ||
|
|
||
| SET statement_timeout = 0; | ||
| SET lock_timeout = 0; | ||
|
|
@@ -2066,7 +2066,11 @@ CREATE TABLE public.org_review_ai_settings ( | |
| helper_mode character varying(32) DEFAULT 'concise_then_expand'::character varying NOT NULL, | ||
| created_at timestamp with time zone DEFAULT now() NOT NULL, | ||
| updated_at timestamp with time zone DEFAULT now() NOT NULL, | ||
| CONSTRAINT org_review_ai_settings_helper_mode_check CHECK (((helper_mode)::text = ANY (ARRAY[('concise_then_expand'::character varying)::text, ('polish_only'::character varying)::text]))) | ||
| preferred_max_concurrent_batches integer, | ||
| preferred_review_timeout_seconds integer, | ||
| CONSTRAINT org_review_ai_settings_helper_mode_check CHECK (((helper_mode)::text = ANY (ARRAY[('concise_then_expand'::character varying)::text, ('polish_only'::character varying)::text]))), | ||
| CONSTRAINT org_review_ai_settings_preferred_max_concurrent_batches_check CHECK (((preferred_max_concurrent_batches IS NULL) OR (preferred_max_concurrent_batches > 0))), | ||
| CONSTRAINT org_review_ai_settings_preferred_review_timeout_seconds_check CHECK (((preferred_review_timeout_seconds IS NULL) OR (preferred_review_timeout_seconds > 0))) | ||
| ); | ||
|
|
||
|
|
||
|
|
@@ -2467,6 +2471,41 @@ CREATE SEQUENCE public.pull_requests_id_seq | |
| ALTER SEQUENCE public.pull_requests_id_seq OWNED BY public.pull_requests.id; | ||
|
|
||
|
|
||
| -- | ||
| -- Name: push_subscriptions; Type: TABLE; Schema: public; Owner: - | ||
| -- | ||
|
|
||
| CREATE TABLE public.push_subscriptions ( | ||
| id bigint NOT NULL, | ||
| user_id bigint NOT NULL, | ||
| endpoint text NOT NULL, | ||
| p256dh text NOT NULL, | ||
| auth_key text NOT NULL, | ||
| user_agent text, | ||
| created_at timestamp with time zone DEFAULT now() NOT NULL, | ||
| last_used_at timestamp with time zone | ||
| ); | ||
|
|
||
|
|
||
| -- | ||
| -- Name: push_subscriptions_id_seq; Type: SEQUENCE; Schema: public; Owner: - | ||
| -- | ||
|
|
||
| CREATE SEQUENCE public.push_subscriptions_id_seq | ||
| START WITH 1 | ||
| INCREMENT BY 1 | ||
| NO MINVALUE | ||
| NO MAXVALUE | ||
| CACHE 1; | ||
|
|
||
|
|
||
| -- | ||
| -- Name: push_subscriptions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - | ||
| -- | ||
|
|
||
| ALTER SEQUENCE public.push_subscriptions_id_seq OWNED BY public.push_subscriptions.id; | ||
|
|
||
|
|
||
| -- | ||
| -- Name: quota_batch_settlements; Type: TABLE; Schema: public; Owner: - | ||
| -- | ||
|
|
@@ -2772,6 +2811,7 @@ CREATE TABLE public.review_commits ( | |
| ref character varying(160) NOT NULL, | ||
| ref_type text DEFAULT 'commit'::text NOT NULL, | ||
| created_at timestamp with time zone DEFAULT now() NOT NULL, | ||
| message text, | ||
| CONSTRAINT review_commits_ref_type_check CHECK ((ref_type = ANY (ARRAY['commit'::text, 'range'::text]))) | ||
| ); | ||
|
|
||
|
|
@@ -3032,6 +3072,7 @@ CREATE TABLE public.reviews ( | |
| author_username text, | ||
| friendly_name text, | ||
| pull_request_id bigint, | ||
| first_finding_notified_at timestamp with time zone, | ||
| CONSTRAINT reviews_status_check CHECK (((status)::text = ANY (ARRAY[('created'::character varying)::text, ('in_progress'::character varying)::text, ('completed'::character varying)::text, ('failed'::character varying)::text]))) | ||
| ); | ||
|
|
||
|
|
@@ -4023,6 +4064,18 @@ CREATE SEQUENCE public.user_management_audit_id_seq | |
| ALTER SEQUENCE public.user_management_audit_id_seq OWNED BY public.user_management_audit.id; | ||
|
|
||
|
|
||
| -- | ||
| -- Name: user_notification_preferences; Type: TABLE; Schema: public; Owner: - | ||
| -- | ||
|
|
||
| CREATE TABLE public.user_notification_preferences ( | ||
| user_id bigint NOT NULL, | ||
| notify_first_finding boolean DEFAULT true NOT NULL, | ||
| notify_completion boolean DEFAULT true NOT NULL, | ||
| updated_at timestamp with time zone DEFAULT now() NOT NULL | ||
| ); | ||
|
|
||
|
|
||
| -- | ||
| -- Name: user_role_history; Type: TABLE; Schema: public; Owner: - | ||
| -- | ||
|
|
@@ -4095,7 +4148,8 @@ CREATE TABLE public.users ( | |
| onboarding_api_key text, | ||
| last_cli_used_at timestamp with time zone, | ||
| default_org_id bigint, | ||
| review_level integer DEFAULT 1 NOT NULL | ||
| review_level integer DEFAULT 1 NOT NULL, | ||
| time_zone text | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: info The Suggestions:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: critical The Suggestions:
|
||
| ); | ||
|
|
||
|
|
||
|
|
@@ -4484,6 +4538,13 @@ ALTER TABLE ONLY public.prompt_chunks ALTER COLUMN id SET DEFAULT nextval('publi | |
| ALTER TABLE ONLY public.pull_requests ALTER COLUMN id SET DEFAULT nextval('public.pull_requests_id_seq'::regclass); | ||
|
|
||
|
|
||
| -- | ||
| -- Name: push_subscriptions id; Type: DEFAULT; Schema: public; Owner: - | ||
| -- | ||
|
|
||
| ALTER TABLE ONLY public.push_subscriptions ALTER COLUMN id SET DEFAULT nextval('public.push_subscriptions_id_seq'::regclass); | ||
|
|
||
|
|
||
| -- | ||
| -- Name: quota_batch_settlements id; Type: DEFAULT; Schema: public; Owner: - | ||
| -- | ||
|
|
@@ -5220,6 +5281,22 @@ ALTER TABLE ONLY public.pull_requests | |
| ADD CONSTRAINT pull_requests_pkey PRIMARY KEY (id); | ||
|
|
||
|
|
||
| -- | ||
| -- Name: push_subscriptions push_subscriptions_endpoint_key; Type: CONSTRAINT; Schema: public; Owner: - | ||
| -- | ||
|
|
||
| ALTER TABLE ONLY public.push_subscriptions | ||
| ADD CONSTRAINT push_subscriptions_endpoint_key UNIQUE (endpoint); | ||
|
|
||
|
|
||
| -- | ||
| -- Name: push_subscriptions push_subscriptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - | ||
| -- | ||
|
|
||
| ALTER TABLE ONLY public.push_subscriptions | ||
| ADD CONSTRAINT push_subscriptions_pkey PRIMARY KEY (id); | ||
|
|
||
|
|
||
| -- | ||
| -- Name: quota_batch_settlements quota_batch_settlements_pkey; Type: CONSTRAINT; Schema: public; Owner: - | ||
| -- | ||
|
|
@@ -5692,6 +5769,14 @@ ALTER TABLE ONLY public.user_management_audit | |
| ADD CONSTRAINT user_management_audit_pkey PRIMARY KEY (id); | ||
|
|
||
|
|
||
| -- | ||
| -- Name: user_notification_preferences user_notification_preferences_pkey; Type: CONSTRAINT; Schema: public; Owner: - | ||
| -- | ||
|
|
||
| ALTER TABLE ONLY public.user_notification_preferences | ||
| ADD CONSTRAINT user_notification_preferences_pkey PRIMARY KEY (user_id); | ||
|
|
||
|
|
||
| -- | ||
| -- Name: user_role_history user_role_history_pkey; Type: CONSTRAINT; Schema: public; Owner: - | ||
| -- | ||
|
|
@@ -7412,6 +7497,13 @@ CREATE INDEX idx_pull_requests_org_state ON public.pull_requests USING btree (or | |
| CREATE INDEX idx_pull_requests_repository_state ON public.pull_requests USING btree (repository_id, state); | ||
|
|
||
|
|
||
| -- | ||
| -- Name: idx_push_subscriptions_user_id; Type: INDEX; Schema: public; Owner: - | ||
| -- | ||
|
|
||
| CREATE INDEX idx_push_subscriptions_user_id ON public.push_subscriptions USING btree (user_id); | ||
|
|
||
|
|
||
| -- | ||
| -- Name: idx_quota_batch_settlements_org_idempotency; Type: INDEX; Schema: public; Owner: - | ||
| -- | ||
|
|
@@ -9768,6 +9860,14 @@ ALTER TABLE ONLY public.pull_requests | |
| ADD CONSTRAINT pull_requests_repository_id_fkey FOREIGN KEY (repository_id) REFERENCES public.repositories(id) ON DELETE CASCADE; | ||
|
|
||
|
|
||
| -- | ||
| -- Name: push_subscriptions push_subscriptions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | ||
| -- | ||
|
|
||
| ALTER TABLE ONLY public.push_subscriptions | ||
| ADD CONSTRAINT push_subscriptions_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; | ||
|
|
||
|
|
||
| -- | ||
| -- Name: quota_batch_settlements quota_batch_settlements_org_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | ||
| -- | ||
|
|
@@ -10256,6 +10356,14 @@ ALTER TABLE ONLY public.user_management_audit | |
| ADD CONSTRAINT user_management_audit_target_user_id_fkey FOREIGN KEY (target_user_id) REFERENCES public.users(id) ON DELETE CASCADE; | ||
|
|
||
|
|
||
| -- | ||
| -- Name: user_notification_preferences user_notification_preferences_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | ||
| -- | ||
|
|
||
| ALTER TABLE ONLY public.user_notification_preferences | ||
| ADD CONSTRAINT user_notification_preferences_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE; | ||
|
|
||
|
|
||
| -- | ||
| -- Name: user_role_history user_role_history_changed_by_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - | ||
| -- | ||
|
|
@@ -10490,7 +10598,12 @@ INSERT INTO public.schema_migrations (version) VALUES | |
| ('20261025120100'), | ||
| ('20261025130000'), | ||
| ('20261029104047'), | ||
| ('20261029120000'), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: critical The migration version Suggestions:
|
||
| ('20261029134500'), | ||
| ('20261029150000'), | ||
| ('20261029150100'), | ||
| ('20261029150200'); | ||
| ('20261029150200'), | ||
| ('20261030134721'), | ||
| ('20261031070300'), | ||
| ('20261031120000'), | ||
| ('20261031140000'); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Severity: warning
make db-dump calls scripts/dbmate-dump.sh, which is not added in this diff. If the script is not already committed, the new target is broken.
Suggestions: