This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Develop

It's not possible to provide a recipe on how to write secure code in a few simple and concise articles. However, it doesn't mean it's an insurmountable task reserved only for security experts! Everyone developing software at Bouvet should be familiar with OWASP Top 10, which is a good starting point to understand the challenges faced by developers.

The articles you find under the topic Develop on this page will focus on what happens on a developer's workstation, apart from actual code. They won't delve into specific attack methods or how to defend against them. You will also find advice on how a team should work to prevent, identify, and fix vulnerable code.

1 - Development Environment, Tools, and Build Environment

The environments and tools we work with are essential for the project. The team should standardize tool usage, document configuration, and reduce risk in the development and build chain.

The development environment and build environment are among the most important aspects of a development project. These environments are both a productivity factor and a critical part of the attack surface. Some teams develop and build locally on their own laptops, while others use dedicated development and build environments based on cloud services or on-premises development servers. Regardless of the solution, some core principles should always be in place: clear ownership, documented choices, standardized setups, and regular follow-up.

Tools

A typical team uses:

  • an IDE and extensions
  • version control, typically git
  • AI-based code assistants
  • a CI/CD tool
  • other services the team operates or consumes, such as messaging services, file transfer, and AI tools

These tools have significant impact on the security and quality of the delivery. The team should therefore clarify which tools are permitted, how they are configured, and how changes or updates are handled where relevant.

Extensions and plugins

Many tools allow the use of extensions to provide third-party functionality not built into the tool by the vendor. This makes it possible to use standard tools like VS Code, customized for each project with the extensions relevant to the technology choices and workflow.

Extensions are, however, a major attack vector. It is important to have some guiding principles to help the team assess their quality. Vendors such as KOI offer solutions to help with this, but in the absence of dedicated tooling, some basic principles can help:

  • Do not adopt extensions uncritically. Assess history, origin, and update frequency.
  • Does the extension have a history of vulnerabilities or similar issues? If so, how were they handled?
  • Keep track of the extensions you use, review changes before updating. Also consider waiting a few days after an update becomes available before applying it.

As with everything on the internet: stars, ratings, and reviews cannot be trusted and should not be the sole basis for deciding whether to adopt something.

AI in the development environment

Generative AI tools can increase development velocity, but also introduce new risks. Use of AI must be agreed with the customer before tools are adopted.

Key points to clarify:

  • which AI services are approved
  • which data may be used in prompts and as context
  • how the vendor stores and reuses data
  • how AI usage is documented within the team

A simple rule of thumb: treat code, architecture, logs, and configuration as sensitive information until otherwise agreed.

Version control

Version control provides traceability and control over changes, but the security benefit depends on how the workflow is used in practice. Repositories and branching strategy should be configured to the project’s needs.

Source code is a critical asset in the project and should be considered in the context of disaster recovery and backups.

CI/CD as a security control

A good CI/CD system (Continuous Integration / Continuous Deployment) can significantly increase security by automating quality and security controls early in the chain.

Relevant controls include:

Several controls require additional tools and licenses. Be particularly mindful of tools that send code to external services for analysis. It is also important to be aware that actions used as part of CI/CD are an attack vector and must be assessed and treated like any other component in the supply chain.

Supply chain security in practice

Modern software is built from many dependencies. The team must therefore have control over the entire supply chain, not only its own code. Remember that the supply chain covers all dependencies the team has, not just packages and libraries.

The team should ensure you have control over dependencies and the risks of using them. Common examples of components to have an overview of:

  • IDEs and other code editors
  • AI tools
  • Extensions/plugins
  • Support tools such as Figma, database modelling tools, or similar
  • Cloud components
  • Version control
  • CI/CD — including actions that are executed
  • Frameworks, packages, and libraries

The team may not control everything, but should still have a considered view of how the lifecycle of these components is managed and how change processes work in practice.

Development environment and build environment

One of the major risk elements in all development is if:

  • unauthorized persons can access a machine used for development, building, or production
  • the machine can access the internet without significant restrictions
  • such machines lack monitoring

All developers have dependencies on libraries and packages from open-source ecosystems. Some of these can be compromised or spoofed, thereby introducing backdoors or data exfiltration.

Important measures to reduce risk include:

  • blocking all incoming network traffic
  • blocking all outgoing network traffic
  • only opening the accesses that are absolutely necessary
  • avoiding the use of general machines used for web browsing, office activities, and similar for development

