~seirdy/moac

8e2dbd7cbf4e480d3225ebfea8df4d0bd6718475 — Rohan Kumar 7 months ago 1452c46
Doc: add more project requirements

- Add SECURITY.md to list the project's security requirements
- Update the contribution guidelines/requirements in CONTRIBUTING.md
  with a reference to the new SECURITY.md
- Explicitly set shfmt formatting flags to meet portability
  requirements.
3 files changed, 45 insertions(+), 7 deletions(-)

M CONTRIBUTING.md
M Makefile
A doc/SECURITY.md
M CONTRIBUTING.md => CONTRIBUTING.md +10 -6
@@ 20,13 20,13 @@ I also check issues in the GitHub, GitLab, and Codeberg mirrors linked at the to

### Patches, questions, and feature requests

Preferred location: <https://lists.sr.ht/~seirdy/moac>. Send emails and patches to [~seirdy/moac@lists.sr.ht](mailto:~seirdy/moac@lists.sr.ht). I also check the GitHub, GitLab, and Codeberg mirrors for issues and PRs.
Preferred location: <https://lists.sr.ht/~seirdy/moac>. Send emails and patches to [~seirdy/moac@lists.sr.ht](mailto:~seirdy/moac@lists.sr.ht). I also check the GitHub, GitLab, and Codeberg mirrors for issues and PRs. In fact, I'll even accept a contribution as a link to a completely different Git remote; Sourcehut's CI is remote-neutral.

#### Coding standards

Contributions don't need to follow these standards to be useful. If a useful patch doesn't pass the below checks, I might clean it up myself.

This project uses `gofumpt`, `fieldalignment`, `shfmt`, and `mdfmt -stxHeaders` for formatting; it uses `golangci-lint`, `gokart`, `go-consistent`, `shfmt` (again), and `checkmake` for linting. You can install all of them to your `GOBIN` by running `.builds/install-linters.sh`
This project uses `gofumpt`, `fieldalignment`, `shfmt -ln posix -bn -p -s -d`, and `mdfmt -stxHeaders` for formatting; it uses `golangci-lint`, GoKart, `go-consistent`, `go-arch-lint`, ShellCheck, `shfmt` (again), and `checkmake` for linting. You can install all except ShellCheck to your `GOBIN` by running `.builds/install-linters.sh`

