diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1c65f45 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{cs,csx}] +indent_style = space +indent_size = 4 + +[*.{json,yml,yaml,md}] +indent_size = 2 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..272beed --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Default owners for the whole repository. +* @coinbase/core-dotnet-write @coinbase/core-dotnet-admin diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..2e115bc --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,20 @@ +## Description + + + +## Type of Change + + + +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change +- [ ] Dependency update +- [ ] Refactor / cleanup +- [ ] Other (describe below) + +## Checklist + +- [ ] Tests included / updated +- [ ] Changelog updated +- [ ] Version bump if needed diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 0000000..6596feb --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,26 @@ +name: format +on: [pull_request] + +permissions: + contents: read + +jobs: + format: + runs-on: ubuntu-latest + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Setup .NET + uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1 + with: + dotnet-version: "10.0.x" + + - name: Verify formatting + run: dotnet format core-dotnet.sln --verify-no-changes diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..06f4d30 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,26 @@ +name: lint +on: [pull_request] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Setup .NET + uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1 + with: + dotnet-version: "10.0.x" + + - name: Build + run: dotnet build core-dotnet.sln -c Release diff --git a/.github/workflows/salus-scan.yml b/.github/workflows/salus-scan.yml new file mode 100644 index 0000000..22e8d2e --- /dev/null +++ b/.github/workflows/salus-scan.yml @@ -0,0 +1,21 @@ +name: Salus Security Scan +on: [pull_request, push] + +permissions: + contents: read + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Salus Scan + id: salus_scan + uses: federacy/scan-action@4303a88eba7fe183a9a2d9ca9a13af11203cb835 # 0.1.5 + with: + active_scanners: "\n - PatternSearch\n - Semgrep\n - Trufflehog" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..70836d8 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,29 @@ +name: test +on: [pull_request] + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Setup .NET + uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1 + with: + dotnet-version: "10.0.x" + + - name: Build + run: dotnet build core-dotnet.sln -c Release + + - name: Test + run: dotnet test core-dotnet.sln -c Release --no-build diff --git a/CHANGELOG.md b/CHANGELOG.md index 91ecda0..9e33f40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.0] - 2026-06-01 + +### Changed + +- Moved the canonical repository to [coinbase/core-dotnet](https://github.com/coinbase/core-dotnet). +- Added GitHub Actions for format, build (lint), and tests. +- CI uses the .NET 10 SDK; the package continues to target `net8.0`. +- Added SECURITY.md, CONTRIBUTING.md, and pull request template aligned with other Coinbase SDKs. + +### Notes + +- No intentional API breaking changes; minor bump signals repository migration. +- Equivalent to 0.1.1 on coinbase-samples aside from repository and tooling. + +## [0.1.1] - 2026-06-01 + +### Changed + +- Marked coinbase-samples/core-dotnet as deprecated in the README. +- Documented migration to coinbase/core-dotnet and CoinbaseSdk.Core 0.2.0+. + +### Notes + +- No intentional API changes. + ## [0.1.0] - 2025-11-24 ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..88af414 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,5 @@ +# Contributing to core-dotnet + +At this time we are not accepting Pull Requests. Therefore, if you have an idea for a new feature or have found a non-security bug, please open an issue. We will examine the issue and determine if it is a good fit for the project and if it is, we will provide the change internally and release it to the public. + +If you have found a security bug, please refer to the [security policy](SECURITY.md) for how to report it. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..42a439f --- /dev/null +++ b/Makefile @@ -0,0 +1,72 @@ +.PHONY: help check-dotnet lint format format-fix build test ci tools setup + +SLN := core-dotnet.sln +CONFIG ?= Release + +# Prefer the Microsoft dotnet host on macOS when it includes the net8.0 runtime. +DOTNET_DIR := $(firstword $(wildcard /usr/local/share/dotnet) $(HOME)/.dotnet) +ifneq ($(DOTNET_DIR),) +export PATH := $(DOTNET_DIR):$(PATH) +endif +DOTNET ?= dotnet + +# Match .github/workflows/*.yml (SDK 10 builds net8.0; tests need the net8.0 runtime). +DOTNET_SDK_CHANNEL ?= 10.0 +DOTNET_RUNTIME_CHANNEL ?= 8.0 +DOTNET_INSTALL_DIR ?= $(HOME)/.dotnet + +.DEFAULT_GOAL := help + +help: + @echo "Usage: make " + @echo "" + @echo "Targets:" + @echo " lint Build with StyleCop (same as CI lint job)" + @echo " format Verify formatting (dotnet format --verify-no-changes)" + @echo " format-fix Apply formatting fixes locally" + @echo " build dotnet build ($(CONFIG))" + @echo " test Build and run tests" + @echo " ci format, lint, and test" + @echo " tools Install .NET SDK $(DOTNET_SDK_CHANNEL) and runtime $(DOTNET_RUNTIME_CHANNEL)" + @echo " setup Alias for tools" + @echo "" + @echo "First-time setup: make tools" + @echo "Then add to your shell: export PATH=\"$(DOTNET_INSTALL_DIR):\$$PATH\"" + +check-dotnet: + @command -v $(DOTNET) >/dev/null 2>&1 || { \ + echo "$(DOTNET) not found; install with: make tools"; \ + exit 1; \ + } + @$(DOTNET) --list-runtimes 2>/dev/null | grep -q 'Microsoft.NETCore.App 8\.' || { \ + echo "Microsoft.NETCore.App 8.x runtime not found (required for net8.0 tests)."; \ + echo "Install with: make tools"; \ + echo "On macOS with Homebrew dotnet 10 only, use: export PATH=\"/usr/local/share/dotnet:\$$PATH\""; \ + exit 1; \ + } + +lint: build + +format: check-dotnet + $(DOTNET) format $(SLN) --verify-no-changes + +format-fix: check-dotnet + $(DOTNET) format $(SLN) + +build: check-dotnet + $(DOTNET) build $(SLN) -c $(CONFIG) + +test: build + $(DOTNET) test $(SLN) -c $(CONFIG) --no-build + +ci: format lint test + +tools setup: + @echo "Installing .NET SDK $(DOTNET_SDK_CHANNEL) and runtime $(DOTNET_RUNTIME_CHANNEL) to $(DOTNET_INSTALL_DIR)..." + @curl -sSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh + @chmod +x /tmp/dotnet-install.sh + @/tmp/dotnet-install.sh --channel $(DOTNET_SDK_CHANNEL) --install-dir $(DOTNET_INSTALL_DIR) + @/tmp/dotnet-install.sh --runtime dotnet --channel $(DOTNET_RUNTIME_CHANNEL) --install-dir $(DOTNET_INSTALL_DIR) + @echo "" + @echo "Done. Add to ~/.zshrc or ~/.bashrc:" + @echo " export PATH=\"$(DOTNET_INSTALL_DIR):\$$PATH\"" diff --git a/README.md b/README.md index ac66200..a97f4ce 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Coinbase .NET Core Library +The canonical source repository is **[coinbase/core-dotnet](https://github.com/coinbase/core-dotnet)**. The former [coinbase-samples/core-dotnet](https://github.com/coinbase-samples/core-dotnet) repository is deprecated. + ## Overview The **Coinbase .NET Core Library** (`CoinbaseSdk.Core`) is the foundational building block for Coinbase .NET SDKs. It provides a standardized, robust, and thread-safe infrastructure for building API clients, handling authentication, HTTP communication, and serialization. @@ -74,6 +76,17 @@ var client = new MyCoinbaseClient(credentials, httpClient: httpClient); dotnet add package CoinbaseSdk.Core ``` +## Development + +```bash +git clone https://github.com/coinbase/core-dotnet.git +cd core-dotnet +make tools # first time: install .NET SDK 10 and net8.0 runtime +make ci # format, lint (build), and test +``` + +Individual targets: `make format`, `make lint`, `make build`, `make test`. Use `make format-fix` to apply formatting locally. + ## License This project is licensed under the [Apache License, Version 2.0](LICENSE). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..190eda2 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# Security Policy for core-dotnet + +## Reporting a Security Bug + +If you think you have discovered a security issue within any part of this codebase, please let us know by providing a description of the flaw and any related information (e.g. steps to reproduce, version, etc.). There are two ways to report a security bug: + +- The first way is to submit a report to [HackerOne](https://hackerone.com/coinbase). This way of submitting a report will make you eligible for a bounty but will require you to follow a certain process, including possible limitations on when the results can be publicly disclosed. + +- If you do not wish to submit via HackerOne, then you can send us a direct email at sa@coinbase.com. This way of submitting a report will not make you eligible for a bounty but will allow you to responsibly disclose on your terms. diff --git a/src/CoinbaseSdk/Core/CoinbaseSdk.Core.csproj b/src/CoinbaseSdk/Core/CoinbaseSdk.Core.csproj index a7cb382..40f9231 100644 --- a/src/CoinbaseSdk/Core/CoinbaseSdk.Core.csproj +++ b/src/CoinbaseSdk/Core/CoinbaseSdk.Core.csproj @@ -2,12 +2,12 @@ CoinbaseSdk.Core is the core library for .NET Coinbase SDKs. - 0.1.0 + 0.2.0 12 Coinbase net8.0 coinbase - https://github.com/coinbase-samples/core-dotnet + https://github.com/coinbase/core-dotnet LICENSE true True diff --git a/src/CoinbaseSdk/Core/http/CallOptions.cs b/src/CoinbaseSdk/Core/http/CallOptions.cs index b9a9ac7..1b9986a 100644 --- a/src/CoinbaseSdk/Core/http/CallOptions.cs +++ b/src/CoinbaseSdk/Core/http/CallOptions.cs @@ -74,4 +74,4 @@ internal bool HasRetryConfiguration() return this.MaxRetries > 0; } } -} \ No newline at end of file +} diff --git a/src/CoinbaseSdk/Core/serialization/IJsonUtility.cs b/src/CoinbaseSdk/Core/serialization/IJsonUtility.cs index 144bbe8..00e126f 100644 --- a/src/CoinbaseSdk/Core/serialization/IJsonUtility.cs +++ b/src/CoinbaseSdk/Core/serialization/IJsonUtility.cs @@ -22,4 +22,4 @@ public interface IJsonUtility T Deserialize(string json); } -} \ No newline at end of file +} diff --git a/src/CoinbaseSdk/Core/serialization/JsonUtility.cs b/src/CoinbaseSdk/Core/serialization/JsonUtility.cs index c80040b..bfc33af 100644 --- a/src/CoinbaseSdk/Core/serialization/JsonUtility.cs +++ b/src/CoinbaseSdk/Core/serialization/JsonUtility.cs @@ -26,7 +26,7 @@ namespace CoinbaseSdk.Core.Serialization public class JsonUtility : IJsonUtility { - private static readonly object DefaultOptionsLock = new (); + private static readonly object DefaultOptionsLock = new object(); private static JsonSerializerOptions? defaultOptions; private readonly JsonSerializerOptions options; @@ -91,7 +91,7 @@ private static JsonSerializerOptions EnsureDefaultOptions() private static JsonSerializerOptions BuildDefaultOptions() { - JsonSerializerOptions baseOptions = new (JsonSerializerDefaults.Web) + JsonSerializerOptions baseOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web) { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNameCaseInsensitive = true, diff --git a/tests/CoinbaseSdk.Core.Tests/Client/CoinbaseClientTests.cs b/tests/CoinbaseSdk.Core.Tests/Client/CoinbaseClientTests.cs index f74124c..365dde6 100644 --- a/tests/CoinbaseSdk.Core.Tests/Client/CoinbaseClientTests.cs +++ b/tests/CoinbaseSdk.Core.Tests/Client/CoinbaseClientTests.cs @@ -34,8 +34,8 @@ public class TestCoinbaseClient : CoinbaseClient public TestCoinbaseClient( CoinbaseCredentials credentials, string apiBasePath, - IJsonUtility jsonUtility = null, - IHttpClient httpClient = null) + IJsonUtility? jsonUtility = null, + IHttpClient? httpClient = null) : base(credentials, apiBasePath, jsonUtility, httpClient) { } @@ -58,7 +58,7 @@ public CoinbaseClientTests() [Fact] public void Constructor_NullCredentials_ThrowsArgumentException() { - Assert.Throws(() => new TestCoinbaseClient(null, _apiBasePath)); + Assert.Throws(() => new TestCoinbaseClient(null!, _apiBasePath)); } [Fact]