Skip to content

Commit 69e20d9

Browse files
authored
Restrict which projects can reference which projects (#502)
1 parent 97c11f9 commit 69e20d9

File tree

4 files changed

+113
-2
lines changed

4 files changed

+113
-2
lines changed

Directory.Packages.props

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@
6161
<PackageVersion Include="SharpFuzz" Version="2.2.0" />
6262
<PackageVersion Include="WinSharpFuzz" Version="1.0.0" />
6363
</ItemGroup>
64+
<!--
65+
Serialization project references that are restricted to Tests and Benchmarks projects only.
66+
Verifiable.Json and Verifiable.Cbor are interchangeable serialization layers. Library projects
67+
must not depend on them directly, keeping the core stack serialization-agnostic.
68+
-->
69+
<PropertyGroup>
70+
<RestrictedSerializationProjects>Verifiable.Cbor;Verifiable.Json</RestrictedSerializationProjects>
71+
</PropertyGroup>
6472
<!--
6573
Validates that labeled packages are only referenced in appropriate projects.
6674
Runs after package references are collected but before compilation begins.
@@ -80,6 +88,20 @@
8088
-->
8189
<ValidateScopedPackages Condition="'$(_IsLibraryProject)' == 'true' AND '@(PackageReference)' != ''" ProjectName="$(MSBuildProjectName)" PackageReferences="@(PackageReference)" DirectoryPackagesPropsPath="$(MSBuildThisFileFullPath)" />
8290
</Target>
91+
<!--
92+
Validates that serialization projects (Verifiable.Json, Verifiable.Cbor) are only referenced
93+
by Tests and Benchmarks projects. This enforces a clean architecture boundary where the core
94+
library stack remains serialization-agnostic and consumers choose their serialization layer.
95+
Runs after project references are resolved but before compilation begins.
96+
-->
97+
<Target Name="ValidateProjectReferences" AfterTargets="ResolveProjectReferences" Condition="'$(DesignTimeBuild)' != 'true'">
98+
<PropertyGroup>
99+
<_IsTestProject Condition="$(MSBuildProjectName.EndsWith('.Tests'))">true</_IsTestProject>
100+
<_IsBenchmarkProject Condition="$(MSBuildProjectName.EndsWith('.Benchmarks'))">true</_IsBenchmarkProject>
101+
<_IsAllowedToReferenceSerializationProjects Condition="'$(_IsTestProject)' == 'true' OR '$(_IsBenchmarkProject)' == 'true'">true</_IsAllowedToReferenceSerializationProjects>
102+
</PropertyGroup>
103+
<ValidateSerializationProjectReferences Condition="'$(_IsAllowedToReferenceSerializationProjects)' != 'true' AND '@(ProjectReference)' != ''" ProjectName="$(MSBuildProjectName)" ProjectReferences="@(ProjectReference)" RestrictedProjects="$(RestrictedSerializationProjects)" />
104+
</Target>
83105
<!--Inline Roslyn task for validating package references. Compiled once per build session and cached in memory.-->
84106
<UsingTask TaskName="ValidateScopedPackages" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
85107
<ParameterGroup>
@@ -147,4 +169,59 @@
147169
]]></Code>
148170
</Task>
149171
</UsingTask>
172+
<!--
173+
Inline Roslyn task for validating project references to serialization projects.
174+
Ensures that only Tests and Benchmarks projects can reference Verifiable.Json and Verifiable.Cbor.
175+
Compiled once per build session and cached in memory.
176+
-->
177+
<UsingTask TaskName="ValidateSerializationProjectReferences" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
178+
<ParameterGroup>
179+
<ProjectName Required="true" />
180+
<ProjectReferences ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
181+
<RestrictedProjects Required="true" />
182+
</ParameterGroup>
183+
<Task>
184+
<Using Namespace="System" />
185+
<Using Namespace="System.Collections.Generic" />
186+
<Using Namespace="System.IO" />
187+
<Using Namespace="System.Linq" />
188+
<Code Type="Method" Language="cs"><![CDATA[
189+
public override bool Execute()
190+
{
191+
//Early exit if no project references.
192+
if(ProjectReferences == null || ProjectReferences.Length == 0)
193+
{
194+
return true;
195+
}
196+
197+
//Parse the semicolon-delimited list of restricted project names.
198+
var restricted = new HashSet<string>(
199+
RestrictedProjects.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries),
200+
StringComparer.OrdinalIgnoreCase);
201+
202+
//Extract project name from each reference path and check against the restricted set.
203+
var violations = new List<string>();
204+
foreach(var projRef in ProjectReferences)
205+
{
206+
var referencedProjectName = Path.GetFileNameWithoutExtension(projRef.ItemSpec);
207+
if(restricted.Contains(referencedProjectName))
208+
{
209+
violations.Add(referencedProjectName);
210+
}
211+
}
212+
213+
if(violations.Count > 0)
214+
{
215+
Log.LogError(
216+
$"Restricted project reference(s) '{string.Join(", ", violations)}' " +
217+
$"found in '{ProjectName}'. " +
218+
"Verifiable.Json and Verifiable.Cbor can only be referenced by Tests and Benchmarks projects.");
219+
return false;
220+
}
221+
222+
return true;
223+
}
224+
]]></Code>
225+
</Task>
226+
</UsingTask>
150227
</Project>

