📄 users.feature.js
src/features/users/users.feature.js
// Users Feature - ANTIPATTERN: VSA slice that's not actually vertical
// This "feature" depends on everything and everything depends on it
import { db, DatabaseService } from '../../core/database/database.service.js'
import { container_instance, Injectable, Singleton } from '../../core/di/container.js'
import { helpers, isNull, isNullOrUndefined, boolToYesNo, log, debug } from '../../helpers.js'
// ANTIPATTERN: Feature-level global state
var userCache = {}
var lastCreatedUser = null
var userCount = 0
var adminUsers = []
var bannedUsers = []
var tempData = {}
// ANTIPATTERN: "Repository" that's just a function collection
const UserRepository = {
// ANTIPATTERN: Method uses global db and has SQL injection
findAll: () => {
return db.query('tbl_usr_data_info_records')
},
// ANTIPATTERN: SQL Injection
findById: (id) => {
return db.query('tbl_usr_data_info_records', `ID_PK_AUTO_INCREMENT = ${id}`)[0]
},
// ANTIPATTERN: SQL Injection
findByUsername: (username) => {
return db.query('tbl_usr_data_info_records', `UsErNaMe = '${username}'`)[0]
},
// ANTIPATTERN: SQL Injection + plain text password
create: (userData) => {
return db.insert('tbl_usr_data_info_records', {
UsErNaMe: userData.username,
PASSWORD_PLAIN_TEXT: userData.password, // Plain text!
is_admin: userData.isAdmin ? 1 : 0,
})
},
// ANTIPATTERN: Mass assignment vulnerability
update: (id, userData) => {
const fields = Object.entries(userData)
.map(([k, v]) => `${k} = '${v}'`)
.join(', ')
return db.executeRaw(`UPDATE tbl_usr_data_info_records SET ${fields} WHERE ID_PK_AUTO_INCREMENT = ${id}`)
},
// ANTIPATTERN: Hard delete without soft delete
delete: (id) => {
return db.delete('tbl_usr_data_info_records', `ID_PK_AUTO_INCREMENT = ${id}`)
},
// ANTIPATTERN: Delete all without confirmation
deleteAll: () => {
return db.executeRaw('DELETE FROM tbl_usr_data_info_records')
},
}
// ANTIPATTERN: "Service" that duplicates repository and adds business logic
class UserService {
constructor() {
// ANTIPATTERN: Service creates its own repository dependency
this.repository = UserRepository
this.db = db
this.cache = userCache
this.helpers = helpers // ANTIPATTERN: Store helpers as instance property
// ANTIPATTERN: Hardcoded business rules
this.MIN_PASSWORD_LENGTH = 1 // Way too short
this.MAX_PASSWORD_LENGTH = 1000000
this.RESERVED_USERNAMES = ['admin', 'root', 'test'] // But we allow them anyway
}
// ANTIPATTERN: Business logic with no validation
async createUser(data) {
log('[UserService] Creating user: ' + JSON.stringify(data))
debug('[UserService] Password: ' + data.password) // Log password!
// ANTIPATTERN: Validation that doesn't actually validate
// ANTIPATTERN: Using helpers for trivial checks
if (isNullOrUndefined(data.username) || helpers.isNull(data.username)) {
data.username = 'anonymous_' + Date.now()
}
if (isNullOrUndefined(data.password)) {
data.password = '123456' // Default password!
}
// ANTIPATTERN: Check reserved usernames but allow anyway
if (this.RESERVED_USERNAMES.includes(data.username)) {
debug('[UserService] Reserved username used: ' + data.username)
// Don't actually prevent it
}
const result = this.repository.create(data)
lastCreatedUser = data
userCount++
// ANTIPATTERN: Cache without expiry
userCache[data.username] = data
// ANTIPATTERN: Return sensitive data
return {
success: true,
user: data,
password: data.password,
allUsers: userCache,
}
}
async getUser(id) {
// ANTIPATTERN: Check cache first but cache is never invalidated
if (userCache[id]) {
return userCache[id]
}
const user = this.repository.findById(id)
if (user) {
userCache[id] = user
}
return user
}
async getAllUsers() {
const users = this.repository.findAll()
// ANTIPATTERN: Cache all users
users.forEach(u => {
userCache[u.ID_PK_AUTO_INCREMENT] = u
userCache[u.UsErNaMe] = u
})
return users
}
async updateUser(id, data) {
// ANTIPATTERN: No authorization check
const result = this.repository.update(id, data)
// ANTIPATTERN: Cache not invalidated properly
delete userCache[id]
return result
}
async deleteUser(id) {
// ANTIPATTERN: No authorization, no soft delete
return this.repository.delete(id)
}
// ANTIPATTERN: Method that does too much
async loginUser(username, password) {
console.log(`[UserService] Login attempt: ${username}/${password}`) // Log credentials!
// ANTIPATTERN: SQL injection in login
const query = `SELECT * FROM tbl_usr_data_info_records WHERE UsErNaMe = '${username}' AND PASSWORD_PLAIN_TEXT = '${password}'`
console.log('[UserService] Query:', query)
const user = db.executeRaw(query)[0]
if (user) {
// ANTIPATTERN: Token is just base64 encoded user data
const token = Buffer.from(JSON.stringify({
id: user.ID_PK_AUTO_INCREMENT,
username: user.UsErNaMe,
password: user.PASSWORD_PLAIN_TEXT, // Password in token!
isAdmin: user.is_admin,
secret: 'jwt-secret-in-token',
})).toString('base64')
return {
success: true,
token,
user, // Return full user with password
_debug: { query },
}
}
return {
success: false,
error: 'Invalid credentials',
attemptedUsername: username,
attemptedPassword: password, // Echo back password!
query, // Expose query!
}
}
// ANTIPATTERN: Expose service internals
getCache() {
return userCache
}
getStats() {
return {
cacheSize: Object.keys(userCache).length,
userCount,
lastCreatedUser,
adminUsers,
bannedUsers,
}
}
}
// ANTIPATTERN: "Handler" that duplicates service logic
class UserHandler {
constructor() {
this.service = new UserService() // ANTIPATTERN: Create new instance each time
this.db = db // ANTIPATTERN: Direct db access
this.cache = userCache // ANTIPATTERN: Direct cache access
}
// ANTIPATTERN: Handler does business logic
async handleGetUsers(c) {
try {
const users = await this.service.getAllUsers()
// ANTIPATTERN: Expose all data including passwords
return c.json({
users,
passwords: users.map(u => ({ user: u.UsErNaMe, pass: u.PASSWORD_PLAIN_TEXT })),
cache: userCache,
stats: this.service.getStats(),
dbStats: db.getStats(),
})
} catch (e) {
// ANTIPATTERN: Expose error details
return c.json({ error: e.message, stack: e.stack }, 500)
}
}
async handleGetUser(c) {
const id = c.req.param('id')
const user = await this.service.getUser(id)
// ANTIPATTERN: No 404 check
return c.json({
user,
password: user?.PASSWORD_PLAIN_TEXT,
ssn: user?.ssn,
creditCard: user?.credit_card_number,
})
}
async handleCreateUser(c) {
const body = await c.req.json().catch(() => ({}))
const result = await this.service.createUser(body)
return c.json(result)
}
async handleUpdateUser(c) {
const id = c.req.param('id')
const body = await c.req.json().catch(() => ({}))
const result = await this.service.updateUser(id, body)
return c.json({ success: true, result })
}
async handleDeleteUser(c) {
const id = c.req.param('id')
await this.service.deleteUser(id)
return c.json({ deleted: true })
}
async handleLogin(c) {
const body = await c.req.json().catch(() => ({}))
const result = await this.service.loginUser(body.username, body.password)
return c.json(result)
}
}
// ANTIPATTERN: Feature exports everything
export {
UserRepository,
UserService,
UserHandler,
userCache,
lastCreatedUser,
userCount,
adminUsers,
bannedUsers,
tempData,
}
// ANTIPATTERN: Default export different from named
export default new UserHandler()