al-sem 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 +21 -0
- package/README.md +361 -0
- package/package.json +64 -0
- package/scripts/d40-diff.ts +44 -0
- package/scripts/fetch-native-parser.ts +179 -0
- package/scripts/precision-sample.ts +99 -0
- package/scripts/precision-study.ts +42 -0
- package/scripts/precision-tabulate.ts +52 -0
- package/src/cli/baseline.ts +31 -0
- package/src/cli/diff.ts +199 -0
- package/src/cli/events-chains.ts +56 -0
- package/src/cli/events-fanout.ts +87 -0
- package/src/cli/exit-code.ts +30 -0
- package/src/cli/fingerprint-indexes.ts +130 -0
- package/src/cli/fingerprint-query.ts +543 -0
- package/src/cli/fingerprint-witness.ts +493 -0
- package/src/cli/fingerprint.ts +292 -0
- package/src/cli/format-compact-json.ts +45 -0
- package/src/cli/format-events.ts +77 -0
- package/src/cli/format-fingerprint.ts +295 -0
- package/src/cli/format-html.ts +503 -0
- package/src/cli/format-json.ts +13 -0
- package/src/cli/format-policy.ts +95 -0
- package/src/cli/format-sarif.ts +186 -0
- package/src/cli/format-terminal.ts +153 -0
- package/src/cli/index.ts +566 -0
- package/src/cli/policy.ts +204 -0
- package/src/config/roots-config.ts +302 -0
- package/src/deps/cache-versions.ts +74 -0
- package/src/deps/canonical-json.ts +27 -0
- package/src/deps/dependency-artifact.ts +144 -0
- package/src/deps/dependency-cache.ts +262 -0
- package/src/deps/dependency-dag.ts +128 -0
- package/src/deps/dependency-package-discovery.ts +85 -0
- package/src/deps/dependency-pipeline.ts +483 -0
- package/src/deps/dependency-projection.ts +211 -0
- package/src/deps/dependency-resolver.ts +154 -0
- package/src/deps/workspace-dependencies.ts +114 -0
- package/src/detectors/capability-query.ts +145 -0
- package/src/detectors/confidence.ts +52 -0
- package/src/detectors/d1-db-op-in-loop.ts +457 -0
- package/src/detectors/d10-self-modifying-loop.ts +114 -0
- package/src/detectors/d11-modify-without-get.ts +129 -0
- package/src/detectors/d12-dead-integration-event.ts +81 -0
- package/src/detectors/d13-cross-app-internal-call.ts +105 -0
- package/src/detectors/d14-dead-routine.ts +151 -0
- package/src/detectors/d16-obsolete-routine-call.ts +94 -0
- package/src/detectors/d17-min-version-drift.ts +157 -0
- package/src/detectors/d18-constant-filter-in-loop.ts +151 -0
- package/src/detectors/d19-unused-parameter.ts +116 -0
- package/src/detectors/d2-event-fanout-in-loop.ts +240 -0
- package/src/detectors/d20-unreachable-after-exit.ts +92 -0
- package/src/detectors/d21-read-without-load.ts +128 -0
- package/src/detectors/d22-flowfield-without-calcfields.ts +168 -0
- package/src/detectors/d29-subscriber-modify-on-event-record.ts +163 -0
- package/src/detectors/d3-load-state.ts +72 -0
- package/src/detectors/d3-missing-setloadfields.ts +234 -0
- package/src/detectors/d32-constant-boolean-parameter.ts +185 -0
- package/src/detectors/d33-unfiltered-bulk-write.ts +173 -0
- package/src/detectors/d34-commit-in-loop.ts +206 -0
- package/src/detectors/d35-commit-in-event-subscriber.ts +138 -0
- package/src/detectors/d36-late-setloadfields.ts +162 -0
- package/src/detectors/d37-validate-without-persist.ts +271 -0
- package/src/detectors/d38-subscriber-to-obsolete-event.ts +140 -0
- package/src/detectors/d39-record-left-dirty-across-chain.ts +165 -0
- package/src/detectors/d4-repeated-lookup-in-loop.ts +128 -0
- package/src/detectors/d40-transitive-load-missing.ts +217 -0
- package/src/detectors/d41-transitive-filter-loss.ts +200 -0
- package/src/detectors/d42-cross-call-wrong-setloadfields.ts +243 -0
- package/src/detectors/d43-event-ishandled-skip.ts +257 -0
- package/src/detectors/d44-event-multi-subscriber-overlap.ts +223 -0
- package/src/detectors/d45-event-transitive-table-exposure.ts +159 -0
- package/src/detectors/d5-set-based-opportunity.ts +162 -0
- package/src/detectors/d7-recursive-event-expansion.ts +151 -0
- package/src/detectors/d8-commit-in-transaction.ts +132 -0
- package/src/detectors/d9-transaction-span-summary.ts +107 -0
- package/src/detectors/detector-context.ts +121 -0
- package/src/detectors/finding-grouping.ts +61 -0
- package/src/detectors/path-merge.ts +174 -0
- package/src/detectors/registry.ts +176 -0
- package/src/detectors/table-display.ts +42 -0
- package/src/diff/diff-abi.ts +195 -0
- package/src/diff/diff-capabilities.ts +179 -0
- package/src/diff/diff-engine.ts +146 -0
- package/src/diff/diff-events.ts +323 -0
- package/src/diff/diff-identity.ts +73 -0
- package/src/diff/diff-indexes.ts +199 -0
- package/src/diff/diff-permissions.ts +260 -0
- package/src/diff/diff-policy.ts +101 -0
- package/src/diff/diff-preflight.ts +66 -0
- package/src/diff/diff-renames.ts +104 -0
- package/src/diff/diff-schema.ts +232 -0
- package/src/diff/format-diff.ts +148 -0
- package/src/engine/attribute-parser.ts +50 -0
- package/src/engine/capability-cone.ts +531 -0
- package/src/engine/combined-graph.ts +357 -0
- package/src/engine/control-flow-walker.ts +1317 -0
- package/src/engine/dispatch-sites.ts +199 -0
- package/src/engine/effect-lattice.ts +81 -0
- package/src/engine/entry-points.ts +57 -0
- package/src/engine/event-flow.ts +524 -0
- package/src/engine/event-relay.ts +92 -0
- package/src/engine/op-classification.ts +92 -0
- package/src/engine/path-walker.ts +189 -0
- package/src/engine/reverse-call-graph.ts +23 -0
- package/src/engine/root-classifier-overlay.ts +194 -0
- package/src/engine/root-classifier.ts +135 -0
- package/src/engine/scc.ts +110 -0
- package/src/engine/source-anchor.ts +25 -0
- package/src/engine/summary-context.ts +104 -0
- package/src/engine/summary-engine.ts +296 -0
- package/src/engine/summary-runner.ts +560 -0
- package/src/engine/transaction-spans.ts +112 -0
- package/src/engine/uncertainty-util.ts +54 -0
- package/src/hash.ts +31 -0
- package/src/index/attribute-from-node.ts +141 -0
- package/src/index/callee-from-node.ts +181 -0
- package/src/index/capability/background.ts +90 -0
- package/src/index/capability/commit.ts +44 -0
- package/src/index/capability/dispatch.ts +164 -0
- package/src/index/capability/events.ts +65 -0
- package/src/index/capability/extractor.ts +124 -0
- package/src/index/capability/file-blob.ts +137 -0
- package/src/index/capability/http.ts +159 -0
- package/src/index/capability/hyperlink.ts +60 -0
- package/src/index/capability/isolated-storage.ts +179 -0
- package/src/index/capability/table.ts +113 -0
- package/src/index/capability/telemetry.ts +84 -0
- package/src/index/capability/ui.ts +55 -0
- package/src/index/capability/value-source.ts +202 -0
- package/src/index/expression-from-node.ts +117 -0
- package/src/index/indexer.ts +102 -0
- package/src/index/intraprocedural-body.ts +1467 -0
- package/src/index/intraprocedural-ops.ts +253 -0
- package/src/index/intraprocedural-refs.ts +188 -0
- package/src/index/object-indexer.ts +279 -0
- package/src/index/routine-indexer.ts +282 -0
- package/src/index/routine-signature.ts +46 -0
- package/src/index/variable-indexer.ts +134 -0
- package/src/index/variable-initializer-extractor.ts +155 -0
- package/src/index/variable-type-normalizer.ts +83 -0
- package/src/index.ts +267 -0
- package/src/mcp/server.ts +72 -0
- package/src/mcp/session.ts +49 -0
- package/src/mcp/tools/explain-path.ts +75 -0
- package/src/mcp/tools/get-analysis-health.ts +62 -0
- package/src/mcp/tools/get-finding.ts +47 -0
- package/src/mcp/tools/get-routine-summary.ts +126 -0
- package/src/mcp/tools/list-findings.ts +85 -0
- package/src/mcp/tools/list-hotspots.ts +78 -0
- package/src/mcp/tools/list-rollups.ts +103 -0
- package/src/mcp/tools/validators.ts +25 -0
- package/src/model/attributes.ts +120 -0
- package/src/model/callee.ts +45 -0
- package/src/model/capability.ts +187 -0
- package/src/model/coverage.ts +85 -0
- package/src/model/entities.ts +628 -0
- package/src/model/expression.ts +98 -0
- package/src/model/finding.ts +110 -0
- package/src/model/graph-edge.ts +93 -0
- package/src/model/graph.ts +62 -0
- package/src/model/identity.ts +81 -0
- package/src/model/ids.ts +90 -0
- package/src/model/index.ts +13 -0
- package/src/model/model.ts +51 -0
- package/src/model/permission.ts +76 -0
- package/src/model/root-classification.ts +116 -0
- package/src/model/stable-identity.ts +102 -0
- package/src/model/summary.ts +96 -0
- package/src/parser/ast.ts +82 -0
- package/src/parser/native/ffi.ts +145 -0
- package/src/parser/native/parse-index-pool.ts +148 -0
- package/src/parser/native/parse-index-worker.ts +94 -0
- package/src/parser/native/wrapper.ts +353 -0
- package/src/parser/parser-init.ts +43 -0
- package/src/perf/profiler.ts +66 -0
- package/src/policy/policy-default.yaml +83 -0
- package/src/policy/policy-engine.ts +339 -0
- package/src/policy/policy-loader.ts +257 -0
- package/src/policy/policy-schema.json +379 -0
- package/src/policy/policy-types.ts +81 -0
- package/src/policy/predicate-compiler.ts +151 -0
- package/src/policy/predicate-evaluator.ts +267 -0
- package/src/policy/predicate-fields.ts +439 -0
- package/src/projection/actionable-anchor.ts +48 -0
- package/src/projection/finding-filters.ts +44 -0
- package/src/projection/finding-fingerprint.ts +54 -0
- package/src/projection/finding-groups.ts +41 -0
- package/src/projection/finding-summary.ts +110 -0
- package/src/projection/rollup-findings.ts +105 -0
- package/src/providers/discover.ts +88 -0
- package/src/providers/external.ts +46 -0
- package/src/providers/types.ts +36 -0
- package/src/providers/workspace.ts +117 -0
- package/src/resolve/call-resolver.ts +117 -0
- package/src/resolve/coverage.ts +61 -0
- package/src/resolve/event-graph.ts +166 -0
- package/src/resolve/implicit-edges.ts +53 -0
- package/src/resolve/record-types.ts +36 -0
- package/src/resolve/resolver.ts +23 -0
- package/src/resolve/semantic-graph.ts +29 -0
- package/src/resolve/symbol-table.ts +69 -0
- package/src/snapshot/app-snapshot.ts +74 -0
- package/src/snapshot/compose.ts +100 -0
- package/src/snapshot/derive/callsite-evidence.ts +76 -0
- package/src/snapshot/derive/capability-facts.ts +70 -0
- package/src/snapshot/derive/contracts.ts +131 -0
- package/src/snapshot/derive/coverage.ts +35 -0
- package/src/snapshot/derive/event-declarations.ts +140 -0
- package/src/snapshot/derive/identity-table.ts +58 -0
- package/src/snapshot/derive/inputs.ts +91 -0
- package/src/snapshot/derive/operation-evidence.ts +70 -0
- package/src/snapshot/derive/permissions.ts +186 -0
- package/src/snapshot/derive/root-classifications.ts +56 -0
- package/src/snapshot/derive/schema.ts +130 -0
- package/src/snapshot/derive/typed-edges.ts +60 -0
- package/src/snapshot/derive/workspace-fingerprint.ts +19 -0
- package/src/snapshot/deserialize.ts +40 -0
- package/src/snapshot/serialize-cbor-gz.ts +12 -0
- package/src/snapshot/serialize-cbor.ts +19 -0
- package/src/snapshot/serialize-json.ts +22 -0
- package/src/snapshot/shard.ts +134 -0
- package/src/snapshot/types.ts +181 -0
- package/src/symbols/app-manifest.ts +96 -0
- package/src/symbols/app-package-zip.ts +50 -0
- package/src/symbols/embedded-source-reader.ts +41 -0
- package/src/symbols/package-hash.ts +81 -0
- package/src/symbols/symbol-reader.ts +101 -0
- package/src/symbols/symbol-reference-parser.ts +378 -0
- package/src/symbols/symbol-reference-reader.ts +27 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// src/model/expression.ts
|
|
2
|
+
// Structured, tree-sitter-derived classification of one expression — used for
|
|
3
|
+
// CallSite arguments and RecordOperation field-arguments. Detectors read this
|
|
4
|
+
// instead of re-shredding `text` with regex.
|
|
5
|
+
//
|
|
6
|
+
// The `kind` mirrors tree-sitter-al's expression node types verbatim, so the
|
|
7
|
+
// builder in `src/index/expression-from-node.ts` is a 1:1 map; "other" is the
|
|
8
|
+
// fallback for unmodeled expression shapes (binary expressions, parenthesized
|
|
9
|
+
// expressions, subscripts, etc.). Detectors only need a handful of kinds for
|
|
10
|
+
// their literal/identity decisions — the helpers below encode those rules
|
|
11
|
+
// once so consumers never branch on `kind` strings directly.
|
|
12
|
+
|
|
13
|
+
/** Grammar-aligned expression kinds. Closed enum; `"other"` is the catch-all. */
|
|
14
|
+
export type ExpressionKind =
|
|
15
|
+
| "string_literal"
|
|
16
|
+
| "integer"
|
|
17
|
+
| "decimal"
|
|
18
|
+
| "boolean"
|
|
19
|
+
| "identifier"
|
|
20
|
+
| "quoted_identifier"
|
|
21
|
+
| "qualified_enum_value"
|
|
22
|
+
| "database_reference"
|
|
23
|
+
| "unary_expression"
|
|
24
|
+
| "member_expression"
|
|
25
|
+
| "call_expression"
|
|
26
|
+
| "parenthesized_expression"
|
|
27
|
+
| "other";
|
|
28
|
+
|
|
29
|
+
export interface ExpressionInfo {
|
|
30
|
+
kind: ExpressionKind;
|
|
31
|
+
/** Source-faithful text including any surrounding quotes / signs. */
|
|
32
|
+
text: string;
|
|
33
|
+
/**
|
|
34
|
+
* Consumer-friendly value:
|
|
35
|
+
* - string_literal: contents between the single quotes
|
|
36
|
+
* - quoted_identifier: contents between the double quotes
|
|
37
|
+
* - integer / decimal: decimal text (unchanged)
|
|
38
|
+
* - boolean: "true" / "false"
|
|
39
|
+
* - identifier: the bare name
|
|
40
|
+
* - qualified_enum_value: RHS of `::` (the `value` field)
|
|
41
|
+
* - database_reference: RHS of `::` (the `table_name` field, unquoted)
|
|
42
|
+
* Undefined for member_expression / call_expression / unary_expression /
|
|
43
|
+
* parenthesized_expression / other — those need full sub-tree inspection
|
|
44
|
+
* the model layer deliberately omits.
|
|
45
|
+
*/
|
|
46
|
+
value?: string;
|
|
47
|
+
/** LHS of `::` for qualified_enum_value / database_reference. */
|
|
48
|
+
qualifier?: string;
|
|
49
|
+
/** RHS of `::` for qualified_enum_value / database_reference (unquoted for db-ref). */
|
|
50
|
+
member?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A string-like literal — either single-quoted (`'val'`) or
|
|
55
|
+
* double-quoted-identifier (`"val"`). D4 and D22 want this: they treat
|
|
56
|
+
* either form as "the same key" for comparison after stripping the quotes.
|
|
57
|
+
* The `value` field is the unquoted contents in both cases.
|
|
58
|
+
*/
|
|
59
|
+
export function isStringLikeLiteral(info: ExpressionInfo): boolean {
|
|
60
|
+
return info.kind === "string_literal" || info.kind === "quoted_identifier";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A loop-invariant literal — string, number, boolean, or qualified enum value.
|
|
65
|
+
* D18 uses this to decide whether a `SetRange` / `SetFilter` value is constant
|
|
66
|
+
* across iterations. Identifiers and calls are NOT literals (we cannot prove
|
|
67
|
+
* loop-invariance without dataflow). Unary `+`/`-` over a numeric literal is
|
|
68
|
+
* literal too — `expressionInfoFromNode` sets `value` on a `unary_expression`
|
|
69
|
+
* iff its operand is `integer` / `decimal`, so the value-set check is the
|
|
70
|
+
* predicate.
|
|
71
|
+
*/
|
|
72
|
+
export function isLiteralExpression(info: ExpressionInfo): boolean {
|
|
73
|
+
switch (info.kind) {
|
|
74
|
+
case "string_literal":
|
|
75
|
+
case "quoted_identifier":
|
|
76
|
+
case "integer":
|
|
77
|
+
case "decimal":
|
|
78
|
+
case "boolean":
|
|
79
|
+
case "qualified_enum_value":
|
|
80
|
+
return true;
|
|
81
|
+
case "unary_expression":
|
|
82
|
+
return info.value !== undefined;
|
|
83
|
+
default:
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a field-name argument to its lowered, unquoted form for
|
|
90
|
+
* case-insensitive comparison against `Field.name`. Quoted identifiers
|
|
91
|
+
* (`"Document No."`) and string literals (`'Discount Amount'`) yield their
|
|
92
|
+
* inner value; plain identifiers (`Quantity`) yield their text. Returns the
|
|
93
|
+
* source text lowercased as a safe fallback for anything else.
|
|
94
|
+
*/
|
|
95
|
+
export function unquotedFieldName(info: ExpressionInfo): string {
|
|
96
|
+
if (info.value !== undefined) return info.value.toLowerCase();
|
|
97
|
+
return info.text.toLowerCase();
|
|
98
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Evidence } from "./graph.ts";
|
|
2
|
+
import type { SourceAnchor } from "./identity.ts";
|
|
3
|
+
import type { CallsiteId, LoopId, ObjectId, OperationId, RoutineId, TableId } from "./ids.ts";
|
|
4
|
+
|
|
5
|
+
export interface FixOption {
|
|
6
|
+
description: string;
|
|
7
|
+
safety: "high" | "medium" | "low";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface EvidenceStep {
|
|
11
|
+
routineId: RoutineId;
|
|
12
|
+
operationId?: OperationId;
|
|
13
|
+
callsiteId?: CallsiteId;
|
|
14
|
+
loopId?: LoopId;
|
|
15
|
+
sourceAnchor: SourceAnchor;
|
|
16
|
+
note: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface FindingConfidence {
|
|
20
|
+
level: "confirmed" | "likely" | "possible";
|
|
21
|
+
cappedBy?: (
|
|
22
|
+
| "unresolved-call"
|
|
23
|
+
| "opaque-callee"
|
|
24
|
+
| "dynamic-dispatch"
|
|
25
|
+
| "parse-incomplete"
|
|
26
|
+
| "version-mismatch"
|
|
27
|
+
)[];
|
|
28
|
+
evidence: Evidence[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface Finding {
|
|
32
|
+
id: string;
|
|
33
|
+
rootCauseKey: string;
|
|
34
|
+
detector: string;
|
|
35
|
+
title: string;
|
|
36
|
+
rootCause: string;
|
|
37
|
+
severity: "critical" | "high" | "medium" | "low" | "info";
|
|
38
|
+
confidence: FindingConfidence;
|
|
39
|
+
primaryLocation: SourceAnchor;
|
|
40
|
+
evidencePath: EvidenceStep[];
|
|
41
|
+
/**
|
|
42
|
+
* Additional supporting traces for the SAME terminal anchor. Set only on detectors
|
|
43
|
+
* that emit one Finding per (terminal-op | publisher) regardless of how many
|
|
44
|
+
* in-loop ancestor routines reach it (D1, D2). The canonical, max-severity path
|
|
45
|
+
* is in `evidencePath`; this carries the others, sorted deterministically.
|
|
46
|
+
*
|
|
47
|
+
* Absent (undefined) when there's a single reaching path — the common case for
|
|
48
|
+
* all other detectors and for most D1/D2 findings.
|
|
49
|
+
*/
|
|
50
|
+
additionalPaths?: EvidenceStep[][];
|
|
51
|
+
affectedObjects: ObjectId[];
|
|
52
|
+
affectedTables: TableId[];
|
|
53
|
+
fixOptions: FixOption[];
|
|
54
|
+
provenance: Evidence[];
|
|
55
|
+
/** Set in later phases. The primary-app source anchor a developer can act on, when primaryLocation falls in a dependency. */
|
|
56
|
+
actionableAnchor?: SourceAnchor;
|
|
57
|
+
/** Set in later phases. Stable edit-survival key used by baselines + SARIF. */
|
|
58
|
+
fingerprint?: string;
|
|
59
|
+
/**
|
|
60
|
+
* For event-flow detectors (D43/D44/D45). The kind of event whose
|
|
61
|
+
* dispatch / multi-subscriber / transitive exposure produced the finding.
|
|
62
|
+
* Mirrors EventKind from src/engine/event-flow.ts but kept loose
|
|
63
|
+
* (string union) to avoid a model→engine import cycle.
|
|
64
|
+
*
|
|
65
|
+
* Set to one of: "integration", "business", "internal".
|
|
66
|
+
* Absent on non-event-flow findings.
|
|
67
|
+
*/
|
|
68
|
+
eventKind?: "integration" | "business" | "internal";
|
|
69
|
+
/**
|
|
70
|
+
* For event-flow detectors. Subset of the event's subscribers that
|
|
71
|
+
* live in a different app than the publisher (subscriberAppId !==
|
|
72
|
+
* publisherAppId). Sorted RoutineId list. Empty when all subscribers
|
|
73
|
+
* are in-app. Absent on non-event-flow findings.
|
|
74
|
+
*
|
|
75
|
+
* Evidence only — no detector filters on it. Consumers (baselines,
|
|
76
|
+
* dashboards, al-perf) may weight findings differently based on
|
|
77
|
+
* cross-app subscriber presence.
|
|
78
|
+
*/
|
|
79
|
+
crossExtensionSubscribers?: readonly RoutineId[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface Diagnostic {
|
|
83
|
+
severity: "error" | "warning" | "info";
|
|
84
|
+
stage: "discover" | "parse" | "symbol-read" | "index" | "resolve" | "summarize" | "detect";
|
|
85
|
+
message: string;
|
|
86
|
+
sourceRef?: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface DetectorStats {
|
|
90
|
+
detector: string;
|
|
91
|
+
candidatesConsidered: number;
|
|
92
|
+
findingsEmitted: number;
|
|
93
|
+
/** Named skip counters. The well-known fields below are documented for shared
|
|
94
|
+
* conventions (`temporaryRecord`, `parseIncomplete`, etc.); detector-specific
|
|
95
|
+
* counters are also allowed via the index signature so each detector can name
|
|
96
|
+
* its own skip paths (e.g. `nonModifyEvent`, `noPublisherRoutine`). */
|
|
97
|
+
skipped: {
|
|
98
|
+
opaqueCallee?: number;
|
|
99
|
+
dynamicDispatch?: number;
|
|
100
|
+
parseIncomplete?: number;
|
|
101
|
+
temporaryRecord?: number;
|
|
102
|
+
dependencyTerminal?: number;
|
|
103
|
+
/** Not skipped — downgraded to "info" severity (D1 temp records). */
|
|
104
|
+
downgradedToInfo?: number;
|
|
105
|
+
/** Event dispatch edge has no resolvable subscriber routine (D2). */
|
|
106
|
+
unresolvedSubscriber?: number;
|
|
107
|
+
other?: number;
|
|
108
|
+
[counter: string]: number | undefined;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { ValueSource } from "./capability.ts";
|
|
2
|
+
import type { SourceAnchor } from "./identity.ts";
|
|
3
|
+
import type { CallsiteId, EventId, ObjectId, OperationId, RoutineId } from "./ids.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Typed graph edges. Per the capability-stack roadmap §3.2, the v2
|
|
7
|
+
* combined graph's generic call + event-dispatch edges with `uncertainty`
|
|
8
|
+
* annotations are promoted to a discriminated union. Every propagating
|
|
9
|
+
* edge is routine-to-routine and carries an anchor for witness
|
|
10
|
+
* reconstruction.
|
|
11
|
+
*
|
|
12
|
+
* - `direct-call` resolved bare or member call
|
|
13
|
+
* - `object-run-resolved` Codeunit.Run/Page.Run/Report.Run with
|
|
14
|
+
* resolved target entrypoint — composer walks
|
|
15
|
+
* through `to`
|
|
16
|
+
* - `object-run-unresolved` dispatch where target is dynamic or
|
|
17
|
+
* entrypoint isn't resolved — composer does
|
|
18
|
+
* NOT walk through (no `to`); produces an
|
|
19
|
+
* execute CapabilityFact directly + a coverage
|
|
20
|
+
* reason on the source routine
|
|
21
|
+
* - `event-dispatch` composed publisher→subscriber edge
|
|
22
|
+
* (one per resolved (publisher event,
|
|
23
|
+
* subscriber binding) pair) — composer walks
|
|
24
|
+
* - `implicit-trigger` record op raises a table OnInsert/OnModify/
|
|
25
|
+
* OnValidate/OnDelete trigger
|
|
26
|
+
* - `dependency-export` primary-app routine calls into a
|
|
27
|
+
* dependency-app exported routine
|
|
28
|
+
*
|
|
29
|
+
* Permission facts are NOT graph edges; see `src/model/permission.ts`.
|
|
30
|
+
*/
|
|
31
|
+
export type GraphEdgeKind =
|
|
32
|
+
| "direct-call"
|
|
33
|
+
| "object-run-resolved"
|
|
34
|
+
| "object-run-unresolved"
|
|
35
|
+
| "event-dispatch"
|
|
36
|
+
| "implicit-trigger"
|
|
37
|
+
| "dependency-export";
|
|
38
|
+
|
|
39
|
+
/** Table-trigger kind raised by an implicit-trigger edge. */
|
|
40
|
+
export type TriggerKind = "OnInsert" | "OnModify" | "OnValidate" | "OnDelete" | "OnRename";
|
|
41
|
+
|
|
42
|
+
export type GraphEdge =
|
|
43
|
+
| {
|
|
44
|
+
kind: "direct-call";
|
|
45
|
+
callsiteId: CallsiteId;
|
|
46
|
+
from: RoutineId;
|
|
47
|
+
to: RoutineId;
|
|
48
|
+
sourceAnchor: SourceAnchor;
|
|
49
|
+
}
|
|
50
|
+
| {
|
|
51
|
+
kind: "object-run-resolved";
|
|
52
|
+
callsiteId: CallsiteId;
|
|
53
|
+
from: RoutineId;
|
|
54
|
+
to: RoutineId;
|
|
55
|
+
targetObject: ObjectId;
|
|
56
|
+
objectType: "Codeunit" | "Page" | "Report";
|
|
57
|
+
sourceAnchor: SourceAnchor;
|
|
58
|
+
}
|
|
59
|
+
| {
|
|
60
|
+
kind: "object-run-unresolved";
|
|
61
|
+
callsiteId: CallsiteId;
|
|
62
|
+
from: RoutineId;
|
|
63
|
+
/** Set when the target object is known but the entrypoint isn't. */
|
|
64
|
+
targetObject?: ObjectId;
|
|
65
|
+
/** Always set — describes the source of the runtime-resolved target id. */
|
|
66
|
+
targetIdSource: ValueSource;
|
|
67
|
+
objectType: "Codeunit" | "Page" | "Report";
|
|
68
|
+
sourceAnchor: SourceAnchor;
|
|
69
|
+
}
|
|
70
|
+
| {
|
|
71
|
+
kind: "event-dispatch";
|
|
72
|
+
from: RoutineId;
|
|
73
|
+
to: RoutineId;
|
|
74
|
+
eventId: EventId;
|
|
75
|
+
publishAnchor: SourceAnchor;
|
|
76
|
+
subscriberAnchor: SourceAnchor;
|
|
77
|
+
}
|
|
78
|
+
| {
|
|
79
|
+
kind: "implicit-trigger";
|
|
80
|
+
from: RoutineId;
|
|
81
|
+
to: RoutineId;
|
|
82
|
+
triggerKind: TriggerKind;
|
|
83
|
+
operationId: OperationId;
|
|
84
|
+
sourceAnchor: SourceAnchor;
|
|
85
|
+
}
|
|
86
|
+
| {
|
|
87
|
+
kind: "dependency-export";
|
|
88
|
+
callsiteId: CallsiteId;
|
|
89
|
+
from: RoutineId;
|
|
90
|
+
to: RoutineId;
|
|
91
|
+
targetAppGuid: string;
|
|
92
|
+
sourceAnchor: SourceAnchor;
|
|
93
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ParameterSymbol } from "./entities.ts";
|
|
2
|
+
import type { CallsiteId, EventId, ObjectId, OperationId, RoutineId } from "./ids.ts";
|
|
3
|
+
|
|
4
|
+
export interface Evidence {
|
|
5
|
+
source: "tree-sitter" | "symbol-package" | "external-source";
|
|
6
|
+
note?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Shared provenance constant for tree-sitter-derived edges and symbols. */
|
|
10
|
+
export const TREE_SITTER_EVIDENCE: Evidence = { source: "tree-sitter" };
|
|
11
|
+
|
|
12
|
+
export type DispatchKind =
|
|
13
|
+
| "direct"
|
|
14
|
+
| "method"
|
|
15
|
+
| "interface"
|
|
16
|
+
| "codeunit-run"
|
|
17
|
+
| "report-run"
|
|
18
|
+
| "page-run"
|
|
19
|
+
| "event-dispatch"
|
|
20
|
+
| "implicit-trigger"
|
|
21
|
+
| "dynamic"
|
|
22
|
+
| "unresolved";
|
|
23
|
+
|
|
24
|
+
export type ResolutionQuality = "resolved" | "maybe" | "unknown" | "opaque";
|
|
25
|
+
|
|
26
|
+
export interface CallEdge {
|
|
27
|
+
from: RoutineId;
|
|
28
|
+
to?: RoutineId; // absent when unresolved
|
|
29
|
+
callsiteId: CallsiteId;
|
|
30
|
+
operationId: OperationId;
|
|
31
|
+
dispatchKind: DispatchKind;
|
|
32
|
+
resolution: ResolutionQuality;
|
|
33
|
+
provenance: Evidence[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface EventSymbol {
|
|
37
|
+
id: EventId;
|
|
38
|
+
publisherObjectId: ObjectId;
|
|
39
|
+
publisherRoutineId?: RoutineId;
|
|
40
|
+
eventName: string;
|
|
41
|
+
eventKind: "integration" | "business" | "trigger" | "internal" | "unknown";
|
|
42
|
+
elementName?: string;
|
|
43
|
+
signatureHash: string;
|
|
44
|
+
parameters: ParameterSymbol[];
|
|
45
|
+
provenance: Evidence[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface EventEdge {
|
|
49
|
+
eventId: EventId;
|
|
50
|
+
subscriberRoutineId: RoutineId;
|
|
51
|
+
subscriberAppId: string;
|
|
52
|
+
skipOnMissingLicense?: boolean;
|
|
53
|
+
skipOnMissingPermission?: boolean;
|
|
54
|
+
resolution: "resolved" | "maybe" | "unknown";
|
|
55
|
+
provenance: Evidence[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The event graph: publisher symbols and subscriber edges. */
|
|
59
|
+
export interface EventGraph {
|
|
60
|
+
events: EventSymbol[];
|
|
61
|
+
edges: EventEdge[];
|
|
62
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { ManifestDependency } from "../symbols/app-manifest.ts";
|
|
2
|
+
|
|
3
|
+
/** A character range in a source file. */
|
|
4
|
+
export interface SourceRange {
|
|
5
|
+
startLine: number; // 0-based
|
|
6
|
+
startColumn: number;
|
|
7
|
+
endLine: number;
|
|
8
|
+
endColumn: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A stable reference to a location in source. The fingerprint hash fields are a SEAM
|
|
13
|
+
* only in Phase 1 — left undefined; computation is deferred to sub-project D.
|
|
14
|
+
*/
|
|
15
|
+
export interface SourceAnchor {
|
|
16
|
+
sourceUnitId: string;
|
|
17
|
+
range: SourceRange;
|
|
18
|
+
enclosingRoutineId: string;
|
|
19
|
+
syntaxKind: string;
|
|
20
|
+
normalizedTextHash?: string;
|
|
21
|
+
leadingContextHash?: string;
|
|
22
|
+
trailingContextHash?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type SourceKind = "workspace" | "app-source" | "symbol-only" | "external-source";
|
|
26
|
+
|
|
27
|
+
export interface AppIdentity {
|
|
28
|
+
appGuid: string;
|
|
29
|
+
publisher: string;
|
|
30
|
+
name: string;
|
|
31
|
+
version: string;
|
|
32
|
+
packageHash?: string;
|
|
33
|
+
symbolReferenceHash?: string;
|
|
34
|
+
sourceAggregateHash?: string;
|
|
35
|
+
sourceKind: SourceKind;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Top-level version identity of one analysis run. Keys the cache. */
|
|
39
|
+
export interface ModelIdentity {
|
|
40
|
+
schemaVersion: string;
|
|
41
|
+
analyzerVersion: string;
|
|
42
|
+
grammarVersion: string;
|
|
43
|
+
symbolReaderVersion: string;
|
|
44
|
+
createdAt: string;
|
|
45
|
+
workspace?: { rootHash?: string; appJsonHash?: string };
|
|
46
|
+
primaryApp?: AppIdentity;
|
|
47
|
+
apps: AppIdentity[];
|
|
48
|
+
dependencyGraphHash: string;
|
|
49
|
+
runtime?: { platform?: string; application?: string; runtime?: string };
|
|
50
|
+
/**
|
|
51
|
+
* Dependencies declared by the primary workspace's app.json (both explicit
|
|
52
|
+
* dependencies[] and implicit Microsoft platform-tier entries). Each carries
|
|
53
|
+
* the declared MinVersion the user committed to. Used by D17 to detect drift
|
|
54
|
+
* against the actually-resolved dependency version stored in apps[].version.
|
|
55
|
+
*/
|
|
56
|
+
primaryDependencies?: ManifestDependency[];
|
|
57
|
+
/**
|
|
58
|
+
* App GUIDs (lowercased, sorted) listed in the primary workspace's
|
|
59
|
+
* `internalsVisibleTo` array — apps granted access to this app's `internal`
|
|
60
|
+
* procedures. Empty/undefined means no external app can reach `internal`, so
|
|
61
|
+
* D14 treats them as app-scoped for dead-routine reachability.
|
|
62
|
+
*/
|
|
63
|
+
primaryInternalsVisibleTo?: string[];
|
|
64
|
+
/**
|
|
65
|
+
* Phase 1 §4.3 — `roots.config.json` provenance. Populated when the file
|
|
66
|
+
* was loaded AND hashed successfully (i.e., `loadRootsConfig` returned a
|
|
67
|
+
* `contentHash`). Undefined when the file is missing, when
|
|
68
|
+
* `--no-roots-config` / `options.noRootsConfig` skipped loading, or when
|
|
69
|
+
* a file-read error prevented hashing (the read error is still surfaced
|
|
70
|
+
* via diagnostics). Parse/validation errors that occur AFTER hashing
|
|
71
|
+
* keep the field populated so the fingerprint reflects the byte content
|
|
72
|
+
* that was attempted.
|
|
73
|
+
*
|
|
74
|
+
* Whether to surface read-failure as a `rootsConfigReadError?` metadata
|
|
75
|
+
* flag is deferred; today the diagnostic is the only signal.
|
|
76
|
+
*
|
|
77
|
+
* The path is the absolute path that was attempted; consumers may
|
|
78
|
+
* workspace-relativize it for stability across machines.
|
|
79
|
+
*/
|
|
80
|
+
rootsConfig?: { path: string; contentHash: string };
|
|
81
|
+
}
|
package/src/model/ids.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { sha256OfStrings } from "../hash.ts";
|
|
2
|
+
|
|
3
|
+
/** Routine kinds — table/page/report triggers are routines, indexed from day one. */
|
|
4
|
+
export type RoutineKind = "procedure" | "trigger" | "event-publisher" | "event-subscriber";
|
|
5
|
+
|
|
6
|
+
// --- ID string aliases. Opaque outside this module. ---
|
|
7
|
+
export type ObjectId = string; // "{appGuid}/{objectType}/{objectNumber}"
|
|
8
|
+
export type TableId = string; // "{appGuid}/table/{number}" (physical table)
|
|
9
|
+
export type FieldId = string; // "{tableId}/{fieldNumber}"
|
|
10
|
+
export type KeyId = string; // "{tableId}/key/{index}"
|
|
11
|
+
export type RoutineId = string; // encodes { canonicalKey, modelInstanceId }
|
|
12
|
+
export type CallsiteId = string; // "{routineId}/cs{index}"
|
|
13
|
+
export type LoopId = string; // "{routineId}/loop{index}"
|
|
14
|
+
export type OperationId = string; // "{routineId}/op{index}"
|
|
15
|
+
export type RecordVariableId = string; // "{routineId}/rv/{name}"
|
|
16
|
+
export type EventId = string; // "{publisherObjectId}/event/{eventName}"
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Stable across app version bumps when the symbol is semantically the same.
|
|
20
|
+
* This is the key regression comparison (sub-project D) and profile fusion
|
|
21
|
+
* (sub-project B) join on.
|
|
22
|
+
*/
|
|
23
|
+
export interface CanonicalRoutineKey {
|
|
24
|
+
appGuid: string;
|
|
25
|
+
objectType: string;
|
|
26
|
+
objectNumber: number;
|
|
27
|
+
routineKind: RoutineKind;
|
|
28
|
+
routineName: string;
|
|
29
|
+
normalizedSignatureHash: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function encodeObjectId(
|
|
33
|
+
appGuid: string,
|
|
34
|
+
objectType: string,
|
|
35
|
+
objectNumber: number,
|
|
36
|
+
): ObjectId {
|
|
37
|
+
return `${appGuid}/${objectType}/${objectNumber}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function encodeTableId(appGuid: string, tableNumber: number): TableId {
|
|
41
|
+
return `${appGuid}/table/${tableNumber}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function encodeFieldId(tableId: TableId, fieldNumber: number): FieldId {
|
|
45
|
+
return `${tableId}/${fieldNumber}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function encodeKeyId(tableId: TableId, keyIndex: number): KeyId {
|
|
49
|
+
return `${tableId}/key/${keyIndex}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Order-sensitive, collision-free hash of the canonical key fields. */
|
|
53
|
+
export function encodeCanonicalRoutineKey(key: CanonicalRoutineKey): string {
|
|
54
|
+
return sha256OfStrings([
|
|
55
|
+
key.appGuid,
|
|
56
|
+
key.objectType,
|
|
57
|
+
String(key.objectNumber),
|
|
58
|
+
key.routineKind,
|
|
59
|
+
key.routineName.toLowerCase(),
|
|
60
|
+
key.normalizedSignatureHash,
|
|
61
|
+
]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Model-instance-scoped concrete routine identity. */
|
|
65
|
+
export function encodeRoutineId(key: CanonicalRoutineKey, modelInstanceId: string): RoutineId {
|
|
66
|
+
return `${modelInstanceId}/${encodeCanonicalRoutineKey(key)}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function encodeCallsiteId(routineId: RoutineId, index: number): CallsiteId {
|
|
70
|
+
return `${routineId}/cs${index}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function encodeLoopId(routineId: RoutineId, index: number): LoopId {
|
|
74
|
+
return `${routineId}/loop${index}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function encodeOperationId(routineId: RoutineId, index: number): OperationId {
|
|
78
|
+
return `${routineId}/op${index}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function encodeRecordVariableId(
|
|
82
|
+
routineId: RoutineId,
|
|
83
|
+
variableName: string,
|
|
84
|
+
): RecordVariableId {
|
|
85
|
+
return `${routineId}/rv/${variableName.toLowerCase()}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function encodeEventId(publisherObjectId: ObjectId, eventName: string): EventId {
|
|
89
|
+
return `${publisherObjectId}/event/${eventName.toLowerCase()}`;
|
|
90
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./ids.ts";
|
|
2
|
+
export * from "./identity.ts";
|
|
3
|
+
export * from "./entities.ts";
|
|
4
|
+
export * from "./summary.ts";
|
|
5
|
+
export * from "./graph.ts";
|
|
6
|
+
export * from "./finding.ts";
|
|
7
|
+
export * from "./model.ts";
|
|
8
|
+
// Phase 0a additions:
|
|
9
|
+
export * from "./stable-identity.ts";
|
|
10
|
+
export * from "./coverage.ts";
|
|
11
|
+
export * from "./capability.ts";
|
|
12
|
+
export * from "./graph-edge.ts";
|
|
13
|
+
export * from "./permission.ts";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { App, ObjectDecl, Routine, Table } from "./entities.ts";
|
|
2
|
+
import type { GraphEdge } from "./graph-edge.ts";
|
|
3
|
+
import type { CallEdge, EventGraph } from "./graph.ts";
|
|
4
|
+
import type { ModelIdentity } from "./identity.ts";
|
|
5
|
+
import type { CallsiteId, OperationId, RoutineId } from "./ids.ts";
|
|
6
|
+
import type { RootClassification } from "./root-classification.ts";
|
|
7
|
+
|
|
8
|
+
/** First-class "no silent clean" coverage record. Populated in Phase 2. */
|
|
9
|
+
export interface AnalysisCoverage {
|
|
10
|
+
sourceUnitsTotal: number;
|
|
11
|
+
sourceUnitsParsed: number;
|
|
12
|
+
routinesTotal: number;
|
|
13
|
+
routinesBodyAvailable: number;
|
|
14
|
+
routinesParseIncomplete: RoutineId[];
|
|
15
|
+
opaqueApps: string[];
|
|
16
|
+
unresolvedCallsites: CallsiteId[];
|
|
17
|
+
dynamicDispatchSites: OperationId[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The Phase 1 deliverable: identity + apps + objects + routines (with intraprocedural
|
|
22
|
+
* features, no summaries) + tables. No call graph, no event graph.
|
|
23
|
+
*/
|
|
24
|
+
export interface SemanticIndex {
|
|
25
|
+
identity: ModelIdentity;
|
|
26
|
+
apps: App[];
|
|
27
|
+
objects: ObjectDecl[];
|
|
28
|
+
routines: Routine[];
|
|
29
|
+
tables: Table[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The full model — Phase 2 extends a SemanticIndex with graphs and coverage. */
|
|
33
|
+
export interface SemanticModel extends SemanticIndex {
|
|
34
|
+
callGraph: CallEdge[];
|
|
35
|
+
eventGraph: EventGraph;
|
|
36
|
+
coverage: AnalysisCoverage;
|
|
37
|
+
/**
|
|
38
|
+
* Typed discriminated-union graph edges. Populated by `buildCombinedGraph`
|
|
39
|
+
* (Phase 0b-β Task 19). Kinds emitted initially: `direct-call`,
|
|
40
|
+
* `object-run-resolved`, `object-run-unresolved`. Event-dispatch and
|
|
41
|
+
* implicit-trigger edges are composed in Task 20+.
|
|
42
|
+
*/
|
|
43
|
+
typedEdges?: readonly GraphEdge[];
|
|
44
|
+
/**
|
|
45
|
+
* Phase 1 §4.3 — per-routine root classification with provenance.
|
|
46
|
+
* Populated by root-classifier (post-summary engine, pre-snapshot
|
|
47
|
+
* composition). Empty when no routine qualifies as a root. Routines
|
|
48
|
+
* with no qualifying RootKind are NOT in the array.
|
|
49
|
+
*/
|
|
50
|
+
rootClassifications: RootClassification[];
|
|
51
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { CapabilityOp } from "./capability.ts";
|
|
2
|
+
import type { CoverageStatus } from "./coverage.ts";
|
|
3
|
+
import type { CallsiteId } from "./ids.ts";
|
|
4
|
+
import type {
|
|
5
|
+
StableObjectId,
|
|
6
|
+
StablePermissionSetId,
|
|
7
|
+
StableRoutineId,
|
|
8
|
+
StableTableId,
|
|
9
|
+
} from "./stable-identity.ts";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* AL permission right set. R/I/M/D apply to TableData; X is execute
|
|
13
|
+
* for codeunits/pages/reports/xmlports/queries.
|
|
14
|
+
*/
|
|
15
|
+
export type PermissionRight = "R" | "I" | "M" | "D" | "X";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* What an AL permission entry targets. `TableData` is the
|
|
19
|
+
* read/insert/modify/delete pseudo-target; `Table` is the table
|
|
20
|
+
* definition itself (rare). `System` covers a small set of system
|
|
21
|
+
* permissions.
|
|
22
|
+
*/
|
|
23
|
+
export type PermissionTargetKind =
|
|
24
|
+
| "TableData"
|
|
25
|
+
| "Table"
|
|
26
|
+
| "Codeunit"
|
|
27
|
+
| "Page"
|
|
28
|
+
| "Report"
|
|
29
|
+
| "XmlPort"
|
|
30
|
+
| "Query"
|
|
31
|
+
| "System";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A permission entry from an AL `PermissionSet` object. Materialized at
|
|
35
|
+
* index time from the workspace and from `.app` symbol packages.
|
|
36
|
+
*
|
|
37
|
+
* `scope: "Inherent"` = permissions granted by inherent entitlements (set
|
|
38
|
+
* via object-level `InherentEntitlements` / `InherentPermissions`
|
|
39
|
+
* properties). `scope: "Assignable"` = permissions declared in a
|
|
40
|
+
* `PermissionSet` object that can be assigned to a user.
|
|
41
|
+
*/
|
|
42
|
+
export interface DeclaredPermissionFact {
|
|
43
|
+
kind: "declared";
|
|
44
|
+
permissionSet: StablePermissionSetId;
|
|
45
|
+
target: StableTableId | StableObjectId;
|
|
46
|
+
targetKind: PermissionTargetKind;
|
|
47
|
+
rights: PermissionRight[];
|
|
48
|
+
scope: "Inherent" | "Assignable";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A permission inferred as REQUIRED by a routine's capability cone.
|
|
53
|
+
* Derived from `CapabilityFact`s on the routine's transitive reachable
|
|
54
|
+
* set (table read/insert/modify/delete → R/I/M/D on TableData; execute
|
|
55
|
+
* → X on codeunit/page/report).
|
|
56
|
+
*
|
|
57
|
+
* G6 enforcement: every RequiredPermissionFact carries the coverage
|
|
58
|
+
* status of the underlying cone. Required perms are "may be incomplete"
|
|
59
|
+
* when the cone is partial — Phase 4 policy must surface this.
|
|
60
|
+
*
|
|
61
|
+
* al-sem DOES NOT claim runtime authorization outcomes. RequiredPermissionFact
|
|
62
|
+
* only says "the reachable code surface implies this permission was
|
|
63
|
+
* needed at some path"; Phase 4 compares it against DeclaredPermissionFact
|
|
64
|
+
* sets in scope.
|
|
65
|
+
*/
|
|
66
|
+
export interface RequiredPermissionFact {
|
|
67
|
+
kind: "required";
|
|
68
|
+
subject: StableRoutineId;
|
|
69
|
+
target: StableTableId | StableObjectId;
|
|
70
|
+
targetKind: PermissionTargetKind;
|
|
71
|
+
rights: PermissionRight[];
|
|
72
|
+
derivedFromCapability: { op: CapabilityOp; witnessCallsiteId?: CallsiteId };
|
|
73
|
+
coverage: CoverageStatus;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type PermissionFact = DeclaredPermissionFact | RequiredPermissionFact;
|