It is not always possible to justify dedicated machines for development from a cost/benefit perspective. This should nonetheless be considered deliberately, so the team understands how the choice affects the risk profile.

Minimum that should be documented

The following should at minimum be documented and maintained:

  • approved tools and versions
  • requirements for the setup of development machines and build environments
  • network accesses and their justifications
  • logging and monitoring of the environments
  • routines for patching and upgrades
  • responsibility for managing tools and environments

Further reading

2 - Version Control

Version control is an essential tool in all development projects, but how do you use it effectively and what should you consider?

Git is today the standard in most projects run at Bouvet, but if you end up in a project that uses something else many of the principles will still be transferable. Git itself is the version control tool that manages history and versioning, while GitHub handles the surrounding services such as CI/CD, security testing and similar. Azure DevOps offers many of the same features described below; even though GitHub is used as an example you should assume the same applies for Azure DevOps.

Source code is critical

Remember that source code is part of the project, and must be considered in relation to disaster recovery and backups!

.gitignore

The first thing you should do in a new or existing repository is check that there is a .gitignore file. This is used to exclude files from git so that they can live in your local folders without being committed to git. There are ready-made templates for most programming languages that ensure build artifacts, .env files and other items that may contain sensitive information are not committed to git.

Do not rely on remembering not to include files when you commit; it is easy to miss when you are fixing something in passing. .gitignore is checked in like other files so that others in the team can benefit from it. Just remember that it only applies to new files; if you also want to exclude files that already exist in the repo they must first be removed from the git history.

Secrets and history

Many people test locally on their own machine while developing and often have keys or similar hardcoded while testing. It happens relatively often that such secrets are forgotten and committed along with other code. If this happens the secret must be invalidated. Keys, passwords, certificates and the like must be rotated so that what ended up in git is no longer valid.

Overwriting in git is not enough; the history will still be available to everyone with access. Everyone who has cloned the repo will also have a copy of the history; although there is a technical possibility to rewrite history this can in many cases be difficult or impossible.

Handling secrets

Use password vaults to manage secrets: You reference the secret, but can separate environments by pointing to different vaults!

Secret scanning

Consider setting up workflows that scan the repo for secrets, for example using Trufflehog or similar so that merges are blocked if secrets are found. It will not be a perfect solution but it will alert on findings and help prevent secrets from reaching main.

GitHub also supports the use of various actions that can perform tasks on checked-in code such as CI/CD, security testing and much more.

Access control

Access control is the first item on the agenda in a new project. Who should have access to the repo - individual users, the organization, or should it be public? Be aware that we are only allowed to create private repos by default, and if it should be opened up this must be done via a BSD case.

Code signing

Git by itself has no form of access control; anyone can configure their git client and call themselves Pelle Pellesen with the email pelle@bouvet.no@, and that is normally what will be shown in the history. Depending on the project and any customer requirements one should consider using signed commits to ensure that all commits are signed cryptographically.

Signed code
The image above shows, for example, how a commit to this very article appears when it is signed.

Git hooks

Git hooks are scripts that typically run client side and are triggered by various actions so that you can run linting, code scanning and other checks to ensure the quality of what is being developed. Although there are both client side and server side hooks it is important to be aware that GitHub does not support running server side hooks.

Hooks normally live under .git; since this is not version controlled scripts should be placed under other folders so they can be shared across the team. Remember that hooks must be set up by each developer, so do not rely solely on them for critical checks!

Workflows

Workflows run on GitHub and can be triggered by various events such as pushing code, in connection with pull requests, tags and more. A workflow can consist of a series of different actions that run against the codebase. Workflows are often used to validate, test, build or deploy; for a pull request you can for example run a number of tests to ensure that changes do not break existing functionality, before running a separate workflow after merging to main that builds and deploys the code to the desired target.

Be aware that workflows spin up a VM or a container in the background where the different actions run, and that many of these in reality have dependencies on third parties. Do not run actions uncritically, but assess the risk and benefit they provide like any other dependency.

Branching strategy

Git supports different branching models depending on how complex a workflow you need. Regardless of which you choose it is important to consider how the workflow will be if vulnerabilities or critical issues suddenly appear that must be fixed regardless of what else you are working on.

A typical approach is to operate with a production branch, often main or master. This should be protected so that all changes are made in separate feature branches which are then merged in via pull requests with the accompanying review from others in the team. The production branch then becomes the basis for all deployments going forward.

Trunk-based merging

