From 40095c032046ebd2aa1db4e832b7cb9d792cf07b Mon Sep 17 00:00:00 2001 From: jd-apprentice Date: Sun, 16 Feb 2025 19:24:47 -0300 Subject: [PATCH 1/5] fix: codeQL suggestions --- bun.lockb | Bin 170892 -> 171325 bytes src/app/constants/cors.ts | 8 ++++++++ src/app/main.ts | 5 +++-- src/common/utils/limiter.ts | 7 +++++++ src/image/image-routes.ts | 6 ++++-- src/tag/tag-routes.ts | 5 +++-- src/user/user-routes.ts | 7 ++----- 7 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 src/app/constants/cors.ts create mode 100644 src/common/utils/limiter.ts diff --git a/bun.lockb b/bun.lockb index edf6dc635d0f43b2cc292e6f7135211c7ab0ba1f..2f9c3989b32013b18c6fb8047b08bbb7d136aca3 100755 GIT binary patch delta 28556 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 Zet1xS#2>WTdZ@m+s@B#c3FfGb{{=%rGsXY_ diff --git a/src/app/constants/cors.ts b/src/app/constants/cors.ts new file mode 100644 index 0000000..60371fb --- /dev/null +++ b/src/app/constants/cors.ts @@ -0,0 +1,8 @@ +const allowedOrigins = ['https://waifuland.jonathan.com.ar', 'https://waifus.jonathan.com.ar']; +export const corsConfiguration = { + origin: allowedOrigins, + methods: ['GET', 'POST', 'PUT'], + allowedHeaders: ['Content-Type', 'Authorization'], + credentials: true, + maxAge: 3600 +} \ No newline at end of file diff --git a/src/app/main.ts b/src/app/main.ts index e00a152..4b01285 100644 --- a/src/app/main.ts +++ b/src/app/main.ts @@ -5,14 +5,15 @@ import cors from 'cors'; // Internal Modules import { routes } from './routes/index'; +import { corsConfiguration } from './constants/cors'; // Express const app = express(); app.use(express.json()); app.use(express.urlencoded({ extended: false })); -app.use(cors()); +app.use(cors(corsConfiguration)); app.use(helmet()); -app.use('/api', routes); +app.use('/v1/api', routes); app.use('/', (req, res) => res.status(200).json({ message: 'Allo! Catch-all route.' }), ); diff --git a/src/common/utils/limiter.ts b/src/common/utils/limiter.ts new file mode 100644 index 0000000..5899590 --- /dev/null +++ b/src/common/utils/limiter.ts @@ -0,0 +1,7 @@ +import rateLimit from 'express-rate-limit'; + +export const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 100, + message: 'Too many requests from this IP, please try again after 15 minutes', +}); \ No newline at end of file diff --git a/src/image/image-routes.ts b/src/image/image-routes.ts index 558504d..9b93543 100644 --- a/src/image/image-routes.ts +++ b/src/image/image-routes.ts @@ -5,17 +5,19 @@ import { Router } from 'express'; import { isAdmin, validateToken } from '../user/user-middleware'; import ImageController from './image-controller'; import upload from './image-middleware'; +import { limiter } from 'src/common/utils/limiter'; const imageRouter = Router(); imageRouter.post( '/', + limiter, validateToken, isAdmin, upload.single('image'), ImageController.uploadFile, ); -imageRouter.get('/', ImageController.getRandomImage); -imageRouter.get('/all', ImageController.getImages); +imageRouter.get('/', limiter, ImageController.getRandomImage); +imageRouter.get('/all', limiter, ImageController.getImages); export default imageRouter; diff --git a/src/tag/tag-routes.ts b/src/tag/tag-routes.ts index b8b2174..5b874fa 100644 --- a/src/tag/tag-routes.ts +++ b/src/tag/tag-routes.ts @@ -3,10 +3,11 @@ import { Router } from 'express'; // Internal Modules import tagController from './tag-controller'; +import { limiter } from 'src/common/utils/limiter'; const tagRouter = Router(); -tagRouter.get('/', tagController.getTags); -tagRouter.get('/:id', tagController.getTagsId); +tagRouter.get('/', limiter, tagController.getTags); +tagRouter.get('/:id', limiter, tagController.getTagsId); export default tagRouter; diff --git a/src/user/user-routes.ts b/src/user/user-routes.ts index 3f38924..8ea6276 100644 --- a/src/user/user-routes.ts +++ b/src/user/user-routes.ts @@ -1,18 +1,15 @@ // External Modules import { Router } from 'express'; -import rateLimit from 'express-rate-limit'; // Internal Modules import userController from './user-controller'; import upload from '../image/image-middleware'; import { userExists, validateUser, validateToken } from './user-middleware'; +import { limiter } from 'src/common/utils/limiter'; const userRouter = Router(); -const limiter = rateLimit({ - windowMs: 15 * 60 * 1000, // 15 minutes - max: 100, // limit each IP to 100 requests per windowMs -}); + userRouter.get('/', limiter, validateToken, userController.getUsers); userRouter.get('/info', limiter, validateToken, userController.getUserInfo); From f1ccb6894723887f042583ad61143360363d6d59 Mon Sep 17 00:00:00 2001 From: jd Date: Sun, 16 Feb 2025 19:58:18 -0300 Subject: [PATCH 2/5] Potential fix for code scanning alert no. 129: Database query built from user-controlled sources Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- src/user/user-middleware.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/user-middleware.ts b/src/user/user-middleware.ts index 76fea11..9743414 100644 --- a/src/user/user-middleware.ts +++ b/src/user/user-middleware.ts @@ -60,7 +60,7 @@ const isAdmin = async ( ): Promise => { try { const { username } = req.body; - const user = await User.findOne({ username }); + const user = await User.findOne({ username: { $eq: username } }); return user?.isAdmin ? next() : res.json(boom.unauthorized('Not admin')); } catch (error) { return res.status(400) && res.json(boom.badRequest('User not found')); From f69898753494550b43229bf9982661fe806354dd Mon Sep 17 00:00:00 2001 From: jd-apprentice Date: Sun, 16 Feb 2025 20:08:05 -0300 Subject: [PATCH 3/5] fix: codeQL suggestions --- src/image/image-repository.ts | 2 +- src/tag/tag-repository.ts | 2 +- src/user/user-middleware.ts | 4 ++-- src/user/user-repository.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/image/image-repository.ts b/src/image/image-repository.ts index 970645f..7b8fdd6 100644 --- a/src/image/image-repository.ts +++ b/src/image/image-repository.ts @@ -11,7 +11,7 @@ class ImageRepository { * @return { Promise } - A new image created */ async create(image: IImage): Promise { - const tagExists = await Tag.findOne({ tag_id: image.tag.tag_id }); + const tagExists = await Tag.findOne({ tag_id: { $eq: image.tag } }); const _idTag = tagExists?._id; return Image.create({ ...image, diff --git a/src/tag/tag-repository.ts b/src/tag/tag-repository.ts index 49e5cfe..7e55813 100644 --- a/src/tag/tag-repository.ts +++ b/src/tag/tag-repository.ts @@ -20,7 +20,7 @@ class TagRepository { * @returns {Promise} - One single tag */ async findByTagId(tagId: string): Promise { - return Tag.findOne({ tag_id: tagId }); + return Tag.findOne({ tag_id: { $eq: tagId } }); } } diff --git a/src/user/user-middleware.ts b/src/user/user-middleware.ts index 9743414..63bd0e1 100644 --- a/src/user/user-middleware.ts +++ b/src/user/user-middleware.ts @@ -20,7 +20,7 @@ const userExists = async ( next: NextFunction, ): Promise => { const { username }: UsernameType = req.body; - const user: UsernameType | null = await User.findOne({ username }); + const user: UsernameType | null = await User.findOne({ username: { $eq: username } }); return user ? res.json(boom.conflict('User already exists')) : next(); }; @@ -37,7 +37,7 @@ const validateUser = async ( ): Promise => { try { const { username, password } = req.body; - const userExists = await User.findOne({ username }); + const userExists = await User.findOne({ username: { $eq: username } }); const user = userExists?.username; const pass = userExists?.password; const isMatch = pass && (await bcrypt.compare(password, pass)); // Compare the password with the hash password diff --git a/src/user/user-repository.ts b/src/user/user-repository.ts index b56f404..6357217 100644 --- a/src/user/user-repository.ts +++ b/src/user/user-repository.ts @@ -24,7 +24,7 @@ class UserRepository { * @param {string} id - id of the user */ async findUser(id: string): Promise { - return User.findOne({ _id: id }); + return User.findOne({ _id: { $eq: id } }); } /** @@ -32,7 +32,7 @@ class UserRepository { * @param {string} username - username of the user */ async findUserByUsername(username: string): Promise { - return User.findOne({ username }); + return User.findOne({ username: { $eq: username } }); } /** From 21153cf2882878b4227baaae723ad2fa07e05c59 Mon Sep 17 00:00:00 2001 From: jd-apprentice Date: Sun, 16 Feb 2025 20:12:41 -0300 Subject: [PATCH 4/5] fix: tests --- __tests__/app/__tests__/main.test.ts | 4 ++-- __tests__/image/__tests__/integration/images.test.ts | 2 +- __tests__/tag/__tests__/tags.test.ts | 2 +- __tests__/user/__tests__/users.test.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/__tests__/app/__tests__/main.test.ts b/__tests__/app/__tests__/main.test.ts index d3830f1..41d2649 100644 --- a/__tests__/app/__tests__/main.test.ts +++ b/__tests__/app/__tests__/main.test.ts @@ -18,9 +18,9 @@ describe('INTEGRATION - App Module', () => { }) }); - test('GET /api - when asking for the api route should return 200 and api message', async () => { + test('GET /v1/api - when asking for the api route should return 200 and api message', async () => { await request(app) - .get('/api') + .get('/v1/api') .expect(contentTypeKey, contentTypeValue) .expect(httpSuccess) .then((response) => { diff --git a/__tests__/image/__tests__/integration/images.test.ts b/__tests__/image/__tests__/integration/images.test.ts index f932b60..c3d11e3 100644 --- a/__tests__/image/__tests__/integration/images.test.ts +++ b/__tests__/image/__tests__/integration/images.test.ts @@ -6,7 +6,7 @@ import { jest, describe, test, beforeAll, expect, beforeEach } from "bun:test"; import request from "supertest"; import { Response } from "supertest"; -const baseRoute = "/api/images"; +const baseRoute = "/v1/api/images"; const contentTypeKey = "Content-Type"; const contentTypeValue = /json/; const httpSuccess = 200; diff --git a/__tests__/tag/__tests__/tags.test.ts b/__tests__/tag/__tests__/tags.test.ts index 9b28e22..523d3f0 100644 --- a/__tests__/tag/__tests__/tags.test.ts +++ b/__tests__/tag/__tests__/tags.test.ts @@ -8,7 +8,7 @@ import { describe, test, expect, beforeAll, jest, beforeEach } from 'bun:test'; const contentTypeKey = 'Content-Type'; const contentTypeValue = /json/; const httpSuccess = 200; -const baseRoute = "/api/tags"; +const baseRoute = "/v1/api/tags"; const tagResponse = { _id: expect.any(String), diff --git a/__tests__/user/__tests__/users.test.ts b/__tests__/user/__tests__/users.test.ts index 36b34c5..06c55b5 100644 --- a/__tests__/user/__tests__/users.test.ts +++ b/__tests__/user/__tests__/users.test.ts @@ -10,7 +10,7 @@ const contentTypeKey = 'Content-Type'; const contentTypeValue = /json/; const httpSuccess = 200; const httpUnauthorized = 401; -const baseRoute = "/api/user"; +const baseRoute = "/v1/api/user"; const userResponse = { _id: expect.any(String), From 006db639dcd4b09ae701b56d7926f87896365fe4 Mon Sep 17 00:00:00 2001 From: jd-apprentice Date: Sun, 16 Feb 2025 20:36:34 -0300 Subject: [PATCH 5/5] chore: remove codacy --- .github/workflows/ci.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3ddfec..928d812 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,14 +54,6 @@ jobs: bun install bun test - codacy: - uses: jd-apprentice/jd-workflows/.github/workflows/codacy.yml@main - needs: [gitleaks, test] - with: - name: Codacy - secrets: - project_token: ${{ secrets.CODACY_PROJECT_TOKEN }} - dev: runs-on: ubuntu-latest needs: [gitleaks, test]