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 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | 29x 29x 29x 29x 116x 116x 116x 5240x 5240x 991x 3x 5240x 3x 1x 5240x 2x 1x 5240x 21x 1x 5240x 346x 346x 346x 346x 20x 5240x 5240x 11623x 12x 116x 29x 29x 29x 346x 346x 346x 2101x 2101x 439x 439x 1662x 346x 26x | // Walks an entire spec. // Assertation 1: // `type` values for properties must be strings // multi-type properties are not allowed // Assertation 2: // In specific areas of a spec, allowed $ref values are restricted. // Assertation 3: // Sibling keys with $refs are not allowed - default set to `off` // http://watson-developer-cloud.github.io/api-guidelines/swagger-coding-style#sibling-elements-for-refs const match = require('matcher'); const { walk } = require('../../../utils'); const MessageCarrier = require('../../../utils/messageCarrier'); module.exports.validate = function({ jsSpec, isOAS3 }, config) { const messages = new MessageCarrier(); config = config.walker; walk(jsSpec, [], function(obj, path) { // parent keys that allow non-string "type" properties. for example, // having a definition called "type" is allowed const allowedParents = isOAS3 ? [ 'schemas', 'properties', 'responses', 'parameters', 'requestBodies', 'headers', 'securitySchemes' ] : [ 'definitions', 'properties', 'parameters', 'responses', 'securityDefinitions' ]; ///// "type" should always have a string-type value, everywhere. if (obj.type && allowedParents.indexOf(path[path.length - 1]) === -1) { if (typeof obj.type !== 'string') { messages.addMessage( [...path, 'type'], '"type" should be a string', 'error' ); } } ///// Minimums and Maximums if (obj.maximum && obj.minimum) { if (greater(obj.minimum, obj.maximum)) { messages.addMessage( path.concat(['minimum']), 'Minimum cannot be more than maximum', 'error' ); } } if (obj.maxProperties && obj.minProperties) { if (greater(obj.minProperties, obj.maxProperties)) { messages.addMessage( path.concat(['minProperties']), 'minProperties cannot be more than maxProperties', 'error' ); } } if (obj.maxLength && obj.minLength) { if (greater(obj.minLength, obj.maxLength)) { messages.addMessage( path.concat(['minLength']), 'minLength cannot be more than maxLength', 'error' ); } } ///// Restricted $refs -- only check internal refs if (obj.$ref && typeof obj.$ref === 'string' && obj.$ref.startsWith('#')) { const blacklistPayload = getRefPatternBlacklist(path, isOAS3); const refBlacklist = blacklistPayload.blacklist || []; const matches = match([obj.$ref], refBlacklist); if (refBlacklist && refBlacklist.length && matches.length) { // Assertation 2 // use the slice(1) to remove the `!` negator from the string messages.addMessage( [...path, '$ref'], `${ blacklistPayload.location } $refs must follow this format: ${refBlacklist[0].slice(1)}`, config.incorrect_ref_pattern, 'incorrect_ref_pattern' ); } } const keys = Object.keys(obj); keys.forEach(k => { if (keys.indexOf('$ref') > -1 && k !== '$ref') { messages.addMessage( path.concat([k]), 'Values alongside a $ref will be ignored.', config.$ref_siblings, '$ref_siblings' ); } }); }); return messages; }; // values are globs! const unacceptableRefPatternsS2 = { responses: ['!*#/responses*'], schema: ['!*#/definitions*'], parameters: ['!*#/parameters*'] }; const unacceptableRefPatternsOAS3 = { responses: ['!*#/components/responses*'], schema: ['!*#/components/schemas*'], parameters: ['!*#/components/parameters*'], requestBody: ['!*#/components/requestBodies*'], security: ['!*#/components/securitySchemes*'], callbacks: ['!*#/components/callbacks*'], examples: ['!*#/components/examples*'], headers: ['!*#/components/headers*'], links: ['!*#/components/links*'] }; const exceptionedParents = ['properties']; function getRefPatternBlacklist(path, isOAS3) { const unacceptableRefPatterns = isOAS3 ? unacceptableRefPatternsOAS3 : unacceptableRefPatternsS2; let location = ''; const blacklist = path.reduce((prev, curr, i) => { const parent = path[i - 1]; if ( unacceptableRefPatterns[curr] && exceptionedParents.indexOf(parent) === -1 ) { location = curr; return unacceptableRefPatterns[curr]; } else { return prev; } }, null); return { blacklist, location }; } function greater(a, b) { // is a greater than b? return a > b; } |