i found moderation endpoint in JS files made me remove any post from the default feed. | nadler // security research{let{data:a}=await e.post(`/api/posts/${t}/moderate`,aq,aK,r);return a},
// shared enums<br>aG={NC17:"NC17",PG:"PG",R:"R"},<br>aH={BLOCKED:"BLOCKED",COMPLETE:"COMPLETE",DELETED:"DELETED",REVIEW:"REVIEW"},
// POST /api/posts/{id}/moderation — staff status/category path (correctly 403 for FREE)<br>aY=s.z.object({category:s.z.enum(aG).optional().catch(void<br>0),reason:s.z.string().trim().min(1).max(500).optional(),status:s.z.enum(aH).optional().catch(void 0)}).refine(e=>void 0!==e.status||void<br>0!==e.category,{message:"At least one of status or category must be provided."}),<br>aX=async(e,t,r)=>{let{data:a}=await e.post(`/api/posts/${t}/moderation`,aY,aY,r);return a},
// GET /api/posts/{id}/moderation — moderation decision log<br>io=s.z.object({createdAt:s.z.string(),decision:s.z.string(),fromCat:s.z.enum(aG).nullable().catch(null),fromStatus:s.z.enum(aH).catch("REVIEW"<br>),id:s.z.string(),reason:s.z.string().nullable(),reviewerId:s.z.string().nullable(),toCat:s.z.enum(aG).nullable().catch(null),toStatus:s.z.enu<br>m(aH).catch("REVIEW")}),<br>is=s.z.object({data:s.z.array(io)}),<br>il=async(e,t)=>{let{data:r}=await e.get(`/api/posts/${t}/moderation`,is);return r}<br>the two endpoints (this is the whole bug)<br>look closely — there are two near-identical moderation routes, one character apart:'>{let{data:a}=await e.post(`/api/posts/${t}/moderate`,aq,aK,r);return a}, // shared enums aG={NC17:"NC17",PG:"PG",R:"R"}, aH={BLOCKED:"BLOCKED",COMPLETE:"COMPLETE",DELETED:"DELETED",REVIEW:"REVIEW"}, // POST /api/posts/{id}/moderation — staff status/category path (correctly 403 for FREE) aY=s.z.object({category:s.z.enum(aG).optional().catch(void 0),reason:s.z.string().trim().min(1).max(500).optional(),status:s.z.enum(aH).optional().catch(void 0)}).refine(e=>void 0!==e.status||void 0!==e.category,{message:"At least one of status or category must be provided."}), aX=async(e,t,r)=>{let{data:a}=await e.post(`/api/posts/${t}/moderation`,aY,aY,r);return a}, // GET /api/posts/{id}/moderation — moderation decision log io=s.z.object({createdAt:s.z.string(),decision:s.z.string(),fromCat:s.z.enum(aG).nullable().catch(null),fromStatus:s.z.enum(aH).catch("REVIEW" ),id:s.z.string(),reason:s.z.string().nullable(),reviewerId:s.z.string().nullable(),toCat:s.z.enum(aG).nullable().catch(null),toStatus:s.z.enu m(aH).catch("REVIEW")}), is=s.z.object({data:s.z.array(io)}), il=async(e,t)=>{let{data:r}=await e.get(`/api/posts/${t}/moderation`,is);return r} the two endpoints (this is the whole bug) look closely — there are two near-identical moderation routes, one character apart:'>{let{data:a}=await e.post(`/api/posts/${t}/moderate`,aq,aK,r);return a}, // shared enums aG={NC17:"NC17",PG:"PG",R:"R"}, aH={BLOCKED:"BLOCKED",COMPLETE:"COMPLETE",DELETED:"DELETED",REVIEW:"REVIEW"}, // POST /api/posts/{id}/moderation — staff status/category path (correctly 403 for FREE) aY=s.z.object({category:s.z.enum(aG).optional().catch(void 0),reason:s.z.string().trim().min(1).max(500).optional(),status:s.z.enum(aH).optional().catch(void 0)}).refine(e=>void 0!==e.status||void 0!==e.category,{message:"At least one of status or category must be provided."}), aX=async(e,t,r)=>{let{data:a}=await e.post(`/api/posts/${t}/moderation`,aY,aY,r);return a}, // GET /api/posts/{id}/moderation — moderation decision log io=s.z.object({createdAt:s.z.string(),decision:s.z.string(),fromCat:s.z.enum(aG).nullable().catch(null),fromStatus:s.z.enum(aH).catch("REVIEW" ),id:s.z.string(),reason:s.z.string().nullable(),reviewerId:s.z.string().nullable(),toCat:s.z.enum(aG).nullable().catch(null),toStatus:s.z.enu m(aH).catch("REVIEW")}), is=s.z.object({data:s.z.array(io)}), il=async(e,t)=>{let{data:r}=await e.get(`/api/posts/${t}/moderation`,is);return r} the two endpoints (this is the whole bug) look closely — there are two near-identical moderation routes, one character apart:'>i found moderation endpoint in JS files made me remove any post from the default feed.<br>June 22, 2026 · 2 min · 403 words · nadler<br>Table of Contentsrecon<br>the two endpoints (this is the whole bug)<br>exploitation
recon#<br>since i can’t disclose the program let’s call it target.com. the app had a lot<br>of functionality, and reading the js files was the move to understand those<br>functions and find hidden endpoints. while analyzing the bundles i hit a really<br>interesting block:<br>// POST /api/posts/{id}/moderate — community "flag" (the BFLA endpoint), request schema only allows reason:"MATURE"<br>aq=s.z.object({description:s.z.string().optional(),reason:s.z.literal("MATURE")}),<br>aK=s.z.object({adult:s.z.boolean().optional(),message:s.z.string(),postId:s.z.string()}),<br>aV=async(e,t,r)=>{let{data:a}=await e.post(`/api/posts/${t}/moderate`,aq,aK,r);return a},
// shared enums<br>aG={NC17:"NC17",PG:"PG",R:"R"},<br>aH={BLOCKED:"BLOCKED",COMPLETE:"COMPLETE",DELETED:"DELETED",REVIEW:"REVIEW"},
// POST /api/posts/{id}/moderation — staff status/category...