Security and Secret Handling in Selenium Suites
Keeping real credentials out of a Selenium suite's committed code entirely, and the specific risks a real, credential-driven browser automation suite needs to manage deliberately.
What you'll learn
- Design test credential handling that never commits a real secret to version control
- Explain the specific risk a captured screenshot or trace can pose if taken carelessly
- Identify what a test-only account should and shouldn't be able to do
Prerequisites
Explanation
A Selenium suite that logs in as part of its normal operation genuinely needs credentials somewhere — the discipline this course has referenced throughout (environment variables, never literal values in committed files) applies here with the same force as anywhere else: String username = System.getenv("TEST_USER");, never String username = "realuser@company.com"; hard-coded directly in a committed .java file. A committed .env.example (listing variable names only, with no real values) documents what a real, local, gitignored .env needs to supply, and a CI platform's own secret-store mechanism supplies the same variables in that environment — the committed code never contains, or needs to contain, the actual secret value at any point.
Captured artifacts carry a real, specific, easy-to-overlook risk: a screenshot or a trace taken during a real, credentialed test run can genuinely contain sensitive data visible on the page at that moment — a real (if test-only) account's email, a password briefly visible in an unmasked field, session tokens embedded in a URL, or personally-identifiable data if the test environment happens to contain any. Before treating "capture screenshots on every failure" as an unconditionally safe default, it's worth confirming a test environment's data is genuinely synthetic (dummy accounts, dummy content) — capturing evidence from a test suite that happens to run against a copy of real production data is a genuinely different, higher-risk situation than capturing evidence from data specifically created for testing.
A test-only account should be provisioned with the least privilege actually needed for the scenarios it drives, the same principle covered for database roles in an entirely different context (if you've taken this platform's PostgreSQL course) — a test account used only to verify a learner-facing dashboard has no legitimate reason to also hold administrative privileges, and giving it more access than its actual test scenarios require expands the blast radius if that test account's credentials are ever compromised, for zero real benefit to what the tests actually need to verify. Rotating test credentials periodically, and immediately if a leak is ever suspected, applies the same operational discipline used for any other credential — a test account is a real account with real access, not somehow exempt from the security practices that apply to every other credential in a system.
Example
Modeling the env-var-name-not-value discipline and least-privilege test-account provisioning, as data.
function isSafeCredentialReference(codeValue) {
// A safe committed reference NAMES an environment variable; it never
// contains a literal-looking real value.
return typeof codeValue === "string" && codeValue.startsWith("System.getenv(");
}
console.log(isSafeCredentialReference('System.getenv("TEST_USER")')); // true -- safe reference
console.log(isSafeCredentialReference("realuser@company.com")); // false -- a literal value should never appear here
function isLeastPrivilege(testAccountRole, scenariosNeeded) {
const rolePrivileges = { learner: ["view-dashboard", "complete-lesson"], admin: ["view-dashboard", "complete-lesson", "delete-users", "manage-billing"] };
const granted = rolePrivileges[testAccountRole] ?? [];
const unnecessary = granted.filter((p) => !scenariosNeeded.includes(p));
return unnecessary.length === 0;
}
console.log(isLeastPrivilege("learner", ["view-dashboard", "complete-lesson"])); // true -- exactly what's needed
console.log(isLeastPrivilege("admin", ["view-dashboard", "complete-lesson"])); // false -- admin grants far more than these scenarios needTry it yourself
Call isSafeCredentialReference with a value that LOOKS like an env-var call but has a real value concatenated in, and consider whether this simple check would actually catch that.
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Guided exercise
Guided exercise
Write isSafeCredentialReference(codeValue) returning true only if codeValue is a string starting with 'System.getenv(' (a safe environment-variable reference), false otherwise.
Checks: recognizes a genuine env-var reference as safe · rejects a literal-looking hard-coded value · rejects a non-string value
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Independent exercise
Independent exercise
Write isLeastPrivilege(grantedPrivileges, scenariosNeeded) returning true only if EVERY privilege in grantedPrivileges is actually present in scenariosNeeded (no unnecessary extra access granted). Then write blastRadius(grantedPrivileges) returning grantedPrivileges.length as a simple, concrete proxy for how much damage a compromised account's credentials could do.
Checks: correctly confirms a least-privilege grant · correctly flags an over-privileged grant · computes blast radius as a simple count of granted privileges
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Common mistakes
- Hard-coding a real (even if 'just a test account') credential directly in a committed test file -- credentials belong in environment variables, referenced by name only, regardless of how disposable the account seems.
- Enabling automatic screenshot/trace capture on every failure without confirming the test environment's data is genuinely synthetic -- captured evidence from a suite running against real or real-derived data carries genuine sensitive-data exposure risk.
- Provisioning a test account with more privilege than its actual test scenarios need, 'just in case a future test needs it' -- this expands the real blast radius of a credential leak for no benefit to the tests that actually exist today.
Knowledge check
Takeaway
Test credentials belong in environment variables, referenced by name only, never as literal values in committed code, regardless of how disposable the account seems; captured failure artifacts can genuinely expose sensitive data if the test environment isn't confirmed synthetic; and a test account should hold only the privilege its actual scenarios need, to limit the real blast radius of a potential credential leak.
Summary
Reference test credentials via environment variables (System.getenv), never literal values, in committed code — a committed .env.example documents variable names only. Captured screenshots/traces can expose real sensitive data if the test environment isn't genuinely synthetic. Provision test accounts with least privilege, matching only their actual test scenarios' real needs.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.