There are other and more complex approaches as well, for example with separate branches plus tagging of versions. This is especially useful if you maintain multiple versions across environments, need the ability for hotfixes or similar:

More advanced merging

In this example all developers work in their own feature branches against the develop branch which is protected from direct changes. This is deployed to the dev environment to verify that everything works as it should.

When the team is satisfied with the state of develop it is merged to test via a pipeline that handles tagging of version numbers automatically. This pipeline can require approvals to run so that one person starts it and another must approve. The test branch is deployed to the test environment, and when the customer is happy with what has been delivered it is merged to the prod branch in the same way as to test. For both test and prod we use the version number as part of the branch name so that we can have Test/v1 and Test/v2 branches which correspond to Prod/v1 and Prod/v2.

If there is a need for hotfixing against prod this can for example be done against the relevant prod branch so that critical bugs can be fixed quickly and then the hotfix can be brought back into dev.

More information

3 - Data Validation

Data validation reduces both security risk and quality issues. Never trust data blindly, whether it comes from users, integrations, or AI pipelines.

Data validation is fundamental in all development projects. The goal is to ensure that data has the expected structure, content, and quality before it is used further in the system.

Incorrect or malicious data can lead to:

  • security vulnerabilities such as injection and XSS
  • functional errors and unstable operations
  • poor decision basis in reporting and analysis
  • reduced quality in AI models and evaluations

OWASP Top 10 has consistently shown that inadequate input validation is a recurring cause of serious vulnerabilities. The main rule is simple: data should be validated at the trust boundary, regardless of the source.

Core principles

Data validation should be built into architecture and code from the start, not added as an afterthought.

  • Validate early: Stop invalid data as close to the entry point as possible.
  • Validate explicitly: Define what is valid instead of trying to block everything that looks dangerous.
  • Validate in layers: Combine client-side validation, server-side validation, and validation in messaging and integration layers.
  • Fail closed: If data cannot be verified, reject it or quarantine it.
  • Log anomalies: Log validation errors with enough context for debugging, without leaking sensitive data.

Minimum controls that should be in place in typical development projects:

  • Schema and contracts: Use clear schemas (such as JSON Schema, OpenAPI, or equivalent) between services to catch contract violations early.
  • Type and format validation: Validate data type, length, allowed characters, ranges, date formats, and encoding.
  • Semantic validation: Check rules not captured by type alone, such as valid state transitions or dependencies between fields.
  • Context-aware output escaping: Escape data correctly before rendering in HTML, URL, JavaScript, or SQL contexts.
  • File validation: Verify MIME type, file signature, file size, and allowed file types. Scan uploaded files for malware.
  • Isolated processing of risky data: Consider sandboxing for parsing and processing potentially dangerous files.
  • Browser protection: Use Content Security Policy (CSP) to reduce the impact of XSS.

Data quality and data preparation for AI systems

For solutions using AI, the controls above must also be extended. Data validation is both a security measure and a quality measure.

Controls that should be explicitly defined:

  • Input validation for model calls: Validate format, size, language, structure, and allowed fields before data goes to the model or embedding pipeline.
  • Data quality for AI: Check for missing values, duplicates, outliers, labeling errors, and skews that affect model performance.
  • Data preparation: Make preprocessing deterministic, testable, and versioned so that transformations can be reproduced.
  • Dataset hygiene for evaluation: Keep training, validation, and test sets separate and avoid data leakage between them.
  • Contamination: Ensure evaluation sets are not polluted by production data, prompt history, or manual corrections from operations.
  • Traceability: Document data sources, transformations, dataset versions, and evaluation basis.

Practical implementation in teams

Data validation works best when it is a regular part of the delivery process.

  • Define validation rules as code and version them.
  • Include validation tests in CI/CD for contracts, schemas, and critical transformations.
  • Monitor validation failures in production to catch data drift and integration breaks early.
  • Conduct regular reviews of rules when data sources, APIs, or models change.

Further reading

4 - Security Practices

There is much to consider when building a secure solution, and an important starting point is the OWASP Top 10.

There are many different types of vulnerabilities and weaknesses to consider when developing new applications. The Open Worldwide Application Security Project (OWASP) publishes a prioritized list of common application vulnerabilities that many teams use as a practical starting point.

OWASP Top 10 is not a complete security standard, but a useful prioritization of what typically goes wrong in real-world projects.

Current points on the OWASP Top 10

