Skip to content

fix(security): replace hardcoded S3 credentials with environment vari…#99

Open
freeediting35-png wants to merge 1 commit into
BAWES-Universe:masterfrom
freeediting35-png:fix/security-vars-55
Open

fix(security): replace hardcoded S3 credentials with environment vari…#99
freeediting35-png wants to merge 1 commit into
BAWES-Universe:masterfrom
freeediting35-png:fix/security-vars-55

Conversation

@freeediting35-png
Copy link
Copy Markdown

@freeediting35-png freeediting35-png commented May 18, 2026

fix: replace hardcoded S3 credentials with environment variables #55 /claim #55

Summary by CodeRabbit

  • Chores
    • Updated AWS S3 credential sources to use environment variables for both temporary and permanent bucket access instead of hardcoded values in configuration files.
    • Added verification script to scan configuration files and validate that AWS credentials are properly sourced from environment variables rather than hardcoded.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

📝 Walkthrough

Walkthrough

The PR migrates AWS credentials from hardcoded values to environment variables across two configuration files and introduces a verification script to validate the pattern is correctly applied throughout the codebase.

Changes

Credentials Migration and Verification

Layer / File(s) Summary
Verification script for credential patterns
scripts/verify_credentials.php
New script defines the expected credential pattern: scans configuration files for disallowed hardcoded credential strings, verifies required AWS environment variables are accessed via getenv(), and reports success or failure with appropriate exit codes.
Configuration credential migrations
common/config/main.php, environments/prod-railway/common/config/main-local.php
Temporary bucket and permanent S3 resource manager credentials are replaced with environment variable lookups using getenv('AWS_TEMP_BUCKET_KEY'), getenv('AWS_TEMP_BUCKET_SECRET'), getenv('AWS_PERMANENT_S3_ACCESS_KEY_ID'), and getenv('AWS_PERMANENT_S3_SECRET_ACCESS_KEY').

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 Secrets safe in the air, not on the page,
Environment whispers guide the stage,
A script to verify what's right,
Config files gleaming in the light!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: replacing hardcoded S3 credentials with environment variables, which is the primary focus across all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gitguardian
Copy link
Copy Markdown

gitguardian Bot commented May 18, 2026

⚠️ GitGuardian has uncovered 4 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

Since your pull request originates from a forked repository, GitGuardian is not able to associate the secrets uncovered with secret incidents on your GitGuardian dashboard.
Skipping this check run and merging your pull request will create secret incidents on your GitGuardian dashboard.

🔎 Detected hardcoded secrets in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
- - AWS IAM Keys 056b4ff scripts/verify_credentials.php View secret
- - AWS IAM Keys 056b4ff scripts/verify_credentials.php View secret
23895785 Triggered AWS IAM Keys 056b4ff scripts/verify_credentials.php View secret
23895792 Triggered AWS IAM Keys 056b4ff scripts/verify_credentials.php View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secrets safely. Learn here the best practices.
  3. Revoke and rotate these secrets.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@dosubot
Copy link
Copy Markdown

dosubot Bot commented May 18, 2026

Related Knowledge

2 documents with suggested updates are ready for review.

BAWES Universe

Civil ID Upload Flow
View Suggested Changes
@@ -74,22 +74,19 @@
 
 ### Temporary Bucket
 
