
Research
Malicious npm Package Brand-Squats TanStack to Exfiltrate Environment Variables
A brand-squatted TanStack npm package used postinstall scripts to steal .env files and exfiltrate developer secrets to an attacker-controlled endpoint.
sqlitebruv
Advanced tools
A Tiny Type-Safe, Secure SQLite Query Builder with D1/Turso support with built-in migrations and security features.
npm install sqlite-bruv
# bun
bun add sqlite-bruv
# npm
npm install sqlite-bruv
import { SqliteBruv, Schema } from "sqlite-bruv";
// Define your schema
const UserSchema = new Schema<{
name: string;
email: string;
role: "admin" | "user";
createdAt: Date;
}>({
name: "users",
columns: {
name: { type: "TEXT", required: true },
email: { type: "TEXT", unique: true },
role: { type: "TEXT", default: () => "user" },
createdAt: { type: "DATETIME", default: () => new Date() },
},
});
const PostSchema = new Schema({
name: "posts",
columns: {
title: { type: "TEXT", required: true },
content: { type: "TEXT" },
userId: {
type: "TEXT",
target: "users",
relationType: "ONE",
},
},
});
const CommentSchema = new Schema({
name: "comments",
columns: {
content: { type: "TEXT", required: true },
postId: {
type: "TEXT",
target: "posts",
relationType: "MANY",
},
},
});
// Initialize database
const db = new SqliteBruv({
schema: [UserSchema],
});
Platform-Specific Setup Cloudflare D1
const db = new SqliteBruv({
D1: {
accountId: process.env.CF_ACCOUNT_ID,
databaseId: process.env.D1_DATABASE_ID,
apiKey: process.env.CF_API_KEY,
},
schema: [UserSchema, PostSchema, CommentSchema],
});
Turso;
const db = new SqliteBruv({
turso: {
url: process.env.TURSO_URL,
authToken: process.env.TURSO_AUTH_TOKEN,
},
schema: [UserSchema, PostSchema, CommentSchema],
});
const queryBuilder = new SqliteBruv({
schema: [UserSchema, PostSchema, CommentSchema],
});
// Insert
await queryBuilder
.from("users")
.insert({ name: "John Doe", email: "john@example.com" })
.then((changes) => {
// console.log({ changes });
});
// Update
await queryBuilder
.from("users")
.where("id = ?", 1)
.update({ name: "Jane Doe" })
.then((changes) => {
// console.log({ changes });
});
// Search
await queryBuilder
.from("users")
.where("id = ?", 1)
.andWhere("name LIKE ?", `%oh%`)
.get()
.then((changes) => {
// console.log({ changes });
});
// Delete
await queryBuilder
.from("users")
.where("id = ?", 1)
.delete()
.then((changes) => {
console.log({ changes });
});
// Get all users
queryBuilder
.from("users")
.get()
.then((changes) => {
// console.log({ changes });
});
// Get one user
await queryBuilder
.from("users")
.where("id = ?", 1)
.getOne()
.then((changes) => {
// console.log({ changes });
});
// Select specific columns
await queryBuilder
.from("users")
.select("id", "name")
.get()
.then((changes) => {
// console.log({ changes });
});
// Where conditions
await queryBuilder
.from("users")
.where("age > ?", 18)
.get()
.then((changes) => {
// console.log({ changes });
});
// AndWhere conditions
await queryBuilder
.from("users")
.where("age > ?", 18)
.andWhere("country = ?", "USA")
.get()
.then((changes) => {
// console.log({ changes });
});
// OrWhere conditions
await queryBuilder
.from("users")
.where("age > ?", 18)
.orWhere("country = ?", "Canada")
.get()
.then((changes) => {
// console.log({ changes });
});
// Limit and Offset
await queryBuilder
.from("users")
.limit(10)
.offset(5)
.get()
.then((changes) => {
// console.log({ changes });
});
// OrderBy
await queryBuilder
.from("users")
.orderBy("name", "ASC")
.get()
.then((changes) => {
// console.log({ changes });
});
await queryBuilder
.from("users")
.orderBy("name", "ASC")
.get()
.then((changes) => {
// console.log({ changes });
});
Complex Queries
// Relations and joins
const posts = await db
.from("posts")
.select("posts.*", "users.name as author")
.where("posts.published = ?", true)
.andWhere("posts.views > ?", 1000)
.orderBy("posts.createdAt", "DESC")
.limit(10)
.get();
// Raw queries with safety
await db.raw("SELECT * FROM users WHERE id = ?", [userId]);
// Cache usage
const users = await db
.from("users")
.select("*")
.where("active = ?", true)
.cacheAs("active-users")
.get();
// Cache invalidation
db.invalidateCache("active-users");
// JSON interface structure
interface Query {
from: string;
select?: string[];
where?: {
condition: string;
params: any[];
}[];
andWhere?: {
condition: string;
params: any[];
}[];
orWhere?: {
condition: string;
params: any[];
}[];
orderBy?: {
column: string;
direction: "ASC" | "DESC";
};
limit?: number;
offset?: number;
cacheAs?: string;
invalidateCache?: string;
action?: "get" | "getOne" | "insert" | "update" | "delete" | "count";
/**
### For insert and update only
*/
data?: any;
}
// Example usage in an Express.js route
import express from "express";
const app = express();
app.use(express.json());
app.post("/execute-query", async (req, res) => {
try {
const queryInput = req.body;
// do your role authentication here,
// use query.from to know the table being accessed
const result = await qb.executeJsonQuery(queryInput);
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
Migrations are automatically generated when schema changes are detected:
-- Generated in ./Bruv-migrations/timestamp_add_user_role.sql:
-- Up
ALTER TABLE users ADD COLUMN role TEXT DEFAULT 'user';
-- Down
ALTER TABLE users DROP COLUMN role;
This if your DB is new and your are not using any orm, just call toString and query your db with the queryBuilder.raw() method.
Note: raw is not secured, it can be used to apply migrations too. be careful what you do with queryBuilder.raw().
console.log(user.toString());
const raw = await qb.raw(user.toString());
console.log({ raw });
The query builder implements several security measures to prevent SQL injection and malicious queries:
=, >, <, >=, <=, LIKE, IN, BETWEEN, IS NULL, IS NOT NULL; DROP, DELETE, UPDATE, INSERT, ALTER, EXEC, UNION// ✅ Safe queries
db.from("users")
.where("email LIKE ?", "%@example.com") // ✅ Safe
.andWhere("role = ?", "admin") // ✅ Safe
.get();
db.from("users")
.where("age > ?", 18)
.andWhere("status = ?", "active")
.orWhere("role IN (?)", ["admin", "mod"]);
// ❌ These will throw security errors:
db.where("1=1; DROP TABLE users;"); // Dangerous pattern
db.where("col = (SELECT ...)"); // Complex subqueries blocked
db.where("name = ?", "a".repeat(1001)); // String too long
Cloudflare D1
Turso
📊 Performance
🚔 Security
🤝 Contributing
MIT License - see LICENSE file
Contributions are always welcome! creates issues and pull requests. Documentation GitHub Issues Discord Community
FAQs
A Simple and Efficient Query Builder for D1/Turso and Bun's SQLite
We found that sqlitebruv demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
A brand-squatted TanStack npm package used postinstall scripts to steal .env files and exfiltrate developer secrets to an attacker-controlled endpoint.

Research
Compromised SAP CAP npm packages download and execute unverified binaries, creating urgent supply chain risk for affected developers and CI/CD environments.

Company News
Socket has acquired Secure Annex to expand extension security across browsers, IDEs, and AI tools.