src/Verifiable.Tpm/packages.lock.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,33 @@
114114
"System.Security.Cryptography.Pkcs": "9.0.0"
115115
}
116116
},
117+
"verifiable.core": {
118+
"type": "Project",
119+
"dependencies": {
120+
"Verifiable.JCose": "[0.0.0, )",
121+
"Verifiable.JsonPointer": "[0.0.0, )"
122+
}
123+
},
117124
"verifiable.cryptography": {
118125
"type": "Project"
119126
},
127+
"verifiable.jcose": {
128+
"type": "Project",
129+
"dependencies": {
130+
"Verifiable.Cryptography": "[0.0.0, )"
131+
}
132+
},
133+
"verifiable.json": {
134+
"type": "Project",
135+
"dependencies": {
136+
"Verifiable.Core": "[0.0.0, )",
137+
"Verifiable.JCose": "[0.0.0, )",
138+
"Verifiable.JsonPointer": "[0.0.0, )"
139+
}
140+
},
141+
"verifiable.jsonpointer": {
142+
"type": "Project"
143+
},
120144
"System.Security.Cryptography.ProtectedData": {
121145
"type": "CentralTransitive",
122146
"requested": "[10.0.3, )",

src/Verifiable/packages.lock.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,13 +656,22 @@
656656
"Verifiable.Cryptography": "[0.0.0, )"
657657
}
658658
},
659+
"verifiable.json": {
660+
"type": "Project",
661+
"dependencies": {
662+
"Verifiable.Core": "[0.0.0, )",
663+
"Verifiable.JCose": "[0.0.0, )",
664+
"Verifiable.JsonPointer": "[0.0.0, )"
665+
}
666+
},
659667
"verifiable.jsonpointer": {
660668
"type": "Project"
661669
},
662670
"verifiable.tpm": {
663671
"type": "Project",
664672
"dependencies": {
665-
"Verifiable.Cryptography": "[0.0.0, )"
673+
"Verifiable.Cryptography": "[0.0.0, )",
674+
"Verifiable.Json": "[0.0.0, )"
666675
}
667676
},
668677
"System.Security.Cryptography.ProtectedData": {

test/Verifiable.Tests/packages.lock.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,8 @@
991991
"verifiable.tpm": {
992992
"type": "Project",
993993
"dependencies": {
994-
"Verifiable.Cryptography": "[0.0.0, )"
994+
"Verifiable.Cryptography": "[0.0.0, )",
995+
"Verifiable.Json": "[0.0.0, )"
995996
}
996997
},
997998
"BouncyCastle.Cryptography": {

0 commit comments

Comments
 (0)