Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PROD Release - Work Manager Security Issues (5442) #1602

Merged
merged 28 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
fa11dcc
Fix for PROD-4375
jmgasper Aug 7, 2024
46442d7
New reminder to use the new app when launching challenges
jmgasper Aug 29, 2024
38e5efd
Revert "New reminder to use the new app when launching challenges"
jmgasper Nov 7, 2024
67f995d
PM-216 clean up injection in test script
kkartunov Dec 10, 2024
a3c3674
fix: removed cookie file and started using local storage instead of c…
hentrymartin Dec 10, 2024
8f1339b
fix: removed cookie file and started using local storage instead of c…
hentrymartin Dec 10, 2024
300fd14
Merge pull request #1589 from topcoder-platform/PM-216
kkartunov Dec 10, 2024
2f0eb77
fix: default user privilege
hentrymartin Dec 10, 2024
5ba2a81
Merge pull request #1591 from topcoder-platform/pm-219
hentrymartin Dec 11, 2024
5ec54e9
Merge pull request #1590 from topcoder-platform/pm-225
hentrymartin Dec 11, 2024
86961da
fix: removed password from config json
hentrymartin Dec 11, 2024
0276a77
PM-222 - tackle SAST/open-redirect: remove deprecated segment integra…
vas3a Dec 12, 2024
bbe2de2
PM-222 - tackle SAST/open-redirect: ensure uninav domain is as expected
vas3a Dec 12, 2024
e12f3ba
PM-226 Replace dangerouslySetInnerHtml with plaintext rendering
Dec 12, 2024
9db44be
fix: poor validation cross site scripting
hentrymartin Dec 12, 2024
04a3e22
Fix code scanning alert no. 19: Incomplete regular expression for hos…
jmgasper Dec 12, 2024
74515a6
fix: deleted dockerfile
hentrymartin Dec 12, 2024
b571de1
Merge pull request #1594 from topcoder-platform/PM-226
himaniraghav3 Dec 16, 2024
ae5f520
Merge pull request #1593 from topcoder-platform/PM-222_open-redirect
vas3a Dec 16, 2024
c4abefe
Merge pull request #1596 from topcoder-platform/pm-220_1
hentrymartin Dec 17, 2024
572872d
Merge pull request #1595 from topcoder-platform/pm-217
hentrymartin Dec 17, 2024
269138f
Merge pull request #1592 from topcoder-platform/pm-223
hentrymartin Dec 17, 2024
0d24df3
fix: added https to nav url as it is breaking url constructor
hentrymartin Dec 18, 2024
d908ec8
fix: added https to nav url as it is breaking url constructor
hentrymartin Dec 18, 2024
62fb063
Merge pull request #1600 from topcoder-platform/fix-url-error
hentrymartin Dec 18, 2024
0630a14
PM-218 Fix DOS issues with fs
Dec 18, 2024
afe1c7c
Typo
Dec 18, 2024
addbf7e
Merge pull request #1601 from topcoder-platform/PM-218
himaniraghav3 Dec 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/constants/development.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ module.exports = {
// duration to show the prompt saying user will be logged out, before actually logging out the user
IDLE_TIMEOUT_GRACE_MINUTES: 5,
MULTI_ROUND_CHALLENGE_TEMPLATE_ID: 'd4201ca4-8437-4d63-9957-3f7708184b07',
UNIVERSAL_NAV_URL: '//uni-nav.topcoder-dev.com/v1/tc-universal-nav.js',
UNIVERSAL_NAV_URL: 'https://uni-nav.topcoder-dev.com/v1/tc-universal-nav.js',
HEADER_AUTH_URLS_HREF: `https://accounts-auth0.${DOMAIN}?utm_source=community-app-main`,
HEADER_AUTH_URLS_LOCATION: `https://accounts-auth0.${DOMAIN}?retUrl=%S&utm_source=community-app-main`,
SKILLS_V5_API_URL: `${API_V5}/standardized-skills/skills/autocomplete`,
Expand Down
2 changes: 1 addition & 1 deletion config/constants/production.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ module.exports = {
IDLE_TIMEOUT_MINUTES: 10,
IDLE_TIMEOUT_GRACE_MINUTES: 5,
MULTI_ROUND_CHALLENGE_TEMPLATE_ID: 'd4201ca4-8437-4d63-9957-3f7708184b07',
UNIVERSAL_NAV_URL: '//uni-nav.topcoder.com/v1/tc-universal-nav.js',
UNIVERSAL_NAV_URL: 'https://uni-nav.topcoder.com/v1/tc-universal-nav.js',
HEADER_AUTH_URLS_HREF: `https://accounts-auth0.${DOMAIN}?utm_source=community-app-main`,
HEADER_AUTH_URLS_LOCATION: `https://accounts-auth0.${DOMAIN}?retUrl=%S&utm_source=community-app-main`,
SKILLS_V5_API_URL: `${API_V5}/standardized-skills/skills/autocomplete`,
Expand Down
2 changes: 1 addition & 1 deletion config/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ dotenvFiles.forEach(dotenvFile => {
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
// We also resolve them to make sure all tools using them work consistently.
const appDirectory = fs.realpathSync(process.cwd())
const appDirectory = process.cwd()
process.env.NODE_PATH = (process.env.NODE_PATH || '')
.split(path.delimiter)
.filter(folder => folder && !path.isAbsolute(folder))
Expand Down
2 changes: 1 addition & 1 deletion config/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const url = require('url')

// Make sure any symlinks in the project folder are resolved:
// https://github.com/facebook/create-react-app/issues/637
const appDirectory = fs.realpathSync(process.cwd())
const appDirectory = process.cwd()
const resolveApp = relativePath => path.resolve(appDirectory, relativePath)

const envPublicUrl = process.env.PUBLIC_URL
Expand Down
5 changes: 1 addition & 4 deletions config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'
// makes for a smoother build process.
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false'

// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig)

// style files regexes
const cssRegex = /\.css$/
const cssModuleRegex = /\.module\.css$/
Expand Down Expand Up @@ -257,7 +254,7 @@ module.exports = function (webpackEnv) {
// for React Native Web.
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
.filter(ext => !ext.includes('ts')),
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
Expand Down
4 changes: 4 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Use the base image with Node.js
FROM node:12
RUN useradd -m -s /bin/bash appuser
ARG NODE_ENV
ARG BABEL_ENV

Expand All @@ -18,6 +19,9 @@ COPY . /challenge-engine-ui
# Set working directory for future use
WORKDIR /challenge-engine-ui

RUN chown -R appuser:appuser /challenge-engine-ui
USER appuser

# Install the dependencies from package.json
RUN echo "NODE ENV in Docker: $NODE_ENV"
RUN echo "BABEL ENV in Docker: $BABEL_ENV"
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@
"jwt-decode": "^2.2.0",
"lodash": "^4.17.11",
"mini-css-extract-plugin": "0.4.3",
"moment": "^2.24.0",
"moment-duration-format": "^2.2.2",
"moment-timezone": "^0.5.34",
"moment": "^2.29.4",
"moment-duration-format": "^2.3.2",
"moment-timezone": "^0.5.43",
"node-sass": "^4.14.0",
"normalize-text": "^2.4.1",
"optimize-css-assets-webpack-plugin": "5.0.1",
Expand Down
2 changes: 1 addition & 1 deletion scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const printBuildError = require('react-dev-utils/printBuildError')
const measureFileSizesBeforeBuild =
FileSizeReporter.measureFileSizesBeforeBuild
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild
const useYarn = fs.existsSync(paths.yarnLockFile)
const useYarn = false

// These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024
Expand Down
2 changes: 1 addition & 1 deletion scripts/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const paths = require('../config/paths')
const configFactory = require('../config/webpack.config')
const createDevServerConfig = require('../config/webpackDevServer.config')

const useYarn = fs.existsSync(paths.yarnLockFile)
const useYarn = false
const isInteractive = process.stdout.isTTY

// Warn and crash if required files are missing
Expand Down
22 changes: 1 addition & 21 deletions scripts/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,16 @@ process.on('unhandledRejection', err => {
require('../config/env')

const jest = require('jest')
const execSync = require('child_process').execSync
let argv = process.argv.slice(2)

function isInGitRepository () {
try {
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' })
return true
} catch (e) {
return false
}
}

function isInMercurialRepository () {
try {
execSync('hg --cwd . root', { stdio: 'ignore' })
return true
} catch (e) {
return false
}
}

// Watch unless on CI, in coverage mode, or explicitly running all tests
if (
!process.env.CI &&
argv.indexOf('--coverage') === -1 &&
argv.indexOf('--watchAll') === -1
) {
// https://github.com/facebook/create-react-app/issues/5210
const hasSourceControl = isInGitRepository() || isInMercurialRepository()
argv.push(hasSourceControl ? '--watch' : '--watchAll')
argv.push('--watchAll')
}

jest.run(argv)
8 changes: 2 additions & 6 deletions src/components/ChallengeEditor/ChallengeView/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import PropTypes from 'prop-types'
import cn from 'classnames'
import { withRouter } from 'react-router-dom'
import styles from './ChallengeView.module.scss'
import xss from 'xss'
import Track from '../../Track'
import NDAField from '../NDAField'
import UseSchedulingAPIField from '../UseSchedulingAPIField'
Expand All @@ -18,7 +17,6 @@ import ChallengeTotalField from '../ChallengeTotal-Field'
import Loader from '../../Loader'
import AssignedMemberField from '../AssignedMember-Field'
import { getResourceRoleByName } from '../../../util/tc'
import { isBetaMode } from '../../../util/cookie'
import { loadGroupDetails } from '../../../actions/challenges'
import {
REVIEW_TYPES,
Expand All @@ -29,6 +27,7 @@ import {
} from '../../../config/constants'
import PhaseInput from '../../PhaseInput'
import CheckpointPrizesField from '../CheckpointPrizes-Field'
import { isBetaMode } from '../../../util/localstorage'

const ChallengeView = ({
projectDetail,
Expand Down Expand Up @@ -114,10 +113,7 @@ const ChallengeView = ({
<div className={cn(styles.row, styles.topRow)}>
<div className={styles.col}>
<span>
<span className={styles.fieldTitle}>Project:</span>
<span dangerouslySetInnerHTML={{
__html: xss(projectDetail ? projectDetail.name : '')
}} />
<span className={styles.fieldTitle}>Project: {projectDetail ? projectDetail.name : ''}</span>
</span>
</div>
{selectedMilestone &&
Expand Down
9 changes: 2 additions & 7 deletions src/components/ChallengeEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import moment from 'moment-timezone'
import { pick } from 'lodash/fp'
import { withRouter } from 'react-router-dom'
import { toastr } from 'react-redux-toastr'
import xss from 'xss'

import {
VALIDATION_VALUE_TYPE,
PRIZE_SETS_TYPE,
Expand Down Expand Up @@ -69,11 +67,11 @@ import Tooltip from '../Tooltip'
import CancelDropDown from './Cancel-Dropdown'
import UseSchedulingAPIField from './UseSchedulingAPIField'

import { isBetaMode } from '../../util/cookie'
import MilestoneField from './Milestone-Field'
import DiscussionField from './Discussion-Field'
import CheckpointPrizesField from './CheckpointPrizes-Field'
import { canChangeDuration } from '../../util/phase'
import { isBetaMode } from '../../util/localstorage'

const theme = {
container: styles.modalContainer
Expand Down Expand Up @@ -1704,10 +1702,7 @@ class ChallengeEditor extends Component {
<div className={cn(styles.row, styles.topRow)}>
<div className={styles.col}>
<span>
<span className={styles.fieldTitle}>Project:</span>
<span dangerouslySetInnerHTML={{
__html: xss(projectDetail ? projectDetail.name : '')
}} />
<span className={styles.fieldTitle}>Project: {projectDetail ? projectDetail.name : ''}</span>
</span>
</div>
<div className={styles.col}>
Expand Down
10 changes: 3 additions & 7 deletions src/components/ChallengesComponent/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { CONNECT_APP_URL, PROJECT_ROLES } from '../../config/constants'
import { PrimaryButton } from '../Buttons'
import ChallengeList from './ChallengeList'
import styles from './ChallengesComponent.module.scss'
import xss from 'xss'
import { checkReadOnlyRoles } from '../../util/tc'

const ChallengesComponent = ({
Expand Down Expand Up @@ -61,12 +60,9 @@ const ChallengesComponent = ({
<Helmet title={activeProject ? activeProject.name : ''} />
{!dashboard && <div className={styles.titleContainer}>
<div className={styles.titleLinks}>
<div
className={styles.title}
dangerouslySetInnerHTML={{
__html: xss(activeProject ? activeProject.name : '')
}}
/>
<div className={styles.title}>
{activeProject ? activeProject.name : ''}
</div>
{activeProject && activeProject.id && (
<span>
(
Expand Down
3 changes: 1 addition & 2 deletions src/components/ProjectCard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from 'react'
import PT from 'prop-types'
import { Link } from 'react-router-dom'
import cn from 'classnames'
import xss from 'xss'

import styles from './ProjectCard.module.scss'

Expand All @@ -14,7 +13,7 @@ const ProjectCard = ({ projectName, projectId, selected, setActiveProject }) =>
className={cn(styles.projectName, { [styles.selected]: selected })}
onClick={() => setActiveProject(parseInt(projectId))}
>
<div className={styles.name} dangerouslySetInnerHTML={{ __html: xss(projectName) }} />
<div className={styles.name}>{projectName}</div>
</Link>
</div>
)
Expand Down
1 change: 0 additions & 1 deletion src/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export const {
CP_TRACK_ID,
CHALLENGE_TYPE_ID,
MARATHON_TYPE_ID,
SEGMENT_API_KEY,
MULTI_ROUND_CHALLENGE_TEMPLATE_ID,
UNIVERSAL_NAV_URL,
HEADER_AUTH_URLS_HREF,
Expand Down
58 changes: 26 additions & 32 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,35 @@ import ReactDOM from 'react-dom'
import './styles/main.scss'
import 'react-redux-toastr/lib/css/react-redux-toastr.min.css'
import App from './App'
import { SEGMENT_API_KEY, UNIVERSAL_NAV_URL } from './config/constants'
import { UNIVERSAL_NAV_URL } from './config/constants'

ReactDOM.render(<App />, document.getElementById('root'))

/* eslint-disable */
if (!_.isEmpty(SEGMENT_API_KEY)) {
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on","addSourceMiddleware","addIntegrationMiddleware","setAnonymousId","addDestinationMiddleware"];analytics.factory=function(e){return function(){var t=Array.prototype.slice.call(arguments);t.unshift(e);analytics.push(t);return analytics}};for(var e=0;e<analytics.methods.length;e++){var t=analytics.methods[e];analytics[t]=analytics.factory(t)}analytics.load=function(e,t){var n=document.createElement("script");n.type="text/javascript";n.async=!0;n.src="https://cdn.segment.com/analytics.js/v1/"+e+"/analytics.min.js";var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(n,a);analytics._loadOptions=t};analytics.SNIPPET_VERSION="4.1.0";
analytics.load(SEGMENT_API_KEY);
analytics.page();
}}();
}
/* eslint-enable */

// <!-- Start of topcoder Topcoder Universal Navigation script -->
// eslint-disable-next-line no-unused-expressions
!(function (n, t, e, a, c, i, o) {
// eslint-disable-next-line no-unused-expressions, no-sequences
;(n['TcUnivNavConfig'] = c),
(n[c] =
n[c] ||
function () {
;(n[c].q = n[c].q || []).push(arguments)
}),
(n[c].l = 1 * new Date())
// SAST/open-redirect handling: make sure script hostname matches what we expect
if ((new URL(UNIVERSAL_NAV_URL)).hostname.match(/uni-nav\.topcoder(-dev)?\.com$/i)) {
// eslint-disable-next-line no-unused-expressions
!(function (n, t, e, a, c, i, o) {
// eslint-disable-next-line no-unused-expressions, no-sequences
;(i = t.createElement(e)), (o = t.getElementsByTagName(e)[0])
i.async = 1
i.type = 'module'
i.src = a
o.parentNode.insertBefore(i, o)
})(
window,
document,
'script',
UNIVERSAL_NAV_URL,
'tcUniNav'
)
;(n['TcUnivNavConfig'] = c),
(n[c] =
n[c] ||
function () {
;(n[c].q = n[c].q || []).push(arguments)
}),
(n[c].l = 1 * new Date())
// eslint-disable-next-line no-unused-expressions, no-sequences
;(i = t.createElement(e)), (o = t.getElementsByTagName(e)[0])
i.async = 1
i.type = 'module'
i.src = a
o.parentNode.insertBefore(i, o)
})(
window,
document,
'script',
UNIVERSAL_NAV_URL,
'tcUniNav'
)
}
// <!-- End of topcoder Topcoder Universal Navigation script -->
12 changes: 6 additions & 6 deletions src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import { saveToken } from './actions/auth'
import { loadChallengeDetails } from './actions/challenges'
import { connect } from 'react-redux'
import { checkAllowedRoles, checkOnlyReadOnlyRoles, checkReadOnlyRoles } from './util/tc'
import { setCookie, removeCookie, isBetaMode } from './util/cookie'
import IdleTimer from 'react-idle-timer'
import modalStyles from './styles/modal.module.scss'
import ConfirmationModal from './components/Modal/ConfirmationModal'
import Users from './containers/Users'
import { isBetaMode, removeFromLocalStorage, saveToLocalStorage } from './util/localstorage'

const { ACCOUNTS_APP_LOGIN_URL, IDLE_TIMEOUT_MINUTES, IDLE_TIMEOUT_GRACE_MINUTES, COMMUNITY_APP_URL } = process.env

Expand Down Expand Up @@ -94,9 +94,9 @@ class Routes extends React.Component {
getFreshToken().then((token) => {
this.props.saveToken(token)
}).catch((error) => {
console.error(error)
const redirectBackToUrl = window.location.origin + this.props.location.pathname
window.location = ACCOUNTS_APP_LOGIN_URL + '?retUrl=' + redirectBackToUrl
console.error(error.message)
const redirectBackToUrl = encodeURIComponent(window.location.origin + this.props.location.pathname)
window.location = `${ACCOUNTS_APP_LOGIN_URL}?retUrl=${redirectBackToUrl}`
})
}

Expand All @@ -105,9 +105,9 @@ class Routes extends React.Component {
const params = new URLSearchParams(search)
if (!_.isEmpty(params.get('beta'))) {
if (params.get('beta') === 'true' && !isBetaMode()) {
setCookie(BETA_MODE_COOKIE_TAG, 'true')
saveToLocalStorage(BETA_MODE_COOKIE_TAG, 'true')
} else if (params.get('beta') === 'false' && isBetaMode()) {
removeCookie(BETA_MODE_COOKIE_TAG)
removeFromLocalStorage(BETA_MODE_COOKIE_TAG)
}
this.props.history.push(this.props.location.pathname)
}
Expand Down
Loading
Loading