fix: mega audit - security, performance, error handling, type safety, dates - #84
Merged
Conversation
…nput validation - Replace Random.Shared with RandomNumberGenerator.GetInt32 for verification codes (SendCodeCommand, RequestAccountDeletionCommand) - Add [Authorize] to ConfigController - Add Strict-Transport-Security and Content-Security-Policy headers to SecurityHeadersMiddleware - Add fixed-window rate limiter (5 req/min/IP) for /api/auth/send-code and /api/auth/verify-code - Set ForwardLimit=1 on ForwardedHeadersOptions; add note about KnownProxies per-deployment config - Add note that IMemoryCache rate limiting is not shared across replicas - Reduce ChatController multipart limit from 20MB to 10MB; add 4000-char message length guard - Add MaximumLength(2000) validation for habit Description in Create/Update validators - Add MaximumLength(500) validation for ChecklistItem.Text in Create/Update validators - Replace ex.Message with generic error in BulkDeleteHabitsCommand, BulkLogHabitsCommand, and NotificationController.TestPush Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…actoring TODOs - Replace diverged IsScheduledForDate in GamificationService with a direct delegate to HabitScheduleService.IsHabitDueOnDate (the single source of truth). The local copy was skipping modular arithmetic for Weekly/Monthly/Yearly habits. - Add TODO to NotificationController noting it bypasses MediatR and injects OrbitDbContext directly -- needs migration to Query/Command handlers. - Add TODOs to SubscriptionController.HandleWebhook for extracting event handling into dedicated MediatR commands (ActivateSubscription, Renew, Cancel). - Add TODOs to SubscriptionController Infrastructure imports flagging that StripeSettings and IGeoLocationService interfaces should move to Application layer. - Add TODO to GamificationService Portuguese translations dictionary noting hardcoded strings need a proper i18n/localization strategy. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SubscriptionController: return 500 on business logic exceptions so Stripe retries - CheckReferralCompletionCommand: log warning on push notification failure; wrap GrantCoupon in try-catch with fallback - StripeCouponRewardService: log error before throwing InvalidOperationException for missing user - CreateHabitCommand: log warning on gamification failure instead of bare catch - CreateGoalCommand: log warning on gamification failure instead of bare catch - UpdateGoalStatusCommand: log warning on gamification failure instead of bare catch - GetCalendarEventsQuery: log warning on RRULE fetch failure instead of bare catch - GoalsController.GetGoals: wrap in try-catch returning 500 on unexpected errors - Add ProcessUserChatCommandValidator (message not empty, max 4000 chars) - Add GoogleAuthCommandValidator (access token not empty) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…formats Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… leap-day fallback - HabitMetricsCalculator: use user local timezone when converting CreatedAtUtc to start date - GetHabitMetricsQuery + GamificationService: pass userTimeZone to Calculate() - CacheInvalidationHelper: widen summary cache invalidation buffer from +/-1 to +/-2 days - GeminiRoutineAnalysisService: add comment noting UTC cutoff for 60-day analysis window - Habit.cs: add comment on UTC fallback in Create() EndDate validation guard - Habit.AdvanceDueDate/CatchUpDueDate: preserve original day-of-month to prevent monthly drift - HabitScheduleService: IsMonthlyMatch clamps to last day of month; IsYearlyMatch adds Feb 29 -> Feb 28 fallback for non-leap years Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…leanup - Add CanCreateGoals paygate to IPayGateService + PayGateService (GoalsProOnly config key, defaults to Pro-only) - Gate CreateGoalCommand behind CanCreateGoals paygate check - Fix IPayGateService comment: free AI messages is 20/month not 50 - Move hardcoded Stripe product ID out of StripeCouponRewardService into StripeSettings.ProProductId Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Push Notification fixes: - 164: Add FK from PushSubscriptions.UserId to Users.Id with CASCADE delete - 165: Cap push subscriptions at 5 per user, removing oldest on subscribe - 166: Daily cleanup of SentReminder and SentSlipAlert rows older than 90 days (via AccountDeletionService) - 167: Document URL dedup hack in GoalDeadlineNotificationService with TODO for proper entity - 168: Guard against null FirebaseMessaging.DefaultInstance before sending FCM push - 169: Raise MinBucketCountForTimePeak from 2 to 3 in SlipPatternDetectionService - 170: Rename MinOccurrencesPerDay to MinTotalLogs in SlipPatternDetectionService - 171: Add SenderIdMismatch to FCM stale token removal in PushNotificationService AI Chat fixes: - 188: Sanitize habit titles in system prompt (strip control chars, truncate to 100 chars) - 189: Warn in AssignTagsTool description that it replaces all existing tags - 190: Add recursive parent-chain cycle detection in MoveHabitTool - 191: Add UserId ownership filter to BulkLogHabitsTool habit query Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
thomasluizon
added a commit
that referenced
this pull request
Jun 4, 2026
Add eight grounded markdown explainer files (streaks, frequencies, gamification, paygate, schedule-math, freezes, notifications, ai-memory) so the in-app AI answers feature questions from code-accurate content. Each file carries a 6-key YAML frontmatter and a body whose constants are copied verbatim from AppConstants, LevelDefinitions, and the streak/schedule/paygate/reminder services. Ship the bundle inside the API binary via the first EmbeddedResource glob in the repo; resources embed as Orbit.Application.Chat.Content.FeatureExplanations.<key>.md, the documented loader contract for the downstream consumer. Refs thomasluizon/orbit-ui-mobile#84 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thomasluizon
added a commit
that referenced
this pull request
Jun 4, 2026
* feat(api): auto-activate streak freeze on inactive day (#108) Add a dedicated StreakFreezeAutoActivationService BackgroundService that auto-activates a streak freeze for a Pro user who held an active streak but logged nothing on their fully-elapsed local "yesterday". - Mirrors SlipAlertSchedulerService / HabitDueDateAdvancementService: poll interval, conservative UTC pre-filter, authoritative per-user TimeZoneHelper local-yesterday guard, single SaveChanges per tick. - Pro-only (matches ActivateStreakFreezeCommand). Spends one freeze per missed day, bounded by MaxStreakFreezesAccumulated (inventory) and MaxStreakFreezesPerMonth (monthly). - Presence-based: inserting a StreakFreeze row for the missed date preserves the streak on the next on-read RecalculateAsync; no direct streak mutation. - Idempotent: new SentStreakFreezeAlert guard entity (unique UserId+FrozenDate) plus the existing StreakFreeze unique index, both re-checked before spending. - Notifies via in-app Notification + push (IPushNotificationService); localized copy via LocaleHelper, mirroring GoalDeadlineNotificationService. - EF migration AddSentStreakFreezeAlert; account reset purges the guard table; registered in ServiceCollectionExtensions and BackgroundServiceHealthCheck. Unit tests cover the eligibility predicate, local-yesterday computation, interval default, notification copy, and the guard entity. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(api): remove manual streak-freeze activation (auto-only) (#108) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(api): exclude soft-deleted habits from auto-freeze completion check (#108) Aligns StreakFreezeAutoActivationService.LoadRecentCompletionsAsync with UserStreakService.LoadStreakDataAsync by filtering out soft-deleted habits when computing recent completions, so a deleted habit's log can no longer count as activity for a date and suppress the auto-freeze. Adds DB-backed tests locking the LoadRecentCompletionsAsync contract for soft-deleted and live habit logs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(api): author feature-explanation markdown bundle (#84) Add eight grounded markdown explainer files (streaks, frequencies, gamification, paygate, schedule-math, freezes, notifications, ai-memory) so the in-app AI answers feature questions from code-accurate content. Each file carries a 6-key YAML frontmatter and a body whose constants are copied verbatim from AppConstants, LevelDefinitions, and the streak/schedule/paygate/reminder services. Ship the bundle inside the API binary via the first EmbeddedResource glob in the repo; resources embed as Orbit.Application.Chat.Content.FeatureExplanations.<key>.md, the documented loader contract for the downstream consumer. Refs thomasluizon/orbit-ui-mobile#84 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(chat): freezes.md describes auto-activation model (#108) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(chat): repoint freezes.md derived_from to auto-activation service (#108) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Comprehensive audit fix addressing 206 findings from 30 parallel audit agents across both repos. This PR covers the orbit-api side (7 commits).
Security
RandomNumberGenerator(crypto PRNG)ConfigControllergated with[Authorize]Strict-Transport-Security+Content-Security-PolicyheadersForwardedHeadersrestricted withForwardLimit = 1ex.Messageexposure in bulk commands and notificationsPerformance
IX_Habits_UserIdindex (critical - was missing after composite index drop)GetHabitByIdQueryqueries by ID directly (was loading ALL user habits)GetHabitLogsQuerycapped to 365-day lookbackQueryHabitsToolfilters at DB level (was loading all into memory)PushNotificationServiceusesIHttpClientFactory(was creating HttpClient per call)Architecture
GamificationService.IsScheduledForDatediverged duplicate - now delegates to realHabitScheduleServiceError Handling
catch {}blocks (gamification, referral, calendar)ProcessUserChatCommandValidator,GoogleAuthCommandValidatorType Safety
TryGetPropertypattern in Google auth (was throwing on missing keys)AppConfigKeysstatic class (11 constants replacing 13 scattered strings).GetString() ?? string.Emptyacross AI toolsTryParseExact("yyyy-MM-dd")in 7 AI tool filesnameof()in EF Core join configDate/Timezone
CreatedAtUtcto user timezoneSubscription & Pay Gates
IPayGateService.CanCreateGoalsDefaultFreeAiMessagescomment fixedPush Notifications & AI Chat
PushSubscriptionFK to Users with CASCADE deleteMoveHabitTooldeep cycle detectionBulkLogHabitsToolfilters by UserId at DB levelEnv var changes
Stripe__ProProductId=prod_UBUPrTlZg8chukto RenderTest plan
dotnet buildpasses (verified)Companion PR: orbit-ui fix/mega-audit-2026-03-28
🤖 Generated with Claude Code