As of today, the latest published version is OWASP Top 10:2025:

  1. A01:2025 - Broken Access Control
  2. A02:2025 - Security Misconfiguration
  3. A03:2025 - Software Supply Chain Failures
  4. A04:2025 - Cryptographic Failures
  5. A05:2025 - Injection
  6. A06:2025 - Insecure Design
  7. A07:2025 - Authentication Failures
  8. A08:2025 - Software or Data Integrity Failures
  9. A09:2025 - Security Logging and Alerting Failures
  10. A10:2025 - Mishandling of Exceptional Conditions

From Top 10 to practice in the team

If the team has no processes around secure development, this is a good place to start. To have a real effect, the points must be translated into concrete controls in the delivery process.

For a more practical walkthrough of how such measures can be built into product development, see also Slik etablerer bedriften gode sikkerhetstiltak i produktutviklingen.

A minimum usually means:

  • defining security requirements early and linking them to architecture and user stories
  • conducting code reviews and tests that include security-relevant scenarios
  • using automated controls in CI/CD for dependencies, configuration, and secrets
  • ensuring traceability of changes, builds, and deployments
  • updating the threat model and risk assessment on significant changes

For teams with greater maturity in application security, OWASP ASVS is a natural next step. ASVS goes deeper than Top 10 and can be used as a verification basis at different maturity levels.

What about AI?

OWASP Top 10 for web applications still covers many fundamental problems, including in solutions that use AI. At the same time, AI introduces its own threats that are not fully covered by this list alone.

For projects with AI components, the team should also use:

Details on safe use of AI in the development process are described in Use of Artificial Intelligence. Risks around dependencies, artifacts, and the build chain are described in Software Supply Chain.

OWASP publishes much more in addition, including other Top 10 lists and what they call “cheat sheets”, which provide more detailed information on specific security topics.

Further reading

5 - Software Supply Chain

Everyone who develops software uses third-party packages. All third-party packages represent code written by others and pose a risk to the delivery if we do not have control over what we use and an overview of weaknesses and risks associated with them.

When we build software, we depend on a multitude of different third-party packages from ecosystems like npmjs, nuget, PyPi, or others. This is code “written by others,” where we rely on these producing packages without major weaknesses and without malicious intent. Sources such as Gartner, Sonatype, and Snyk have estimated that as much as 90% of the code in a typical development project may consist of such third-party packages, so the risk is significant if we do not have control.

An important tool is to conduct a so-called Source Composition Analysis - SCA to get an overview of the packages we use, both direct and indirect (transient) dependencies, as well as the risks associated with them.

Security Risks

There are several security risks associated with third-party packages, but unfortunately, the ecosystems for such packages are not very proactive on the security front. Infected packages that are identified are removed, but for those using automation to keep dependencies updated to the latest version, this is often too late.

Vulnerabilities

They may contain known vulnerabilities (CVE) that can be exploited. Some of these can be mitigated by upgrading the packages to the latest version, while others have more fundamental challenges that can be mitigated in other ways. Sometimes, some CVEs are created for weaknesses that are technically a vulnerability but are ‘by design’. An example of this is the Python package Pandas which among others has the vulnerability CVE-2020-13091. This allows users to deserialize files without checking where they come from - this has been registered as a vulnerability as it could be exploited if other measures are not in place.

Malicious Code

The packages we download and use run in the same context as our code and can use the same resources and accesses it has. The content of the packages we consume is entirely beyond our control, and if one of them contains malicious software (malware), the consequences can be significant.

In recent years, there have also been examples of packages often referred to as “protestware,” as they contain logic that performs actions if users can be geolocated to specific countries. The package “peacenotwar” is one such example, which was added as a dependency to node-ipc, a widely used package. This resulted in peacenotwar also being downloaded by many users; those geolocated to Russia or Belarus had data deleted and were subjected to DOS attacks via the package.

Whether something is meant as malware or protestware, the consequences are serious, and all development projects should have measures in place to limit risk and consequences in case an incident occurs.

Use of CDN

Content-delivery-networks to distribute Javascript libraries have been used by many as an easy way to include these in the code without having to include them in the build or deploy process. The idea is good, but you then have a direct dependency on a source you have no control over, which can be abused to spread malware.

License Model

There are many different license models for available components; some are completely free and impose no requirements for use, while others like AGPL and GPL can have significant consequences for what you develop. Most ecosystems also allow the use of proprietary licenses, which can limit what you can use a package for without a valid license. Some have specific requirements in the license, others are free for personal use but require you to purchase a license if used commercially.

