functions/security-definitions.js (49 lines of code) (raw):

// Check API definition to ensure conformance to Azure security schemes guidelines. // Check: // - There is at least one security scheme. // - All security schemes are either: // - type: oauth2 or // - type: apiKey with in: header // - An oauth2 security scheme defines at least one scope. // - All scopes defined in an oauth2 security scheme match a pattern: // - https:\/\/[\w-]+(\.[\w-]+)+/[\w-.]+ // @param doc - the entire API document module.exports = (doc) => { if (doc === null || typeof doc !== 'object') { return []; } if (!doc.securityDefinitions || (typeof doc.securityDefinitions === 'object' && Object.keys(doc.securityDefinitions).length === 0)) { return [{ message: 'At least one security scheme must be defined.', path: ['securityDefinitions'], }]; } const schemes = doc.securityDefinitions; const errors = []; Object.keys(schemes).forEach((schemeKey) => { const scheme = schemes[schemeKey]; // Silently ignore scheme if not an object -- oas2-schema will flag this as an error. // The check here is just to avoid runtime exceptions. if (typeof scheme === 'object') { const path = ['securityDefinitions', schemeKey]; if (scheme.type === 'oauth2') { if (!scheme.scopes || (typeof scheme.scopes === 'object' && Object.keys(scheme.scopes).length === 0)) { errors.push({ message: 'Security scheme with type: oauth2 should have non-empty "scopes" array.', path: [...path, 'scopes'], }); } else { // All scopes must match the pattern Object.keys(scheme.scopes).forEach((scope) => { if (!scope.match(/^https:\/\/[\w-]+(\.[\w-]+)+\/[\w-.]+$/)) { errors.push({ message: 'Oauth2 scope names should have the form: https://<audience>/<permission>', path: [...path, 'scopes', scope], }); } }); } } else if (scheme.type === 'apiKey') { if (scheme.in !== 'header') { errors.push({ message: 'Security scheme with type "apiKey" should specify "in: header".', path: [...path, 'in'], }); } } else { errors.push({ message: 'Security scheme must be type: oauth2 or type: apiKey.', path: [...path, 'type'], }); } } }); return errors; };