7 Commits

Author SHA1 Message Date
6114416b8b fix: check for JWT validity when attempting to decode it
All checks were successful
Update changelog / changelog (push) Successful in 26s
2026-01-27 12:52:21 +01:00
5771a182fe fix: add robots.txt to forbidden url schemas 2026-01-27 12:51:49 +01:00
dfc3f4cd87 fix: add a sane default for CORS trusted origins (127.0.0.1)
All checks were successful
Build and push Docker image / build (push) Successful in 2m50s
Release new version / release (push) Successful in 26s
Update changelog / changelog (push) Successful in 25s
2026-01-21 00:57:39 +01:00
e0d8849bd1 fix: Docker fixes
All checks were successful
Build and push Docker image / build (push) Successful in 2m53s
Release new version / release (push) Successful in 29s
Update changelog / changelog (push) Successful in 24s
2026-01-20 22:29:28 +01:00
429613c67e chore: bump version
All checks were successful
Build and push Docker image / build (push) Successful in 2m49s
Release new version / release (push) Successful in 30s
Update changelog / changelog (push) Successful in 25s
2026-01-08 13:22:27 +01:00
681555fef8 fix: fix subdomain retrieval
All checks were successful
Update changelog / changelog (push) Successful in 25s
2026-01-08 13:20:32 +01:00
066b9884c2 docs: minor markdown rendering fixes to readme
All checks were successful
Update changelog / changelog (push) Successful in 24s
2026-01-07 23:11:18 +01:00
8 changed files with 17 additions and 9 deletions

View File

@@ -1,3 +1,4 @@
.env .env
*/.env */.env
*.md build
node_modules

View File

@@ -1,6 +1,6 @@
# Server config # Server config
ACCESS_TOKEN_PRIVATE_KEY=CHANGE_ME_TO_SOMETHING_RANDOM # Used to generate user tokens. Make sure this is pretty random. ACCESS_TOKEN_PRIVATE_KEY=CHANGE_ME_TO_SOMETHING_RANDOM # Used to generate user tokens. Make sure this is pretty random.
TRUSTED_ORIGINS=http://localhost:6568 # Comma separated list of trusted origins. Make sure to include your PUBLIC_URL here. TRUSTED_ORIGINS=http://localhost:6568,http://127.0.0.1:6568 # Comma separated list of trusted origins. Make sure to include your PUBLIC_URL here.
# TypeORM specific # TypeORM specific
# Please make sure these match with docker-compose.yml, or your own postgres server. # Please make sure these match with docker-compose.yml, or your own postgres server.

View File

@@ -4,7 +4,7 @@ FROM node:24-trixie-slim AS builder
WORKDIR /app WORKDIR /app
COPY package*.json ./ COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force RUN npm ci && npm cache clean --force
COPY . . COPY . .
FROM node:24-trixie-slim AS production FROM node:24-trixie-slim AS production

View File