How Do We Secure Ourselves?

To protect the system against these threats, there are several effective measures we can take. Many of these are already covered elsewhere, but there are still some threats unique to external packages.

Threat Model and Complexity

In many cases, we depend on many packages that we do not use directly, so-called transitive dependencies. Each package in a solution represents an increase in complexity and attack surface, increasing the chance that something will go wrong at a later stage.

An important question everyone must ask is “do we need this package?” What is the cost of creating the functionality ourselves compared to the risk and complexity associated with adding it?

Monitoring Dependencies

An important tool is analysis tools that help us control vulnerabilities and risks in our dependencies. There are many actors on the market, ranging from simple ones like GitHub’s Advanced Security with Dependabot to more advanced ones like Snyk and Sonatype. In many cases, the data basis for the solution makes the big difference, but there are also a number of useful features to consider:

  • Policy Management - Sonatype tags all packages with metadata, allowing you to define policies that dictate what can be used or not.
  • Automatic Remediation - Tools like dependabot (GitHub) automatically upgrade packages when vulnerabilities are discovered, and updates are available.
  • Integrations and Notifications - Many tools like Snyk SCA can integrate into IDEs and CICD.
  • Risk Overview - Many solutions provide an overall view where you can see the total risk picture for individual applications or larger parts of the portfolio.

Maturity

This can be difficult to quantify, but how active is the community around a package? Is it maintained by individuals, groups of developers, or does it have economic or other support from a company?

How likely is it that the package will still be maintained in, for example, 5 years? How is the history in terms of vulnerabilities and quality; is there an active community reporting weaknesses that are then addressed, or do reported issues linger in limbo for extended periods? Tools like libraries.io and Security Scorecard can be useful for finding out more.

AI dependencies are also part of the supply chain

For systems that use AI, dependencies are not limited to libraries from npm, nuget, or PyPI. Models, tokenizers, embeddings, evaluation datasets, and inference containers must be treated as third-party dependencies with the same requirements for control.

This means the team should:

  • use approved sources and avoid downloading artifacts directly from unknown mirrors
  • pin versions and refer to immutable identifiers (a tag alone is not sufficient)
  • document which AI artifacts are actually included in a release
  • assess license and terms of use for models and datasets, not only code

Maturity

This can be difficult to quantify, but how active is the community around a package? Is it maintained by individuals, groups of developers, or does it have economic or other support from a company?

How likely is it that the package will still be maintained in, for example, 5 years? How is the history in terms of vulnerabilities and quality; is there an active community reporting weaknesses that are then addressed, or do reported issues linger in limbo for extended periods? Tools like libraries.io and Security Scorecard can be useful for finding out more.

Pinning Versions

One attack vector is when malicious actors take over popular packages and publish their own version with malicious content. If we have build or deploy processes that fetch the latest version of dependencies each time, they will automatically fetch the infected package. A measure here can be to pin the package versions we use, for example, in package-lock.json or similar.

A related measure is to introduce a cooldown or minimum-age requirement for new versions, so updates are not adopted until they are a certain number of days old. The purpose is to reduce the risk of pulling in fresh malware or compromised packages before the ecosystem has time to detect the problem and remove or flag the version. This can be especially useful for automated update flows, but it should be applied based on risk so critical security fixes can still be brought in quickly through a controlled exception process.

Provenance and policy-gates in CI/CD

In addition to pinning, the build pipeline should verify the origin and integrity of dependencies before they are used. The team should establish policy-gates in CI/CD that can stop a merge or deployment when critical issues are detected, such as severe vulnerabilities, disallowed licenses, or dependencies from unapproved sources.

Internal mirrors and forks of critical dependencies

For especially critical dependencies, it may be appropriate to host artifacts in internal registries or maintain forks of packages, actions, and other build components. This reduces exposure to incidents at third parties, but also increases internal maintenance responsibilities.

This should be used selectively, typically when the dependency is business-critical, has high impact if compromised, and the team actually has the capacity to maintain it over time.

If you choose this approach, you should at minimum have:

  • clear ownership for maintenance, patching, and update frequency
  • routines for importing and verifying new upstream versions
  • traceability on what is an internal copy/fork and what is the original source
  • the same requirements for scanning, license control, and review as for other dependencies

Use of SBOM