Run `make fmt` to format code, `make lint` to run the linters (except `mdfmt`), and `make test` to run unit tests. `make pre-commit` runs all three. I recommend using [committer](https://github.com/Gusto/committer) to auto-run pre-commit checks; just add `committer` to your hooks.



@@ 34,6 34,8 @@ The linters are very opinionated. If you find this annoying, you can send your p

See the "Testing" section near the bottom for info about the tests.

[`doc/SECURITY.md`](https://git.sr.ht/~seirdy/moac/tree/master/item/doc/SECURITY.md) lays out some additional requirements.

### Other ways to help

- See if you can/can't reproduce binaries for a given installation of the Go toolchain, and share your findings to the mailing list or GitHub/GitLab/Codeberg issue trackers.


@@ 45,16 47,18 @@ Quick architecture overview
Excluding tests, MOAC has <1k SLOC; it shouldn't be hard to grok. Here's a one-minute overview:

- `givens.go` handles given physical values (what you'd call "the givens" if you were solving a physics problem) and computes missing values/bottlenecks.
- `charsets` handles parsing, building, and de-duplicating charsets to use when calculating password entropy or building passwords.
- `entropy`, well, calculates entropy. It figures out what charsets are contained in a password (saving these in a data structure defined by `charsets`) and figures out how many combinations can fit in the resulting space.
- `pwgen` contains the `GenPW` function builds passwords that match the given requirements: length bounds, target entropy, and charsets to use.
- `charsets/` handles parsing, building, and de-duplicating charsets to use when calculating password entropy or building passwords.
- `entropy/`, well, calculates entropy. It figures out what charsets are contained in a password (saving these in a data structure defined by `charsets`) and figures out how many combinations can fit in the resulting space.
- `pwgen/` contains the `GenPW` function builds passwords that match the given requirements: length bounds, target entropy, and charsets to use.

`.go-arch-lint.yml` in the repo root lists who-imports-whom.

Testing
-------

For the library: everything possible should be covered by tests. If a branch that handles an error should be impossible to reach and is therefore uncovered, replace it with a panic to indicate the presence of a bug. Any uncovered line that isn't a panic or a deprecated function is in need of a test.

That being said, don't write tests just for the sake of ticking off a box. Statement coverage isn't sufficient to show that most/all statements are useful and correct. One way to verify this is to check branch coverage (see [gobco](https://github.com/rillig/gobco) and mutation scores (see [go-mutesting](https://github.com/zimmski/go-mutesting)). Be aware of false positives, especially in the case of the latter.
That being said, don't write tests just for the sake of ticking off a box. Statement coverage isn't sufficient to show that most/all statements are useful and correct. Other ways to measure test comprehensiveness include branch coverage (see [gobco](https://github.com/rillig/gobco)) and mutation scores (see [go-mutesting](https://github.com/zimmski/go-mutesting)). Should you choose to give these tools a spin (you don't have to), be aware of false positives. I try to keep the mutation score above 0.7 for now.

For the CLI: this uses [testscript](https://godocs.io/github.com/rogpeppe/go-internal/testscript) to test CLI behavior.


M Makefile => Makefile +1 -1
@@ 99,7 99,7 @@ lint: shfmt-lint go-consistent checkmake gokart-lint golangci-lint nancy archlin
.PHONY: .fmt-go

.fmt-sh: $(SH_SRC)
	$(SHFMT) -p -s -w $(SH_SRC)
	$(SHFMT) -p -s -w -bn $(SH_SRC)
.PHONY: .fmt-sh

# Format all go/shell files

A doc/SECURITY.md => doc/SECURITY.md +34 -0
@@ 0,0 1,34 @@
MOAC Security
=============

Security requirements
---------------------

- Password generation is the only source of non-determinism from random-number generation. It exclusively uses CSPRNG offered by the `crypto/rand` package from the Go standard library.
- Entropy measurement is based solely on password length and charsets used; it does not take into account any other characteristics such as dictionary words, repetition, etc. Entropy measurement was designed under the assumption that the measured passwords were randomly generated.
- Password strength metrics depends only on physical laws, never needing to be updated to account for advancements in computing power.
- Password-crackability metrics do not assume the presence of a key-derivation function or key stretching/strengthening. Making fewer assumptions helps maintain simplicity and applicability to the widest range of threat models.
- Simplicity: MOAC should have a limited scope (password analysis and generation) and size (<1k SLOC, excluding tests). This isn't technically a security requirement, but it does keep attack surface low and reduce room for bugs.

Dependencies
------------

- The MOAC library has no third-party dependencies. The CLI utilities' third-party dependencies are limited to official libraries from `golang.org/x/`, a simple `getopts`-like flag parser, and a testing library. Some of these include indirect dependencies that are only used for testing the dependencies and are not included in the final binaries.
- A CI job scans dependencies against Sonatype's OSS Index on every push.

Builds
------

- MOAC should never require any use of C libraries or dynamic linking; binaries can be 100% Go-based (with the exception of OpenBSD, which uses CGO for all Go binaries as of Go 1.16) to ensure a high level of memory safety. Depending on build flags, it's still possible to use CGO (e.g. if using `-buildmode=pie`), but it should never be a requirement.
- MOAC supports reproducible builds that contain bit-for-bit identical binaries for a given Go toolchain.

Checks and enforcement
----------------------

Every push triggers CI jobs that run several tests in VMs

- Every reachable, non-deprecated statement in the library should be covered by tests. Mutation testing should reveal a mutation score above 0.7.
- Password generation is tested especially heavily, with thousands of test-cases assembled from combinations of valid parameters covering known edge cases. Furthermore, each test case is tested 512 times in an OpenBSD VM and 128 times in an Alpine VM due to the non-determinism of password generation.
- One VM runs tests with Go's memory sanitizer and race detector.
- Every push undergoes strict static analysis that includes GoKart and every relevant linter in golangci-lint. Check those projects to see which vulnerabilities they cover.