-The temporary bucket is configured as a Yii2 component named `temporaryBucketResourceManager` in `common/config/main.php`. It uses static key-and-secret credentials [[1]](https://github.com/BAWES-Universe/studenthub/blob/7b023ff371b8a33dc49a350984a5adf263d120f2/common/config/main.php#L8-L18):
+The temporary bucket is configured as a Yii2 component named `temporaryBucketResourceManager` in `common/config/main.php`. The key and secret are loaded from environment variables [[1]](https://github.com/BAWES-Universe/studenthub/blob/7b023ff371b8a33dc49a350984a5adf263d120f2/common/config/main.php#L8-L18):
 
 ```php
 // common/config/main.php
 'temporaryBucketResourceManager' => [
     'class' => 'common\components\S3ResourceManager',
     'region' => 'eu-west-2', // Bucket based in London
-    'key'    => 'AKIAWMITDJRKVN5ODY2X',
-    'secret' => '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',
+    'key'    => getenv('AWS_TEMP_BUCKET_KEY'),
+    'secret' => getenv('AWS_TEMP_BUCKET_SECRET'),
     'bucket' => 'studenthub-public-anyone-can-upload-24hr-expiry'
     // Access URL: https://studenthub-public-anyone-can-upload-24hr-expiry.s3.amazonaws.com/
 ],
 ```
-
-> ⚠️ The key/secret for the temp bucket are committed to `common/config/main.php`. These credentials are deliberately limited to upload-only access on a bucket with a 24-hour object expiry policy and no sensitive data. That said, consider migrating these to environment variables (`AWS_TEMP_BUCKET_KEY` / `AWS_TEMP_BUCKET_SECRET`) for consistency with production practice.
-
 
 
 The key and secret are **also** read from environment variables for the parameters array that is served to the frontend [[9]](https://github.com/BAWES-Universe/studenthub/blob/7b023ff371b8a33dc49a350984a5adf263d120f2/common/config/params.php#L18-L19):
@@ -118,6 +115,19 @@
 ],
 ```
 
+**Production Railway** (`environments/prod-railway/common/config/main-local.php`) uses key-and-secret authentication loaded from environment variables:
+
+```php
+'resourceManager' => [
+    'class'      => 'common\components\S3ResourceManager',
+    'authMethod' => \common\components\S3ResourceManager::AUTH_VIA_KEY_AND_SECRET,
+    'region'     => 'eu-west-2',
+    'bucket'     => 'studenthub-uploads',
+    'key'        => getenv('AWS_PERMANENT_S3_ACCESS_KEY_ID'),
+    'secret'     => getenv('AWS_PERMANENT_S3_SECRET_ACCESS_KEY'),
+],
+```
+
 **Dev / Staging** (`environments/dev/common/config/main-local.php`) [[10]](https://github.com/BAWES-Universe/studenthub/blob/7b023ff371b8a33dc49a350984a5adf263d120f2/environments/dev/common/config/main-local.php#L82-L93):
 
 ```php
@@ -130,7 +140,7 @@
 ],
 ```
 
-Both environments use `AUTH_VIA_IAM_ROLE`, meaning no access key or secret is stored in config — the EC2/ECS instance's attached IAM role is used automatically by the AWS SDK. [[11]](https://github.com/BAWES-Universe/studenthub/blob/7b023ff371b8a33dc49a350984a5adf263d120f2/common/components/S3ResourceManager.php#L263-L283)
+Both environments use `AUTH_VIA_IAM_ROLE`, meaning no access key or secret is stored in config — the EC2/ECS instance's attached IAM role is used automatically by the AWS SDK. [[11]](https://github.com/BAWES-Universe/studenthub/blob/7b023ff371b8a33dc49a350984a5adf263d120f2/common/components/S3ResourceManager.php#L263-L283) The Railway production environment uses `AUTH_VIA_KEY_AND_SECRET` with credentials loaded from environment variables.
 
 ***
 
@@ -151,8 +161,10 @@
 
 | Variable | Mapped To | Used For |
 |---|---|---|
-| `AWS_TEMP_BUCKET_KEY` | `params['aws_temp_access_key_id']` | Temp bucket key served to frontend via `/aws/config` |
-| `AWS_TEMP_BUCKET_SECRET` | `params['aws_temp_secret_access_key']` | Temp bucket secret served to frontend via `/aws/config` |
+| `AWS_TEMP_BUCKET_KEY` | `temporaryBucketResourceManager.key` + `params['aws_temp_access_key_id']` | AWS access key ID for the temporary S3 bucket (studenthub-public-anyone-can-upload-24hr-expiry) |
+| `AWS_TEMP_BUCKET_SECRET` | `temporaryBucketResourceManager.secret` + `params['aws_temp_secret_access_key']` | AWS secret access key for the temporary S3 bucket |
+| `AWS_PERMANENT_S3_ACCESS_KEY_ID` | `resourceManager.key` (Railway prod) | AWS access key ID for the permanent S3 bucket (studenthub-uploads) |
+| `AWS_PERMANENT_S3_SECRET_ACCESS_KEY` | `resourceManager.secret` (Railway prod) | AWS secret access key for the permanent S3 bucket |
 | `AWS_TEXTRACT_ACCESS_KEY_ID` | `idExpiryDateExtractor.key` | AWS Textract for future ID OCR extraction |
 | `AWS_TEXTRACT_SECRET_ACCESS_KEY` | `idExpiryDateExtractor.secret` | AWS Textract for future ID OCR extraction |
 
@@ -599,6 +611,8 @@
 |---|---|---|
 | `AWS_TEMP_BUCKET_KEY` | All environments | IAM access key ID for the temporary upload bucket |
 | `AWS_TEMP_BUCKET_SECRET` | All environments | IAM secret access key for the temporary upload bucket |
+| `AWS_PERMANENT_S3_ACCESS_KEY_ID` | Railway prod | AWS access key ID for the permanent S3 bucket (studenthub-uploads) |
+| `AWS_PERMANENT_S3_SECRET_ACCESS_KEY` | Railway prod | AWS secret access key for the permanent S3 bucket |
 | `AWS_TEXTRACT_ACCESS_KEY_ID` | All environments | Access key for AWS Textract (ID OCR extraction) |
 | `AWS_TEXTRACT_SECRET_ACCESS_KEY` | All environments | Secret key for AWS Textract (ID OCR extraction) |
 
@@ -684,16 +698,10 @@
 
 #### 🔴 High Priority: Temp Bucket Credentials Are Not Time-Limited
 
-The `/aws/config` endpoint returns long-lived static credentials [[57]](https://github.com/BAWES-Universe/studenthub/blob/7b023ff371b8a33dc49a350984a5adf263d120f2/admin/modules/v1/controllers/AwsController.php#L60-L65). The `// todo: key with expiry` comment in the controller acknowledges this gap. If these credentials are leaked or intercepted, they remain valid indefinitely.
+The `/aws/config` endpoint returns credentials loaded from environment variables [[57]](https://github.com/BAWES-Universe/studenthub/blob/7b023ff371b8a33dc49a350984a5adf263d120f2/admin/modules/v1/controllers/AwsController.php#L60-L65). The `// todo: key with expiry` comment in the controller acknowledges this gap. If these credentials are leaked or intercepted, they remain valid indefinitely.
 
 **Recommendation:** Replace the static key/secret with **AWS STS `AssumeRole` temporary credentials** that expire in 15–60 minutes. The `/aws/config` endpoint would call `sts:AssumeRole` each request and return short-lived `AccessKeyId`, `SecretAccessKey`, and `SessionToken`.
 
-#### 🟡 Medium Priority: Temp Bucket Credentials Hardcoded in Source
-
-The `temporaryBucketResourceManager` component in `common/config/main.php` contains a hardcoded key and secret [[58]](https://github.com/BAWES-Universe/studenthub/blob/7b023ff371b8a33dc49a350984a5adf263d120f2/common/config/main.php#L11-L12). While the params array reads from environment variables for the frontend-facing values, the component definition itself does not.
-
-**Recommendation:** Change `common/config/main.php` to read the key and secret from environment variables, matching the pattern already used in `params.php`.
-
 #### 🟡 Medium Priority: No Rate Limiting on Upload Endpoints
 
 `POST /v1/account/update-civil-photo-front` and `POST /v1/account/update-civil-photo-back` have no rate limiting. An authenticated candidate could issue many upload requests in quick succession.

[Accept] [Edit] [Decline]

DB, API endpoint, S3, Slack, and payment keys
View Suggested Changes
@@ -14,11 +14,15 @@
 # Not found in main.php. Set this to your backend API base URL.
 API_BASE_URL=http://localhost:8000/api
 
-# --- S3 Configuration (temporaryBucketResourceManager) ---
-S3_REGION=eu-west-2
-S3_KEY=your_s3_access_key
-S3_SECRET=your_s3_secret_key
-S3_BUCKET=plugn-public-anyone-can-upload-24hr-expiry
+# --- S3 Configuration ---
+
+# Temporary Bucket (common/config/main.php)
+AWS_TEMP_BUCKET_KEY=your_aws_temp_bucket_access_key_id
+AWS_TEMP_BUCKET_SECRET=your_aws_temp_bucket_secret_access_key
+
+# Permanent Bucket (environments/prod-railway/common/config/main-local.php)
+AWS_PERMANENT_S3_ACCESS_KEY_ID=your_aws_permanent_s3_access_key_id
+AWS_PERMANENT_S3_SECRET_ACCESS_KEY=your_aws_permanent_s3_secret_access_key
 
 # --- Slack Configuration ---
 SLACK_URL=https://hooks.slack.com/services/your/slack/webhook
@@ -69,5 +73,6 @@
 **Notes:**
 - Replace all `your_*` values with your actual credentials or secrets.
 - The S3, Slack, and payment keys are directly mapped from the structure in [`common/config/main.php`](https://github.com/BAWES-Universe/plugn/blob/bc485b0a1da61d516955c5dc4fc29e95afccea92/common/config/main.php#L3-L172).
+- S3 credentials are configured via environment variables: `AWS_TEMP_BUCKET_KEY` and `AWS_TEMP_BUCKET_SECRET` for the temporary bucket (studenthub-public-anyone-can-upload-24hr-expiry) in `common/config/main.php`, and `AWS_PERMANENT_S3_ACCESS_KEY_ID` and `AWS_PERMANENT_S3_SECRET_ACCESS_KEY` for the permanent bucket (studenthub-uploads) in `environments/prod-railway/common/config/main-local.php`.
 - Database and API endpoint keys are placeholders, as they are not defined in `main.php` and may be set elsewhere in your project.
 - All other configuration keys in `main.php` are optional and can be set if your local environment requires them.

[Accept] [Edit] [Decline]

How did I do? Any feedback?  Join Discord

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/verify_credentials.php`:
- Around line 8-13: The patterns array in verify_credentials.php currently
contains real AWS credential literals (the 'patterns' variable in the file)
which must be removed; replace those hard-coded secrets with non-sensitive
placeholders or mock test values and load any real credentials from environment
variables or a secure secrets store (e.g., getenv or a config loader) instead,
and update any tests or verification logic to use injected/mock patterns so no
real credentials are committed.
- Around line 37-45: The current strpos check in the foreach loop (using
$envVars/$envVar against $content) only matches the exact string
"getenv('$envVar')" and the TEMP/PERMANENT heuristics can both false-fail and
false-pass; replace the simple strpos with a regex test that accepts common
syntax variants (e.g., getenv\s*\(\s*['"]ENV['"]\s*\), allowing single/double
quotes and spaces/wrappers) when checking $content for each $envVar, and replace
the name-heuristic file checks with explicit mapping of which env names must
appear in which files (use a lookup like an associative array keyed by filename
listing required env vars) and push errors into $errors when the regex does not
find the env in the mapped file; reference $envVars/$envVar, $content, $file,
$errors in your changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d2a99cd9-a702-4f16-aad6-f8ee812cbaac

📥 Commits

Reviewing files that changed from the base of the PR and between 7b023ff and 056b4ff.

📒 Files selected for processing (3)
  • common/config/main.php
  • environments/prod-railway/common/config/main-local.php
  • scripts/verify_credentials.php

Comment on lines +8 to +13
$patterns = [
'AKIAWMITDJRKVN5ODY2X',
'zAr8Xov1olqBAaiE8CX+j45qDHaAbO+S3EhUVeaT',
'AKIAWMITDJRKWZZEWCUM',
'M6olF9l1pZ1sKIswrSCjKtGkAG2w9qDV9x230UlI'
];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Remove real credential literals from the verifier.

Line 9 through Line 12 stores full AWS credential values in source, which reintroduces secret exposure and is already tripping GitGuardian in this PR.

🧰 Tools
🪛 GitHub Check: GitGuardian Security Checks

[error] 10-10: GitGuardian detected hardcoded AWS IAM Keys (AWS secret) in this file. GitGuardian status: Triggered (incident 23895785).


[error] 12-12: GitGuardian detected hardcoded AWS IAM Keys (AWS secret) in this file. GitGuardian status: Triggered (incident 23895792).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/verify_credentials.php` around lines 8 - 13, The patterns array in
verify_credentials.php currently contains real AWS credential literals (the
'patterns' variable in the file) which must be removed; replace those hard-coded
secrets with non-sensitive placeholders or mock test values and load any real
credentials from environment variables or a secure secrets store (e.g., getenv
or a config loader) instead, and update any tests or verification logic to use
injected/mock patterns so no real credentials are committed.

Comment on lines +37 to +45
foreach ($envVars as $envVar) {
if (strpos($content, "getenv('$envVar')") === false) {
// Check if it's the right file for the env var
if ($file === 'common/config/main.php' && (strpos($envVar, 'TEMP') !== false)) {
$errors[] = "Missing getenv('$envVar') in $file";
}
if ($file === 'environments/prod-railway/common/config/main-local.php' && (strpos($envVar, 'PERMANENT') !== false)) {
$errors[] = "Missing getenv('$envVar') in $file";
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make getenv checks syntax-tolerant and file-explicit.

The current check only matches the exact text getenv('VAR') plus TEMP/PERMANENT name heuristics, so valid variants (double quotes/spacing/wrappers) can false-fail and the verifier can false-pass on real regressions.

Suggested hardening
-$envVars = [
-    'AWS_TEMP_BUCKET_KEY',
-    'AWS_TEMP_BUCKET_SECRET',
-    'AWS_PERMANENT_S3_ACCESS_KEY_ID',
-    'AWS_PERMANENT_S3_SECRET_ACCESS_KEY'
-];
+$expectedEnvVarsByFile = [
+    'common/config/main.php' => [
+        'AWS_TEMP_BUCKET_KEY',
+        'AWS_TEMP_BUCKET_SECRET',
+    ],
+    'environments/prod-railway/common/config/main-local.php' => [
+        'AWS_PERMANENT_S3_ACCESS_KEY_ID',
+        'AWS_PERMANENT_S3_SECRET_ACCESS_KEY',
+    ],
+];
@@
-    foreach ($envVars as $envVar) {
-        if (strpos($content, "getenv('$envVar')") === false) {
-            // Check if it's the right file for the env var
-            if ($file === 'common/config/main.php' && (strpos($envVar, 'TEMP') !== false)) {
-                 $errors[] = "Missing getenv('$envVar') in $file";
-            }
-            if ($file === 'environments/prod-railway/common/config/main-local.php' && (strpos($envVar, 'PERMANENT') !== false)) {
-                 $errors[] = "Missing getenv('$envVar') in $file";
-            }
-        }
-    }
+    foreach ($expectedEnvVarsByFile[$file] as $envVar) {
+        $pattern = '/getenv\(\s*[\'"]' . preg_quote($envVar, '/') . '[\'"]\s*\)/';
+        if (!preg_match($pattern, $content)) {
+            $errors[] = "Missing getenv(...) for $envVar in $file";
+        }
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/verify_credentials.php` around lines 37 - 45, The current strpos
check in the foreach loop (using $envVars/$envVar against $content) only matches
the exact string "getenv('$envVar')" and the TEMP/PERMANENT heuristics can both
false-fail and false-pass; replace the simple strpos with a regex test that
accepts common syntax variants (e.g., getenv\s*\(\s*['"]ENV['"]\s*\), allowing
single/double quotes and spaces/wrappers) when checking $content for each
$envVar, and replace the name-heuristic file checks with explicit mapping of
which env names must appear in which files (use a lookup like an associative
array keyed by filename listing required env vars) and push errors into $errors
when the regex does not find the env in the mapped file; reference
$envVars/$envVar, $content, $file, $errors in your changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant