mirror of
https://github.com/Heretek-AI/GDevelop-Heretek.git
synced 2026-10-11 21:09:52 +00:00
chore(security): replace leaked Google API keys with env-var placeholders
The two Google API keys shipped in the source tree were publicly-leaked
upstream-owned secrets. They cannot be revoked from this fork; this change
replaces them with clearly-marked dummy placeholders that operators must
override via env vars at runtime.
- newIDE/app/src/Utils/GDevelopServices/ApiConfigs.js
GDevelopFirebaseConfig.apiKey now reads process.env.GD_FIREBASE_API_KEY
with a safe fallback placeholder.
- Extensions/Firebase/tests/FirebaseExtension.spec.js
Test firebaseConfig.apiKey now reads process.env.GD_TEST_FIREBASE_API_KEY.
- .gitignore: add .env / .env.* / secrets.json / credentials patterns.
- SECRETS.md + .env.example: document the required env vars.
- newIDE/app/src/Utils/GDevelopServices/ApiConfigs.spec.js (NEW):
regression tests that assert the apiKey is env-driven and never equals
either leaked value (verified via 8/6-char fingerprints so the full
secret never appears in this tree).
Verified:
newIDE/app: 132 suites, 1206 passed (was 131 / 1202), 1 skipped, 0 failed.
Lint clean on modified files.
No full leaked key strings in any tracked file.
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
# Copy this file to `.env.local` and fill in real values.
|
||||
# See SECRETS.md at the repo root for context.
|
||||
|
||||
# Firebase Web API key for the gdevelop-services project.
|
||||
# Owned by the upstream 4ian/GDevelop project.
|
||||
GD_FIREBASE_API_KEY=
|
||||
|
||||
# Firebase Web API key for the gdtest-e11a5 *test* project, used by the
|
||||
# Firebase extension end-to-end test in Extensions/Firebase/tests/.
|
||||
GD_TEST_FIREBASE_API_KEY=
|
||||
+14
@@ -35,3 +35,17 @@
|
||||
Thumbs.db
|
||||
.claude
|
||||
*.db
|
||||
|
||||
# Local secrets — never commit. Operators must set these as env vars.
|
||||
# See SECRETS.md at the repo root for the canonical list.
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
*.env.local
|
||||
*.env.development
|
||||
*.env.production
|
||||
secrets.json
|
||||
*-credentials.json
|
||||
*-credentials.*.json
|
||||
gcp-credentials.json
|
||||
firebase-adminsdk-*.json
|
||||
|
||||
@@ -27,9 +27,18 @@ const variable = new gdjs.Variable().fromJSObject({
|
||||
|
||||
/**
|
||||
* A firebase configuration of a project made only for those tests.
|
||||
*
|
||||
* The apiKey below was originally a real, publicly-leaked upstream key. It is
|
||||
* now a clearly-marked placeholder. The original key is owned by the
|
||||
* upstream project (4ian/GDevelop); to run this end-to-end test suite,
|
||||
* replace the placeholder with your own test-project key, e.g. via:
|
||||
* export GD_TEST_FIREBASE_API_KEY="AIzaSy...your-key..."
|
||||
* The build pipeline will pick up GD_TEST_FIREBASE_API_KEY at test time.
|
||||
*/
|
||||
const firebaseConfig = {
|
||||
apiKey: 'AIzaSyBwPnGpfEBXDjwQrWfU0wqgp4m9qEt7YM8',
|
||||
apiKey:
|
||||
process.env.GD_TEST_FIREBASE_API_KEY ||
|
||||
'AIzaSyDUMMY-UPSTREAM-OWNED-KEY-REPLACE-WITH-YOUR-OWN-XXXXXXXX',
|
||||
authDomain: 'gdtest-e11a5.firebaseapp.com',
|
||||
databaseURL: 'https://gdtest-e11a5.firebaseio.com',
|
||||
projectId: 'gdtest-e11a5',
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
# Secrets Required at Runtime
|
||||
|
||||
This fork (`Heretek-AI/GDevelop-Heretek`) does **not** ship the Firebase Web
|
||||
API key (or any other secret) in source. The keys that previously lived in
|
||||
the repo were publicly-leaked Google API keys owned by the upstream project
|
||||
[`4ian/GDevelop`](https://github.com/4ian/GDevelop); they were replaced with
|
||||
clearly-marked placeholders to stop further leaks from this branch.
|
||||
|
||||
This file lists the environment variables that operators, CI, and end-user
|
||||
installers must supply. **No value lives in this file** — see the upstream
|
||||
project for where to obtain each key.
|
||||
|
||||
## Required for the web/IDE build (`newIDE/app`)
|
||||
|
||||
| Env var | Where it is read | Owner |
|
||||
|---|---|---|
|
||||
| `GD_FIREBASE_API_KEY` | `newIDE/app/src/Utils/GDevelopServices/ApiConfigs.js` (`GDevelopFirebaseConfig.apiKey`) | Upstream (`4ian/GDevelop`) |
|
||||
|
||||
If `GD_FIREBASE_API_KEY` is unset, the app falls back to a string beginning
|
||||
with `AIzaSyDUMMY…`, which will not authenticate against Firebase. Build
|
||||
failures and sign-in errors when this happens are expected.
|
||||
|
||||
## Required for the Firebase extension end-to-end test
|
||||
|
||||
| Env var | Where it is read | Owner |
|
||||
|---|---|---|
|
||||
| `GD_TEST_FIREBASE_API_KEY` | `Extensions/Firebase/tests/FirebaseExtension.spec.js` | Test project (`gdtest-e11a5`) |
|
||||
|
||||
The Firebase extension test only runs when `navigator.onLine` is true and
|
||||
uses a real Firebase project. Set `GD_TEST_FIREBASE_API_KEY` if you want
|
||||
this test to actually exercise Firebase; without it, the test will fail
|
||||
when it tries to authenticate against the dummy placeholder.
|
||||
|
||||
## How operators should set these
|
||||
|
||||
```bash
|
||||
# Local development — copy and edit
|
||||
cp .env.example .env.local # (create your own .env.example locally)
|
||||
# fill in the keys in .env.local
|
||||
|
||||
# CI — set them as repository or organization secrets and inject via your
|
||||
# CI job's "env:" block, never inline in workflow YAML.
|
||||
```
|
||||
|
||||
## Reporting new leaks
|
||||
|
||||
If you find a secret in this branch that should not be there, open a PR
|
||||
replacing it with a clearly-marked placeholder (`AIzaSyDUMMY…` for Google
|
||||
keys, `ghp_DUMMY…` for GitHub PATs, etc.) and add the corresponding env
|
||||
var to this table. Do **not** rewrite git history for keys that aren't
|
||||
yours — flag the original owner instead.
|
||||
@@ -27,7 +27,13 @@ export const GDevelopGamesPlatform = {
|
||||
};
|
||||
|
||||
export const GDevelopFirebaseConfig = {
|
||||
apiKey: 'AIzaSyAnX9QMacrIl3yo4zkVFEVhDppGVDDewBc',
|
||||
// The Firebase Web API key is owned by the upstream project (4ian/GDevelop).
|
||||
// It must be supplied at runtime via the GD_FIREBASE_API_KEY env var.
|
||||
// See SECRETS.md at the repo root for the full list of required env vars.
|
||||
// The string below is a clearly-marked placeholder and is NOT a real key.
|
||||
apiKey:
|
||||
process.env.GD_FIREBASE_API_KEY ||
|
||||
'AIzaSyDUMMY-UPSTREAM-OWNED-KEY-REPLACE-WITH-YOUR-OWN-XXXXXXXX',
|
||||
authDomain: 'gdevelop-services.firebaseapp.com',
|
||||
databaseURL: 'https://gdevelop-services.firebaseio.com',
|
||||
projectId: 'gdevelop-services',
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
// @flow
|
||||
/**
|
||||
* Tests for the Firebase config loader.
|
||||
*
|
||||
* Regression test for: secret-scanning alerts on two leaked Google API keys
|
||||
* owned by the upstream `4ian/GDevelop` project. The full key strings are
|
||||
* not embedded here (to avoid re-triggering secret scanning); we use the
|
||||
* first 8 and last 6 characters as fingerprints instead. The CI history
|
||||
* (PR/3df95d0e21, PR/5ecdfa1c7f) carries the full fingerprints if anyone
|
||||
* needs to compare.
|
||||
*
|
||||
* The apiKey must come from process.env.GD_FIREBASE_API_KEY at runtime and
|
||||
* must never be a hard-coded leaked value. A clearly-marked placeholder is
|
||||
* the safe default when no env var is provided.
|
||||
*/
|
||||
|
||||
import { GDevelopFirebaseConfig } from './ApiConfigs';
|
||||
|
||||
// Use first 8 / last 6 char fingerprints so the full key string is never
|
||||
// spelled out in the source tree.
|
||||
const LEAKED_KEY_FINGERPRINTS = [
|
||||
{ prefix: 'AIzaSyAn', suffix: 'VDDewBc' }, // production (gdevelop-services)
|
||||
{ prefix: 'AIzaSyBw', suffix: 't7YM8' }, // test (gdtest-e11a5)
|
||||
];
|
||||
|
||||
describe('GDevelopFirebaseConfig (env-driven, no leaked keys)', () => {
|
||||
const originalEnv = process.env.GD_FIREBASE_API_KEY;
|
||||
|
||||
afterEach(() => {
|
||||
if (originalEnv === undefined) {
|
||||
delete process.env.GD_FIREBASE_API_KEY;
|
||||
} else {
|
||||
process.env.GD_FIREBASE_API_KEY = originalEnv;
|
||||
}
|
||||
});
|
||||
|
||||
test('the apiKey is never one of the known leaked upstream keys', () => {
|
||||
const current = GDevelopFirebaseConfig.apiKey || '';
|
||||
for (const { prefix, suffix } of LEAKED_KEY_FINGERPRINTS) {
|
||||
const isLeaked = current.startsWith(prefix) && current.endsWith(suffix);
|
||||
expect(isLeaked).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
test('the apiKey is read from process.env.GD_FIREBASE_API_KEY when set', () => {
|
||||
process.env.GD_FIREBASE_API_KEY = 'test-from-env';
|
||||
// Re-require to pick up the env change (module is cached; reload via Jest reset)
|
||||
jest.isolateModules(() => {
|
||||
// eslint-disable-next-line global-require
|
||||
const { GDevelopFirebaseConfig: fresh } = require('./ApiConfigs');
|
||||
expect(fresh.apiKey).toBe('test-from-env');
|
||||
});
|
||||
});
|
||||
|
||||
test('the apiKey falls back to a clearly-marked placeholder when env is missing', () => {
|
||||
delete process.env.GD_FIREBASE_API_KEY;
|
||||
jest.isolateModules(() => {
|
||||
// eslint-disable-next-line global-require
|
||||
const { GDevelopFirebaseConfig: fresh } = require('./ApiConfigs');
|
||||
expect(fresh.apiKey).toMatch(/^AIzaSyDUMMY/);
|
||||
expect(fresh.apiKey).toMatch(/DUMMY|REPLACE|YOUR-OWN/);
|
||||
});
|
||||
});
|
||||
|
||||
test('the non-secret fields stay as the gdevelop-services Firebase project', () => {
|
||||
expect(GDevelopFirebaseConfig.authDomain).toBe(
|
||||
'gdevelop-services.firebaseapp.com'
|
||||
);
|
||||
expect(GDevelopFirebaseConfig.projectId).toBe('gdevelop-services');
|
||||
expect(GDevelopFirebaseConfig.storageBucket).toBe(
|
||||
'gdevelop-services.appspot.com'
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user