From a668920e96f5cc3d4686c5e457fd51bfa8fe47c0 Mon Sep 17 00:00:00 2001 From: wenjiefan Date: Mon, 17 Aug 2026 13:16:12 +0200 Subject: [PATCH 1/3] Correct table-level data classification guidance Document that valid table-level classifications are inherited by fields and update the privacy fixture and related guidance accordingly. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../data-classification-required-on-pii-fields.md | 2 +- .../table-level-data-classification-cascades.good.al | 6 +++++- .../table-level-data-classification-cascades.md | 10 +++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md b/microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md index d3e1e55..97c9858 100644 --- a/microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md +++ b/microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md @@ -11,7 +11,7 @@ application-area: [all] ## Description -`DataClassification` is the AL property that tells the platform what kind of data a table field stores so that telemetry, GDPR data-subject requests, and the platform's audit surfaces can treat it correctly. It is required on any field that holds personal, customer, or organization data. When the property is omitted, AL applies `ToBeClassified` — a placeholder meaning "not yet reviewed", not a safe default. Leaving a field that actually holds PII (an email address, a customer name, an employee code) as `ToBeClassified`, or setting it to `SystemMetadata` ("no user or customer data") to silence the requirement, are both under-classifications and privacy bugs, even though the code still compiles. +`DataClassification` tells the platform what kind of data a table field stores so that telemetry, GDPR data-subject requests, and the platform's audit surfaces can treat it correctly. A field can declare its own value or inherit the table-level value. When neither scope supplies a valid classification, the field remains `ToBeClassified` — a placeholder meaning "not yet reviewed", not a safe default. Leaving a field that actually holds PII (an email address, a customer name, an employee code) as `ToBeClassified`, or classifying it as `SystemMetadata` ("no user or customer data"), are both under-classifications and privacy bugs, even though the code still compiles. ## Best Practice diff --git a/microsoft/knowledge/privacy/table-level-data-classification-cascades.good.al b/microsoft/knowledge/privacy/table-level-data-classification-cascades.good.al index 80a4345..11cbbb6 100644 --- a/microsoft/knowledge/privacy/table-level-data-classification-cascades.good.al +++ b/microsoft/knowledge/privacy/table-level-data-classification-cascades.good.al @@ -1,10 +1,11 @@ table 50202 "System Configuration Log" { + DataClassification = SystemMetadata; + fields { field(1; "Entry No."; Integer) { - DataClassification = SystemMetadata; } field(2; "Changed By"; Code[50]) { @@ -14,6 +15,9 @@ table 50202 "System Configuration Log" { DataClassification = CustomerContent; } + field(4; "Changed At"; DateTime) + { + } } keys diff --git a/microsoft/knowledge/privacy/table-level-data-classification-cascades.md b/microsoft/knowledge/privacy/table-level-data-classification-cascades.md index cd31d07..55ac5d6 100644 --- a/microsoft/knowledge/privacy/table-level-data-classification-cascades.md +++ b/microsoft/knowledge/privacy/table-level-data-classification-cascades.md @@ -1,24 +1,24 @@ --- bc-version: [all] domain: privacy -keywords: [data-classification, table-level, normal-field, appsourcecop, as0016] +keywords: [data-classification, table-level, field-inheritance, appsourcecop, as0016, false-positive] technologies: [al] countries: [w1] application-area: [all] --- -# Set DataClassification on every Normal table field +# Table-level DataClassification is inherited by fields ## Description -AppSourceCop AS0016 requires every field whose `FieldClass` is `Normal` to declare `DataClassification` and use a value other than `ToBeClassified`. A table-level `DataClassification` property does not satisfy that field-level requirement. FlowFields and FlowFilters are handled separately by the platform and are covered by `flowfield-flowfilter-classification-systemmetadata.md`. +A valid table-level `DataClassification` is the effective default for fields that do not declare their own value. A field-level value overrides that default only for the field on which it is set. AppSourceCop AS0016 accepts Normal fields that inherit a valid table classification; they do not remain `ToBeClassified`. FlowFields and FlowFilters are handled separately by the platform and are covered by `flowfield-flowfilter-classification-systemmetadata.md`. ## Best Practice -Classify each Normal field according to the data it stores, even when every field in the table has the same classification. Repeat the property explicitly so AS0016 can verify every field. +Use a table-level classification when it accurately describes the table's fields, and add a field-level classification only where a field stores a different kind of data. Do not flag a Normal field solely because it omits an explicit property when its table supplies a valid default; verify whether the inherited value matches the field's data instead. See sample: `table-level-data-classification-cascades.good.al`. ## Anti Pattern -Relying on `DataClassification` at table scope and leaving Normal fields unclassified. The table property does not cascade in the way AS0016 requires, so the fields still fail AppSourceCop validation. +Reporting every Normal field without an explicit `DataClassification` when the table already supplies a valid default, or requiring redundant field-level declarations that repeat the table value. A real issue exists when neither scope supplies a valid classification, or when a field's data requires an override of the inherited value. From f8acb6cbddc35e410c501da382adcbc1b3c2ef9c Mon Sep 17 00:00:00 2001 From: wenjiefan Date: Mon, 17 Aug 2026 15:14:53 +0200 Subject: [PATCH 2/3] Scope DataClassification inheritance to fields declared in the table Table-level DataClassification is the effective default only for Normal fields declared inside that table object. A tableextension cannot set the property (AL0246) and its added fields do not inherit the base table value, so AS0016 still requires each of them to classify itself. State this in both privacy articles so the guidance cannot suppress genuine findings on the tableextension pattern, which is how most partner code adds fields. Also narrow the inheritance claim to verified AppSourceCop behaviour rather than asserting platform-level resolution, and make the sample's table-level default semantically representative of its fields while keeping a legitimate field-level override and demonstrating the tableextension boundary. Verified with alc.exe 18.0.37.11445 + Microsoft.Dynamics.Nav.AppSourceCop.dll: the revised sample produces no AS0016, and removing the explicit classification from the tableextension field makes AS0016 fire. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- ...a-classification-required-on-pii-fields.md | 2 +- ...level-data-classification-cascades.good.al | 23 +++++++++++++++---- ...able-level-data-classification-cascades.md | 8 +++---- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md b/microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md index 97c9858..e377256 100644 --- a/microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md +++ b/microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md @@ -11,7 +11,7 @@ application-area: [all] ## Description -`DataClassification` tells the platform what kind of data a table field stores so that telemetry, GDPR data-subject requests, and the platform's audit surfaces can treat it correctly. A field can declare its own value or inherit the table-level value. When neither scope supplies a valid classification, the field remains `ToBeClassified` — a placeholder meaning "not yet reviewed", not a safe default. Leaving a field that actually holds PII (an email address, a customer name, an employee code) as `ToBeClassified`, or classifying it as `SystemMetadata` ("no user or customer data"), are both under-classifications and privacy bugs, even though the code still compiles. +`DataClassification` tells the platform what kind of data a table field stores so that telemetry, GDPR data-subject requests, and the platform's audit surfaces can treat it correctly. A field declared inside a table object can set its own value or inherit a valid table-level value; a field added by a `tableextension` has no table-level value to inherit and must always set its own. When neither scope supplies a valid classification, the field remains `ToBeClassified` — a placeholder meaning "not yet reviewed", not a safe default. Leaving a field that actually holds PII (an email address, a customer name, an employee code) as `ToBeClassified`, or classifying it as `SystemMetadata` ("no user or customer data"), are both under-classifications and privacy bugs, even though the code still compiles. ## Best Practice diff --git a/microsoft/knowledge/privacy/table-level-data-classification-cascades.good.al b/microsoft/knowledge/privacy/table-level-data-classification-cascades.good.al index 11cbbb6..bb0a8cf 100644 --- a/microsoft/knowledge/privacy/table-level-data-classification-cascades.good.al +++ b/microsoft/knowledge/privacy/table-level-data-classification-cascades.good.al @@ -7,16 +7,15 @@ table 50202 "System Configuration Log" field(1; "Entry No."; Integer) { } - field(2; "Changed By"; Code[50]) + field(2; "Setting Name"; Text[100]) { - DataClassification = EndUserIdentifiableInformation; } - field(3; "Change Description"; Text[250]) + field(3; "Changed At"; DateTime) { - DataClassification = CustomerContent; } - field(4; "Changed At"; DateTime) + field(4; "Changed By"; Code[50]) { + DataClassification = EndUserIdentifiableInformation; } } @@ -25,3 +24,17 @@ table 50202 "System Configuration Log" key(PK; "Entry No.") { Clustered = true; } } } + +tableextension 50203 "System Config Log Correlation" extends "System Configuration Log" +{ + fields + { + // A table extension cannot set the table-level property and does not inherit + // the base table's default, so this field must classify itself even though + // SystemMetadata is the value the base table already declares. + field(50203; "Correlation Id"; Guid) + { + DataClassification = SystemMetadata; + } + } +} diff --git a/microsoft/knowledge/privacy/table-level-data-classification-cascades.md b/microsoft/knowledge/privacy/table-level-data-classification-cascades.md index 55ac5d6..68b5920 100644 --- a/microsoft/knowledge/privacy/table-level-data-classification-cascades.md +++ b/microsoft/knowledge/privacy/table-level-data-classification-cascades.md @@ -1,7 +1,7 @@ --- bc-version: [all] domain: privacy -keywords: [data-classification, table-level, field-inheritance, appsourcecop, as0016, false-positive] +keywords: [data-classification, table-level, field-inheritance, tableextension, appsourcecop, as0016, false-positive] technologies: [al] countries: [w1] application-area: [all] @@ -11,14 +11,14 @@ application-area: [all] ## Description -A valid table-level `DataClassification` is the effective default for fields that do not declare their own value. A field-level value overrides that default only for the field on which it is set. AppSourceCop AS0016 accepts Normal fields that inherit a valid table classification; they do not remain `ToBeClassified`. FlowFields and FlowFilters are handled separately by the platform and are covered by `flowfield-flowfilter-classification-systemmetadata.md`. +A valid table-level `DataClassification` is the effective default for the Normal fields declared inside that table object when they do not declare their own value, and AppSourceCop AS0016 accepts those fields rather than reporting them as unclassified. A field-level value overrides that default only for the field on which it is set. The default does not cross object boundaries: a `tableextension` cannot set the table-level property, and the fields it adds do not inherit the base table's value, so every Normal field a table extension adds must classify itself. FlowFields and FlowFilters are handled separately by the platform and are covered by `flowfield-flowfilter-classification-systemmetadata.md`. ## Best Practice -Use a table-level classification when it accurately describes the table's fields, and add a field-level classification only where a field stores a different kind of data. Do not flag a Normal field solely because it omits an explicit property when its table supplies a valid default; verify whether the inherited value matches the field's data instead. +Use a table-level classification when it accurately describes the table's fields, and add a field-level classification only where a field stores a different kind of data. Do not flag a Normal field solely because it omits an explicit property when its own table supplies a valid default; verify whether the inherited value matches the field's data instead. A `tableextension` has no default to inherit, so require an explicit `DataClassification` on every Normal field it adds. See sample: `table-level-data-classification-cascades.good.al`. ## Anti Pattern -Reporting every Normal field without an explicit `DataClassification` when the table already supplies a valid default, or requiring redundant field-level declarations that repeat the table value. A real issue exists when neither scope supplies a valid classification, or when a field's data requires an override of the inherited value. +Reporting every Normal field without an explicit `DataClassification` when its own table already supplies a valid default, or requiring redundant field-level declarations that repeat the table value. The mirror-image mistake is waving through an unclassified Normal field added by a `tableextension` because the base table carries a default — a table extension inherits nothing. A real issue exists when neither scope supplies a valid classification, when a field's data requires an override of the inherited value, or when the field is added by a table extension. From c1057d38b29f439f0d36ffa5265cd73822baa03c Mon Sep 17 00:00:00 2001 From: wenjiefan Date: Mon, 17 Aug 2026 15:25:14 +0200 Subject: [PATCH 3/3] Scope tableextension requirement to Normal fields lacking a classification The requirement to declare DataClassification explicitly in a tableextension applies to the Normal fields it adds; FlowFields and FlowFilters are SystemMetadata automatically and are covered by their own article. Being added by a table extension is also not itself a finding - the finding is a Normal field added by a table extension that has no valid explicit DataClassification. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../privacy/data-classification-required-on-pii-fields.md | 2 +- .../privacy/table-level-data-classification-cascades.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md b/microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md index e377256..b431c12 100644 --- a/microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md +++ b/microsoft/knowledge/privacy/data-classification-required-on-pii-fields.md @@ -11,7 +11,7 @@ application-area: [all] ## Description -`DataClassification` tells the platform what kind of data a table field stores so that telemetry, GDPR data-subject requests, and the platform's audit surfaces can treat it correctly. A field declared inside a table object can set its own value or inherit a valid table-level value; a field added by a `tableextension` has no table-level value to inherit and must always set its own. When neither scope supplies a valid classification, the field remains `ToBeClassified` — a placeholder meaning "not yet reviewed", not a safe default. Leaving a field that actually holds PII (an email address, a customer name, an employee code) as `ToBeClassified`, or classifying it as `SystemMetadata` ("no user or customer data"), are both under-classifications and privacy bugs, even though the code still compiles. +`DataClassification` tells the platform what kind of data a table field stores so that telemetry, GDPR data-subject requests, and the platform's audit surfaces can treat it correctly. A field declared inside a table object can set its own value or inherit a valid table-level value; a `tableextension` has no table-level value to inherit, so every Normal field it adds must set its own. When neither scope supplies a valid classification, the field remains `ToBeClassified` — a placeholder meaning "not yet reviewed", not a safe default. Leaving a field that actually holds PII (an email address, a customer name, an employee code) as `ToBeClassified`, or classifying it as `SystemMetadata` ("no user or customer data"), are both under-classifications and privacy bugs, even though the code still compiles. ## Best Practice diff --git a/microsoft/knowledge/privacy/table-level-data-classification-cascades.md b/microsoft/knowledge/privacy/table-level-data-classification-cascades.md index 68b5920..253d2cc 100644 --- a/microsoft/knowledge/privacy/table-level-data-classification-cascades.md +++ b/microsoft/knowledge/privacy/table-level-data-classification-cascades.md @@ -21,4 +21,4 @@ See sample: `table-level-data-classification-cascades.good.al`. ## Anti Pattern -Reporting every Normal field without an explicit `DataClassification` when its own table already supplies a valid default, or requiring redundant field-level declarations that repeat the table value. The mirror-image mistake is waving through an unclassified Normal field added by a `tableextension` because the base table carries a default — a table extension inherits nothing. A real issue exists when neither scope supplies a valid classification, when a field's data requires an override of the inherited value, or when the field is added by a table extension. +Reporting every Normal field without an explicit `DataClassification` when its own table already supplies a valid default, or requiring redundant field-level declarations that repeat the table value. The mirror-image mistake is waving through an unclassified Normal field added by a `tableextension` because the base table carries a default — a table extension inherits nothing. A real issue exists when neither scope supplies a valid classification, when a field's data requires an override of the inherited value, or when a Normal field added by a `tableextension` lacks a valid explicit `DataClassification`.