Software Bill Of Materials (SBOM) is an approach where we generate an overview of all dependencies with versions from our solutions. This overview should be tied to each release so it is clear which dependencies were actually in use at a given point in time.

For AI solutions, the overview should also include relevant AI artifacts such as models, model digests, tokenizers, and key dataset references where relevant. There are several more or less standardized file formats for SBOM, and they can be archived or included in other solutions to simplify central monitoring.

More Information

6 - Documentation

Good documentation is essential for continuity, traceability, and security. This applies to the choices made, the threats assessed, and the system’s actual behavior — including in AI systems.

Documentation is often deprioritized during delivery, but insufficient documentation creates real risk. Source code describes how something works, but not why certain choices were made, what threats were assessed, or what assumptions the solution depends on.

Security context that only exists in the heads of individuals is a vulnerability. Security mechanisms that are not documented risk being removed by someone who does not understand the consequences. Threat models that are not written down cannot be maintained or revised.

Good documentation gives the team the ability to assess risk over time, not only at the point of completion.

Documentation solution

The team must choose a solution that is actually used. Documentation that lives in a tool no one opens provides little value.

Popular approaches include docs-as-code, where documentation is written in Markdown and versioned in git alongside the source code. This lowers the barrier for updates and provides full history of changes.

Regardless of the solution: remember that good documentation can be just as sensitive as the source code. Threat models with open findings, architecture descriptions, and security configurations must be protected on par with the system they describe.

What should be documented

What needs to be documented will vary with the project’s size and criticality, but the following should always be in place:

  • System design: Diagrams and drawings showing infrastructure, data flows, IAM, and integrations — at a level that makes it possible to review the architectural decisions made.
  • Threat model: Which threats have been assessed, which mitigations have been introduced, and what residual risk has been accepted. The threat model must be maintained and updated when changes are made.
  • Security configuration: Security-critical configurations must be documented so they are not inadvertently changed or omitted during upgrades and re-provisioning.
  • Incident log and deviations: Security incidents and deviations should be logged and documented, including what was done and what lessons were identified.
  • Critical dependencies: External services, third-party libraries, and integrations that the solution depends on for correct security function.

The criticality of the solution guides the required depth: a system with 24/7 requirements and high impact from downtime needs more detailed documentation than an internal low-risk system.

Documentation of AI systems

AI systems impose documentation requirements that traditional technical documentation does not fully cover. Models, training data, and evaluation results change over time and affect system behavior in ways that are not always visible in code alone.

The following should be documented for solutions with AI components:

  • System purpose and intended use: What the system is designed to do, who it is intended for, and which usage scenarios are out of scope.
  • Model description: Which model is used, who developed it, which version and architecture. If the model is trained in-house: the training setup and methodology.
  • Training data and data provenance: A description of which data the model was trained on, including sources, licensing, any restrictions, and known weaknesses in the data foundation.
  • Evaluation results: Documented measurements of performance, accuracy, robustness, and any bias findings. The evaluation basis and metrics should be described so that results can be reproduced and compared over time.
  • Risk assessment: Which risks have been identified related to the model’s behavior, and which mitigations have been introduced. This includes risk of misclassification, hallucinations, unfair treatment, and misuse.
  • Human oversight and limits on autonomy: Where and how humans are involved in decisions, and what constraints are placed on what the system can do autonomously.
  • Change management: How model versions are managed, tested, and rolled out. Changes to the model, training data, or configuration should be tracked and justified.

Minimum template for AI documentation

To make this practical and consistent, the team can use a simple minimum template per model version or significant change:

  • identifier: model name, version, date, and responsible party
  • purpose and scope: what the model is approved for, and what is out of scope
  • data foundation: sources, constraints, license, and known weaknesses
  • evaluation: test sets, metrics, results, and baseline comparison
  • risk and mitigations: key findings, compensating controls, and residual risk
  • approval: decision, approver, and any conditions for production use
  • observability: what is logged/monitored in operation and which thresholds trigger follow-up

The template does not need to be comprehensive, but it should be consistent each time so that comparison, review, and reuse are straightforward.

This documentation is necessary both for internal control and to demonstrate compliance to customers, regulatory authorities, and other stakeholders.

Further reading

7 - Secrets

All development projects require secrets such as connection strings, identities, passwords, certificates, and much more. These must be stored securely, and we must ensure that we use them in a secure context with the right support tools and processes.

Secrets in development projects are represented as connection strings, passwords, keys, certificates, and any other sensitive information we do not want others to know about.

