Skip to content

Commit d740a1a

Browse files
committed
Initial commit
0 parents  commit d740a1a

File tree

140 files changed

+7816
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

140 files changed

+7816
-0
lines changed

.gitattributes

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

.github/workflows/vercel-merge.yml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Deploy to vercel on merge
2+
on:
3+
push:
4+
branches:
5+
- main
6+
jobs:
7+
build_and_deploy:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v2
11+
- uses: amondnet/vercel-action@v20
12+
with:
13+
vercel-token: ${{ secrets.VERCEL_TOKEN }}
14+
github-token: ${{ secrets.GITHUB_TOKEN }}
15+
vercel-args: '--prod'
16+
vercel-org-id: ${{ secrets.ORG_ID}}
17+
vercel-project-id: ${{ secrets.PROJECT_ID}}
18+
github-comment: false
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Create vercel preview URL on pull request
2+
on:
3+
pull_request:
4+
branches:
5+
- main
6+
jobs:
7+
build_and_deploy:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v2
11+
- uses: amondnet/vercel-action@v20
12+
id: vercel-deploy
13+
with:
14+
vercel-token: ${{ secrets.VERCEL_TOKEN }}
15+
github-token: ${{ secrets.GITHUB_TOKEN }}
16+
vercel-org-id: ${{ secrets.ORG_ID}}
17+
vercel-project-id: ${{ secrets.PROJECT_ID}}
18+
- name: preview-url
19+
run: |
20+
echo ${{ steps.vercel-deploy.outputs.preview-url }}

.gitignore

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# dependencies
2+
/node_modules
3+
/.pnp
4+
.pnp.js
5+
6+
# testing
7+
/coverage
8+
9+
# next.js
10+
/.next/
11+
/out/
12+
13+
# production
14+
/build
15+
16+
# misc
17+
.DS_Store
18+
*.pem
19+
.idea
20+
21+
# rCTF
22+
/data
23+
/conf.d/04-email.yaml
24+
/conf.d/05-uploads.yaml
25+
/conf.d/06-secrets.yaml
26+
27+
# debug
28+
npm-debug.log*
29+
yarn-debug.log*
30+
yarn-error.log*
31+
32+
.env*
33+
34+
# vercel
35+
.vercel
36+
37+
# typescript
38+
*.tsbuildinfo
39+
next-env.d.ts

