Retour sur dependency-check, et son intégration dans Azure DevOps

Cela fait maintenant deux ans que je suis amené à utiliser dependecy-check quotidiennement. Il est temps de vous faire un petit retour sur cet outil, et son intégration dans Azure DevOps.


De quoi s'agit-il ?

OWASP dependency-check sert à identifier les dépendances vulnérables d'une application. Pour atteindre cet objectif, dependency-check doit scanner vos projets, et interroger l'API NVD afin de vérifier celles-ci.


Par où commencer ?

La documentation de dependency-check se trouve ici. Le scan d'une application pouvant être long, on préfèrera confier celui-ci à un serveur de builds.

Même si la documention n'en parle pas beaucoup, il existe une extension pour Azure DevOps. On peut la trouver sur le marketplace de Azure DevOps.

Après installation de celle-ci, il faudra veiller à se procurer une clé pour l'API NVD. Cela se passe ici.


QUID de la configuration ?

La configuration de dedendency-check ne se fait pas au niveau de l'extension, mais sein de chaque pipeline. J'opte habituellement pour les librairies d'Azure DevOps pour stocker la clé de l'API NVD, mais Azure KeyVault fait aussi très bien l'affaire.

Voici un petit exemple de paramétrage de la tâche dependency-check-build-task@6 via YAML:


  - task: dependency-check-build-task@6
    displayName: Dependency Check
    continueOnError: True
    inputs:
      projectName: $(product) $(version)
      scanPath: $(Build.BinariesDirectory)\bin
      additionalArguments: --nvdApiKey ${{ nvdApiKey }}}
      warnOnCVSSViolation: true


La documentation pour Azure DevOps, n'est malheureusement pas très fournie. Mais on peut modifier ou créer sa définition de build via Azure DevOps pour consulter l'ensemble de propriétés de la tâche dependency-check-build-task@6.

Voici quelques captures d'écran pour que vous vous fassiez une petite idée :

Options de la tâche dependency-check

Comme on peut le voir, beaucoup d'options sont disponibles. Certaines peuvent surprendre, comme le fait de pouvoir limiter la version du dependency-check utilisée. J'en parlerai un peu plus tard dans cet article. Au moment de livrer mes conclusions. D'autres permettent d'utiliser pour faire du "quality gate". Ce qui est beaucoup plus intéressant.

L’option continueOnError:true trouvera son explication dans la conclusion ;)

Les options de formats sont les plus intéressantes. Ce sont elles qui permettent de gérer la manière dont dependency-check remonte ses résultats. On peut distinguer deux usages :

  • Le format HTML. Celui-ci produit un fichier qui sera téléchargeable avec les artefacts de builds. Mais l'intégration s'arrêtera là.
  • Le format JUNIT : Qui peut être gérée par AzureDevOps et donc s'afficher telle une liste de tests unitaires. Avec une erreur par librairie comportant une faille connue.

Voici un exemple tiré du marketplace Azure DevOps :

List de tests liés à dependency-check


Quelques particularités liées à .net, et Azure DevOps ?

L'analyse d'une solution .net peut poser quelques difficultés à dependency-check.

Parmi celles-ci on en distinguera deux :

  • dependency-check ne sait pas se rendre compte qu'une application .net utilisant plusieurs technologies. Il n'ira regarder que la liste des librairies .net. Si vous utilisez du javascipt, il n'y fera pas attention.
  • dependency-check ne sait pas utiliser un fichier .sln. Il préfèrera avoir un masque de recherche du style *.csproj ou qu'on lui indique un dossier de DLL à scanner (seule solution si vous avez plusieurs solutions ou que vos projets incluent des projets VB.net).

Du côté d'Azure DevOps, je vous encourage vivement à ne pas utiliser dependency-check avec les agents autohébergés de Microsoft. On préfèrera utiliser un agent de build que l'on héberge soit même (sur un serveur on premise, ou dans Azure par exemple).

La raison est toute simple. dependency-check se connecte à l'API NVD afin de construire une base de données locale des vulnérabilités (cela prendra plusieurs minutes) :

  • Avec un agent que l'on héberge soit même, la base est actualisée lors de la première exécution de la journée. Les exécutions suivantes réutilisent ensuite la base.
  • Avec un agent autohébergé par Microsoft, il faudrait pouvoir utiliser les solutions de cache fournies par Azure DevOps. Malheureusement, la tâche dependency-check-build-task@6 ne semble pas fonctionner correctement quand on lui indique une autre localisation pour sa base que celle par défaut. Cette solution n'est donc pas viable.

Note : dependency-check est une application JAVA. Il faut donc disposer d'un JRE. OpenJDK fait très bien l'affaire.


Mon avis après deux ans

Dependency-check est une bonne solution tant que l'on ne se retrouve pas confronté a ses limites. Comme toute solution du genre, me direz-vous. Oui, mais voilà, on se retrouve encore avec une application qui n’est pas spécialisée dans le .net qui pense pouvoir répondre aux besoins des développeurs .net (oui, ce n'est pas la première fois que je rencontre ce genre de contexte).