There are many different approaches to handling these, and this has evolved as new services emerge. For cloud solutions, services like Azure Key Vault and similar have almost become standard, as these services handle multiple aspects of how to use and manage secrets.

Basic Principles for Handling Secrets

  • Secrets should never be hard-coded or checked into version control systems
  • Verify compliance by scanning the code - reject commits with secrets and rotate them immediately!
  • Keep track of which secrets you have and what access they provide
  • Limit the lifetime of secrets - no secrets should live more than a year, passwords and keys much shorter
  • Team members should not always have access to all secrets; elevate or provide access as needed

Secrets and AI tools

When AI tools are used in the development process, new ways for secrets to be exposed arise.

Secrets in prompts and context

AI tools such as Copilot, Claude, or others often copy larger parts of the codebase to understand the context. If the repository contains hard-coded secrets, example configuration files with real values, or test data with passwords, these may be sent to the AI service’s servers. If this happens, there is a good chance it constitutes a breach of the agreement with the customer. There is also always a risk that the AI tool may attempt to connect using the credentials. Without other security barriers in place, this could in the worst case result in data being modified or otherwise affected.

Main rule

Never paste secrets, tokens, or real credentials into a prompt, even when debugging or asking for help.

AI-generated code that logs secrets

It is not uncommon for AI tools to hallucinate or produce code that does not follow best practices. You must be especially vigilant in catching cases where AI-generated code stores sensitive information such as secrets, database queries, or configuration files in logs.

Before running AI-generated code, check carefully:

  • Is sensitive data being logged or written to stdout?
  • Are secrets placed in cache or temporary files?
  • Are secrets surfaced as part of error messages?

Reduced access and exclusions

Some AI tools such as GitHub Copilot offer content exclusion, allowing you to exclude folders or file types from being read by the tool. Use this to prevent entire configuration files or test data from being sent to AI services. This should not be the only security barrier preventing AI tools from accessing secrets, but it can provide an extra layer of protection.

Secret rotation

Secrets that have been available to AI tools, logs, checked into source code, or otherwise exposed to others must be rotated. It is not enough to remove the secret or hope that nothing goes wrong; once it has been exposed, you should always assume it can be exploited. This applies to certificates, keys, and other credentials as well.

Cryptography and Hashing

Cryptography and hashing algorithms are complex topics, and it requires significant resources and a lot of competence to build good algorithms that are secure. For this simple reason, you should never create your own, no matter how clever and secure it seems to be.

What you should do instead is:

  • Familiarize yourself with best practices for your programming language, framework, and platform
  • Ensure you are not using vulnerable algorithms such as SHA1, MD5, or DES
  • For cryptography, understand the recommendations for key lengths and usage
Remember

Secrets should never be checked into the source code system!

More Information

8 - Security Testing

In line with other elements associated with team quality, we depend on testing to verify that we have achieved the goal. Security testing is an important part of this, as it allows us to demonstrate that the delivery is not vulnerable to certain attack methods.

Security testing should always be an element of all deliveries. Many associate security testing with penetration testing, but it is much more than that. Some forms of testing can be done automatically as part of CICD, while others are more manual and typically performed against a deployed solution.

Before starting security testing, it is important to understand what it can provide you - there is no single measure that solves all security problems, and no single test method that uncovers all weaknesses. Security testing is also one of the areas that often require specific expertise for the results to be good and/or interpreted correctly.

Warning

The use of tools like nmap and others used in connection with security testing must always be clarified with the owners of infrastructure and network, as it is difficult to distinguish between friendly testing and malicious attacks. This also applies internally at Bouvet; Intern-IT & Security must always be in the loop before you start a security test!

If this is not taken into account, it can have consequences, both for the customer relationship and for technical solutions against the network provider and Microsoft.

Test Environment

When conducting security testing against a running environment, it is important to always clarify this well in advance. Many types of testing can be destructive, so if the environments are not sufficiently separated, you risk affecting other environments than intended.

A good solution, especially if using infrastructure-as-code (IaC), is to have a pipeline that deploys a dedicated environment for security testing. If this is designed into the delivery from the start, it is often easy to set up environments identical to the production environment, where you can also copy databases and possibly run anonymization processes on the data.

Static Application Security Testing (SAST)

Static code analysis is a low-threshold technique that analyzes the code with dependencies to find weaknesses. SAST can be carried out completely automatically, and there are many good tools that can integrate this into CICD so that you can scan as part of the processes here.

