Skip to content

Coding Conventions

Elliot Chernofsky edited this page Jun 13, 2024 · 14 revisions

common.vm dependency

All packages should depend on the package common.vm and import its module vm.common near the top of the code via:

Import-Module vm.common -Force -DisableNameChecking

vm.common/vm.common.psm1 defines functions that start with VM- to reuse code among packages and make package creation easier. The VM- functions muss be used when possible. Other code structure and variables names should be kept similar to the one in the existent VM- functions and packages.

Script to Generate Packages

In order to easily use the VM- functions and keep the code consistent, use the package template generation script create_package_template.py. The script can automate the creating of many package types and provides an initial file setup for the rest. Use the following command to get more information about usage and available options and check the Package Creation Using Python Template section for more details:

python3 scripts/utils/create_package_template.py  --help

Error Handling

Package installers should set their error preferences to Stop when possible to ensure Chocolatey is notified of errors and that the packaged failed to install. This should be the first line of an installer (e.g., chocolateyinstall.ps1):

$ErrorActionPreference = 'Stop'

Where possible, leverage the function VM-Assert-Path to assert specific paths exist in order to fail the package as quickly as possible. For instance, after downloading a tool check that the file path to the tool exists:

# Download and unzip
$url = "https://github.com/mandiant/capa/releases/download/v1.6.3/capa-v1.6.3-windows.zip"
$checksum = "00e8d32941b3a1a58a164efc38826099fd70856156762647c4bbd9e946e41606"
$packageArgs = @{
  packageName   = ${Env:ChocolateyPackageName}
  unzipLocation = $toolDir
  url           = $url
  checksum      = $checksum
  checksumType  = 'sha256'
}
Install-ChocolateyZipPackage @packageArgs
VM-Assert-Path (Join-Path $toolDir "capa.exe")

If joining a path via Join-Path that you expect to immediately exist, suffix it with -Resolve to have PowerShell check to see if the path exists and throw an error if it doesn't. For example:

$toolDir = Join-Path ${Env:RAW_TOOLS_DIR} 'x64dbg\release' -Resolve

Additionally, check for a few expected files to ensure installation succeeded. For example:

# Check for a few expected files to ensure installation succeeded
Join-Path $toolDir 'x64dbgpy.h' -Resolve | Out-Null

# -OR-

VM-Assert-Path (Join-Path $toolDir 'x64dbgpy.h')

Uninstalling & Updating are Best Effort

Uninstalling

When uninstalling a package we do our best effort to remove everything related to the package. But this is not always possible (at least not in an easy way). Because of that, the uninstall shouldn't fail if any of the steps fail. This should be the first line of an uninstaller (e.g., chocolateyuninstall.ps1):

$ErrorActionPreference = 'Continue'

Updating

Changing a tool of category may cause problems with updates. We discuss the package category when adding a package and we do our best effort to not change categories often and only when it really makes sense. For example:

  • We made a mistake and the category needs to be corrected
  • We have man tools that would qualify for a new category and help making existent categories easier to navigate.

Note also that we are not testing updates, only new installations. Please if updating fails, make a fresh install.

ZIP Files Cleanup

If installing the contents of a ZIP file (i.e., using Install-ChocolateyZipPackage) preface the installation with the line of code below to assist potential upgrades:

VM-Remove-PreviousZipPackage ${Env:chocolateyPackageFolder}

The helper function above reads a .txt file within the Chocolatey package folder that lists line by line the location of each file copied to disk from the installed ZIP file and deletes each file. This ensures the files of the previous file are all deleted before installing the new ZIP.

Small Pull Requests

In general, atomic/small changes make reviewing pull requests (PRs) easier/faster. In VM-Packages, we automatically test packages after merging a PR and push/release them to MyGet if all the packages in the PR succeed. It could happen that a package break between the CI run in the PR and the merge run, which would cause all packages to not be pushed/released to MyGet. Because of that, it is important to avoid updating/adding many packages in the same PR, specially if the changes could be split in several PRs.

Because of the same reasons, ensure the CI in PRs run in the same day before merging it, specially if many packages are updated at the same time.

Note we check for version mitmatches daily and document the results in the MyGet Version Mismatches Wiki page to ensure we are aware of the described failures.

Common Variable Names

Below are common variables used throughout most of our chocolateyinstall.ps1 files. Please reuse these names so each installer file feels and reads similar.

$toolName

The name of the tool being installed, normally different from the package name. For example fakenet-ng.vm (package name) vs FakeNet-NG (tool name). For example:

$toolName = 'FakeNet-NG'

$toolDir

Represents the directory where the tool is actually installed. Typically, a subdirectory inside the tools directory (i.e., ${Env:RAW_TOOLS_DIR}); however, this can also be somewhere like ${Env:ProgramFiles}. For example:

$toolDir = Join-Path ${Env:RAW_TOOLS_DIR} $toolName

$category

The tool category. It needs to be set to a category in categories.txt. For example:

$category = 'Utilities'

If the package doesn 't create a shortcut in the tools directory (for example if it only installs libraries), then the package needs to be excluded in the UsesInvalidCategory linter.

$shortcutDir

Path to a subdirectory inside the tools shortcuts directory (i.e., ${Env:TOOL_LIST_DIR}). The subdirectory includes the tool category. For example:

$shortcutDir = Join-Path ${Env:TOOL_LIST_DIR} $category

$shortcut

The .LNK shortcut file that opens the tool. This file is located in the $shortcutDir. For example:

$shortcut = Join-Path $shortcutDir "$toolName.lnk"

$packageArgs

Arguments used to call Install-ChocolateyZipPackage or Install-ChocolateyInstallPackage. For example:

$packageArgs = @{
  packageName   = ${Env:ChocolateyPackageName}
  unzipLocation = $toolDir
  url           = $url
  checksum      = $checksum
  checksumType  = 'sha256'
}
Install-ChocolateyZipPackage @packageArgs

Proxy Execution of a Tool

Some tools are command-line tools that we execute using a proxy such as cmd.exe or powershell.exe. Below are some common variables used in these cases.

$executablePath

Path to the tool's executable file. Generally used as the target of a shortcut and tool's file path added to the PATH.

$executableIcon

Path to the tool's icon. If the tool executable has an icon within its resource section, you can reuse the tool's path $executablePath.

$executableCmd

Proxy to execute the tool's executable. For example:

$executableCmd = Join-Path ${Env:WinDir} "system32\cmd.exe" -Resolve

$executableDir

Working directory to execute the tool from.

Digital Signature Verification vs Checksum Comparison

Packages are checked against the SHA256 checksum of the tool that is downloaded from the source of the tool creator. The first checksum must be supplied manually by the tool package creator. This checksum is updated automatically when if a tool is detected to have an update via the package update script.

Digital Signatures should only be used as an alternative when the source of the tool is a verified highly trusted entity (Google/Microsoft for example) and preferably only in the case when a checksum verification cannot be used.