All files / src/plugins/validation/2and3/semantic-validators security-ibm.js

96.15% Statements 50/52
93.18% Branches 41/44
100% Functions 11/11
100% Lines 50/50

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149                        29x 29x   29x 104x   104x     104x         104x 6x             104x 104x 195x 195x 247x 247x 115x               104x       104x 49x 121x         121x 121x   121x   122x     122x 6x           116x 116x 116x       116x       116x 2x                   116x   111x 111x   111x 111x 215x     215x 3x                           104x         182x         33x 33x 31x 32x       29x 29x         33x 2x   33x    
// from openapi spec -
// Assertation 1:
// Swagger 2
// For security scheme types other than OAuth2, the security array MUST be empty.
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#securityRequirementObject
// OpenAPI 3
// If the security scheme is of type "oauth2" or "openIdConnect", then the value is a list of scope
//   names required for the execution. For other security scheme types, the array MUST be empty.
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#securityRequirementObject
// Assertation 2
// Items in `security` must match a `securityDefinition`.
 
const each = require('lodash/each');
const MessageCarrier = require('../../../utils/messageCarrier');
 
module.exports.validate = function({ jsSpec, isOAS3 }, config) {
  const messages = new MessageCarrier();
 
  config = config.security;
 
  // check all instances of 'security' objects
  const securityObjects = [];
 
  // security objects can exist at either:
 
  // 1) the top level of the spec (global definition)
  if (jsSpec.security) {
    securityObjects.push({
      security: jsSpec.security,
      path: 'security'
    });
  }
 
  // 2) within operations objects
  const paths = jsSpec.paths;
  each(paths, (operations, pathName) => {
    Iif (pathName.slice(0, 2) === 'x-') return;
    each(operations, (operation, opName) => {
      Iif (opName.slice(0, 2) === 'x-') return;
      if (operation.security) {
        securityObjects.push({
          security: operation.security,
          path: `paths.${pathName}.${opName}.security`
        });
      }
    });
  });
 
  const securityDefinitions = isOAS3
    ? jsSpec.components && jsSpec.components.securitySchemes
    : jsSpec.securityDefinitions;
 
  if (securityObjects.length) {
    securityObjects.forEach(obj => {
      validateSecurityObject(obj);
    });
  }
 
  function validateSecurityObject({ security, path }) {
    security.forEach(schemeObject => {
      const schemeNames = Object.keys(schemeObject);
 
      schemeNames.forEach(schemeName => {
        const schemeIsDefined =
          securityDefinitions && securityDefinitions[schemeName];
 
        // ensure the security scheme is defined
        if (!schemeIsDefined) {
          messages.addMessage(
            `${path}.${schemeName}`,
            'security requirements must match a security definition',
            'error'
          );
        } else {
          const schemeType = securityDefinitions[schemeName].type;
          const isNonEmptyArray = schemeObject[schemeName].length > 0;
          const schemesWithNonEmptyArrays = isOAS3
            ? ['oauth2', 'openIdConnect']
            : ['oauth2'];
 
          const isSchemeWithNonEmptyArray = schemesWithNonEmptyArrays.includes(
            schemeType
          );
 
          if (isNonEmptyArray && !isSchemeWithNonEmptyArray) {
            messages.addMessage(
              `${path}.${schemeName}`,
              `For security scheme types other than ${schemesWithNonEmptyArrays.join(
                ' or '
              )}, the value must be an empty array.`,
              config.invalid_non_empty_security_array,
              'invalid_non_empty_security_array'
            );
          }
 
          if (isSchemeWithNonEmptyArray) {
            // check for resolution of specific scopes
            const scopes = schemeObject[schemeName];
            Eif (Array.isArray(scopes)) {
              // Check for unknown scopes
              const securityDefinition = securityDefinitions[schemeName];
              scopes.forEach((scope, i) => {
                const scopeIsDefined = isOAS3
                  ? checkOAS3Scopes(scope, securityDefinition)
                  : checkSwagger2Scopes(scope, securityDefinition);
                if (!scopeIsDefined) {
                  messages.addMessage(
                    `${path}.${schemeName}.${i}`,
                    `Definition could not be resolved for security scope: ${scope}`,
                    'error'
                  );
                }
              });
            }
          }
        }
      });
    });
  }
 
  return messages;
};
 
// return true if scope is defined
function checkSwagger2Scopes(scope, definition) {
  return Boolean(definition.scopes && definition.scopes[scope]);
}
 
// return true if scope is defined
function checkOAS3Scopes(scope, definition) {
  let scopeIsDefined = false;
  if (definition.flows) {
    Object.keys(definition.flows).forEach(flowType => {
      if (
        definition.flows[flowType].scopes &&
        definition.flows[flowType].scopes[scope]
      ) {
        scopeIsDefined = true;
        return;
      }
    });
  }
  // scopes for openIdConnet are not definied in the document
  if (definition.type && definition.type === 'openIdConnect') {
    scopeIsDefined = true;
  }
  return scopeIsDefined;
}