Skip to content

Commit 8c528d8

Browse files
Copilotsamsmithnz
andauthored
Add ModelState validation to controller actions to resolve security warnings (#345)
* Initial plan * Add ModelState.IsValid validation to all controller actions Co-authored-by: samsmithnz <8389039+samsmithnz@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samsmithnz <8389039+samsmithnz@users.noreply.github.com>
1 parent b7f304d commit 8c528d8

File tree

3 files changed

+237
-0
lines changed

3 files changed

+237
-0
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
using Microsoft.ApplicationInsights;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.AspNetCore.Mvc.ModelBinding;
4+
using Microsoft.Extensions.Logging;
5+
using Microsoft.Extensions.Logging.Abstractions;
6+
using PipelinesToActionsWeb.Controllers;
7+
using System.ComponentModel.DataAnnotations;
8+
9+
namespace PipelinesToActions.Tests
10+
{
11+
[TestClass]
12+
public class HomeControllerTests
13+
{
14+
private HomeController _controller = null!;
15+
private ILogger<HomeController> _logger = null!;
16+
private TelemetryClient _telemetryClient = null!;
17+
18+
[TestInitialize]
19+
public void Setup()
20+
{
21+
_logger = new NullLogger<HomeController>();
22+
_telemetryClient = new TelemetryClient();
23+
_controller = new HomeController(_logger, _telemetryClient);
24+
}
25+
26+
[TestMethod]
27+
public void Index_Get_ReturnsViewWithEmptyModel()
28+
{
29+
//Arrange
30+
31+
//Act
32+
var result = _controller.Index();
33+
34+
//Assert
35+
Assert.IsNotNull(result);
36+
Assert.IsInstanceOfType(result, typeof(ViewResult));
37+
var viewResult = (ViewResult)result;
38+
Assert.IsNotNull(viewResult.Model);
39+
}
40+
41+
[TestMethod]
42+
public void Index_Post_WithValidModel_ProcessesConversion()
43+
{
44+
//Arrange
45+
string testYaml = "trigger:\n- main\n\npool:\n vmImage: 'ubuntu-latest'\n\nsteps:\n- script: echo Hello world";
46+
bool addWorkflowDispatch = false;
47+
48+
//Act
49+
var result = _controller.Index(testYaml, addWorkflowDispatch);
50+
51+
//Assert
52+
Assert.IsNotNull(result);
53+
Assert.IsInstanceOfType(result, typeof(ViewResult));
54+
var viewResult = (ViewResult)result;
55+
Assert.IsNotNull(viewResult.Model);
56+
}
57+
58+
[TestMethod]
59+
public void Index_Post_WithInvalidModel_ReturnsEmptyResult()
60+
{
61+
//Arrange
62+
string testYaml = "valid yaml";
63+
bool addWorkflowDispatch = false;
64+
65+
// Simulate invalid model state
66+
_controller.ModelState.AddModelError("TestError", "Test validation error");
67+
68+
//Act
69+
var result = _controller.Index(testYaml, addWorkflowDispatch);
70+
71+
//Assert
72+
Assert.IsNotNull(result);
73+
Assert.IsInstanceOfType(result, typeof(ViewResult));
74+
var viewResult = (ViewResult)result;
75+
Assert.IsNotNull(viewResult.Model);
76+
// The model should be an empty ConversionResponse when ModelState is invalid
77+
}
78+
79+
[TestMethod]
80+
public void CIExample_WithValidModel_ReturnsView()
81+
{
82+
//Arrange
83+
84+
//Act
85+
var result = _controller.CIExample();
86+
87+
//Assert
88+
Assert.IsNotNull(result);
89+
Assert.IsInstanceOfType(result, typeof(ViewResult));
90+
}
91+
92+
[TestMethod]
93+
public void CIExample_WithInvalidModel_RedirectsToIndex()
94+
{
95+
//Arrange
96+
_controller.ModelState.AddModelError("TestError", "Test validation error");
97+
98+
//Act
99+
var result = _controller.CIExample();
100+
101+
//Assert
102+
Assert.IsNotNull(result);
103+
Assert.IsInstanceOfType(result, typeof(RedirectToActionResult));
104+
var redirectResult = (RedirectToActionResult)result;
105+
Assert.AreEqual("Index", redirectResult.ActionName);
106+
}
107+
108+
[TestMethod]
109+
public void DotNetFrameworkDesktopExample_WithValidModel_ReturnsView()
110+
{
111+
//Arrange
112+
113+
//Act
114+
var result = _controller.DotNetFrameworkDesktopExample();
115+
116+
//Assert
117+
Assert.IsNotNull(result);
118+
Assert.IsInstanceOfType(result, typeof(ViewResult));
119+
}
120+
121+
[TestMethod]
122+
public void DotNetFrameworkDesktopExample_WithInvalidModel_RedirectsToIndex()
123+
{
124+
//Arrange
125+
_controller.ModelState.AddModelError("TestError", "Test validation error");
126+
127+
//Act
128+
var result = _controller.DotNetFrameworkDesktopExample();
129+
130+
//Assert
131+
Assert.IsNotNull(result);
132+
Assert.IsInstanceOfType(result, typeof(RedirectToActionResult));
133+
var redirectResult = (RedirectToActionResult)result;
134+
Assert.AreEqual("Index", redirectResult.ActionName);
135+
}
136+
137+
[TestMethod]
138+
public void NodeExample_WithInvalidModel_RedirectsToIndex()
139+
{
140+
//Arrange
141+
_controller.ModelState.AddModelError("TestError", "Test validation error");
142+
143+
//Act
144+
var result = _controller.NodeExample();
145+
146+
//Assert
147+
Assert.IsNotNull(result);
148+
Assert.IsInstanceOfType(result, typeof(RedirectToActionResult));
149+
var redirectResult = (RedirectToActionResult)result;
150+
Assert.AreEqual("Index", redirectResult.ActionName);
151+
}
152+
153+
[TestMethod]
154+
public void PythonExample_WithInvalidModel_RedirectsToIndex()
155+
{
156+
//Arrange
157+
_controller.ModelState.AddModelError("TestError", "Test validation error");
158+
159+
//Act
160+
var result = _controller.PythonExample();
161+
162+
//Assert
163+
Assert.IsNotNull(result);
164+
Assert.IsInstanceOfType(result, typeof(RedirectToActionResult));
165+
var redirectResult = (RedirectToActionResult)result;
166+
Assert.AreEqual("Index", redirectResult.ActionName);
167+
}
168+
}
169+
}

PipelinesToActions/PipelinesToActions.Tests/PipelinesToActions.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<PrivateAssets>all</PrivateAssets>
1313
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1414
</PackageReference>
15+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.0" />
1516
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
1617
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
1718
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />

PipelinesToActions/PipelinesToActions/Controllers/HomeController.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ public IActionResult Index()
3333
[HttpPost]
3434
public IActionResult Index(string txtAzurePipelinesYAML, bool chkAddWorkflowDispatch)
3535
{
36+
if (!ModelState.IsValid)
37+
{
38+
// If model state is invalid, return to the form with an empty result
39+
ConversionResponse emptyResult = new ConversionResponse();
40+
return View(model: (emptyResult, chkAddWorkflowDispatch));
41+
}
42+
3643
(ConversionResponse, bool) gitHubResult = ProcessConversion(txtAzurePipelinesYAML, chkAddWorkflowDispatch);
3744

3845
return View(model: gitHubResult);
@@ -116,6 +123,11 @@ public IActionResult Index(string txtAzurePipelinesYAML, bool chkAddWorkflowDisp
116123
[HttpPost]
117124
public IActionResult DotNetFrameworkDesktopExample(bool chkAddWorkflowDispatch = false)
118125
{
126+
if (!ModelState.IsValid)
127+
{
128+
return RedirectToAction(nameof(Index));
129+
}
130+
119131
string yaml = Examples.DotNetFrameworkDesktopExample();
120132
(ConversionResponse, bool) gitHubResult = ProcessConversion(yaml, chkAddWorkflowDispatch);
121133
return View(viewName: "Index", model: gitHubResult);
@@ -125,6 +137,11 @@ public IActionResult DotNetFrameworkDesktopExample(bool chkAddWorkflowDispatch =
125137
[HttpPost]
126138
public IActionResult ASPDotNetFrameworkExample(bool chkAddWorkflowDispatch = false)
127139
{
140+
if (!ModelState.IsValid)
141+
{
142+
return RedirectToAction(nameof(Index));
143+
}
144+
128145
string yaml = Examples.ASPDotNetFrameworkExample();
129146
(ConversionResponse, bool) gitHubResult = ProcessConversion(yaml, chkAddWorkflowDispatch);
130147
return View(viewName: "Index", model: gitHubResult);
@@ -134,6 +151,11 @@ public IActionResult ASPDotNetFrameworkExample(bool chkAddWorkflowDispatch = fal
134151
[HttpPost]
135152
public IActionResult NodeExample(bool chkAddWorkflowDispatch = false)
136153
{
154+
if (!ModelState.IsValid)
155+
{
156+
return RedirectToAction(nameof(Index));
157+
}
158+
137159
string yaml = Examples.NodeExample();
138160
(ConversionResponse, bool) gitHubResult = ProcessConversion(yaml, chkAddWorkflowDispatch);
139161
return View(viewName: "Index", model: gitHubResult);
@@ -143,6 +165,11 @@ public IActionResult NodeExample(bool chkAddWorkflowDispatch = false)
143165
[HttpPost]
144166
public IActionResult CIExample(bool chkAddWorkflowDispatch = false)
145167
{
168+
if (!ModelState.IsValid)
169+
{
170+
return RedirectToAction(nameof(Index));
171+
}
172+
146173
string yaml = Examples.CIExample();
147174
(ConversionResponse, bool) gitHubResult = ProcessConversion(yaml, chkAddWorkflowDispatch);
148175
return View(viewName: "Index", model: gitHubResult);
@@ -152,6 +179,11 @@ public IActionResult CIExample(bool chkAddWorkflowDispatch = false)
152179
[HttpPost]
153180
public IActionResult CDExample(bool chkAddWorkflowDispatch = false)
154181
{
182+
if (!ModelState.IsValid)
183+
{
184+
return RedirectToAction(nameof(Index));
185+
}
186+
155187
string yaml = Examples.CDExample();
156188
(ConversionResponse, bool) gitHubResult = ProcessConversion(yaml, chkAddWorkflowDispatch);
157189
return View(viewName: "Index", model: gitHubResult);
@@ -161,6 +193,11 @@ public IActionResult CDExample(bool chkAddWorkflowDispatch = false)
161193
[HttpPost]
162194
public IActionResult CICDExample(bool chkAddWorkflowDispatch = false)
163195
{
196+
if (!ModelState.IsValid)
197+
{
198+
return RedirectToAction(nameof(Index));
199+
}
200+
164201
string yaml = Examples.CICDExample();
165202
(ConversionResponse, bool) gitHubResult = ProcessConversion(yaml, chkAddWorkflowDispatch);
166203
return View(viewName: "Index", model: gitHubResult);
@@ -170,6 +207,11 @@ public IActionResult CICDExample(bool chkAddWorkflowDispatch = false)
170207
[HttpPost]
171208
public IActionResult DockerExample(bool chkAddWorkflowDispatch = false)
172209
{
210+
if (!ModelState.IsValid)
211+
{
212+
return RedirectToAction(nameof(Index));
213+
}
214+
173215
string yaml = Examples.DockerExample();
174216
(ConversionResponse, bool) gitHubResult = ProcessConversion(yaml, chkAddWorkflowDispatch);
175217
return View(viewName: "Index", model: gitHubResult);
@@ -179,6 +221,11 @@ public IActionResult DockerExample(bool chkAddWorkflowDispatch = false)
179221
[HttpPost]
180222
public IActionResult AntExample(bool chkAddWorkflowDispatch = false)
181223
{
224+
if (!ModelState.IsValid)
225+
{
226+
return RedirectToAction(nameof(Index));
227+
}
228+
182229
string yaml = Examples.AntExample();
183230
(ConversionResponse, bool) gitHubResult = ProcessConversion(yaml, chkAddWorkflowDispatch);
184231
return View(viewName: "Index", model: gitHubResult);
@@ -188,6 +235,11 @@ public IActionResult AntExample(bool chkAddWorkflowDispatch = false)
188235
[HttpPost]
189236
public IActionResult GradleExample(bool chkAddWorkflowDispatch = false)
190237
{
238+
if (!ModelState.IsValid)
239+
{
240+
return RedirectToAction(nameof(Index));
241+
}
242+
191243
string yaml = Examples.GradleExample();
192244
(ConversionResponse, bool) gitHubResult = ProcessConversion(yaml, chkAddWorkflowDispatch);
193245
return View(viewName: "Index", model: gitHubResult);
@@ -197,6 +249,11 @@ public IActionResult GradleExample(bool chkAddWorkflowDispatch = false)
197249
[HttpPost]
198250
public IActionResult MavenExample(bool chkAddWorkflowDispatch = false)
199251
{
252+
if (!ModelState.IsValid)
253+
{
254+
return RedirectToAction(nameof(Index));
255+
}
256+
200257
string yaml = Examples.MavenExample();
201258
(ConversionResponse, bool) gitHubResult = ProcessConversion(yaml, chkAddWorkflowDispatch);
202259
return View(viewName: "Index", model: gitHubResult);
@@ -206,6 +263,11 @@ public IActionResult MavenExample(bool chkAddWorkflowDispatch = false)
206263
[HttpPost]
207264
public IActionResult PythonExample(bool chkAddWorkflowDispatch = false)
208265
{
266+
if (!ModelState.IsValid)
267+
{
268+
return RedirectToAction(nameof(Index));
269+
}
270+
209271
string yaml = Examples.PythonExample();
210272
(ConversionResponse, bool) gitHubResult = ProcessConversion(yaml, chkAddWorkflowDispatch);
211273
return View(viewName: "Index", model: gitHubResult);
@@ -215,6 +277,11 @@ public IActionResult PythonExample(bool chkAddWorkflowDispatch = false)
215277
[HttpPost]
216278
public IActionResult RubyExample(bool chkAddWorkflowDispatch = false)
217279
{
280+
if (!ModelState.IsValid)
281+
{
282+
return RedirectToAction(nameof(Index));
283+
}
284+
218285
string yaml = Examples.RubyExample();
219286
(ConversionResponse, bool) gitHubResult = ProcessConversion(yaml, chkAddWorkflowDispatch);
220287
return View(viewName: "Index", model: gitHubResult);

0 commit comments

Comments
 (0)