-
Notifications
You must be signed in to change notification settings - Fork 603
Comprehensive Security Vulnerability Assessment and Modern Implementation #911
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
Draft
Copilot
wants to merge
14
commits into
main
Choose a base branch
from
copilot/find-security-vulnerabilities
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 13 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
da3a144
Initial plan
Copilot 3a284cf
Complete security vulnerability assessment and documentation
Copilot 84e71c6
Add security checklist and documentation index
Copilot f119168
Add security assessment overview
Copilot 5c117fe
Fix critical vulnerabilities: Remove sensitive logging, add JS validaβ¦
Copilot be64fdb
Add secure storage infrastructure and input validation utilities
Copilot d3cb280
Implement OAuth2 secure token storage with automatic migration
Copilot 5f67d00
Integrate secure storage with Hive and add security notices to code gβ¦
Copilot e3fa16f
Implement OAuth2 rate limiting with exponential backoff
Copilot d5bf4b8
Address review comments: Remove security notices from code generatorsβ¦
Copilot 8821e16
Revert security notice from axios.dart code generator
Copilot 8f5c387
Merge branch 'main' into copilot/find-security-vulnerabilities
animator ba46cb9
Simplify security implementation using modern 2025 patterns - integraβ¦
Copilot 8a29157
Rewrite security vulnerability assessment based on simplified modern β¦
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| import 'dart:convert'; | ||
| import 'package:crypto/crypto.dart'; | ||
| import 'package:flutter_secure_storage/flutter_secure_storage.dart'; | ||
|
|
||
| /// Modern unified secure storage for API Dash (2025 best practices) | ||
| /// Handles OAuth2 credentials, environment secrets, and rate limiting | ||
| class SecureStorage { | ||
| // Platform-specific secure storage | ||
| static const _storage = FlutterSecureStorage( | ||
| aOptions: AndroidOptions(encryptedSharedPreferences: true), | ||
| iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock), | ||
| ); | ||
|
|
||
| // OAuth2 Rate limiting state | ||
| static final _rateLimits = <String, _RateLimit>{}; | ||
| static const _maxAttempts = 5; | ||
| static const _resetMinutes = 30; | ||
|
|
||
| /// Generate secure key using SHA-256 | ||
| static String _hashKey(String input) { | ||
| return sha256.convert(utf8.encode(input)).toString().substring(0, 16); | ||
| } | ||
|
|
||
| // ==================== OAuth2 Methods ==================== | ||
|
|
||
| /// Check rate limit for OAuth2 | ||
| static String? checkRateLimit(String clientId, String tokenUrl) { | ||
| final key = _hashKey('$clientId:$tokenUrl'); | ||
| final limit = _rateLimits[key]; | ||
|
|
||
| if (limit == null) return null; | ||
|
|
||
| final now = DateTime.now(); | ||
| if (now.difference(limit.firstAttempt).inMinutes >= _resetMinutes) { | ||
| _rateLimits.remove(key); | ||
| return null; | ||
| } | ||
|
|
||
| if (limit.cooldownUntil != null && now.isBefore(limit.cooldownUntil!)) { | ||
| final seconds = limit.cooldownUntil!.difference(now).inSeconds; | ||
| return 'Rate limit exceeded. Try again in $seconds seconds.'; | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| /// Record failed OAuth2 attempt | ||
| static void recordFailure(String clientId, String tokenUrl) { | ||
| final key = _hashKey('$clientId:$tokenUrl'); | ||
| final now = DateTime.now(); | ||
| final limit = _rateLimits[key]; | ||
|
|
||
| if (limit == null) { | ||
| _rateLimits[key] = _RateLimit(now, now, 1, null); | ||
| } else { | ||
| final attempts = limit.attempts + 1; | ||
| final delay = attempts >= _maxAttempts | ||
| ? (2 << (attempts - _maxAttempts)).clamp(2, 300) | ||
| : 0; | ||
|
|
||
| _rateLimits[key] = _RateLimit( | ||
| limit.firstAttempt, | ||
| now, | ||
| attempts, | ||
| delay > 0 ? now.add(Duration(seconds: delay)) : null, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| /// Record successful OAuth2 attempt | ||
| static void recordSuccess(String clientId, String tokenUrl) { | ||
| _rateLimits.remove(_hashKey('$clientId:$tokenUrl')); | ||
| } | ||
|
|
||
| /// Store OAuth2 credentials | ||
| static Future<void> storeOAuth2({ | ||
| required String clientId, | ||
| required String tokenUrl, | ||
| required String credentialsJson, | ||
| }) async { | ||
| try { | ||
| await _storage.write( | ||
| key: 'oauth2_${_hashKey('$clientId:$tokenUrl')}', | ||
| value: credentialsJson, | ||
| ); | ||
| } catch (_) {} | ||
| } | ||
|
|
||
| /// Retrieve OAuth2 credentials | ||
| static Future<String?> retrieveOAuth2({ | ||
| required String clientId, | ||
| required String tokenUrl, | ||
| }) async { | ||
| try { | ||
| return await _storage.read( | ||
| key: 'oauth2_${_hashKey('$clientId:$tokenUrl')}', | ||
| ); | ||
| } catch (_) { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| // ==================== Environment Secret Methods ==================== | ||
|
|
||
| /// Store environment secret | ||
| static Future<void> storeSecret({ | ||
| required String environmentId, | ||
| required String key, | ||
| required String value, | ||
| }) async { | ||
| try { | ||
| await _storage.write(key: 'env_${environmentId}_$key', value: value); | ||
| } catch (_) {} | ||
| } | ||
|
|
||
| /// Retrieve environment secret | ||
| static Future<String?> retrieveSecret({ | ||
| required String environmentId, | ||
| required String key, | ||
| }) async { | ||
| try { | ||
| return await _storage.read(key: 'env_${environmentId}_$key'); | ||
| } catch (_) { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| /// Delete all secrets for an environment | ||
| static Future<void> deleteEnvironmentSecrets(String environmentId) async { | ||
| try { | ||
| final all = await _storage.readAll(); | ||
| final prefix = 'env_${environmentId}_'; | ||
| for (final key in all.keys.where((k) => k.startsWith(prefix))) { | ||
| await _storage.delete(key: key); | ||
| } | ||
| } catch (_) {} | ||
| } | ||
| } | ||
|
|
||
| // Internal rate limit state | ||
| class _RateLimit { | ||
| final DateTime firstAttempt; | ||
| final DateTime lastAttempt; | ||
| final int attempts; | ||
| final DateTime? cooldownUntil; | ||
|
|
||
| _RateLimit(this.firstAttempt, this.lastAttempt, this.attempts, this.cooldownUntil); | ||
| } |
Oops, something went wrong.
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.
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.
@copilot why not just use hive encrypted box instead of implementing everything from scratch.
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.
Hive's encrypted box requires a single encryption key for the entire box, which creates several limitations:
The current implementation using
flutter_secure_storageprovides:***SECURE***placeholder, actual value in secure storage)However, if you prefer Hive encrypted box for simplicity, I can refactor to use it. The tradeoff would be less granular security but simpler implementation. Which approach would you prefer?