diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..3af396b2 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,94 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '42 2 * * 1' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: csharp + build-mode: none + - language: ruby + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..b150e5e5 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,33 @@ +name: Azure Bicep +on: + workflow_dispatch + +env: + targetEnv: dev + RG: TechExcel_RG + +permissions: + id-token: write + contents: read + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + + - name: Azure login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + enable-AzPSSession: true + + - name: Run ARM deploy + uses: azure/arm-deploy@v1 + with: + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + resourceGroupName: ${{ env.RG }} + template: ./src/InfrastructureAsCode/main.bicep + parameters: environment=${{ env.targetEnv }} diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 00000000..68e50139 --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,90 @@ +# This workflow will build a .NET project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net + +name: .NET Build + +on: + workflow_dispatch: + push: + paths: + - src/Application/** + pull_request: + paths: + - src/Application/** + +# defaults: +# run: +# shell: bash +# working-directory: ./src/Application/src/RazorPagesTestSample + +permissions: + id-token: write + contents: read + +env: + ACR_NAME: 'f3jiczwhl7laimpnpreg.azurecr.io' + CONTAINER_NAME: 'razorpagestestsample' + WEB_APP_NAME: 'f3jiczwhl7lai-dev' + TAG_NAME: ${{ github.sha }} + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Restore dependencies + run: dotnet restore ./src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj + - name: Build + run: dotnet build --no-restore ./src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj + - name: Test + run: dotnet test --verbosity normal ./src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj + + build: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v4 + - name: Azure login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + enable-AzPSSession: true + + - name: Log into registry ACR + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.ACR_NAME }} + username: ${{ secrets.AZURE_CLIENT_ID }} + password: ${{ secrets.AZURE_CLIENT_SECRET }} + + - name: Build and push container image to registry + uses: docker/build-push-action@v2 + with: + context: ./src/Application/src/RazorPagesTestSample + push: true + tags: ${{ env.ACR_NAME }}/${{ env.CONTAINER_NAME }}:${{ env.TAG_NAME }} + file: ./src/Application/src/RazorPagesTestSample/Dockerfile + + deploy: + needs: build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Azure login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + enable-AzPSSession: true + + - name: Deploys to WebApp + uses: Azure/webapps-deploy@v2 + with: + app-name: ${{ env.WEB_APP_NAME }} + images: '${{ env.ACR_NAME }}/${{ env.CONTAINER_NAME }}:${{ env.TAG_NAME }}' \ No newline at end of file diff --git a/.github/workflows/first-workflow.yml b/.github/workflows/first-workflow.yml new file mode 100644 index 00000000..d53701c9 --- /dev/null +++ b/.github/workflows/first-workflow.yml @@ -0,0 +1,25 @@ +name: First Workflow +on: + workflow_dispatch: + +jobs: + job1: + runs-on: ubuntu-latest + env: + COW_SAYS: "Moo" + steps: + - name: Step one + run: echo "Step 1 Cow says ${{ env.COW_SAYS }}!" + - name: Step two + run: echo "Step 2 complete!" + + + job2: + needs: job1 + runs-on: ubuntu-latest + steps: + - name: Cowsays + uses: mscoutermarsh/cowsays-action@master + with: + text: 'Ready for prod--ship it!' + color: 'magenta' diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml deleted file mode 100644 index cedae546..00000000 --- a/.github/workflows/pages.yml +++ /dev/null @@ -1,62 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# Sample workflow for building and deploying a Jekyll site to GitHub Pages -name: Deploy Jekyll site to Pages - -on: - push: - branches: ["main"] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow one concurrent deployment -concurrency: - group: "pages" - cancel-in-progress: true - -jobs: - # Build job - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.1' # Not needed with a .ruby-version file - bundler-cache: true # runs 'bundle install' and caches installed gems automatically - cache-version: 0 # Increment this number if you need to re-download cached gems - - name: Setup Pages - id: pages - uses: actions/configure-pages@v2 - - name: Build with Jekyll - # Outputs to the './_site' directory by default - run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" - env: - JEKYLL_ENV: production - - name: Upload artifact - # Automatically uploads an artifact from the './_site' directory by default - uses: actions/upload-pages-artifact@v1 - - # Deployment job - deploy: - environment: - name: github-pages - url: "${{ steps.deployment.outputs.page_url }}" - runs-on: ubuntu-latest - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v1 diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..fe260178 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,12 @@ +# Specify owners for all files +* @briandenicola + +# Specify owners for specific directories +/src/ @briandenicola + +# Specify owners for specific files +#/scripts/deploy.sh @devops-owner + +# Specify owners for files with specific extensions +#*.js @js-owner +#*.css @css-owner \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index b3c89efc..f13e4f55 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,6 +6,15 @@ Microsoft takes the security of our software products and services seriously, wh If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. +## Supported Versions + +We release patches for security vulnerabilities in the following versions: + +| Version | Supported | +| ------- | ------------------ | +| 1.x | :white_check_mark: | +| 0.x | :x: | + ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** @@ -38,4 +47,5 @@ We prefer all communications to be in English. Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). +We will respond to your report within 48 hours and keep you updated on the progress. We appreciate your help in keeping our project secure. diff --git a/src/Application/src/RazorPagesTestSample/Data/Message.cs b/src/Application/src/RazorPagesTestSample/Data/Message.cs index ea99cbd6..59f24395 100644 --- a/src/Application/src/RazorPagesTestSample/Data/Message.cs +++ b/src/Application/src/RazorPagesTestSample/Data/Message.cs @@ -9,7 +9,7 @@ public class Message [Required] [DataType(DataType.Text)] - [StringLength(200, ErrorMessage = "There's a 200 character limit on messages. Please shorten your message.")] + [StringLength(250, ErrorMessage = "There's a 250 character limit on messages. Please shorten your message.")] public string Text { get; set; } } #endregion diff --git a/src/Application/src/RazorPagesTestSample/Dockerfile b/src/Application/src/RazorPagesTestSample/Dockerfile new file mode 100644 index 00000000..87a924ee --- /dev/null +++ b/src/Application/src/RazorPagesTestSample/Dockerfile @@ -0,0 +1,10 @@ +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env +WORKDIR /src +COPY . . +RUN dotnet restore +RUN dotnet publish -c Release --nologo -o publish/linux + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 +WORKDIR /app +COPY --from=build-env /src/publish/linux . +ENTRYPOINT [ "dotnet", "RazorPagesTestSample.dll" ] \ No newline at end of file diff --git a/src/Application/src/RazorPagesTestSample/Pages/Index.cshtml b/src/Application/src/RazorPagesTestSample/Pages/Index.cshtml index f7645733..c7b4cdb1 100644 --- a/src/Application/src/RazorPagesTestSample/Pages/Index.cshtml +++ b/src/Application/src/RazorPagesTestSample/Pages/Index.cshtml @@ -1,7 +1,7 @@ @page @model IndexModel @{ - ViewData["Title"] = "Munson's Pickles and Preserves Team Messaging System"; + ViewData["Title"] = "Brian's Pickles Team Messaging System"; }

