fix: authorization bypass vulnerabilities in plan-restricted features
- Add plan validation to custom domain saving (Domains.svelte) - Add plan validation to certificate customization (Design.svelte) - Add plan validation to document upload (AddDocumentToLesson.svelte) - Add plan validation to video upload (UploadVideo.svelte) - Enhance team member function validation (TeamMembers.svelte) Prevents free users from bypassing UI restrictions via DOM manipulation. All restricted functions now enforce server-side plan validation.
This commit is contained in:
@@ -40,6 +40,12 @@
|
|||||||
isSaving = true;
|
isSaving = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Prevent free plan users from bypassing UI restrictions
|
||||||
|
if ($isFreePlan) {
|
||||||
|
errors.description = 'Certificate customization is only available on paid plans';
|
||||||
|
throw new Error(errors.description);
|
||||||
|
}
|
||||||
|
|
||||||
const result = saveCertificateValidation({
|
const result = saveCertificateValidation({
|
||||||
description: $course.description || '',
|
description: $course.description || '',
|
||||||
is_certificate_downloadable: $course.is_certificate_downloadable || false,
|
is_certificate_downloadable: $course.is_certificate_downloadable || false,
|
||||||
|
|||||||
+8
@@ -107,6 +107,14 @@
|
|||||||
|
|
||||||
async function uploadDocument() {
|
async function uploadDocument() {
|
||||||
if (!selectedFile) return;
|
if (!selectedFile) return;
|
||||||
|
|
||||||
|
// Prevent free plan users from bypassing UI restrictions
|
||||||
|
if ($isFreePlan) {
|
||||||
|
$lessonDocUpload.error = $t('upgrade.required');
|
||||||
|
snackbar.error($lessonDocUpload.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$lessonDocUpload.isUploading = true;
|
$lessonDocUpload.isUploading = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
+11
@@ -30,6 +30,17 @@
|
|||||||
async function onUpload() {
|
async function onUpload() {
|
||||||
if (!fileInput) return;
|
if (!fileInput) return;
|
||||||
|
|
||||||
|
// Prevent free plan users from bypassing UI restrictions
|
||||||
|
if ($isFreePlan) {
|
||||||
|
formRes = {
|
||||||
|
type: 'AUTHORIZATION_ERROR',
|
||||||
|
status: 403,
|
||||||
|
message: $t('upgrade.required')
|
||||||
|
};
|
||||||
|
isLoaded = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
videoUploader.initUpload();
|
videoUploader.initUpload();
|
||||||
|
|
||||||
const videoFile = fileInput.files[0];
|
const videoFile = fileInput.files[0];
|
||||||
|
|||||||
@@ -81,6 +81,12 @@
|
|||||||
async function handleSaveCustomDomain() {
|
async function handleSaveCustomDomain() {
|
||||||
if (!isDomainValid) return;
|
if (!isDomainValid) return;
|
||||||
|
|
||||||
|
// Prevent free plan users from bypassing UI restrictions
|
||||||
|
if ($isFreePlan) {
|
||||||
|
errors.customDomain = 'Custom domains are only available on paid plans';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const sanitizedDomain = sanitizeDomain(customDomain);
|
const sanitizedDomain = sanitizeDomain(customDomain);
|
||||||
|
|
||||||
const details = parse(sanitizedDomain);
|
const details = parse(sanitizedDomain);
|
||||||
|
|||||||
@@ -30,6 +30,12 @@
|
|||||||
let isRemoving: number | null = null;
|
let isRemoving: number | null = null;
|
||||||
|
|
||||||
async function onSendInvite() {
|
async function onSendInvite() {
|
||||||
|
// Check if user is on free plan
|
||||||
|
if ($isFreePlan) {
|
||||||
|
snackbar.error('upgrade.required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { hasError, error: _error, emails } = validateEmailInString(emailsStr);
|
const { hasError, error: _error, emails } = validateEmailInString(emailsStr);
|
||||||
|
|
||||||
if (hasError) {
|
if (hasError) {
|
||||||
@@ -105,6 +111,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onRemove(id: number) {
|
async function onRemove(id: number) {
|
||||||
|
// Check if user is on free plan
|
||||||
|
if ($isFreePlan) {
|
||||||
|
snackbar.error('upgrade.required');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log('onRemove called');
|
console.log('onRemove called');
|
||||||
isRemoving = id;
|
isRemoving = id;
|
||||||
const { error } = await supabase.from('organizationmember').delete().match({ id });
|
const { error } = await supabase.from('organizationmember').delete().match({ id });
|
||||||
|
|||||||
Reference in New Issue
Block a user