Aujourd'hui, les développeurs .net ont des besoins beaucoup plus "pluriels". Ils n'utilisent pas qu'un seul format de projet, n'ont pas qu'un seul langage. Mais surtout, le premier fichier rencontré quand on utilise Visual Studio est un fichier .sln qui n'est pas utilisable ici.

À cela doivent s'ajouter quelques déconvenues :

  • À deux reprises, j'ai constaté que l'interface de gestion des packages nuget de Visual Studio m'indiquait que des dépendances comportaient des failles alors que denpendcy-check ne remontait aucune alerte.
  • Fin décembre, dependency-check m'a remonté une alerte sur une faille qu'il indique lui-même comme aillant été découverte en aout.
  • Durant un peu plus d'une une semaine en novembre dernier, dependency-check n'a pas été utilisable. Quelques erreurs dans dependency-check suite à son passage en version 9 qui ont provoquer quelques problèmes de disponibilité de l'API NVD. C'est dans ces moments que vous découvrez que l'on vous demande d'utiliser un outil Open source qui n'est maintenu que par un développeur. Malheureusement, celui-ci n'est accompagné par aucune entreprise.
  • Le passage en version 9 de dependency-check a été accompagné d'un breacking-change important. Il faut lui fournir une clé d'API NVD. En soi, ce n'est pas un problème. Par défaut, la tâche dependency-check télécharge toujours la dernière version de dependency-check avant de l'utiliser. Sans compter sur le gros bug cité un peu plus tôt qui, le jour où la version 9 a été publiée, il fallait modifier toutes les définitions de builds l'utilisant (quel bonheur, quelle joie). Même s’il est aujourd'hui possible de limiter la version de dependency-check utilisée, empêcheriez-vous la mise à jour d'un produit destiner à garantir la sécurité de vos applications ? De mon côté, j'ai beaucoup de mal à le concevoir.


Conclusion

En tant que développeur .net, je déconseille dependency-check. Un développeur JAVA y trouvera peut-être son bonheur, à condition de ne pas utiliser d'outils automatisant le déploiement et la mise à jour des outils de builds (ce que nous sommes tous amenés à faire un jour afin de gagner en efficacité).

À la place, je recommande Ment Bold (anciennement WhitSource). Celui-ci a en plus l'avantage de vous fournir un rapport relatif aux différentes licences utilisées ainsi que les risques associés. Cela fait quelque temps que je ne l’utilise plus régulièrement, mais j’en ai gardé un très bon souvenir.

Jérémy Jeanson

Comment résoudre l'erreur WIX0217 avec une solution .net 8 ?

Suite à la mise à jour de serveurs de build, je me suis retrouvé avec une bonne grosse erreur de Wix Toolset :

##[error]wix.exe(0,0): Error WIX0217: Error executing ICE action 'ICE02'. The following string format was not expected by the external UI message logger: "Impossible d'accéder au service Windows Installer. Ceci peut se produire si le programme d'installation de Windows n'est pas bien installé. Contactez votre support technique pour assistance.".

Celle-ci se produit lors de l'usage de la tâche DotNetCoreCLI d'Azure Pipeline. Cette tâche cherche à compiler une solution Wix Toolset 4 avec le SDK de .net 8.

Petite étrangeté : la même opération fonctionne très bien dans Visual Studio, ou via la CLI de .net 8.

Quand on connait un peu wix toolset, et que l'on consulte les logs, on se rend vite compte que le problème se produit lors de la validation du msi. La tentation est grande de désactiver cette validation. Surtout, ne faites pas cela. Ne touchez pas à votre projet. Même pas pour ajouter un RunWixToolsOutOfProc (cela ne sert à rien). Le problème n'est pas là. Le problème est lié au serveur de build, et au contexte dans lequel celui-ci s'exécute.

Historiquement, l'erreur 217 se produisait avec wix toolset 3. Celui-ci utilisait un bon vieux vbscript pour exécuter la validation. Aujourd'hui, vbscript n'est plus utilisé, mais le code d'erreur reste le même. J'ai jugé bon de tenter la solution que j'avais déjà documentée il y a quelques années ici.

Il se trouve que le changement du compte du service Windows de l'agent en Compte système local avec l'autorisation d'interagir avec le Bureau résout le problème.

Affectation du Compte système local dans le gestionnaire des services de Windows

Important : ne pas oublier de redémarrer le service après changement de ce compte.

Il s'agit certainement d'un problème pour accéder à une ressource de type PATH, DLL, ou un enregistrement de ressource qui ne peuvent pas être utilisés en dehors de ce contexte.

Jérémy Jeanson

Comment résoudre les problèmes d'accès à Azure Artefact via une build utilisant le SDK de .net 8 ?

Voici un problème qui a un petit goût de réchauffé. Il y a quelques moi j'expliquais ici même la démarche à suivre quand Azure Pipeline dit qu’il est impossible de charger l'index nuget d’Azure Artefact. Aujourd'hui, je reviens avec un sujet très similaire.

