Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/lib/affiliates/validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,29 @@ describe("validateOfferInput", () => {
expect(result.errors.some((e) => e.includes("price_sats"))).toBe(true);
});

it("rejects non-finite numeric fields before sanitizing", () => {
const priceResult = validateOfferInput({ ...validInput, price_sats: Number.NaN });
const rateResult = validateOfferInput({ ...validInput, commission_rate: Number.POSITIVE_INFINITY });
const flatResult = validateOfferInput({
...validInput,
commission_type: "flat",
commission_flat_sats: Number.NaN,
});
const cookieResult = validateOfferInput({ ...validInput, cookie_days: Number.POSITIVE_INFINITY });
const settlementResult = validateOfferInput({ ...validInput, settlement_delay_days: Number.NaN });

expect(priceResult.ok).toBe(false);
expect(priceResult.errors.some((e) => e.includes("price_sats"))).toBe(true);
expect(rateResult.ok).toBe(false);
expect(rateResult.errors.some((e) => e.includes("Commission rate"))).toBe(true);
expect(flatResult.ok).toBe(false);
expect(flatResult.errors.some((e) => e.includes("commission_flat_sats"))).toBe(true);
expect(cookieResult.ok).toBe(false);
expect(cookieResult.errors.some((e) => e.includes("Cookie window"))).toBe(true);
expect(settlementResult.ok).toBe(false);
expect(settlementResult.errors.some((e) => e.includes("Settlement delay"))).toBe(true);
});

it("rejects non-string title, description, and product_url before string sanitizers run", () => {
const titleResult = validateOfferInput({ ...validInput, title: 123 as any });
const descriptionResult = validateOfferInput({ ...validInput, description: 123 as any });
Expand Down
21 changes: 13 additions & 8 deletions src/lib/affiliates/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,40 +85,45 @@ export function validateOfferInput(input: OfferInput): ValidationResult {
input.price_sats = 0;
}

if (typeof input.price_sats !== "number" || input.price_sats < 0) {
errors.push("price_sats must be a non-negative number");
if (typeof input.price_sats !== "number" || !Number.isFinite(input.price_sats) || input.price_sats < 0) {
errors.push("price_sats must be a finite non-negative number");
}

const commissionType = input.commission_type || "percentage";

if (commissionType === "percentage") {
const commissionRate = input.commission_rate ?? 0.20;
if (commissionRate < 0.01 || commissionRate > 0.90) {
if (!Number.isFinite(commissionRate) || commissionRate < 0.01 || commissionRate > 0.90) {
errors.push("Commission rate must be between 1% and 90%");
}
} else if (commissionType === "flat") {
const flatSats = input.commission_flat_sats ?? 0;
if (flatSats < 0) {
if (!Number.isFinite(flatSats)) {
errors.push("commission_flat_sats must be a finite number");
} else if (flatSats < 0) {
errors.push("commission_flat_sats must be non-negative");
} else if (flatSats < 1) {
errors.push("Flat commission must be at least 1 sat");
}
}

// Also reject negative commission_flat_sats even when type is percentage (#23)
if (input.commission_flat_sats !== undefined && input.commission_flat_sats < 0) {
if (
input.commission_flat_sats !== undefined &&
(!Number.isFinite(input.commission_flat_sats) || input.commission_flat_sats < 0)
) {
if (!errors.some(e => e.includes("commission_flat_sats"))) {
errors.push("commission_flat_sats must be non-negative");
errors.push("commission_flat_sats must be a finite non-negative number");
}
}

const cookieDays = input.cookie_days ?? 30;
if (cookieDays < 1 || cookieDays > 365) {
if (!Number.isFinite(cookieDays) || cookieDays < 1 || cookieDays > 365) {
errors.push("Cookie window must be 1-365 days");
}

const settlementDays = input.settlement_delay_days ?? 7;
if (settlementDays < 1 || settlementDays > 90) {
if (!Number.isFinite(settlementDays) || settlementDays < 1 || settlementDays > 90) {
errors.push("Settlement delay must be 1-90 days");
}

Expand Down
Loading