A simple, typed client for the CTFd API.
npm i @b01lers/ctfd-api
Nominally, CTFd maintains documentation about their API endpoints through their API docs site. However, many response fields and request parameters remain undocumented.
Furthermore, not every API endpoint is actually accessible through the AccessToken
-based authorization
documented on the site, and certain endpoints have missing or incorrect data when accessed with an AccessToken
;
notably,
- The flag submission endpoint always returns 403 when requested without an authenticated session cookie.
- The challenge list endpoint doesn't populate the
solved_by_me
field correctly when quested without an authenticated session cookie.
Therefore, the client provided by this library is a "user bot" that uses your credentials to authenticate the way a regular user would.
const client = new CTFdClient({
url: 'https://demo.ctfd.io/',
username: 'user',
password: 'password',
});
Many of the types used and exported by this library are reverse engineered from CTFd's client requests as well, and may likely be more comprehensive than what is documented on their official API docs above.
Example usage that fetches challenges from the CTFd demo instance and attempts to submit a flag for a challenge:
import { CTFdClient } from '@b01lers/ctfd-api';
const client = new CTFdClient({
url: 'https://demo.ctfd.io/',
username: 'user',
password: 'password',
});
// Fetch challenges list
const challs = await client.getChallenges();
// Fetch scoreboard data
const scoreboard = await client.getScoreboard();
console.log(scoreboard.slice(0, 5));
// Get details about a challenge, and submits a flag
const chall = challs.find((c) => c.name === 'The Lost Park')!;
const details = await client.getChallengeDetails(chall.id);
console.log(details.description);
await client.submitFlag(chall.id, 'cftd{test_flag}');