mirror of
https://github.com/System-End/spaces.git
synced 2026-04-20 00:35:24 +00:00
security fixes
This commit is contained in:
parent
32e5d00861
commit
44089f8a9f
4 changed files with 144 additions and 29 deletions
|
|
@ -11,8 +11,9 @@
|
|||
let hackatimeApiKey = user.hackatime_api_key || '';
|
||||
|
||||
let newEmail = '';
|
||||
let verificationCode = '';
|
||||
let emailStep = 'input'; // input, verify
|
||||
let oldEmailCode = '';
|
||||
let newEmailCode = '';
|
||||
let emailStep = 'input'; // input, verifyOld, verifyNew
|
||||
|
||||
let message = '';
|
||||
let error = '';
|
||||
|
|
@ -50,7 +51,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
async function sendEmailCode() {
|
||||
async function startEmailChange() {
|
||||
emailMessage = '';
|
||||
emailError = '';
|
||||
|
||||
|
|
@ -71,16 +72,16 @@
|
|||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: newEmail,
|
||||
mode: 'update' // Mode doesn't really matter for send logic, but good for logging if added
|
||||
email: user.email,
|
||||
mode: 'update'
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
emailMessage = 'Verification code sent to ' + newEmail;
|
||||
emailStep = 'verify';
|
||||
emailMessage = 'Verification code sent to your current email (' + user.email + ')';
|
||||
emailStep = 'verifyOld';
|
||||
} else {
|
||||
emailError = data.message || 'Failed to send code';
|
||||
}
|
||||
|
|
@ -90,25 +91,61 @@
|
|||
}
|
||||
}
|
||||
|
||||
async function verifyEmailChange() {
|
||||
async function verifyOldEmail() {
|
||||
emailMessage = '';
|
||||
emailError = '';
|
||||
|
||||
if (!verificationCode) {
|
||||
if (!oldEmailCode) {
|
||||
emailError = 'Please enter the verification code';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/users/verify-email-change`, {
|
||||
const response = await fetch(`${API_BASE}/users/send`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': authorization
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
newEmail,
|
||||
verificationCode
|
||||
email: newEmail,
|
||||
mode: 'update'
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
emailMessage = 'Verification code sent to your new email (' + newEmail + ')';
|
||||
emailStep = 'verifyNew';
|
||||
} else {
|
||||
emailError = data.message || 'Failed to send code to new email';
|
||||
}
|
||||
} catch (err) {
|
||||
emailError = 'An error occurred. Please try again.';
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyNewEmailAndUpdate() {
|
||||
emailMessage = '';
|
||||
emailError = '';
|
||||
|
||||
if (!newEmailCode) {
|
||||
emailError = 'Please enter the verification code';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/users/update/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
authorization,
|
||||
email: newEmail,
|
||||
oldEmailVerificationCode: parseInt(oldEmailCode),
|
||||
emailVerificationCode: parseInt(newEmailCode)
|
||||
})
|
||||
});
|
||||
|
||||
|
|
@ -117,17 +154,26 @@
|
|||
if (response.ok) {
|
||||
emailMessage = 'Email updated successfully';
|
||||
newEmail = '';
|
||||
verificationCode = '';
|
||||
oldEmailCode = '';
|
||||
newEmailCode = '';
|
||||
emailStep = 'input';
|
||||
dispatch('update', data.data);
|
||||
} else {
|
||||
emailError = data.message || 'Failed to verify email';
|
||||
emailError = data.message || 'Failed to update email';
|
||||
}
|
||||
} catch (err) {
|
||||
emailError = 'An error occurred. Please try again.';
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
function cancelEmailChange() {
|
||||
emailStep = 'input';
|
||||
oldEmailCode = '';
|
||||
newEmailCode = '';
|
||||
emailMessage = '';
|
||||
emailError = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="settings-container">
|
||||
|
|
@ -176,20 +222,36 @@
|
|||
placeholder="Enter new email"
|
||||
/>
|
||||
</div>
|
||||
<button on:click={sendEmailCode}>Send Verification Code</button>
|
||||
{:else}
|
||||
<button on:click={startEmailChange}>Change Email</button>
|
||||
{:else if emailStep === 'verifyOld'}
|
||||
<p class="step-info">Step 1 of 2: Verify your current email</p>
|
||||
<div class="form-group">
|
||||
<label for="verificationCode">Verification Code</label>
|
||||
<label for="oldEmailCode">Verification Code (sent to {user.email})</label>
|
||||
<input
|
||||
type="text"
|
||||
id="verificationCode"
|
||||
bind:value={verificationCode}
|
||||
id="oldEmailCode"
|
||||
bind:value={oldEmailCode}
|
||||
placeholder="Enter 6-digit code"
|
||||
/>
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<button on:click={verifyEmailChange}>Verify & Update</button>
|
||||
<button class="secondary" on:click={() => emailStep = 'input'}>Cancel</button>
|
||||
<button on:click={verifyOldEmail}>Continue</button>
|
||||
<button class="secondary" on:click={cancelEmailChange}>Cancel</button>
|
||||
</div>
|
||||
{:else if emailStep === 'verifyNew'}
|
||||
<p class="step-info">Step 2 of 2: Verify your new email</p>
|
||||
<div class="form-group">
|
||||
<label for="newEmailCode">Verification Code (sent to {newEmail})</label>
|
||||
<input
|
||||
type="text"
|
||||
id="newEmailCode"
|
||||
bind:value={newEmailCode}
|
||||
placeholder="Enter 6-digit code"
|
||||
/>
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<button on:click={verifyNewEmailAndUpdate}>Update Email</button>
|
||||
<button class="secondary" on:click={cancelEmailChange}>Cancel</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
|
@ -302,4 +364,11 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.step-info {
|
||||
font-size: 14px;
|
||||
color: var(--muted);
|
||||
margin-bottom: 15px;
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import express from 'express';
|
||||
import { updateUser } from '../../utils/user.js';
|
||||
import { checkEmail } from '../../utils/airtable.js';
|
||||
import pg from '../../utils/db.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/', async (req, res) => {
|
||||
try {
|
||||
const { authorization, email, username, hackatime_api_key } = req.body;
|
||||
const { authorization, email, oldEmailVerificationCode, emailVerificationCode, username, hackatime_api_key } = req.body;
|
||||
|
||||
if (!authorization) {
|
||||
return res.status(401).json({
|
||||
|
|
@ -13,9 +15,51 @@ router.post('/', async (req, res) => {
|
|||
message: 'Authorization token is required'
|
||||
});
|
||||
}
|
||||
|
||||
const user = await pg('users').where('authorization', authorization).first();
|
||||
if (!user) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid authorization token'
|
||||
});
|
||||
}
|
||||
|
||||
const updateData = {};
|
||||
if (email !== undefined) updateData.email = email;
|
||||
|
||||
if (email !== undefined) {
|
||||
if (!oldEmailVerificationCode) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Verification code for current email is required'
|
||||
});
|
||||
}
|
||||
|
||||
if (!emailVerificationCode) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Verification code for new email is required'
|
||||
});
|
||||
}
|
||||
|
||||
const oldCodeValid = await checkEmail(user.email, oldEmailVerificationCode);
|
||||
if (!oldCodeValid) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid or expired verification code for current email'
|
||||
});
|
||||
}
|
||||
|
||||
const newCodeValid = await checkEmail(email, emailVerificationCode);
|
||||
if (!newCodeValid) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid or expired verification code for new email'
|
||||
});
|
||||
}
|
||||
|
||||
updateData.email = email;
|
||||
}
|
||||
|
||||
if (username !== undefined) updateData.username = username;
|
||||
if (hackatime_api_key !== undefined) updateData.hackatime_api_key = hackatime_api_key;
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,6 @@ export const sendEmail = async (email) => {
|
|||
code: code
|
||||
});
|
||||
|
||||
console.log(`Email verification code created for ${email}`);
|
||||
return {
|
||||
success: true,
|
||||
recordId: record.id,
|
||||
|
|
@ -107,7 +106,8 @@ export const sendEmail = async (email) => {
|
|||
|
||||
export const checkEmail = async (email, codeToCheck) => {
|
||||
try {
|
||||
const filterFormula = `{email} = "${email}"`;
|
||||
const sanitizedEmail = email.replace(/["\\]/g, '');
|
||||
const filterFormula = `{email} = "${sanitizedEmail}"`;
|
||||
const records = await findRecords('Spaces Emails', filterFormula, [
|
||||
{ field: 'Time Created', direction: 'desc' }
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -102,8 +102,9 @@ export const createContainer = async (password, type, authorization) => {
|
|||
const setupScript = fs.readFileSync(setupScriptPath, "utf8");
|
||||
|
||||
const hackatimeApiKey = user.hackatime_api_key || "";
|
||||
const sanitizedApiKey = hackatimeApiKey.replace(/[^a-zA-Z0-9\-_]/g, '');
|
||||
const exec = await container.exec({
|
||||
Cmd: ["bash", "-c", `cat > /tmp/setup.sh << 'EOF'\n${setupScript}\nEOF\nchmod +x /tmp/setup.sh && /tmp/setup.sh '${hackatimeApiKey}' > /app/postinstall.log 2>&1`],
|
||||
Cmd: ["bash", "-c", `cat > /tmp/setup.sh << 'EOF'\n${setupScript}\nEOF\nchmod +x /tmp/setup.sh && /tmp/setup.sh '${sanitizedApiKey}' > /app/postinstall.log 2>&1`],
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
});
|
||||
|
|
@ -227,8 +228,9 @@ export const startContainer = async (spaceId, authorization) => {
|
|||
try {
|
||||
console.log("Setting up Hackatime for code-server container...");
|
||||
const hackatimeApiKey = user.hackatime_api_key;
|
||||
const sanitizedApiKey = hackatimeApiKey.replace(/[^a-zA-Z0-9\-_]/g, '');
|
||||
const exec = await container.exec({
|
||||
Cmd: ["bash", "-c", `export HACKATIME_API_KEY='${hackatimeApiKey}' && export HACKATIME_API_URL="https://hackatime.hackclub.com/api/hackatime/v1" && export SUCCESS_URL="https://hackatime.hackclub.com//success.txt" && curl -sSL https://hackatime.hackclub.com/hackatime/setup.sh | bash`],
|
||||
Cmd: ["bash", "-c", `export HACKATIME_API_KEY='${sanitizedApiKey}' && export HACKATIME_API_URL="https://hackatime.hackclub.com/api/hackatime/v1" && export SUCCESS_URL="https://hackatime.hackclub.com//success.txt" && curl -sSL https://hackatime.hackclub.com/hackatime/setup.sh | bash`],
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue