fix(C3): validate screener sector filters
Reject unsupported /api/screener sector values before building the FMP query so only known sector labels reach the upstream stock screener. Refs: docs/AUDIT_REDESIGN.md item C3. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
This commit is contained in:
parent
222d101a7e
commit
c173aeb87a
@ -97,6 +97,20 @@ interface RuntimeHealth {
|
|||||||
canonicalLifecycleOrderRows?: number;
|
canonicalLifecycleOrderRows?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ALLOWED_SCREENER_SECTORS = new Set([
|
||||||
|
'Technology',
|
||||||
|
'Financial Services',
|
||||||
|
'Healthcare',
|
||||||
|
'Consumer Cyclical',
|
||||||
|
'Consumer Defensive',
|
||||||
|
'Industrials',
|
||||||
|
'Energy',
|
||||||
|
'Utilities',
|
||||||
|
'Real Estate',
|
||||||
|
'Communication Services',
|
||||||
|
'Basic Materials',
|
||||||
|
]);
|
||||||
|
|
||||||
interface TradeAuditEvent {
|
interface TradeAuditEvent {
|
||||||
event: string;
|
event: string;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
@ -2818,7 +2832,13 @@ RULES:
|
|||||||
try {
|
try {
|
||||||
const apiKey = process.env.FMP_API_KEY || 'demo';
|
const apiKey = process.env.FMP_API_KEY || 'demo';
|
||||||
const qs = new URLSearchParams();
|
const qs = new URLSearchParams();
|
||||||
if (req.query.sector) qs.set('sector', String(req.query.sector));
|
const sector = String(req.query.sector || '').trim();
|
||||||
|
if (sector && sector !== 'All') {
|
||||||
|
if (!ALLOWED_SCREENER_SECTORS.has(sector)) {
|
||||||
|
return res.status(400).json({ error: 'Unsupported sector filter' });
|
||||||
|
}
|
||||||
|
qs.set('sector', sector);
|
||||||
|
}
|
||||||
if (req.query.marketCapMoreThan) qs.set('marketCapMoreThan', String(req.query.marketCapMoreThan));
|
if (req.query.marketCapMoreThan) qs.set('marketCapMoreThan', String(req.query.marketCapMoreThan));
|
||||||
if (req.query.marketCapLessThan) qs.set('marketCapLessThan', String(req.query.marketCapLessThan));
|
if (req.query.marketCapLessThan) qs.set('marketCapLessThan', String(req.query.marketCapLessThan));
|
||||||
if (req.query.betaMoreThan) qs.set('betaMoreThan', String(req.query.betaMoreThan));
|
if (req.query.betaMoreThan) qs.set('betaMoreThan', String(req.query.betaMoreThan));
|
||||||
|
|||||||
@ -168,6 +168,14 @@ function testFmpProxyContracts() {
|
|||||||
'https://financialmodelingprep.com/api/v3/stock-screener?${qs.toString()}',
|
'https://financialmodelingprep.com/api/v3/stock-screener?${qs.toString()}',
|
||||||
'/api/screener must call FMP stock-screener',
|
'/api/screener must call FMP stock-screener',
|
||||||
);
|
);
|
||||||
|
assertSourceIncludes(
|
||||||
|
'const ALLOWED_SCREENER_SECTORS = new Set([',
|
||||||
|
'/api/screener must keep an explicit sector allow-list',
|
||||||
|
);
|
||||||
|
assertSourceMatches(
|
||||||
|
/if \(sector && sector !== 'All'\)[\s\S]*!ALLOWED_SCREENER_SECTORS\.has\(sector\)[\s\S]*status\(400\)\.json\(\{ error: 'Unsupported sector filter' \}\)[\s\S]*qs\.set\('sector', sector\)/,
|
||||||
|
'/api/screener must reject unsupported sector values before building the FMP query',
|
||||||
|
);
|
||||||
assertSourceIncludes(
|
assertSourceIncludes(
|
||||||
"qs.set('isEtf', 'false')",
|
"qs.set('isEtf', 'false')",
|
||||||
'/api/screener must exclude ETFs by default',
|
'/api/screener must exclude ETFs by default',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user