Compare commits

..

8 Commits

Author SHA1 Message Date
Kalista Payne 0cb1d37352 fix(mongoose): typeKey 2026-05-19 08:10:21 -05:00
Kalista Payne 2ce4a007d7 feat(csp): reporting endpoint rough 2026-05-19 07:57:53 -05:00
Kalista Payne b880a558f4 Revert "feat(CSP): log details of CSP violations"
This reverts commit c1ff366f88.
2026-05-19 07:45:25 -05:00
Kalista Payne 6e13796f70 Revert "fix(auth): disable broken CSP for now"
This reverts commit e8bbdc2cb8.
2026-05-19 07:40:18 -05:00
Kalista Payne c1ff366f88 feat(CSP): log details of CSP violations 2026-05-19 07:25:20 -05:00
Kalista Payne 4c670a3ef2 Revert "fix(auth): downgrade helmet"
This reverts commit 3489b88752.
2026-05-19 07:05:48 -05:00
Kalista Payne 3b9ffae625 fix(faq): pointer cursor 2026-05-12 16:48:49 -05:00
Kalista Payne dd413518f5 Fiz/summaries (#15653)
* dailies monthly weeks of month summary

update monthly day-of-week scheduling summary, add scheduling summary to task form, and add 5th week warning

* formatting

* scheduling warning and summary UI updates

* fix(svg): use extant icon

---------

Co-authored-by: Hafiz <hafizbhamidi@gmail.com>
2026-05-12 16:28:40 -05:00
9 changed files with 104 additions and 30 deletions
+5 -6
View File
@@ -46,7 +46,7 @@
"gulp.spritesmith": "^6.13.0",
"habitica-markdown": "^4.1.0",
"heapdump": "^0.3.15",
"helmet": "^4.6.0",
"helmet": "^8.1.0",
"in-app-purchase": "^1.11.3",
"js2xmlparser": "^5.0.0",
"jsonwebtoken": "^9.0.2",
@@ -12798,12 +12798,11 @@
}
},
"node_modules/helmet": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz",
"integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==",
"license": "MIT",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz",
"integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==",
"engines": {
"node": ">=10.0.0"
"node": ">=18.0.0"
}
},
"node_modules/hex2dec": {
+1 -1
View File
@@ -41,7 +41,7 @@
"gulp.spritesmith": "^6.13.0",
"habitica-markdown": "^4.1.0",
"heapdump": "^0.3.15",
"helmet": "^4.6.0",
"helmet": "^8.1.0",
"in-app-purchase": "^1.11.3",
"js2xmlparser": "^5.0.0",
"jsonwebtoken": "^9.0.2",
+1
View File
@@ -42,6 +42,7 @@ ul {
font-weight: 400;
line-height: 1.75;
color: $purple-200;
cursor: pointer;
}
h4 {
@@ -1,10 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3344_18)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 12H7V10H9V12ZM16 2V14C16 15.1 15.1 16 14 16H2C0.9 16 0 15.1 0 14V2C0 0.9 0.9 0 2 0H14C15.1 0 16 0.9 16 2ZM14 2H2V14H14V2ZM9 4H7V9H9V4Z" fill="#4E4A57"/>
</g>
<defs>
<clipPath id="clip0_3344_18">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 450 B

@@ -66,14 +66,6 @@
<p class="purple-600">
{{ $t('usernameLimitations') }}
</p>
<input
v-if="needsEmailField"
id="emailInput"
v-model="email"
class="form-control dark"
type="text"
:placeholder="$t('email')"
>
<div class="custom-control custom-checkbox mb-4">
<input
id="privacyTOS"
@@ -233,6 +225,7 @@ export default {
if (!this.email && this.registrationMethod !== 'apple') {
return;
}
if ((!this.email || this.email === '') && this.registrationMethod === 'apple') {
this.needsEmailField = true;
}
@@ -423,8 +423,8 @@
class="scheduling-warning mt-2"
>
<span
class="scheduling-warning-icon"
v-html="icons.exclamationInfo"
class="scheduling-warning-icon svg-icon color gray-50"
v-html="icons.alert"
></span>
<span
class="scheduling-warning-text"
@@ -1294,7 +1294,6 @@ import goldIcon from '@/assets/svg/gold.svg?raw';
import chevronIcon from '@/assets/svg/chevron.svg?raw';
import calendarIcon from '@/assets/svg/calendar.svg?raw';
import gripIcon from '@/assets/svg/grip.svg?raw';
import exclamationInfoIcon from '@/assets/svg/exclaimation_info.svg?raw';
import InformationIcon from '@/components/ui/informationIcon.vue';
import alertIcon from '@/assets/svg/for-css/alert-white.svg?raw';
@@ -1332,7 +1331,6 @@ export default {
streak: streakIcon,
calendar: calendarIcon,
grip: gripIcon,
exclamationInfo: exclamationInfoIcon,
alert: alertIcon,
}),
members: [],
@@ -8,6 +8,7 @@ import { model as User } from '../../models/user';
import { model as UserHistory } from '../../models/userHistory';
import { model as Group } from '../../models/group';
import { model as Blocker } from '../../models/blocker';
import { model as CSPReport } from '../../models/cspReport';
import {
NotFound,
} from '../../libs/errors';
@@ -260,4 +261,16 @@ api.getGroup = {
},
};
api.cspReport = {
method: 'POST',
url: '/admin/csp-report',
async handler (req, res) {
const report = new CSPReport(req.body);
await report.save();
res.respond(200, report);
},
};
export default api;
+33 -1
View File
@@ -37,6 +37,7 @@ import {
} from './requestLogHandler';
const IS_PROD = nconf.get('IS_PROD');
const BASE_URL = nconf.get('BASE_URL');
const DISABLE_LOGGING = nconf.get('DISABLE_REQUEST_LOGGING') === 'true';
const ENABLE_HTTP_AUTH = nconf.get('SITE_HTTP_AUTH_ENABLED') === 'true';
const LOG_REQUESTS_EXCESSIVE_MODE = nconf.get('LOG_REQUESTS_EXCESSIVE_MODE') === 'true';
@@ -68,7 +69,38 @@ export default function attachMiddlewares (app, server) {
// See https://helmetjs.github.io/ for the list of headers enabled by default
app.use(helmet({
// New middlewares added by default in Helmet 4 are disabled
contentSecurityPolicy: false, // @TODO implement
contentSecurityPolicy: {
directives: {
defaultSrc: [
'habitica.com',
'*.habitica.com',
'*.amazon.com',
'*.amazonaws.com',
'*.amplitude.com',
'*.loggly.com',
'*.payments-amazon.com',
'*.stripe.com',
'*.stripe.network',
],
imgSrc: [
'*',
'data:',
],
scriptSrc: [
'habitica.com',
'*.habitica.com',
'*.amazon.com',
'*.amazonaws.com',
'*.amplitude.com',
'*.loggly.com',
'*.payments-amazon.com',
'*.stripe.com',
'*.stripe.network',
],
reportTo: `${BASE_URL}/api/v4/admin/csp-report`,
upgradeInsecureRequests: IS_PROD ? [] : null,
},
},
expectCt: false,
permittedCrossDomainPolicies: false,
referrerPolicy: false,
+48
View File
@@ -0,0 +1,48 @@
import mongoose from 'mongoose';
import baseModel from '../libs/baseModel';
export const schema = new mongoose.Schema({
sourceFile: {
$type: String,
},
lineNumber: {
$type: Number,
},
columnNumber: {
$type: Number,
},
documentUrl: {
$type: String,
},
referrer: {
$type: String,
},
blockedURL: {
$type: String,
},
effectiveDirective: {
$type: String,
},
originalPolicy: {
$type: String,
},
sample: {
$type: String,
},
disposition: {
$type: String,
},
statusCode: {
$type: Number,
},
}, {
strict: true,
minimize: false, // So empty objects are returned
typeKey: '$type', // mandatory
});
schema.plugin(baseModel, {
timestamps: true,
});
export const model = mongoose.model('CSPReport', schema);