Skip to content

Commit 71e183e

Browse files
authored
Merge pull request #157 from TFNS/dev
v2.1.0
2 parents c125a07 + c055d3e commit 71e183e

31 files changed

+5456
-2855
lines changed

CONTRIBUTING.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# CTFNote - Contribution Guide
2+
3+
When contributing to this repository, please first discuss the change you wish to make via issue with the collaborators of this repository before making a change.
4+
5+
## Git process
6+
7+
In order to push new code on this repository, you first have to create a new fork within your github workspace.
8+
9+
Once your fork is created (`https://github.com/<your_username>/CTFNote`), you can create a new branch starting with the associated issue number in the name and start working on it.
10+
11+
> If you start from scratch and no issues are associated with your changes, you can create a branch starting with '0-'.
12+
13+
```shell
14+
$ git checkout -b <issue-number>-<branch-name>
15+
```
16+
17+
Examples of branch name:
18+
19+
- `132-add-new-feature`
20+
- `343-add-past-ctf-role`
21+
- `13-fix-bug-in-password-reset`
22+
- `37-leak-flag-to-TFNS`
23+
- `0-contribution-guide`
24+
25+
Once you think the job is done, issue the pull request and target the **dev** branch of the official CTFNote repository.
26+
27+
You can also create the pull request before finishing the job but don't forget to add "WiP: " as a suffix in the title to let the collaborators know you are still working on the request.
28+
29+
## Deploying the dev version
30+
31+
### Prerequisites
32+
33+
- [Install docker](https://docs.docker.com/get-docker/)
34+
- [Install yarn](https://classic.yarnpkg.com/lang/en/docs/install/)
35+
36+
### Install the git hooks
37+
38+
Go at the root folder and install the dependencies and install the git hooks:
39+
40+
```shell
41+
$ yarn
42+
```
43+
44+
This should run the prepare script and install the linting pre-commit hooks:
45+
46+
```
47+
[1/4] 🔍 Resolving packages...
48+
[2/4] 🚚 Fetching packages...
49+
[3/4] 🔗 Linking dependencies...
50+
[4/4] 🔨 Building fresh packages...
51+
$ husky install
52+
[##] 2/2husky - Git hooks installed
53+
✨ Done in 0.40s.
54+
```
55+
56+
### Start the third party containers
57+
58+
```shell
59+
$ docker compose \
60+
-f docker-compose.dev.yml \
61+
up -d hedgedoc db adminer
62+
```
63+
64+
### Start the API
65+
66+
```shell
67+
$ cd api
68+
api/ $ yarn # Install the dependencies
69+
api/ $ yarn dev # Run the dev version (hot reloading included)
70+
```
71+
72+
### Start the Front
73+
74+
```shell
75+
$ cd front
76+
front/ $ yarn # Install the dependencies
77+
front/ $ yarn dev # Run the dev version (hot reloading included)
78+
```
79+
80+
### Exposed endpoints
81+
82+
The following endpoint are exposed and can be used in the developpement environment
83+
84+
- [API](http://localhost:3000/)
85+
- [GraphiQL](http://localhost:3000/graphiql)
86+
- [Hedgedoc](http://localhost:3001/)
87+
- [Quasar APP](http://localhost:8088/)
88+
- [Adminer](http://localhost:3002/?pgsql=db&username=ctfnote&db=ctfnote)
89+
90+
## Review
91+
92+
To merge a pull request, two distinct reviews from two different collaborators are required.

README.md

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
[![CTFNote logo](screenshots/logo_small.webp)](screenshots/logo.png)
2+
23
# CTFNote
34

45
## Introduction
6+
57
CTFNote is a collaborative tool aiming to help CTF teams to organise their work.
68

79
[![Screenshot of the task list](screenshots/task_small.webp)](screenshots/task.png)
810

9-
1011
## Installation
1112

1213
Before starting, make sure to fill in the information in the `.env` file.
@@ -52,12 +53,14 @@ server {
5253

5354
Edit the `docker-compose.yml` file to make sure CTFNote only listens on
5455
localhost:
56+
5557
```diff
5658
- - 8080:80
5759
+ - 127.0.0.1:8080:80
5860
```
5961

6062
Edit the `.env` file to instruct the pad to use TLS:
63+
6164
```diff
6265
# Secure: we're using HTTPS
6366
-# CMD_PROTOCOL_USESSL=true
@@ -68,48 +71,52 @@ Edit the `.env` file to instruct the pad to use TLS:
6871
+CMD_DOMAIN=example.org:1337
6972
```
7073

71-
After deploying this configuration, run `certbot` to make it available over HTTPS.
74+
After deploying this configuration, run `certbot` to make it available over HTTPS.
7275
See [this article](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-20-04) for more information.
7376

7477
### Migration
7578

7679
If you already have an instance of CTFNote in a previous version and wish to
7780
upgrade, you should follow the guide at [MIGRATION.md](MIGRATION.md).
7881

79-
8082
## Privileges
83+
8184
When other players register on your CTFNote instance, they will not be able to
8285
see CTF or tasks. This is because CTFNote uses different roles to restrict CTF
8386

84-
You can manage other players' roles in the *Users* tab of the *Admin* panel.
87+
You can manage other players' roles in the _Users_ tab of the _Admin_ panel.
8588

8689
Additionally, you can generate a secret that lets users create an account with a
87-
different privilege in the *Registration with password* menu in the *Admin*
90+
different privilege in the _Registration with password_ menu in the _Admin_
8891
panel.
8992

9093
![Screenshot of the Registration with password menu](screenshots/reg_password.png)
9194

9295
### Guest
96+
9397
Guest is the default role. This role is meant to be used for guests and friends
9498
helping sporadically on CTF.
9599

96-
You can add a guest to a CTF by ticking their badge in the *Guests* tab on a
100+
You can add a guest to a CTF by ticking their badge in the _Guests_ tab on a
97101
specific CTF.
98102

99103
![Screenshot of the guest menu](screenshots/guests.png)
100104

101105
### Friend
102-
Friend is a role between guest and member which allows the player to automatically
106+
107+
Friend is a role between guest and member which allows the player to automatically
103108
view old CTFs but not active and upcoming CTFs. They are also not allowed to invite any
104109
new players to a CTF. You can use this role to grant guests access to your old CTFs
105110
for them to learn from without granting access to each old CTF individually.
106111

107112
### Member
113+
108114
Member is a role that represents a team member. A certain level of trust is
109115
given to these users: they can see every CTF, future, current and past. They can
110116
also invite guests to CTF.
111117

112118
### Manager
119+
113120
Manager is a role that represents a team captain. They can create, import,
114121
modify and delete CTF.
115122

@@ -118,32 +125,42 @@ They can import CTF directly from [CTFtime](https://ctftime.org).
118125
![Screenshot of the Import CTF feature](screenshots/import.png)
119126

120127
### Admin
121-
Admin is a role with every privileges. They have access to the *Admin* panel
128+
129+
Admin is a role with every privileges. They have access to the _Admin_ panel
122130
that lets them delete accounts, change permissions, reset passwords, create
123131
one-time secrets and, most importantly, change the theme colours.
124132

125133
![Screenshot of the theme menu](screenshots/theme.png)
126134

127-
128135
## Configuration
136+
129137
The configuration can be changed in the `.env` file. This file contains
130138
environment variables for the containers.
131139

132140
The value of every variables are explained in this file.
133141

134-
135142
## Screenshots
143+
136144
### List of the CTF
145+
137146
[![Screenshot of the CTF page](screenshots/ctf_small.webp)](screenshots/ctf.png)
138147

139148
### Calendar
149+
140150
[![Screenshot of the CTF calendar](screenshots/calendar_small.webp)](screenshots/calendar.png)
141151

142152
### Information of a single CTF
153+
143154
[![Screenshot of the CTF info](screenshots/info_small.webp)](screenshots/info.png)
144155

145156
### Task list for a CTF
157+
146158
[![Screenshot of the task list](screenshots/task_small.webp)](screenshots/task.png)
147159

148160
### Shared notepad for a task
161+
149162
[![Screenshot of pad](screenshots/pad_small.webp)](screenshots/pad.png)
163+
164+
## Contributing
165+
166+
A contribution guide is available here: [CONTRIBUTING.md](CONTRIBUTING.md)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
CREATE INDEX ON ctfnote.ctf (title);
2+
CREATE INDEX ON ctfnote.task (title);

api/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"start": "NODE_ENV=production node dist/index.js",
99
"build": "tsc",
1010
"lint": "eslint --fix 'src/**/*.ts'",
11-
"format": "prettier --write 'src/*.ts'",
11+
"format": "prettier --write 'src/**/*.ts'",
1212
"dev": "NODE_ENV=development nodemon src/index.ts",
1313
"dev:migrate": "DATABASE_URL= yarn run db-migrate -e dev up"
1414
},
@@ -27,8 +27,8 @@
2727
"graphile-utils": "^4.11.2",
2828
"graphql": "^15.6.1",
2929
"graphql-upload": "^12.0.0",
30-
"postgraphile": "^4.11.0",
31-
"postgraphile-plugin-connection-filter": "^2.1.1",
30+
"postgraphile": "^4.12.8",
31+
"postgraphile-plugin-connection-filter": "^2.2.2",
3232
"postgres-migrations": "^5.3.0"
3333
},
3434
"devDependencies": {

api/src/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import createTasKPlugin from "./plugins/createTask";
1414
import importCtfPlugin from "./plugins/importCtf";
1515
import uploadLogoPlugin from "./plugins/uploadLogo";
1616
import uploadScalar from "./plugins/uploadScalar";
17+
import ConnectionFilterPlugin from "postgraphile-plugin-connection-filter";
1718

1819
function getDbUrl(role: "user" | "admin") {
1920
const login = config.db[role].login;
@@ -43,6 +44,7 @@ function createOptions() {
4344
importCtfPlugin,
4445
uploadLogoPlugin,
4546
createTasKPlugin,
47+
ConnectionFilterPlugin,
4648
],
4749
ownerConnectionString: getDbUrl("admin"),
4850
enableQueryBatching: true,
@@ -60,6 +62,14 @@ function createOptions() {
6062
postgraphileOptions.jwtSecret = "DEV";
6163
postgraphileOptions.showErrorStack = "json" as const;
6264
postgraphileOptions.extendedErrors = ["hint", "detail", "errcode"];
65+
66+
postgraphileOptions.graphileBuildOptions = {
67+
connectionFilterAllowedOperators: ["includesInsensitive"],
68+
connectionFilterAllowedFieldTypes: ["String"],
69+
connectionFilterComputedColumns: false,
70+
connectionFilterSetofFunctions: false,
71+
connectionFilterArrays: false,
72+
};
6373
}
6474
return postgraphileOptions;
6575
}
@@ -70,7 +80,7 @@ function createApp(postgraphileOptions: PostGraphileOptions) {
7080
app.use(
7181
"/uploads",
7282
express.static("uploads", {
73-
setHeaders: function (res, path, stat) {
83+
setHeaders: function (res) {
7484
res.set("Content-Disposition", "attachment");
7585
},
7686
})

api/src/plugins/createTask.ts

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,52 @@ import axios from "axios";
33
import savepointWrapper from "./savepointWrapper";
44
import config from "../config";
55

6-
async function createPad(): Promise<string> {
6+
function buildNoteContent(
7+
title: string,
8+
description?: string,
9+
category?: string
10+
): string {
11+
let note = "";
12+
13+
note += `# ${title}`;
14+
15+
if (category) {
16+
note += ` - ${category}`;
17+
}
18+
19+
note += "\n\n";
20+
21+
if (description) {
22+
note += `## Description\n`;
23+
note += "\n";
24+
note += `${description}\n`;
25+
26+
note += "\n";
27+
note += "----\n";
28+
}
29+
return note;
30+
}
31+
32+
async function createPad(
33+
title: string,
34+
description?: string,
35+
category?: string
36+
): Promise<string> {
37+
const options = {
38+
headers: {
39+
"Content-Type": "text/markdown",
40+
},
41+
42+
maxRedirects: 0,
43+
validateStatus: (status: number) => status === 302,
44+
};
45+
746
try {
8-
const res = await axios.get(config.pad.createUrl, {
9-
maxRedirects: 0,
10-
validateStatus: (status) => status === 302,
11-
});
47+
const res = await axios.post(
48+
config.pad.createUrl,
49+
buildNoteContent(title, description, category),
50+
options
51+
);
1252
return res.headers.location;
1353
} catch (e) {
1454
throw Error(`Call to ${config.pad.createUrl} during task creation failed.`);
@@ -44,7 +84,17 @@ export default makeExtendSchemaPlugin((build) => {
4484
{ pgClient },
4585
resolveInfo
4686
) => {
47-
const padPathOrUrl = await createPad();
87+
const {
88+
rows: [isAllowed],
89+
} = await pgClient.query(`SELECT ctfnote_private.can_play_ctf($1)`, [
90+
ctfId,
91+
]);
92+
93+
if (isAllowed.can_play_ctf !== true) {
94+
return {};
95+
}
96+
97+
const padPathOrUrl = await createPad(title, description, category);
4898

4999
let padPath: string;
50100
if (padPathOrUrl.startsWith("/")) {

0 commit comments

Comments
 (0)