Suite à l'installation du SDK .net 8 sur un serveur de build, je me suis retrouvé avec quelques problèmes de builds. Des pipelines hébergés via Azure DevOps Server indiquent avoir des problèmes pour charger l'index d'un repository Azure Artefact.

L'erreur se produit sur une tâche DotNetCoreCLI se trouvant pourtant derrière une tâche NugetCommand(restore).

Si on regarde le log (que je n'ai malheureusement plus sous la main), on constate que la tâche tente d'accéder à Azure Artefact malgré la présence des packages fraichement téléchargés via nuget. La présence du SDK de .net 8 semble changer le comportement par défaut de la CLI.

Pour résoudre ce problème une bonne fois pour toutes, il suffit donc de dire à DotNetCoreCLI de ne pas restaurer les packages nuget. Pour cela, on peut utiliser sa propriété argument et lui passer l'argument --no-restore.

Voici un exemple complet :


steps:
  - task: NuGetToolInstaller@1
  - task: NuGetCommand@2
    displayName: 'NuGet restore'
    inputs:
      restoreSolution: $(solution)
      command: restore
      feedsToUse: config
      nugetConfigPath: 'Sources/nuget.config'
          
  - task: DotNetCoreCLI@2
    displayName: 'Compilation'
    inputs:
      command: build
      projects: '$(solution)'
      arguments: '--no-restore --configuration $(buildConfiguration) --property:Platform=$(buildPlatform)'

Moralité

Les paramètres par défaut, c'est mal. Surtout si vos pipelines reposent dessus.

Jérémy Jeanson

Comment résoudre la System.Resources.MissingManifestResourceException pouvant se produire après un Migration .net Framework ?

Dans le cadre d'une migration d'un projet .net Framework, je me suis retrouvé avec des projets ne respectant que très peu les conventions liées aux namespaces. Plusieurs dossiers pourtant bien présents n'apparaissent pas dans les namespaces. Pour compléter le tableau, Visual Studio doit gérer des fichiers C# pour des Ressources, et formulaires WinForm présents dans ces dossiers.

Après migration de ces projets du vieux format csproj / vbproj vers le format actuel (SDK project style), les tests unitaires font remonter une erreur bien étrange :

System.Resources.MissingManifestResourceException : Could not find any resources appropriate for the specified culture or the neutral culture. Make sure "xxxForm.resources" was correctly embedded or linked into assembly "xxx" at compile time, or that all the satellite assemblies required are loadable and fully signed.

Pour saisir l’origine du problème, je vérifie les propriétés de Ressources. Tout est à sa place. Je fais un petit tour dans dotPeek pour comparer les deux DLL avant, et après migration. Je me rends alors compte que les ressources WinFrom ne sont pas nommées de la même manière avant et après migration du projet. Les projets de tests unitaires étant encore à l’ancien format, il s’attend à ce que les ressources respectent un certain nommage.

Un petit tour dans Microsoft Learn (après une bonne heure de recherche infructueuse ici et là), je trouve le Satin Graal : How MSBuild generates manifest file names - .NET | Microsoft Learn

Cette page de documentation fait état d'un paramètre qui rétablit le comportement des namespace pour les applications .net Framework :


<EmbeddedResourceUseDependentUponConvention>true</EmbeddedResourceUseDependentUponConvention>

Après ajout de celui-ci dans le fichier vsproj / vbproj, les ressources sont correctement nommée et l'erreur ne se produit plus.

Moralité

Encore une preuve que les bonnes pratiques ne sont pas juste là pour nous embeter. Si les conventions de nommage des namespaces étaint respectées, l'exception ne se serait jamais produite.

Jérémy Jeanson

Comment identifier, et résoudre les problèmes d'une migration vers PackageReference ratée ?

En temps normal, la migration des références vers PackageReference se passe en douceur. Il suffit de faire un click droit sur le dossier References, et de profiter du menu contextuel qui s'ouvre pour lancer la migration. Quelques secondes plus tard, le fichier package.config disparait.

Mais il arrive que les choses ne se passent pas bien. Ce peut être au moment de la migration, ou après la récupération du code d'un collègue.

Une migration peut être refusée, pour des raisons d'incompatibilité. Le motif est noté dans la fenêtre de sortie de Visual Studio. Il suffit de résoudre le problème indiqué.

Dans d'autres cas, la migration est allée au bout. Mais ce n'est pas pour autant que tout va bien. Cela se matérialise par des références apparaissant en double. Certaines ont l'icône nuget, d'autres non.

Projet avec des références en double

Cela est dû au fait que lors de la migration, l'assistant n'a pas supprimé les liens vers le dossier ..\packages. Pour corriger ce projet, il suffit donc d'éditer le fichier csproj, ou vbproj, et de supprimer les nœuds XML de type Reference dont le HnitPath pointe vers le dossier ..\packages.


<Reference Include="AutoFixture, Version=4.17.0.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
  <HintPath>..\packages\AutoFixture.4.17.0\lib\net452\AutoFixture.dll</HintPath>
</Reference>

Voilà, vous savez maintenant corriger un projet dont la migration vers PackageReference n'a pas réussi du premier coup ;)

Jérémy Jeanson