Dockerfile

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FROM node:20-alpine
2+
3+
WORKDIR /app
4+
COPY . .
5+
RUN npm i && npm run build
6+
7+
CMD ["npm", "start"]

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Kevin Yu
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# bctf
2+
3+
The updated website for b01lers CTF!
4+
5+
This website was heavily inspired by [LA CTF](https://platform.lac.tf/) and the default rCTF frontend.
6+
7+
### Running locally
8+
9+
This is a custom Next.js frontend wrapping [rCTF](https://rctf.redpwn.net/) as a backend. To configure rCTF, edit the
10+
config files in `/conf.d` [as normal](https://rctf.redpwn.net/configuration/). In `conf.d`,
11+
12+
- `01-ui.yaml` defines rCTF's UI config values. These values are mostly ignored by the custom frontend, but necessary
13+
for styling certain things that we don't have full control over (ex. the email template).
14+
- `02-ctf.yaml` defines metadata for the actual CTF, including divisions, start / end time, and the frontend URL.
15+
- `03-db.yaml` defines config options for rCTF's underlying databases.
16+
- `04-email.yaml` defines config options for email verification. This includes an API key so isn't committed, but an
17+
example is included in `04-email.example.yaml`.
18+
- `05-uploads.yaml` defines config options for GCS uploads. This includes a private key and service account email so
19+
isn't committed, but an example is included in `05-uploads.example.yaml`.
20+
- `06-secrets.yaml` defines secret rCTF values such as the token key.
21+
22+
To run just the frontend, first install dependencies with
23+
```bash
24+
npm i
25+
```
26+
In `next.config.js`, set `env.API_URL` to the public API URL of the rCTF instance, and `KLODD_URL` to the public URL of
27+
the Klodd instancer frontend:
28+
```js
29+
const nextConfig = {
30+
env: {
31+
API_BASE: 'http://ctf.b01lers.com:9000/api/v1',
32+
KLODD_URL: 'https://klodd.localhost.direct'
33+
}
34+
}
35+
```
36+
Then, run
37+
```bash
38+
npm run dev
39+
```
40+
to start the development server on `localhost:3000`.
41+
42+
To start rCTF, you'll need a `.env` file in the project root exporting database credentials:
43+
```env
44+
RCTF_DATABASE_PASSWORD=...
45+
RCTF_REDIS_PASSWORD=...
46+
RCTF_GIT_REF=master
47+
```
48+
(this file can be copied after running [rCTF's install script](https://rctf.redpwn.net/installation/)).
49+
50+
You can then start both the rCTF backend and production frontend instance simultaneously with
51+
```bash
52+
docker-compose up -d --build
53+
```
54+
55+
### Configuring
56+
Further config options can be edited in `/util/config.ts`:
57+
```ts
58+
export const SOLVES_PAGE_SIZE = 10;
59+
export const SCOREBOARD_PAGE_SIZE = 100;
60+
61+
export const AUTH_COOKIE_NAME = 'ctf_clearance';
62+
```
63+
- `SOLVES_PAGE_SIZE` — The number of solves to show on each page of the solves modal.
64+
- `SCOREBOARD_PAGE_SIZE` — The number of teams to show on each page of the scoreboard.
65+
- `AUTH_COOKIE_NAME` — The name of the auth token cookie.

app/(home)/Header.tsx

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Timer from '@/app/(home)/Timer';
2+
import { BsChevronCompactDown } from 'react-icons/bs';
3+
import { getConfig } from '@/util/config';
4+
5+
6+
export default async function Header() {
7+
const config = await getConfig();
8+
9+
return (
10+
<header className="container flex flex-col items-center justify-center h-screen">
11+
<img
12+
src="/assets/logo-uwu.png"
13+
className="-mb-7 max-h-96"
14+
/>
15+
<Timer
16+
startTime={config.data.startTime}
17+
endTime={config.data.endTime}
18+
/>
19+
<p className="mb-2 max-w-3xl text-center text-pretty">
20+
b01lers CTF is the public competitive CTF hosted by the b01lers CTF team at Purdue University.
21+
Join our discord at <a href="https://discord.gg/tBMqujE" target="_blank" rel="noopener noreferrer" className="text-theme-bright hover:underline">discord.gg/tBMqujE</a>{' '}
22+
and look out for further info soon!
23+
</p>
24+
<div className="flex divide-x divide-primary text-sm">
25+
<a href="#rules" className="px-4 py-2 uppercase hover:underline">Rules</a>
26+
<a href="#prizes" className="px-4 py-2 uppercase hover:underline">Prizes</a>
27+
<a href="#sponsors" className="px-4 py-2 uppercase hover:underline">Sponsors</a>
28+
</div>
29+
30+
<a href="#rules" className="text-inherit text-4xl mt-6 sm:mt-12 sm:mb-8 text-primary">
31+
<BsChevronCompactDown className="animate-bounce" />
32+
<span className="sr-only">Jump to Rules</span>
33+
</a>
34+
</header>
35+
)
36+
}

app/(home)/Prizes.tsx

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import type { ReactNode } from 'react';
2+
import SectionHeader from '@/components/SectionHeader';
3+
4+
5+
export default function Prizes() {
6+
const openDivisionPrizes = ['$400', '$200', '$100'];
7+
const purdueDivisionPrizes = [
8+
'4x Three Month Tryhackme Premium Vouchers',
9+
'4x Two Month Tryhackme Premium Vouchers',
10+
'4x One month Tryhackme Premium Vouchers'
11+
];
12+
13+
return (
14+
<>
15+
<SectionHeader id="prizes">
16+
Prizes
17+
</SectionHeader>
18+
19+
<div className="flex flex-col lg:flex-row gap-4 mb-4">
20+
<PrizeTable division="Open">
21+
{openDivisionPrizes.map((p, i) => (
22+
<div className="table-row bg-black/20 divide-x divide-secondary" key={i}>
23+
<div className="table-cell p-2 border-t border-secondary text-right">{i + 1}.</div>
24+
<div className="table-cell px-4 py-2 border-t border-secondary">{p}</div>
25+
</div>
26+
))}
27+
</PrizeTable>
28+
29+
<PrizeTable division="Purdue">
30+
{purdueDivisionPrizes.map((p, i) => (
31+
<div className="table-row bg-black/20 divide-x divide-secondary" key={i}>
32+
<div className="table-cell p-2 border-t border-secondary text-right">{i + 1}.</div>
33+
<div className="table-cell px-4 py-2 border-t border-secondary">{p}</div>
34+
</div>
35+
))}
36+
</PrizeTable>
37+
</div>
38+
39+
<p className="text-sm text-primary">
40+
Prize transfers will be arranged with Venmo or Cashapp and can only be transferred to an entity in
41+
the United States.
42+
</p>
43+
</>
44+
)
45+
}
46+
47+
function PrizeTable(props: { children: ReactNode, division: string }) {
48+
return (
49+
<div className="table w-full text-sm border border-secondary">
50+
<div className="table-header-group">
51+
<div className="table-row bg-black/40 font-semibold text-primary divide-x divide-secondary">
52+
<div className="table-cell p-2 w-12 text-right">#</div>
53+
<div className="table-cell px-4 py-2">
54+
{props.division} division prizes
55+
</div>
56+
</div>
57+
</div>
58+
59+
{props.children}
60+
</div>
61+
)
62+
}

app/(home)/Rules.tsx

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import SectionHeader from '@/components/SectionHeader';
2+
3+
4+
export default function Rules() {
5+
return (
6+
<>
7+
<SectionHeader id="rules">
8+
Rules
9+
</SectionHeader>
10+
<ol className="list-decimal list-outside pl-6 space-y-2 mb-16">
11+
<li>
12+
During the competition, each person may only be a part of one team total, and only members of a
13+
given team may assist in solving a challenge for that team.
14+
</li>
15+
<li>
16+
Each team must have a valid email address that should serve as the point of contact.
17+
</li>
18+
<li>
19+
For the <strong>Open</strong> division: there is no limit on team size, and teams can be from
20+
anywhere.
21+
</li>
22+
<li>
23+
For the <strong>Purdue</strong> division: teams must be composed of current Purdue students to be
24+
eligible for prizes, and there is a maximum team size of 4. Sign up with a @purdue.edu email to
25+
gain access to the Purdue division.
26+
</li>
27+
<li>
28+
Flags are of the format{' '}
29+
<code className="bg-black/40 px-2 py-1 text-primary rounded">{'bctf{[ -~]+}'}</code>{' '}
30+
unless otherwise noted on the challenge description. No brute-force guessing flags.
31+
</li>
32+
<li>
33+
No flag or hint sharing. Do not solicit or accept hints or guidance from any person except through
34+
official support channels.
35+
</li>
36+
<li>
37+
Do not attempt to attack or interfere with other teams or any servers used in this competition that
38+
are not explicitly designated for being hacked in a problem.
39+
</li>
40+
<li>
41+
Do not perform any sort of online bruteforce against any of our systems.
42+
</li>
43+
<li>Learn as much as you can, and have a good time!</li>
44+
<li>Pay it forward.</li>
45+
</ol>
46+
</>
47+
)
48+
}

app/(home)/Sponsor.tsx

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type { ReactNode } from 'react';
2+
3+
4+
type SponsorProps = {
5+
href: string,
6+
src: string,
7+
name: string,
8+
children: ReactNode
9+
}
10+
export default function Sponsor(props: SponsorProps) {
11+
return (
12+
<div className="relative flex flex-col md:flex-row gap-x-8 gap-y-6 md:items-center bg-black/30 backdrop-blur-sm rounded px-8 py-6 border border-tertiary hover:border-secondary transition duration-200">
13+
<a
14+
className="absolute inset-0"
15+
href={props.href}
16+
target="_blank"
17+
rel="noopener noreferrer"
18+
/>
19+
<img
20+
className="w-48 h-max flex-none"
21+
src={props.src}
22+
alt={props.name}
23+
/>
24+
<div>
25+
<h3 className="font-semibold mb-2 text-lg">{props.name}</h3>
26+
<p className="text-sm text-primary [&_a]:relative [&_a]:z-10"> {/* TODO: hacky? */}
27+
{props.children}
28+
</p>
29+
</div>
30+
</div>
31+
)
32+
}

0 commit comments

Comments
 (0)