@@ -26,10 +26,14 @@ Running the back-end is as simple as:
npm ci npm ci
``` ```
- Copying the .env.default file to .env, and customizing it to own preferences. - Copying the .env.default file to .env, and customizing it to own preferences.
**Example:** Say, you want to add a domain to the trusted CORS origins list. To do so, your .env file in your editor of choice and append a comma (`,`) with the origin you want to add (say, `http://example.com`). Your .env file might then look as follows: `TRUSTED_ORIGINS=http://localhost:6568,http://example.com`. **Example:** Say, you want to add a domain to the trusted CORS origins list. To do so, your .env file in your editor of choice and append a comma (`,`) with the origin you want to add (say, `http://example.com`). Your .env file might then look as follows: `TRUSTED_ORIGINS=http://localhost:6568,http://example.com`.
**Important:** Make sure to change the `ACCESS_TOKEN_PRIVATE_KEY` variable to something secure, as this secret value will be used to generate user sessions. **Setting a weak key will allow attackers to potentially bruteforce your secret and forge user tokens!** **Important:** Make sure to change the `ACCESS_TOKEN_PRIVATE_KEY` variable to something secure, as this secret value will be used to generate user sessions. **Setting a weak key will allow attackers to potentially bruteforce your secret and forge user tokens!**
- Pasting your wordlist file into `src/tools/wordlist.ts`. - Pasting your wordlist file into `src/tools/wordlist.ts`.
No wordlist file exists by default in `src/tools/wordlist.ts`. This is because wordlists were meant to be as modular as possible (with the philosophy of "bring your own wordlist"). If you leave that as-is, you'll run into runtime errors. No wordlist file exists by default in `src/tools/wordlist.ts`. This is because wordlists were meant to be as modular as possible (with the philosophy of "bring your own wordlist"). If you leave that as-is, you'll run into runtime errors.
However, if you don't want to provide your own wordlist, and just want to get up and running as fast as possible, you're free to use the provided sample `wordlist.example-large.ts` file. Just copy it into `src/tools/wordlist.ts`: However, if you don't want to provide your own wordlist, and just want to get up and running as fast as possible, you're free to use the provided sample `wordlist.example-large.ts` file. Just copy it into `src/tools/wordlist.ts`:
```sh ```sh
cp wordlist.example-large.ts src/tools/wordlist.ts cp wordlist.example-large.ts src/tools/wordlist.ts

View File

@@ -1,6 +1,6 @@
{ {
"name": "kittyBE", "name": "kittyBE",
"version": "0.0.2", "version": "0.0.3",
"description": "Your go-to place for short and memorable URLs.", "description": "Your go-to place for short and memorable URLs.",
"type": "commonjs", "type": "commonjs",
"devDependencies": { "devDependencies": {

View File

@@ -91,7 +91,7 @@ AppDataSource.initialize().then(async () => {
// Retrieve url, subdomain from request. // Retrieve url, subdomain from request.
let uri: string = req.url.slice(1); // discards / from /abc, /abc -> abc let uri: string = req.url.slice(1); // discards / from /abc, /abc -> abc
let subdomain: string | null = req.headers.host!.replace(rs.fqdn, '') || null; let subdomain: string | null = req.headers.host!.replace(rs.fqdn, '').slice(0, -1) || null; // slice() to remove trailing dot
// Try to lookup the url in DB // Try to lookup the url in DB
const reversedLink: Link | null = await linkService.lookupUriWithExpiryValidation(uri, subdomain); const reversedLink: Link | null = await linkService.lookupUriWithExpiryValidation(uri, subdomain);

View File

@@ -94,7 +94,10 @@ export async function createLinkHandler(
) { ) {
// Using locals to retrieve decoded user JWT. // Using locals to retrieve decoded user JWT.
const decodedUser: jwt.JwtDecoded | undefined = res.locals.user?.decoded; // jwt.JwtDecoded when JWT is supplied
// undefined if not
// null if is invalid (expired)
const decodedUser: jwt.JwtDecoded | undefined | null = res.locals.user?.decoded;
const linkService = new LinkService(); const linkService = new LinkService();
const subdomainsAllowed: boolean = env.getBool('useSubdomains', true)!; const subdomainsAllowed: boolean = env.getBool('useSubdomains', true)!;
const rewriteStrings: env.RewriteStrings = env.getRewriteStrings(); const rewriteStrings: env.RewriteStrings = env.getRewriteStrings();
@@ -114,7 +117,7 @@ export async function createLinkHandler(
} }
let user: User | null = null; let user: User | null = null;
if (decodedUser !== undefined) { if (decodedUser !== undefined && decodedUser !== null) {
// If user is logged in, retrieve the account. // If user is logged in, retrieve the account.
const userService = new UserService(); const userService = new UserService();
user = await userService.findById(decodedUser.sub); user = await userService.findById(decodedUser.sub);

View File

@@ -29,4 +29,4 @@ export type ErrorDTO = {
// Used to check against reserved names. // Used to check against reserved names.
export const disallowedUriSchema = z export const disallowedUriSchema = z
.string() .string()
.regex(/^(about|assets|healthcheck|kttydocs|panel)/); .regex(/^(about|assets|healthcheck|kttydocs|panel|robots\.txt)/);