@suss/ir-core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +205 -0
- package/README.md +40 -0
- package/dist/chunk-BWK3IPL7.js +166 -0
- package/dist/index.cjs +626 -0
- package/dist/index.d.cts +264 -0
- package/dist/index.d.ts +264 -0
- package/dist/index.js +435 -0
- package/dist/schemas.cjs +203 -0
- package/dist/schemas.d.cts +272 -0
- package/dist/schemas.d.ts +272 -0
- package/dist/schemas.js +32 -0
- package/package.json +63 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
BoundaryBindingSchema: () => BoundaryBindingSchema,
|
|
24
|
+
ConfidenceInfoSchema: () => ConfidenceInfoSchema,
|
|
25
|
+
ConfidenceLevelSchema: () => ConfidenceLevelSchema,
|
|
26
|
+
ConfidenceSourceSchema: () => ConfidenceSourceSchema,
|
|
27
|
+
FunctionCallSemanticsSchema: () => FunctionCallSemanticsSchema,
|
|
28
|
+
GraphqlOperationSemanticsSchema: () => GraphqlOperationSemanticsSchema,
|
|
29
|
+
GraphqlResolverSemanticsSchema: () => GraphqlResolverSemanticsSchema,
|
|
30
|
+
MessageBusSemanticsSchema: () => MessageBusSemanticsSchema,
|
|
31
|
+
RestSemanticsSchema: () => RestSemanticsSchema,
|
|
32
|
+
RuntimeConfigSemanticsSchema: () => RuntimeConfigSemanticsSchema,
|
|
33
|
+
SemanticsSchema: () => SemanticsSchema,
|
|
34
|
+
SourceLocationSchema: () => SourceLocationSchema,
|
|
35
|
+
StorageRelationalSemanticsSchema: () => StorageRelationalSemanticsSchema,
|
|
36
|
+
SuppressionFileSchema: () => SuppressionFileSchema,
|
|
37
|
+
SuppressionRuleSchema: () => SuppressionRuleSchema,
|
|
38
|
+
TypeShapeSchema: () => TypeShapeSchema,
|
|
39
|
+
applySuppressionsToFindings: () => applySuppressionsToFindings,
|
|
40
|
+
bodyShapesMatch: () => bodyShapesMatch,
|
|
41
|
+
boundaryKey: () => boundaryKey,
|
|
42
|
+
countsForThreshold: () => countsForThreshold,
|
|
43
|
+
functionCallBinding: () => functionCallBinding,
|
|
44
|
+
graphqlOperationBinding: () => graphqlOperationBinding,
|
|
45
|
+
graphqlResolverBinding: () => graphqlResolverBinding,
|
|
46
|
+
messageBusBinding: () => messageBusBinding,
|
|
47
|
+
normalizePath: () => normalizePath,
|
|
48
|
+
normalizeRuleBoundary: () => normalizeRuleBoundary,
|
|
49
|
+
packageExportBinding: () => packageExportBinding,
|
|
50
|
+
restBinding: () => restBinding,
|
|
51
|
+
ruleBoundaryMatchesKey: () => ruleBoundaryMatchesKey,
|
|
52
|
+
runtimeConfigBinding: () => runtimeConfigBinding,
|
|
53
|
+
storageRelationalBinding: () => storageRelationalBinding,
|
|
54
|
+
validateRule: () => validateRule
|
|
55
|
+
});
|
|
56
|
+
module.exports = __toCommonJS(index_exports);
|
|
57
|
+
|
|
58
|
+
// src/schemas.ts
|
|
59
|
+
var import_zod = require("zod");
|
|
60
|
+
var ConfidenceSourceSchema = import_zod.z.enum([
|
|
61
|
+
"inferred_static",
|
|
62
|
+
"inferred_ai",
|
|
63
|
+
"declared",
|
|
64
|
+
"derived"
|
|
65
|
+
]);
|
|
66
|
+
var ConfidenceLevelSchema = import_zod.z.enum(["high", "medium", "low"]);
|
|
67
|
+
var ConfidenceInfoSchema = import_zod.z.object({
|
|
68
|
+
source: ConfidenceSourceSchema,
|
|
69
|
+
level: ConfidenceLevelSchema
|
|
70
|
+
});
|
|
71
|
+
var SourceLocationSchema = import_zod.z.object({
|
|
72
|
+
file: import_zod.z.string(),
|
|
73
|
+
range: import_zod.z.object({ start: import_zod.z.number(), end: import_zod.z.number() }),
|
|
74
|
+
exportName: import_zod.z.string().nullable()
|
|
75
|
+
});
|
|
76
|
+
var RestSemanticsSchema = import_zod.z.object({
|
|
77
|
+
name: import_zod.z.literal("rest"),
|
|
78
|
+
/** Uppercase HTTP method ("GET", "POST", …). */
|
|
79
|
+
method: import_zod.z.string(),
|
|
80
|
+
/** Normalized route path ("/users/{id}"). */
|
|
81
|
+
path: import_zod.z.string(),
|
|
82
|
+
/**
|
|
83
|
+
* Status codes the producing source explicitly declared (OpenAPI
|
|
84
|
+
* responses, CFN MethodResponses, ts-rest router statuses). Kept here
|
|
85
|
+
* so the pairing layer can still see them without unwrapping metadata.
|
|
86
|
+
* Empty / absent for inferred sources.
|
|
87
|
+
*/
|
|
88
|
+
declaredResponses: import_zod.z.array(import_zod.z.number()).optional()
|
|
89
|
+
});
|
|
90
|
+
var FunctionCallSemanticsSchema = import_zod.z.object({
|
|
91
|
+
name: import_zod.z.literal("function-call"),
|
|
92
|
+
/**
|
|
93
|
+
* Optional module identifier for cross-unit references
|
|
94
|
+
* (e.g. `"./components/Button"` for a React component, or the TS
|
|
95
|
+
* module path for a bare function export). Packs that don't do
|
|
96
|
+
* cross-module pairing can leave it unset.
|
|
97
|
+
*/
|
|
98
|
+
module: import_zod.z.string().optional(),
|
|
99
|
+
/** Named export within the module, when applicable. */
|
|
100
|
+
exportName: import_zod.z.string().optional(),
|
|
101
|
+
/**
|
|
102
|
+
* Package name (as written in `package.json`) when this identity
|
|
103
|
+
* refers to a public package export — e.g. `"@suss/behavioral-ir"`.
|
|
104
|
+
* Set alongside `exportPath` by packs that resolve a package's
|
|
105
|
+
* public surface (the `packageExports` discovery variant). Distinct
|
|
106
|
+
* from `module`, which is a repo-relative module path for
|
|
107
|
+
* intra-repo pairing.
|
|
108
|
+
*/
|
|
109
|
+
package: import_zod.z.string().optional(),
|
|
110
|
+
/**
|
|
111
|
+
* Path to the exported binding within the package, starting with
|
|
112
|
+
* the sub-path key when one is used. Examples:
|
|
113
|
+
* `["parseSummary"]` — root export
|
|
114
|
+
* `["schemas", "BoundaryBindingSchema"]` — sub-path `./schemas`
|
|
115
|
+
*
|
|
116
|
+
* The first segment is the sub-path without the leading `./`
|
|
117
|
+
* (`"."` → omitted). The last segment is the exported name.
|
|
118
|
+
*/
|
|
119
|
+
exportPath: import_zod.z.array(import_zod.z.string()).optional()
|
|
120
|
+
});
|
|
121
|
+
var GraphqlResolverSemanticsSchema = import_zod.z.object({
|
|
122
|
+
name: import_zod.z.literal("graphql-resolver"),
|
|
123
|
+
/** GraphQL type the resolver attaches to: "Query", "Mutation", "Subscription", or an object-type name. */
|
|
124
|
+
typeName: import_zod.z.string(),
|
|
125
|
+
/** Field name on that type. */
|
|
126
|
+
fieldName: import_zod.z.string()
|
|
127
|
+
});
|
|
128
|
+
var GraphqlOperationSemanticsSchema = import_zod.z.object({
|
|
129
|
+
name: import_zod.z.literal("graphql-operation"),
|
|
130
|
+
/** Optional operation name — anonymous queries / mutations leave this unset. */
|
|
131
|
+
operationName: import_zod.z.string().optional(),
|
|
132
|
+
operationType: import_zod.z.enum(["query", "mutation", "subscription"])
|
|
133
|
+
});
|
|
134
|
+
var RuntimeConfigSemanticsSchema = import_zod.z.object({
|
|
135
|
+
name: import_zod.z.literal("runtime-config"),
|
|
136
|
+
deploymentTarget: import_zod.z.enum([
|
|
137
|
+
"lambda",
|
|
138
|
+
"ecs-task",
|
|
139
|
+
"container",
|
|
140
|
+
"k8s-deployment"
|
|
141
|
+
]),
|
|
142
|
+
/**
|
|
143
|
+
* Stable identifier for the runtime instance — CFN logical resource
|
|
144
|
+
* ID for Lambda / ECS, k8s deployment name, container name.
|
|
145
|
+
*/
|
|
146
|
+
instanceName: import_zod.z.string()
|
|
147
|
+
});
|
|
148
|
+
var StorageRelationalSemanticsSchema = import_zod.z.object({
|
|
149
|
+
name: import_zod.z.literal("storage-relational"),
|
|
150
|
+
storageSystem: import_zod.z.enum(["postgres", "mysql", "sqlite"]),
|
|
151
|
+
/**
|
|
152
|
+
* ORM / driver scope. Defaults to `"default"` for single-database
|
|
153
|
+
* setups; monorepos with multiple schemas use distinct values.
|
|
154
|
+
*/
|
|
155
|
+
scope: import_zod.z.string(),
|
|
156
|
+
/** Table / model name as declared in the schema. */
|
|
157
|
+
table: import_zod.z.string()
|
|
158
|
+
});
|
|
159
|
+
var MessageBusSemanticsSchema = import_zod.z.object({
|
|
160
|
+
name: import_zod.z.literal("message-bus"),
|
|
161
|
+
messageBus: import_zod.z.enum(["sqs", "eventbridge", "bullmq", "kafka", "nats"]),
|
|
162
|
+
/** Stable channel identifier — CFN logical id, queue/topic name, subject pattern, `bus#detailType`. */
|
|
163
|
+
channel: import_zod.z.string()
|
|
164
|
+
});
|
|
165
|
+
var SemanticsSchema = import_zod.z.discriminatedUnion("name", [
|
|
166
|
+
RestSemanticsSchema,
|
|
167
|
+
FunctionCallSemanticsSchema,
|
|
168
|
+
GraphqlResolverSemanticsSchema,
|
|
169
|
+
GraphqlOperationSemanticsSchema,
|
|
170
|
+
RuntimeConfigSemanticsSchema,
|
|
171
|
+
StorageRelationalSemanticsSchema,
|
|
172
|
+
MessageBusSemanticsSchema
|
|
173
|
+
]);
|
|
174
|
+
var BoundaryBindingSchema = import_zod.z.object({
|
|
175
|
+
transport: import_zod.z.string(),
|
|
176
|
+
semantics: SemanticsSchema,
|
|
177
|
+
recognition: import_zod.z.string()
|
|
178
|
+
});
|
|
179
|
+
var TypeShapeSchema = import_zod.z.lazy(
|
|
180
|
+
() => import_zod.z.discriminatedUnion("type", [
|
|
181
|
+
import_zod.z.object({
|
|
182
|
+
type: import_zod.z.literal("record"),
|
|
183
|
+
properties: import_zod.z.record(import_zod.z.string(), TypeShapeSchema),
|
|
184
|
+
spreads: import_zod.z.array(import_zod.z.object({ sourceText: import_zod.z.string() })).optional()
|
|
185
|
+
}),
|
|
186
|
+
import_zod.z.object({ type: import_zod.z.literal("dictionary"), values: TypeShapeSchema }),
|
|
187
|
+
import_zod.z.object({ type: import_zod.z.literal("array"), items: TypeShapeSchema }),
|
|
188
|
+
import_zod.z.object({
|
|
189
|
+
type: import_zod.z.literal("literal"),
|
|
190
|
+
value: import_zod.z.union([import_zod.z.string(), import_zod.z.number(), import_zod.z.boolean()]),
|
|
191
|
+
raw: import_zod.z.string().optional()
|
|
192
|
+
}),
|
|
193
|
+
import_zod.z.object({ type: import_zod.z.literal("text") }),
|
|
194
|
+
import_zod.z.object({ type: import_zod.z.literal("integer") }),
|
|
195
|
+
import_zod.z.object({ type: import_zod.z.literal("number") }),
|
|
196
|
+
import_zod.z.object({ type: import_zod.z.literal("boolean") }),
|
|
197
|
+
import_zod.z.object({ type: import_zod.z.literal("null") }),
|
|
198
|
+
import_zod.z.object({ type: import_zod.z.literal("undefined") }),
|
|
199
|
+
import_zod.z.object({
|
|
200
|
+
type: import_zod.z.literal("union"),
|
|
201
|
+
variants: import_zod.z.array(TypeShapeSchema)
|
|
202
|
+
}),
|
|
203
|
+
import_zod.z.object({ type: import_zod.z.literal("ref"), name: import_zod.z.string() }),
|
|
204
|
+
import_zod.z.object({ type: import_zod.z.literal("unknown") })
|
|
205
|
+
])
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
// src/boundaryKey.ts
|
|
209
|
+
function normalizePath(path) {
|
|
210
|
+
let normalized = path.replace(/:([a-zA-Z_]\w*)/g, "{$1}");
|
|
211
|
+
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
212
|
+
normalized = normalized.slice(0, -1);
|
|
213
|
+
}
|
|
214
|
+
normalized = normalized.replace(
|
|
215
|
+
/\{[^}]+\}|[^{]+/g,
|
|
216
|
+
(segment) => segment.startsWith("{") ? segment : segment.toLowerCase()
|
|
217
|
+
);
|
|
218
|
+
return normalized;
|
|
219
|
+
}
|
|
220
|
+
function boundaryKey(binding) {
|
|
221
|
+
const semantics = binding.semantics;
|
|
222
|
+
if (semantics.name === "rest") {
|
|
223
|
+
if (semantics.method === "" || semantics.path === "") {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
const method = semantics.method.toUpperCase();
|
|
227
|
+
const path = normalizePath(semantics.path);
|
|
228
|
+
return `${method} ${path}`;
|
|
229
|
+
}
|
|
230
|
+
if (semantics.name === "graphql-resolver") {
|
|
231
|
+
return `gql:${semantics.typeName}.${semantics.fieldName}`;
|
|
232
|
+
}
|
|
233
|
+
if (semantics.name === "function-call") {
|
|
234
|
+
if (semantics.package !== void 0 && semantics.exportPath !== void 0 && semantics.exportPath.length > 0) {
|
|
235
|
+
return `fn:${semantics.package}::${semantics.exportPath.join(".")}`;
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/suppressions.ts
|
|
243
|
+
var import_zod2 = require("zod");
|
|
244
|
+
var SuppressionRuleSchema = import_zod2.z.object({
|
|
245
|
+
/**
|
|
246
|
+
* Finding kind to match — any behavioural or intent finding kind.
|
|
247
|
+
* Open here (this package sits below both IRs); the loader
|
|
248
|
+
* validates against the published kind enums.
|
|
249
|
+
*/
|
|
250
|
+
kind: import_zod2.z.string().optional(),
|
|
251
|
+
/**
|
|
252
|
+
* Boundary as a human-readable key, e.g. "GET /pet/{petId}" or
|
|
253
|
+
* "fn:@acme/api::getUser". REST keys are normalized via the same
|
|
254
|
+
* path normalizer the checkers use, so `:id` and `{id}` compare
|
|
255
|
+
* equal.
|
|
256
|
+
*/
|
|
257
|
+
boundary: import_zod2.z.string().optional(),
|
|
258
|
+
/**
|
|
259
|
+
* Consumer-side discriminators (narrowest useful match). Only
|
|
260
|
+
* meaningful for behavioural findings — a rule that specifies
|
|
261
|
+
* `consumer` never matches an intent finding, which has no
|
|
262
|
+
* consumer side.
|
|
263
|
+
*/
|
|
264
|
+
consumer: import_zod2.z.object({
|
|
265
|
+
summary: import_zod2.z.string().optional(),
|
|
266
|
+
transitionId: import_zod2.z.string().optional()
|
|
267
|
+
}).optional(),
|
|
268
|
+
/**
|
|
269
|
+
* "narrow" (default): requires at least (kind + boundary) OR
|
|
270
|
+
* (kind + consumer.transitionId) — enough to target a specific
|
|
271
|
+
* finding class. "broad" opts in to kind-only or boundary-only
|
|
272
|
+
* matches, which silence future regressions in that category too.
|
|
273
|
+
*/
|
|
274
|
+
scope: import_zod2.z.enum(["narrow", "broad"]).default("narrow"),
|
|
275
|
+
/** Required human-written justification. */
|
|
276
|
+
reason: import_zod2.z.string().min(1),
|
|
277
|
+
/** What to do when a finding matches. */
|
|
278
|
+
effect: import_zod2.z.enum(["mark", "downgrade", "hide"]).default("mark")
|
|
279
|
+
}).strict();
|
|
280
|
+
var SuppressionFileSchema = import_zod2.z.object({
|
|
281
|
+
version: import_zod2.z.literal(1),
|
|
282
|
+
rules: import_zod2.z.array(SuppressionRuleSchema)
|
|
283
|
+
}).strict();
|
|
284
|
+
function validateRule(rule) {
|
|
285
|
+
if (rule.scope === "broad") {
|
|
286
|
+
if (rule.kind === void 0 && rule.boundary === void 0 && rule.consumer === void 0) {
|
|
287
|
+
return "broad-scope rule must constrain at least one field (kind, boundary, or consumer)";
|
|
288
|
+
}
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
const hasKind = rule.kind !== void 0;
|
|
292
|
+
const hasBoundary = rule.boundary !== void 0;
|
|
293
|
+
const hasConsumerTxn = rule.consumer?.transitionId !== void 0;
|
|
294
|
+
if (hasKind && (hasBoundary || hasConsumerTxn)) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
return "narrow-scope rule must specify kind AND (boundary OR consumer.transitionId); set scope: 'broad' to silence wider categories";
|
|
298
|
+
}
|
|
299
|
+
function normalizeRuleBoundary(raw) {
|
|
300
|
+
const trimmed = raw.trim();
|
|
301
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
302
|
+
if (spaceIdx < 0) {
|
|
303
|
+
return trimmed;
|
|
304
|
+
}
|
|
305
|
+
const method = trimmed.slice(0, spaceIdx).toUpperCase();
|
|
306
|
+
const path = trimmed.slice(spaceIdx + 1);
|
|
307
|
+
return `${method} ${normalizePath(path)}`;
|
|
308
|
+
}
|
|
309
|
+
function ruleBoundaryMatchesKey(ruleBoundary, key) {
|
|
310
|
+
if (key === null) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
return ruleBoundary === key || normalizeRuleBoundary(ruleBoundary) === key;
|
|
314
|
+
}
|
|
315
|
+
var SEVERITY_DOWNGRADE = {
|
|
316
|
+
error: "warning",
|
|
317
|
+
warning: "info",
|
|
318
|
+
info: "info"
|
|
319
|
+
};
|
|
320
|
+
function applyRuleToFinding(rule, finding) {
|
|
321
|
+
const common = {
|
|
322
|
+
reason: rule.reason,
|
|
323
|
+
effect: rule.effect
|
|
324
|
+
};
|
|
325
|
+
if (rule.effect === "downgrade") {
|
|
326
|
+
return {
|
|
327
|
+
...finding,
|
|
328
|
+
severity: SEVERITY_DOWNGRADE[finding.severity],
|
|
329
|
+
suppressed: { ...common, originalSeverity: finding.severity }
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
return { ...finding, suppressed: { ...common } };
|
|
333
|
+
}
|
|
334
|
+
function applySuppressionsToFindings(findings, rules, matches, opts = {}) {
|
|
335
|
+
const out = [];
|
|
336
|
+
for (const f of findings) {
|
|
337
|
+
const rule = rules.find(
|
|
338
|
+
(r) => (r.kind === void 0 || r.kind === f.kind) && matches(r, f)
|
|
339
|
+
);
|
|
340
|
+
if (rule === void 0) {
|
|
341
|
+
out.push(f);
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
const applied = applyRuleToFinding(rule, f);
|
|
345
|
+
if (applied.suppressed?.effect === "hide" && !opts.keepHidden) {
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
out.push(applied);
|
|
349
|
+
}
|
|
350
|
+
return out;
|
|
351
|
+
}
|
|
352
|
+
function countsForThreshold(finding) {
|
|
353
|
+
if (finding.suppressed === void 0) {
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
return finding.suppressed.effect === "downgrade";
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// src/typeShapeMatch.ts
|
|
360
|
+
function bodyShapesMatch(actual, declared) {
|
|
361
|
+
if (actual.type === "unknown" || declared.type === "unknown") {
|
|
362
|
+
return "unknown";
|
|
363
|
+
}
|
|
364
|
+
if (actual.type === "ref" || declared.type === "ref") {
|
|
365
|
+
if (actual.type === "ref" && declared.type === "ref" && actual.name === declared.name) {
|
|
366
|
+
return "match";
|
|
367
|
+
}
|
|
368
|
+
return "unknown";
|
|
369
|
+
}
|
|
370
|
+
if (actual.type === "union") {
|
|
371
|
+
return combine(actual.variants.map((v) => bodyShapesMatch(v, declared)));
|
|
372
|
+
}
|
|
373
|
+
if (declared.type === "union") {
|
|
374
|
+
return matchAny(actual, declared.variants);
|
|
375
|
+
}
|
|
376
|
+
if (declared.type === "dictionary") {
|
|
377
|
+
if (actual.type === "dictionary") {
|
|
378
|
+
return bodyShapesMatch(actual.values, declared.values);
|
|
379
|
+
}
|
|
380
|
+
if (actual.type === "record") {
|
|
381
|
+
if (actual.spreads && actual.spreads.length > 0) {
|
|
382
|
+
return "unknown";
|
|
383
|
+
}
|
|
384
|
+
const results = Object.values(actual.properties).map(
|
|
385
|
+
(v) => bodyShapesMatch(v, declared.values)
|
|
386
|
+
);
|
|
387
|
+
return combine(results);
|
|
388
|
+
}
|
|
389
|
+
return "nomatch";
|
|
390
|
+
}
|
|
391
|
+
if (actual.type === "dictionary") {
|
|
392
|
+
return declared.type === "record" ? "unknown" : "nomatch";
|
|
393
|
+
}
|
|
394
|
+
if (declared.type === "record") {
|
|
395
|
+
if (actual.type !== "record") {
|
|
396
|
+
return "nomatch";
|
|
397
|
+
}
|
|
398
|
+
if (actual.spreads && actual.spreads.length > 0 || declared.spreads && declared.spreads.length > 0) {
|
|
399
|
+
return "unknown";
|
|
400
|
+
}
|
|
401
|
+
const results = [];
|
|
402
|
+
for (const [key, declaredValue] of Object.entries(declared.properties)) {
|
|
403
|
+
const actualValue = actual.properties[key];
|
|
404
|
+
if (actualValue === void 0) {
|
|
405
|
+
if (allowsUndefined(declaredValue)) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
return "nomatch";
|
|
409
|
+
}
|
|
410
|
+
results.push(bodyShapesMatch(actualValue, declaredValue));
|
|
411
|
+
}
|
|
412
|
+
return combine(results);
|
|
413
|
+
}
|
|
414
|
+
if (declared.type === "array") {
|
|
415
|
+
if (actual.type !== "array") {
|
|
416
|
+
return "nomatch";
|
|
417
|
+
}
|
|
418
|
+
return bodyShapesMatch(actual.items, declared.items);
|
|
419
|
+
}
|
|
420
|
+
if (declared.type === "literal") {
|
|
421
|
+
if (actual.type !== "literal") {
|
|
422
|
+
return "nomatch";
|
|
423
|
+
}
|
|
424
|
+
return actual.value === declared.value ? "match" : "nomatch";
|
|
425
|
+
}
|
|
426
|
+
if (declared.type === "text") {
|
|
427
|
+
if (actual.type === "text") {
|
|
428
|
+
return "match";
|
|
429
|
+
}
|
|
430
|
+
if (actual.type === "literal" && typeof actual.value === "string") {
|
|
431
|
+
return "match";
|
|
432
|
+
}
|
|
433
|
+
return "nomatch";
|
|
434
|
+
}
|
|
435
|
+
if (declared.type === "integer") {
|
|
436
|
+
if (actual.type === "integer") {
|
|
437
|
+
return "match";
|
|
438
|
+
}
|
|
439
|
+
if (actual.type === "literal" && typeof actual.value === "number" && Number.isInteger(actual.value)) {
|
|
440
|
+
return "match";
|
|
441
|
+
}
|
|
442
|
+
return "nomatch";
|
|
443
|
+
}
|
|
444
|
+
if (declared.type === "number") {
|
|
445
|
+
if (actual.type === "number" || actual.type === "integer") {
|
|
446
|
+
return "match";
|
|
447
|
+
}
|
|
448
|
+
if (actual.type === "literal" && typeof actual.value === "number") {
|
|
449
|
+
return "match";
|
|
450
|
+
}
|
|
451
|
+
return "nomatch";
|
|
452
|
+
}
|
|
453
|
+
if (declared.type === "boolean") {
|
|
454
|
+
if (actual.type === "boolean") {
|
|
455
|
+
return "match";
|
|
456
|
+
}
|
|
457
|
+
if (actual.type === "literal" && typeof actual.value === "boolean") {
|
|
458
|
+
return "match";
|
|
459
|
+
}
|
|
460
|
+
return "nomatch";
|
|
461
|
+
}
|
|
462
|
+
if (declared.type === "null") {
|
|
463
|
+
return actual.type === "null" ? "match" : "nomatch";
|
|
464
|
+
}
|
|
465
|
+
if (declared.type === "undefined") {
|
|
466
|
+
return actual.type === "undefined" ? "match" : "nomatch";
|
|
467
|
+
}
|
|
468
|
+
return "nomatch";
|
|
469
|
+
}
|
|
470
|
+
function matchAny(actual, variants) {
|
|
471
|
+
let sawUnknown = false;
|
|
472
|
+
for (const v of variants) {
|
|
473
|
+
const r = bodyShapesMatch(actual, v);
|
|
474
|
+
if (r === "match") {
|
|
475
|
+
return "match";
|
|
476
|
+
}
|
|
477
|
+
if (r === "unknown") {
|
|
478
|
+
sawUnknown = true;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return sawUnknown ? "unknown" : "nomatch";
|
|
482
|
+
}
|
|
483
|
+
function combine(results) {
|
|
484
|
+
if (results.some((r) => r === "nomatch")) {
|
|
485
|
+
return "nomatch";
|
|
486
|
+
}
|
|
487
|
+
if (results.some((r) => r === "unknown")) {
|
|
488
|
+
return "unknown";
|
|
489
|
+
}
|
|
490
|
+
return "match";
|
|
491
|
+
}
|
|
492
|
+
function allowsUndefined(shape) {
|
|
493
|
+
if (shape.type === "undefined") {
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
if (shape.type === "union") {
|
|
497
|
+
return shape.variants.some(allowsUndefined);
|
|
498
|
+
}
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// src/index.ts
|
|
503
|
+
function restBinding(opts) {
|
|
504
|
+
return {
|
|
505
|
+
transport: opts.transport,
|
|
506
|
+
semantics: {
|
|
507
|
+
name: "rest",
|
|
508
|
+
method: opts.method.toUpperCase(),
|
|
509
|
+
path: opts.path,
|
|
510
|
+
...opts.declaredResponses !== void 0 ? { declaredResponses: opts.declaredResponses } : {}
|
|
511
|
+
},
|
|
512
|
+
recognition: opts.recognition
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
function functionCallBinding(opts) {
|
|
516
|
+
return {
|
|
517
|
+
transport: opts.transport,
|
|
518
|
+
semantics: {
|
|
519
|
+
name: "function-call",
|
|
520
|
+
...opts.module !== void 0 ? { module: opts.module } : {},
|
|
521
|
+
...opts.exportName !== void 0 ? { exportName: opts.exportName } : {},
|
|
522
|
+
...opts.package !== void 0 ? { package: opts.package } : {},
|
|
523
|
+
...opts.exportPath !== void 0 ? { exportPath: opts.exportPath } : {}
|
|
524
|
+
},
|
|
525
|
+
recognition: opts.recognition
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
function packageExportBinding(opts) {
|
|
529
|
+
return functionCallBinding({
|
|
530
|
+
transport: opts.transport ?? "in-process",
|
|
531
|
+
recognition: opts.recognition,
|
|
532
|
+
package: opts.packageName,
|
|
533
|
+
exportPath: opts.exportPath
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
function graphqlResolverBinding(opts) {
|
|
537
|
+
return {
|
|
538
|
+
transport: opts.transport,
|
|
539
|
+
semantics: {
|
|
540
|
+
name: "graphql-resolver",
|
|
541
|
+
typeName: opts.typeName,
|
|
542
|
+
fieldName: opts.fieldName
|
|
543
|
+
},
|
|
544
|
+
recognition: opts.recognition
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
function graphqlOperationBinding(opts) {
|
|
548
|
+
return {
|
|
549
|
+
transport: opts.transport,
|
|
550
|
+
semantics: {
|
|
551
|
+
name: "graphql-operation",
|
|
552
|
+
operationType: opts.operationType,
|
|
553
|
+
...opts.operationName !== void 0 ? { operationName: opts.operationName } : {}
|
|
554
|
+
},
|
|
555
|
+
recognition: opts.recognition
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
function runtimeConfigBinding(opts) {
|
|
559
|
+
return {
|
|
560
|
+
transport: "os",
|
|
561
|
+
semantics: {
|
|
562
|
+
name: "runtime-config",
|
|
563
|
+
deploymentTarget: opts.deploymentTarget,
|
|
564
|
+
instanceName: opts.instanceName
|
|
565
|
+
},
|
|
566
|
+
recognition: opts.recognition
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
function storageRelationalBinding(opts) {
|
|
570
|
+
return {
|
|
571
|
+
transport: opts.storageSystem,
|
|
572
|
+
semantics: {
|
|
573
|
+
name: "storage-relational",
|
|
574
|
+
storageSystem: opts.storageSystem,
|
|
575
|
+
scope: opts.scope,
|
|
576
|
+
table: opts.table
|
|
577
|
+
},
|
|
578
|
+
recognition: opts.recognition
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
function messageBusBinding(opts) {
|
|
582
|
+
return {
|
|
583
|
+
transport: opts.messageBus,
|
|
584
|
+
semantics: {
|
|
585
|
+
name: "message-bus",
|
|
586
|
+
messageBus: opts.messageBus,
|
|
587
|
+
channel: opts.channel
|
|
588
|
+
},
|
|
589
|
+
recognition: opts.recognition
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
593
|
+
0 && (module.exports = {
|
|
594
|
+
BoundaryBindingSchema,
|
|
595
|
+
ConfidenceInfoSchema,
|
|
596
|
+
ConfidenceLevelSchema,
|
|
597
|
+
ConfidenceSourceSchema,
|
|
598
|
+
FunctionCallSemanticsSchema,
|
|
599
|
+
GraphqlOperationSemanticsSchema,
|
|
600
|
+
GraphqlResolverSemanticsSchema,
|
|
601
|
+
MessageBusSemanticsSchema,
|
|
602
|
+
RestSemanticsSchema,
|
|
603
|
+
RuntimeConfigSemanticsSchema,
|
|
604
|
+
SemanticsSchema,
|
|
605
|
+
SourceLocationSchema,
|
|
606
|
+
StorageRelationalSemanticsSchema,
|
|
607
|
+
SuppressionFileSchema,
|
|
608
|
+
SuppressionRuleSchema,
|
|
609
|
+
TypeShapeSchema,
|
|
610
|
+
applySuppressionsToFindings,
|
|
611
|
+
bodyShapesMatch,
|
|
612
|
+
boundaryKey,
|
|
613
|
+
countsForThreshold,
|
|
614
|
+
functionCallBinding,
|
|
615
|
+
graphqlOperationBinding,
|
|
616
|
+
graphqlResolverBinding,
|
|
617
|
+
messageBusBinding,
|
|
618
|
+
normalizePath,
|
|
619
|
+
normalizeRuleBoundary,
|
|
620
|
+
packageExportBinding,
|
|
621
|
+
restBinding,
|
|
622
|
+
ruleBoundaryMatchesKey,
|
|
623
|
+
runtimeConfigBinding,
|
|
624
|
+
storageRelationalBinding,
|
|
625
|
+
validateRule
|
|
626
|
+
});
|