Sanitize url to not end with implicit ports

pull/175/head
Chocobozzz 2017-12-21 09:56:59 +01:00
parent 6725d05c5f
commit 225a89c2af
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
5 changed files with 122 additions and 58 deletions

View File

@ -1,19 +1,15 @@
import * as express from 'express'
import { UserRight } from '../../../../shared/models/users'
import { getFormattedObjects, getServerActor, loadActorUrlOrGetFromWebfinger, logger, retryTransactionWrapper } from '../../../helpers'
import { sequelizeTypescript, SERVER_ACTOR_NAME } from '../../../initializers'
import {
getFormattedObjects, getServerActor, loadActorUrlOrGetFromWebfinger, logger, retryTransactionWrapper,
sanitizeHost
} from '../../../helpers'
import { REMOTE_SCHEME, sequelizeTypescript, SERVER_ACTOR_NAME } from '../../../initializers'
import { getOrCreateActorAndServerAndModel } from '../../../lib/activitypub'
import { sendFollow, sendUndoFollow } from '../../../lib/activitypub/send'
import {
asyncMiddleware,
authenticate,
ensureUserHasRight,
paginationValidator,
removeFollowingValidator,
setBodyHostsPort,
setFollowersSort,
setFollowingSort,
setPagination
asyncMiddleware, authenticate, ensureUserHasRight, paginationValidator, removeFollowingValidator, setBodyHostsPort,
setFollowersSort, setFollowingSort, setPagination
} from '../../../middlewares'
import { followersSortValidator, followingSortValidator, followValidator } from '../../../middlewares/validators'
import { ActorModel } from '../../../models/activitypub/actor'
@ -82,10 +78,12 @@ async function followRetry (req: express.Request, res: express.Response, next: e
const actorName = SERVER_ACTOR_NAME
for (const host of hosts) {
const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP)
// We process each host in a specific transaction
// First, we add the follow request in the database
// Then we send the follow request to other actor
const p = loadActorUrlOrGetFromWebfinger(actorName, host)
const p = loadActorUrlOrGetFromWebfinger(actorName, sanitizedHost)
.then(actorUrl => getOrCreateActorAndServerAndModel(actorUrl))
.then(targetActor => {
const options = {
@ -95,7 +93,7 @@ async function followRetry (req: express.Request, res: express.Response, next: e
return retryTransactionWrapper(follow, options)
})
.catch(err => logger.warn('Cannot follow server %s.', host, err))
.catch(err => logger.warn('Cannot follow server %s.', sanitizedHost, err))
tasks.push(p)
}

View File

@ -11,6 +11,26 @@ import * as mkdirp from 'mkdirp'
import { join } from 'path'
import * as pem from 'pem'
import * as rimraf from 'rimraf'
import { URL } from 'url'
function sanitizeUrl (url: string) {
const urlObject = new URL(url)
if (urlObject.protocol === 'https:' && urlObject.port === '443') {
urlObject.port = ''
} else if (urlObject.protocol === 'http:' && urlObject.port === '80') {
urlObject.port = ''
}
return urlObject.href.replace(/\/$/, '')
}
// Don't import remote scheme from constants because we are in core utils
function sanitizeHost (host: string, remoteScheme: string) {
let toRemove = remoteScheme === 'https' ? 443 : 80
return host.replace(new RegExp(`:${toRemove}$`), '')
}
function isTestInstance () {
return process.env.NODE_ENV === 'test'
@ -114,6 +134,8 @@ export {
root,
escapeHTML,
pageToStartAndCount,
sanitizeUrl,
sanitizeHost,
promisify0,
promisify1,

View File

@ -1,4 +1,5 @@
import { CONFIG } from '../../initializers'
import { CONFIG, REMOTE_SCHEME } from '../../initializers'
import { sanitizeHost } from '../core-utils'
import { exists } from './misc'
function isWebfingerResourceValid (value: string) {
@ -11,7 +12,7 @@ function isWebfingerResourceValid (value: string) {
const host = actorParts[1]
return host === CONFIG.WEBSERVER.HOSTNAME || host === CONFIG.WEBSERVER.HOST
return sanitizeHost(host, REMOTE_SCHEME.HTTP) === CONFIG.WEBSERVER.HOSTNAME
}
// ---------------------------------------------------------------------------

View File

@ -1,15 +1,15 @@
import * as config from 'config'
import { join } from 'path'
import { JobCategory, JobState, VideoRateType } from '../../shared/models'
import { FollowState } from '../../shared/models/actors'
import { ActivityPubActorType } from '../../shared/models/activitypub'
import { FollowState } from '../../shared/models/actors'
import { VideoPrivacy } from '../../shared/models/videos'
// Do not use barrels, remain constants as independent as possible
import { isTestInstance, root } from '../helpers/core-utils'
import { isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
// ---------------------------------------------------------------------------
const LAST_MIGRATION_VERSION = 135
const LAST_MIGRATION_VERSION = 140
// ---------------------------------------------------------------------------
@ -38,6 +38,44 @@ const OAUTH_LIFETIME = {
// ---------------------------------------------------------------------------
// Number of points we add/remove from a friend after a successful/bad request
const SERVERS_SCORE = {
PENALTY: -10,
BONUS: 10,
BASE: 100,
MAX: 1000
}
const FOLLOW_STATES: { [ id: string ]: FollowState } = {
PENDING: 'pending',
ACCEPTED: 'accepted'
}
const REMOTE_SCHEME = {
HTTP: 'https',
WS: 'wss'
}
const JOB_STATES: { [ id: string ]: JobState } = {
PENDING: 'pending',
PROCESSING: 'processing',
ERROR: 'error',
SUCCESS: 'success'
}
const JOB_CATEGORIES: { [ id: string ]: JobCategory } = {
TRANSCODING: 'transcoding',
ACTIVITYPUB_HTTP: 'activitypub-http'
}
// How many maximum jobs we fetch from the database per cycle
const JOBS_FETCH_LIMIT_PER_CYCLE = {
transcoding: 10,
httpRequest: 20
}
// 1 minutes
let JOBS_FETCHING_INTERVAL = 60000
// ---------------------------------------------------------------------------
const CONFIG = {
LISTEN: {
PORT: config.get<number>('listen.port')
@ -93,8 +131,6 @@ const CONFIG = {
}
}
}
CONFIG.WEBSERVER.URL = CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
CONFIG.WEBSERVER.HOST = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
const AVATARS_DIR = {
ACCOUNT: join(CONFIG.STORAGE.AVATARS_DIR, 'account')
@ -238,44 +274,6 @@ const ACTIVITY_PUB_ACTOR_TYPES: { [ id: string ]: ActivityPubActorType } = {
// ---------------------------------------------------------------------------
// Number of points we add/remove from a friend after a successful/bad request
const SERVERS_SCORE = {
PENALTY: -10,
BONUS: 10,
BASE: 100,
MAX: 1000
}
const FOLLOW_STATES: { [ id: string ]: FollowState } = {
PENDING: 'pending',
ACCEPTED: 'accepted'
}
const REMOTE_SCHEME = {
HTTP: 'https',
WS: 'wss'
}
const JOB_STATES: { [ id: string ]: JobState } = {
PENDING: 'pending',
PROCESSING: 'processing',
ERROR: 'error',
SUCCESS: 'success'
}
const JOB_CATEGORIES: { [ id: string ]: JobCategory } = {
TRANSCODING: 'transcoding',
ACTIVITYPUB_HTTP: 'activitypub-http'
}
// How many maximum jobs we fetch from the database per cycle
const JOBS_FETCH_LIMIT_PER_CYCLE = {
transcoding: 10,
httpRequest: 20
}
// 1 minutes
let JOBS_FETCHING_INTERVAL = 60000
// ---------------------------------------------------------------------------
const PRIVATE_RSA_KEY_SIZE = 2048
// Password encryption
@ -334,6 +332,9 @@ if (isTestInstance() === true) {
ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE = 2
}
CONFIG.WEBSERVER.URL = sanitizeUrl(CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT)
CONFIG.WEBSERVER.HOST = sanitizeHost(CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT, REMOTE_SCHEME.HTTP)
// ---------------------------------------------------------------------------
export {

View File

@ -0,0 +1,42 @@
import * as Sequelize from 'sequelize'
import { DataType } from 'sequelize-typescript'
import { createPrivateAndPublicKeys } from '../../helpers'
import { CONFIG } from '../constants'
async function up (utils: {
transaction: Sequelize.Transaction,
queryInterface: Sequelize.QueryInterface,
sequelize: Sequelize.Sequelize
}): Promise<void> {
const toReplace = CONFIG.WEBSERVER.HOSTNAME + ':443'
const by = CONFIG.WEBSERVER.HOST
const replacer = column => `replace("${column}", '${toReplace}', '${by}')`
{
const query = `UPDATE video SET url = ${replacer('url')}`
await utils.sequelize.query(query)
}
{
const query = `
UPDATE actor SET url = ${replacer('url')}, "inboxUrl" = ${replacer('inboxUrl')}, "outboxUrl" = ${replacer('outboxUrl')},
"sharedInboxUrl" = ${replacer('sharedInboxUrl')}, "followersUrl" = ${replacer('followersUrl')},
"followingUrl" = ${replacer('followingUrl')}
`
await utils.sequelize.query(query)
}
{
const query = `UPDATE server SET host = replace(host, ':443', '')`
await utils.sequelize.query(query)
}
}
function down (options) {
throw new Error('Not implemented.')
}
export {
up,
down
}