Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
- Added sanitization.js in the Gruntfile.
- Added methods for adding / removing strategies to the provider.
- Changed isString checking in strategies to checking a 'mode' which is either 'params' or 'text'
- Some refactoring and bugfixes.
- Added documentation.
- Restored $translateInterpolator.useSanitizeValueStrategy methods.
  • Loading branch information
Mark Lagendijk committed Apr 23, 2015
1 parent 3e1d342 commit ab4531c
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 62 deletions.
1 change: 1 addition & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ module.exports = function (grunt) {
'src/service/translate.js',
'src/service/default-interpolation.js',
'src/service/storage-key.js',
'src/service/sanitization.js',
'src/directive/translate.js',
'src/directive/translate-cloak.js',
'src/filter/translate.js'
Expand Down
12 changes: 10 additions & 2 deletions src/service/default-interpolation.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ function $translateDefaultInterpolation ($interpolate, $translateSanitization) {
return $identifier;
};

/**
* @deprecated ToDo: remove in 3.0
*/
$translateInterpolator.useSanitizeValueStrategy = function (value) {
$translateSanitization.useStrategy(value);
return this;
};

/**
* @ngdoc function
* @name pascalprecht.translate.$translateDefaultInterpolation#interpolate
Expand All @@ -57,10 +65,10 @@ function $translateDefaultInterpolation ($interpolate, $translateSanitization) {
*/
$translateInterpolator.interpolate = function (string, interpolationParams) {
interpolationParams = interpolationParams || {};
interpolationParams = $translateSanitization.sanitize(interpolationParams);
interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params');

var interpolatedText = $interpolate(string)(interpolationParams);
interpolatedText = $translateSanitization.sanitize(interpolatedText);
interpolatedText = $translateSanitization.sanitize(interpolatedText, 'text');

return interpolatedText;
};
Expand Down
12 changes: 10 additions & 2 deletions src/service/messageformat-interpolation.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ function $translateMessageFormatInterpolation($translateSanitization, $cacheFact
return $identifier;
};

/**
* @deprecated ToDo: remove in 3.0
*/
$translateInterpolator.useSanitizeValueStrategy = function (value) {
$translateSanitization.useStrategy(value);
return this;
};

/**
* @ngdoc function
* @name pascalprecht.translate.$translateMessageFormatInterpolation#interpolate
Expand All @@ -73,14 +81,14 @@ function $translateMessageFormatInterpolation($translateSanitization, $cacheFact
*/
$translateInterpolator.interpolate = function (string, interpolationParams) {
interpolationParams = interpolationParams || {};
interpolationParams = $translateSanitization.sanitize(interpolationParams);
interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params');

var interpolatedText = $cache.get(string + angular.toJson(interpolationParams));

// if given string wasn't interpolated yet, we do so now and never have to do it again
if (!interpolatedText) {
interpolatedText = $mf.compile(string)(interpolationParams);
interpolatedText = $translateSanitization.sanitize(interpolatedText);
interpolatedText = $translateSanitization.sanitize(interpolatedText, 'text');

$cache.put(string + angular.toJson(interpolationParams), interpolatedText);
}
Expand Down
173 changes: 115 additions & 58 deletions src/service/sanitization.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @name pascalprecht.translate.$translateSanitization
*
* @description
* Sanitizes interpolation parameters and interpolated strings
* Sanitizes interpolation parameters and translated texts.
*
* @return {object} $translateInterpolator Interpolator service
*/
Expand All @@ -13,72 +13,132 @@ function $translateSanitizationProvider(){

var provider = this,
$sanitize,
currentStrategy = null; // ToDo: change to either 'sanitize' or 'escape' in 3.0.
currentStrategy = null,// ToDo: change to either 'sanitize', 'escape' or ['sanitize', 'escapeParameters'] in 3.0.
hasConfiguredStrategy = false,
hasShownNoStrategyConfiguredWarning = false;

provider.strategies = {
sanitize: function(value){
if(angular.isString(value)){
htmlSanitizeValue(value);
}
else{
return value;
/**
* Sanitizes HTML in the translation text using $sanitize.
*/
sanitize: function(value, mode){
if(mode === 'text'){
value = htmlSanitizeValue(value);
}
return value;
},
escape: function(value){
if(angular.isString(value)){
return htmlEscapeValue(value);
}
else{
return value;
/**
* Escapes HTML in the translation.
*/
escape: function(value, mode){
if(mode === 'text'){
value = htmlEscapeValue(value);
}
return value;
},
escapeParameters: function(value){
if(angular.isString(value)){
return value;
}
else{
return mapInterpolationParameters(value, htmlEscapeValue);
/**
* Sanitizes HTML in the values of the interpolation parameters using $sanitize.
*/
sanitizeParameters: function(value, mode){
if(mode === 'params'){
value = mapInterpolationParameters(value, htmlSanitizeValue);
}
return value;
},
sanitizeParameters: function(value){
if(angular.isString(value)){
return value;
}
else{
return mapInterpolationParameters(value, htmlSanitizeValue);
/**
* Escapes HTML in the values of the interpolation parameters.
*/
escapeParameters: function(value, mode){
if(mode === 'params'){
value = mapInterpolationParameters(value, htmlEscapeValue);
}
return value;
}
};
// Support legacy strategy name 'escaped' for backwards compatability. ToDo: should be removed in 3.0
// Support legacy strategy name 'escaped' for backwards compatability. ToDo: should be removed in 3.0.
provider.strategies.escaped = provider.strategies.escapeParameters;

/**
* Adds a sanitization strategy to the list of known strategies.
* @param {string} strategyName
* @param {Function} strategyFunction
*/
provider.addStrategy = function(strategyName, strategyFunction){
provider.strategies[strategyName] = strategyFunction;
};

/**
* Removes a sanitization strategy from the list of known strategies.
* @param {string} strategyName
*/
provider.removeStrategy = function(strategyName){
delete provider.strategies[strategyName];
};

/**
* Selects a sanitization strategy. When an array is provided the strategies will be executed in order.
* @param {string|Function|Array<string|Function>} strategy The sanitization strategy / strategies which should be used. Either a name of an existing strategy, a custom strategy function, or an array consisting of multiple names and / or custom functions.
* @returns {$translateSanitizationProvider}
*/
provider.useStrategy = function(strategy){
hasConfiguredStrategy = true;
currentStrategy = strategy;
return this;
};

provider.$get = function($injector){
provider.$get = function($injector, $log){

var applyStrategies = function(value, mode, strategies){
angular.forEach(strategies, function(strategy){
if(angular.isFunction(strategy)){
value = strategy(value, mode);
}
else if(angular.isFunction(provider.strategies[strategy])){
value = provider.strategies[strategy](value, mode);
}
else{
throw new Error('pascalprecht.translate.$translateSanitization: Unknown sanitization strategy: \'' + strategy + '\'');
}
});
return value;
};

// ToDo: should be removed in 3.0
var showNoStrategyConfiguredWarning = function(){
if(!hasConfiguredStrategy && !hasShownNoStrategyConfiguredWarning){
$log.warn('pascalprecht.translate.$translateSanitization: No sanitization strategy has been configured. This can have serious security implications. See http://angular-translate.github.io/docs/#/guide/19_security for details.');
hasShownNoStrategyConfiguredWarning = true;
}
};

if($injector.has('$sanitize')){
$sanitize = $injector.get('$sanitize');
}

return {
/**
* Selects a sanitization strategy. When an array is provided the strategies will be executed in order.
* @param {string|Function|Array<string|Function>} strategy The sanitization strategy / strategies which should be used. Either a name of an existing strategy, a custom strategy function, or an array consisting of multiple names and / or custom functions.
* @returns {$translateSanitizationProvider}
*/
useStrategy: provider.useStrategy,
sanitize: function(value, strategy){
/**
* Sanitizes a value.
* @param {*} value The value which should be sanitized.
* @param {string} mode The current sanitization mode, either 'params' or 'text'.
* @param {string|Function|Array<string|Function>} [strategy] Optional custom strategy which should be used instead of the currently selected strategy.
* @returns {*}
*/
sanitize: function(value, mode, strategy){
if(!strategy && !currentStrategy){
showNoStrategyConfiguredWarning();
return value;
}

strategy = strategy || currentStrategy;
var strategies = angular.isArray(strategy) ? strategy : [strategy];
angular.forEach(strategies, function(strategy){
if(angular.isFunction(provider.strategies[strategy])){
value = provider.strategies[strategy](value);
}
else if(angular.isFunction(strategy)){
value = strategy(value);
}
else{
throw new Error('pascalprecht.translate.$translateSanitization: Unknown sanitization strategy: \'' + strategy + '\'');
}
});
return value;

return applyStrategies(value, mode, strategies);
}
};
};
Expand All @@ -89,27 +149,24 @@ function $translateSanitizationProvider(){

var htmlSanitizeValue = function(value){
if(!$sanitize){
throw new Error('pascalprecht.translate.$translateSanitization: Error cannot find $sanitize service. Either include the ngSanitize module (https://docs.angularjs.org/api/ngSanitize) or use a sanitization strategy which does not depend on $sanitize, such as \'escape\'.')
throw new Error('pascalprecht.translate.$translateSanitization: Error cannot find $sanitize service. Either include the ngSanitize module (https://docs.angularjs.org/api/ngSanitize) or use a sanitization strategy which does not depend on $sanitize, such as \'escape\'.');
}
return $sanitize(value);
};

var mapInterpolationParameters = function(parameters, iteratee){
var result = {};
for(var key in parameters){
if(Object.prototype.hasOwnProperty.call(parameters, key)){
var value = parameters[key];
if(angular.isNumber(value)){
result[key] = value;
}
else if(angular.isObject(value)){
result[key] = mapInterpolationParameters(value, iteratee);
}
else{
result[key] = iteratee(value);
}
}
var mapInterpolationParameters = function(value, iteratee){
if(angular.isObject(value)){
var result = angular.isArray(value) ? [] : {};
angular.forEach(value, function(propertyValue, propertyKey){
result[propertyKey] = mapInterpolationParameters(propertyValue, iteratee);
});
return result;
}
else if(angular.isNumber(value)){
return value;
}
else{
return iteratee(value);
}
return result;
};
}

0 comments on commit ab4531c

Please sign in to comment.