SAST only checks the solution being created; it does not reveal anything about the configuration of the runtime environment, network, or other surrounding dependencies. Tools used to perform SAST are language-specific, so it is important to understand which tools provide the best results for the language and possibly framework used.

Dynamic Application Security Testing (DAST)

In contrast to SAST, DAST is a technique where a solution is tested in a running state. This is a language-agnostic test method, where, for example, a web application is tested by testing the frontend solution to find weaknesses. DAST can be automated, but often needs to be run manually for certain types of weaknesses to be tested.

DAST will only cover functionality exposed in the running solution, so if there is code that is part of the solution but not accessible to the DAST tool, it will not be tested either.

Security testing of AI systems

Solutions that use artificial intelligence introduce security testing needs that go beyond traditional SAST and DAST. AI systems must be validated to ensure they behave as expected and do not introduce new vulnerability vectors.

Validating AI models

AI models should be validated for:

  • Accuracy and performance: The model must meet defined quality requirements under expected usage conditions.
  • Bias and fairness: Tests to detect whether the model discriminates based on sensitive attributes.
  • Robustness: Testing how the model behaves with unexpected or adversarial input.
  • Hallucinations: Verifying that the model does not generate false information or unsafe patterns.

Validation of AI models often requires domain-specific expertise and should be part of the responsibility of data and ML teams, not only information security.

Security testing of AI components

In addition to model validation, the overall solution must be tested for:

  • Prompt injection: Tests for whether input can manipulate the AI agent into unexpected behavior.
  • Data exfiltration: Verifying that sensitive information cannot be extracted via prompts or outputs.
  • Trust boundaries: Testing how the system behaves when AI assistance exceeds its defined boundaries.

These threats must be handled with the same rigor as traditional application security testing. For projects with AI components, the team should use OWASP Top 10 for LLM Applications as a basis.

Penetration testing

A penetration test goes deeper than SAST and DAST by testing the system as a whole, including infrastructure, network, and possibly physical security. Where DAST primarily focuses on web applications, a pentest is more comprehensive and often uncovers vulnerabilities that only become visible when several threats are combined.

A penetration test always has an agreed scope that regulates what the testers can do, when they can do it, and which resources can be tested. It is not possible to prove that a solution is secure, only that it is not vulnerable to specific attacks. Penetration testing is therefore particularly useful when the solution has strict security requirements or operates under agreements that require independent verification.

Planning and scope

A pentest requires expertise and should not be carried out alone without relevant experience. As a team, you must ensure that the environment to be tested is clearly identified and that the scope is well defined before testing begins. It must be possible to distinguish an actual attack from an agreed test. If you see signs of activity against an environment that is not part of the test and you have segregated the environments, this should be handled as a real incident.

As part of planning, you must also clarify the customer’s routines for penetration testing. Many organizations have a security operations center (SOC) and/or network operations center (NOC) that continuously monitors infrastructure. These must be informed when the test is scheduled, so that an agreed test does not trigger unnecessary escalation.

In some cases, the customer wants an unannounced test to see whether the attack is detected. This must be explicitly agreed, since a pentest is in practice a simulated attack.

When penetration testing is relevant

In an ideal world, you would conduct a pentest after all major changes, but this is rarely practical. How often the test should be conducted depends on customer requirements, regulatory guidance, and the risk the solution carries. This should be clarified early, not only when you want to order a test.

Follow-up during and after the test

If the test has been announced in advance, it is a good opportunity to monitor logs and alerts to see whether the events are being detected. If the findings in the report can be correlated with what you observe in logs and alerts, it provides a solid basis for improving detection and notification.

After the test, the team will normally receive a report describing what was tested, how the test was conducted, and what findings were made. The findings must be reviewed with the product owner, classified, and added to the backlog. Some findings must be closed quickly, while others can be accepted or planned later if other measures reduce the risk. Security is not an individual responsibility; it is the team’s responsibility to follow up on the requirements that apply to the solution.

AI-focused penetration tests

If the solution includes AI components, the penetration test should also cover:

  • Testing API security around the AI model
  • Change and version control of models and training data
  • Logging and monitoring of the AI system’s behavior
  • Access control to sensitive models and data

For solutions with AI components, the pentest should also consider prompt injection, context manipulation, unauthorized access to model calls, and whether the model can be used to extract sensitive information the system would not otherwise expose.

Further reading