Skip to content

test: add regression tests for Jira credential encryption at rest (#1797)#1838

Merged
Priyanshu-byte-coder merged 1 commit into
Priyanshu-byte-coder:mainfrom
Ridanshi:fix/jira-credentials-encryption
Jun 2, 2026
Merged

test: add regression tests for Jira credential encryption at rest (#1797)#1838
Priyanshu-byte-coder merged 1 commit into
Priyanshu-byte-coder:mainfrom
Ridanshi:fix/jira-credentials-encryption

Conversation

@Ridanshi
Copy link
Copy Markdown
Contributor

@Ridanshi Ridanshi commented Jun 1, 2026

Closes #1797

Investigation result

Issue confirmed — and the encryption fix is already applied. The full Jira credential lifecycle was traced:

Storage path (POST /api/integrations/jira/credentials)

const { encrypted, iv } = encryptToken(apiToken);   // AES-256-GCM
await supabaseAdmin.from("jira_credentials").upsert({
  api_token: encrypted,   // ciphertext stored
  token_iv: iv,           // IV stored
  ...                     // plaintext apiToken never written
});

Read path (GET /api/integrations/jira/credentials)

.select("id, jira_domain, email, project_key, is_active, created_at")
// api_token and token_iv are deliberately excluded from the SELECT

Jira API usage (GET /api/integrations/jira)

const decrypted = decryptToken(cred.api_token, cred.token_iv);
// decrypted token used only for the outbound Jira API call
// never returned in the response

The encryption uses AES-256-GCM (authenticated encryption) via the project's existing encryptToken / decryptToken helpers in src/lib/crypto.ts, keyed from ENCRYPTION_KEY in the environment. This matches the pattern already in use for WakaTime keys and GitHub OAuth tokens.

What was missing

Zero regression tests existed for the credential lifecycle. A future refactor could reintroduce plaintext storage with nothing to catch it.

What this PR adds — test/jira-credentials.test.ts (15 new tests)

Category Tests
POST — encryption at rest Encrypted value (not plaintext) is written to DB; response body never contains the token; encryptToken not called when Jira connectivity check fails; 401 for unauthenticated; 400 for invalid domain; 400 for missing fields
GET credentials — token never exposed SELECT clause excludes api_token and token_iv; serialised response contains no token data; empty array when no credentials exist
DELETE Specific credential by ID; all credentials without ID
GET Jira data — decryption flow decryptToken called with DB values; Jira API receives decrypted token; 500 when decryption fails (Jira API not called); 404 for missing credentials; metrics response exposes no credential data

All 15 pass. The 10 pre-existing upstream failures (unrelated to this change) remain unchanged.

…iyanshu-byte-coder#1797)

Issue investigation
-------------------
The Jira credential lifecycle was traced end-to-end:

  POST /api/integrations/jira/credentials
    → validates domain, email, token
    → testJiraConnection() (Jira /myself endpoint)
    → encryptToken(apiToken)          -- AES-256-GCM via crypto.ts
    → supabase.upsert({ api_token: encrypted, token_iv: iv })

  GET /api/integrations/jira/credentials
    → SELECT "id, jira_domain, email, project_key, is_active, created_at"
    → never selects api_token or token_iv

  GET /api/integrations/jira (Jira data)
    → SELECT * from jira_credentials
    → decryptToken(cred.api_token, cred.token_iv)
    → use decrypted token for Jira API call only

The encryption-at-rest fix is already applied in the codebase. The
api_token column stores the AES-256-GCM ciphertext and token_iv stores
the random IV. The plaintext token is never persisted and never returned
to clients.

What was missing: zero regression tests existed for this behaviour.
A future refactor could inadvertently introduce plaintext storage
without any tests to catch it.

test/jira-credentials.test.ts — 15 new tests:

  POST /credentials — encryption at rest
  * stores encrypted token, never the plaintext (regression for Priyanshu-byte-coder#1797)
  * response body never contains the token in any form
  * validates Jira connection before storing; if validation fails,
    encryptToken is never called
  * returns 401 for unauthenticated requests
  * returns 400 for invalid domain format
  * returns 400 for missing required fields

  GET /credentials — token never exposed
  * uses a column-restricted SELECT that excludes api_token and token_iv
  * response serialisation contains no token data (encrypted or plain)
  * returns empty array when no credentials exist

  DELETE /credentials
  * deletes a specific credential by ID
  * deletes all credentials when no ID is provided

  GET /jira — decryption and Jira API usage
  * decryptToken is called with the encrypted values from the DB; the
    outbound Jira API call uses the decrypted token
  * returns 500 when decryption fails; Jira API is never called
  * returns 404 when no active credentials exist
  * Jira metrics response never exposes any credential data

Closes Priyanshu-byte-coder#1797
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

@Ridanshi is attempting to deploy a commit to the PRIYANSHU DOSHI's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added gssoc26 GSSoC 2026 contribution type:feature GSSoC type bonus: new feature type:testing GSSoC type bonus: tests (+10 pts) labels Jun 1, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

GSSoC Label Checklist 🏷️

@Priyanshu-byte-coder — please apply the appropriate labels before merging:

Difficulty (pick one):

  • level:beginner — 20 pts
  • level:intermediate — 35 pts
  • level:advanced — 55 pts
  • level:critical — 80 pts

Quality (optional):

  • quality:clean — ×1.2 multiplier
  • quality:exceptional — ×1.5 multiplier

Validation (required to score):

  • gssoc:approved — counts for points
  • gssoc:invalid / gssoc:spam / gssoc:ai-slop — does not score

Type labels (type:*) are auto-detected from files and title. Review and adjust if needed.
Points formula: (difficulty × quality_multiplier) + type_bonus

@gitguardian
Copy link
Copy Markdown

gitguardian Bot commented Jun 1, 2026

⚠️ GitGuardian has uncovered 1 secret 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 secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
- - Generic High Entropy Secret 12f2ecc test/jira-credentials.test.ts 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 secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  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.

@Priyanshu-byte-coder Priyanshu-byte-coder added the level2 GSSoC Level 2 - Medium complexity (25 points) label Jun 2, 2026
@Priyanshu-byte-coder Priyanshu-byte-coder merged commit 071c70d into Priyanshu-byte-coder:main Jun 2, 2026
9 of 12 checks passed
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

🎉 Merged! Thanks for contributing to DevTrack.

If the project has been useful to you, a ⭐ star on the repo is the easiest way to support it — it helps DevTrack get discovered by more developers.

Keep an eye on open issues for your next contribution!

@Priyanshu-byte-coder Priyanshu-byte-coder added gssoc:approved GSSoC: PR approved for scoring quality:clean GSSoC: Clean quality multiplier (×1.2) labels Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved GSSoC: PR approved for scoring gssoc26 GSSoC 2026 contribution level2 GSSoC Level 2 - Medium complexity (25 points) quality:clean GSSoC: Clean quality multiplier (×1.2) type:feature GSSoC type bonus: new feature type:testing GSSoC type bonus: tests (+10 pts)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Security] /api/integrations/jira/credentials stores Jira tokens without encryption at rest

2 participants