@ViewData["Title"]

diff --git a/src/Application/src/RazorPagesTestSample/Pages/Index.cshtml.cs b/src/Application/src/RazorPagesTestSample/Pages/Index.cshtml.cs index 2e6d94bb..f69b8623 100644 --- a/src/Application/src/RazorPagesTestSample/Pages/Index.cshtml.cs +++ b/src/Application/src/RazorPagesTestSample/Pages/Index.cshtml.cs @@ -94,7 +94,12 @@ public async Task OnPostAnalyzeMessagesAsync() public static void WriteToDirectory(ZipArchiveEntry entry, string destDirectory) { - string destFileName = Path.Combine(destDirectory, entry.FullName); + string destFileName = Path.GetFullPath(Path.Combine(destDirectory, entry.FullName)); + string fullDestDirPath = Path.GetFullPath(destDirectory + Path.DirectorySeparatorChar); + if (!destFileName.StartsWith(fullDestDirPath)) + { + throw new InvalidOperationException("Entry is outside the target dir: " + destFileName); + } entry.ExtractToFile(destFileName); } } diff --git a/src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj b/src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj index 34070413..7d39b2f5 100644 --- a/src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj +++ b/src/Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj @@ -5,8 +5,8 @@ - - + + diff --git a/src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj b/src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj index a66e0a92..76c41e73 100644 --- a/src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj +++ b/src/Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj @@ -8,16 +8,16 @@ - - - - - - + + + + + + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/DataAccessLayerTest.cs b/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/DataAccessLayerTest.cs index 91a91aaa..db305ead 100644 --- a/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/DataAccessLayerTest.cs +++ b/src/Application/tests/RazorPagesTestSample.Tests/UnitTests/DataAccessLayerTest.cs @@ -38,8 +38,9 @@ public async Task AddMessageAsync_MessageIsAdded() // Arrange var recId = 10; var expectedMessage = new Message() { Id = recId, Text = "Message" }; - + var failedMessage = new Message() { Id = recId, Text = "Fake!" }; // Act + await db.AddMessageAsync(expectedMessage); // Assert diff --git a/src/InfrastructureAsCode/main.bicep b/src/InfrastructureAsCode/main.bicep index 6dc69618..00facd2f 100644 --- a/src/InfrastructureAsCode/main.bicep +++ b/src/InfrastructureAsCode/main.bicep @@ -10,8 +10,101 @@ var logAnalyticsName = '${uniqueString(resourceGroup().id)}-mpnp-la' var appInsightsName = '${uniqueString(resourceGroup().id)}-mpnp-ai' var sku = 'S1' var registryName = '${uniqueString(resourceGroup().id)}mpnpreg' +var redisCacheName = '${uniqueString(resourceGroup().id)}-mpnp-cache' + var registrySku = 'Standard' var imageName = 'techexcel/dotnetcoreapp' var startupCommand = '' -// TODO: complete this script +// App Service Plan, a Web App, Application Insights, and Azure Container Registry in your resource group. + +// Generate bicep code to create an Azure Application Insights +resource appInsights 'Microsoft.Insights/components@2020-02-02' = { + name: appInsightsName + location: location + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalytics.id + } +} + +// Generate bicep code to create an Azure Log Analytics Workspace +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { + name: logAnalyticsName + location: location + properties: { + sku: { + name: 'PerGB2018' + } + } +} + +// Generate bicep code to create an Azure Container Registry +resource acr 'Microsoft.ContainerRegistry/registries@2020-11-01-preview' = { + name: registryName + location: location + sku: { + name: registrySku + } +} + + +// Generate bicep code to create an Azure App Service Plan +resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = { + name: appServicePlanName + location: location + kind: 'linux' + properties: { + reserved: true + } + sku: { + name: sku + } +} + +// Generate bicep code to create an Azure Web App using docker image name imageName and startup command of startupCommand + +resource webApp 'Microsoft.Web/sites@2022-09-01' = { + name: webAppName + location: location + properties: { + serverFarmId: appServicePlan.id + siteConfig: { + linuxFxVersion: 'DOCKER|${imageName}' + appCommandLine: startupCommand + appSettings: [ + { + name: 'WEBSITES_PORT' + value: '8080' + } + { + name: 'WEBSITES_ENABLE_APP_SERVICE_STORAGE' + value: 'false' + } + { + name: 'APPINSIGHTS_INSTRUMENTATIONKEY' + value: appInsights.properties.InstrumentationKey + } + { + name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' + value: appInsights.properties.ConnectionString + } + ] + } + } +} + +// Resource: Azure Redis Cache +resource redisCache 'Microsoft.Cache/Redis@2021-06-01' = { + name: redisCacheName + location: location + properties: { + sku: { + name: 'Basic' + family: 'C' + capacity: 0 + } + enableNonSslPort: false + } +} diff --git a/test.txt b/test.txt new file mode 100644 index 00000000..45b983be --- /dev/null +++ b/test.txt @@ -0,0 +1 @@ +hi