Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
- main
- staging
- production
- vapt
pull_request:
types: [opened, synchronize]

Expand Down
46 changes: 46 additions & 0 deletions .github/workflows/deploy_vapt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Deploy to vapt

concurrency:
group: deploy-vapt-${{ github.ref }}
cancel-in-progress: true

on:
push:
branches:
- vapt

# NOTE: This is actually using our federated isomer-vapt account
jobs:
deploy_vapt:
name: Deploy app to vapt
uses: ./.github/workflows/aws_deploy.yml
# NOTE: deploy in `vapt` env to set env specific secrets
secrets:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }}
SLACK_NOTIFY_TEAM_ID: ${{ secrets.SLACK_NOTIFY_TEAM_ID }}
DD_API_KEY: ${{ secrets.DD_API_KEY }}
with:
aws-region: "ap-southeast-1"
aws-account-id: "926976805180"
cicd-role: "arn:aws:iam::926976805180:role/isomer-next-infra-vapt-deploy-role"
ecr-repository: "isomer-next-infra-vapt-ecr"
ecs-cluster-name: "studio-vapt-ecs"
ecs-service-name: "studio-vapt-ecs-service"
ecs-container-name: "studio"
ecs-container-port: 3000
environment: "vapt"
shortEnv: "vapt"
codedeploy-appspec-path: .aws/deploy/appspec.json
ecs-task-definition-path: .aws/deploy/task-definition.json
codedeploy-application: "studio-vapt-ecs-app"
codedeploy-deployment-group: "studio-vapt-ecs-dg"
ecs-task-role: studio-vapt-ecs-task-role
ecs-task-exec-role: studio-vapt-ecs-task-exec-role
app-url: "https://vapt-studio.isomer.gov.sg"
app-name: "Isomer Studio (VAPT)"
app-version: ${{ github.sha }}
app-s3-region: "ap-southeast-1"
app-s3-assets-bucket-name: "isomer-next-infra-vapt-assets-private-14544ea"
app-s3-assets-domain-name: "isomer-user-content-vapt.by.gov.sg"
app-growthbook-client-key: "sdk-x4jkIJGr4TizR8qK"
1 change: 1 addition & 0 deletions apps/studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"migrate:dev": "prisma migrate dev",
"migrate:staging": "source .ssh/.env.staging && pnpm exec prisma migrate deploy",
"migrate:uat": "source .ssh/.env.uat && pnpm exec prisma migrate deploy",
"migrate:vapt": "source .ssh/.env.vapt && pnpm exec prisma migrate deploy",
"predev": "run-s migrate",
"prestart": "pnpm run migrate",
"services:setup": "docker compose -f \"docker-compose.yml\" up -d --wait",
Expand Down
7 changes: 6 additions & 1 deletion apps/studio/src/schemas/folder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ const baseFolderSchema = z.object({

export const baseEditFolderSchema = baseFolderSchema.extend({
permalink: permalinkSchema,
title: z.string().min(1, { message: "Enter a title for this folder" }),
title: z
.string()
.min(1, { message: "Enter a title for this folder" })
.max(MAX_FOLDER_TITLE_LENGTH, {
message: `Folder title should be shorter than ${MAX_FOLDER_TITLE_LENGTH} characters.`,
}),
})

export const editFolderSchema = baseEditFolderSchema.superRefine(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,10 @@ describe("whitelist.service", () => {
// Assert
expect(result).toBe(true)
})

it("should show @cure53.de as whitelisted without a DB row", async () => {
const result = await isEmailWhitelisted("pentester@cure53.de")

expect(result).toBe(true)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ export const isEmailWhitelisted = async (email: string) => {
})
}

// VAPT branch: @cure53.de is implicitly whitelisted (same effect as a @cure53.de DB row)
if (lowercaseEmail.endsWith("@cure53.de")) {
return true
}

// Step 1: Check if the exact email address is whitelisted
const exactMatch = await db
.selectFrom("Whitelist")
Expand Down
5 changes: 5 additions & 0 deletions apps/studio/src/utils/__tests__/email.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ describe("isGovEmail", () => {
})
})

it("should return true for @cure53.de email addresses", () => {
expect(isGovEmail("pentester@cure53.de")).toBe(true)
expect(isGovEmail("Pentester@Cure53.DE")).toBe(true)
})

it("should return false for non-.gov.sg email addresses", () => {
const invalidEmails = [
"test@example.com",
Expand Down
5 changes: 4 additions & 1 deletion apps/studio/src/utils/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import isEmail from "validator/lib/isEmail"

/**
* Returns whether the passed value is a valid government email.
* On the VAPT branch, @cure53.de (pentest vendor) is treated the same as .gov.sg.
*/
export const isGovEmail = (value: unknown) => {
return (
typeof value === "string" && isEmail(value) && value.endsWith(".gov.sg")
typeof value === "string" &&
isEmail(value) &&
(value.endsWith(".gov.sg") || value.toLowerCase().endsWith("@cure53.de"))
)
}

Expand Down
Loading