Skip to content

fix: validate simulator bucket configuration - #152

Open
goanpeca wants to merge 4 commits into
mainfrom
fix/simulator-bucket-config-validation-22
Open

fix: validate simulator bucket configuration#152
goanpeca wants to merge 4 commits into
mainfrom
fix/simulator-bucket-config-validation-22

Conversation

@goanpeca

@goanpeca goanpeca commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Validate simulator bucket configuration payloads on create and update, including CORS, lifecycle, replication, bucketInfo, and default Object Lock retention rules.
  • Match B2 edge behavior for CORS maxAgeSeconds, optional lifecycle action fields, default retention bounds, Object Lock preconditions, and replication response normalization.
  • Honor listBuckets bucketId, bucketName, and bucketTypes filters while rejecting malformed bucketTypes with structured 400 responses.
  • Enforce updateBucket ifRevisionIs conflicts using the documented revision guard field.
  • Tighten strictAuth replication handling by checking nested destination bucket scope, destination bucket existence/configuration, and referenced replication key capabilities with uniform key-validation errors.
  • Share bucket configuration validation dispatch between createBucket and updateBucket.
  • Expand simulator fidelity and validator edge-case coverage so CI coverage thresholds pass.

Linked issue

Closes #22

Tests

  • pnpm exec vitest run src/simulator/fidelity.test.ts src/simulator/validation.test.ts src/bucket-config.test.ts src/object-lock.test.ts
  • pnpm run lint
  • pnpm run lint:docs
  • pnpm run lint:spelling
  • pnpm run typecheck
  • pnpm run typecheck:examples
  • pnpm run test
  • pnpm run test:coverage
  • pnpm run build
  • pnpm run docs
  • pnpm run verify:metadata
  • pnpm run verify:release
  • pnpm run verify:exports

Follow-up notes

  • None.

Copilot AI review requested due to automatic review settings July 30, 2026 18:52
@goanpeca goanpeca added this to the v0.3.0 milestone Jul 30, 2026
@goanpeca goanpeca added bug Something isn't working area: simulator Area: simulator priority: medium Medium severity labels Jul 30, 2026
@goanpeca goanpeca self-assigned this Jul 30, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the B2Simulator’s fidelity to the real B2 API by enforcing bucket-configuration validation during bucket create/update, honoring listBuckets request filters, and enforcing optimistic-concurrency revision guards on update.

Changes:

  • Added simulator-side validation for corsRules, lifecycleRules, replicationConfiguration, and defaultRetention.
  • Updated simulator bucket endpoints to run the new validators and to honor listBuckets filters (bucketId, bucketName, bucketTypes).
  • Added/expanded unit + fidelity tests to cover the new validation and revision-conflict behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/simulator/validation.ts Adds new bucket-configuration validators (CORS, lifecycle, replication, retention) and shared helper validators.
src/simulator/validation.test.ts Adds focused unit tests for the new validation helpers.
src/simulator/index.ts Enforces validation during create/update; implements listBuckets filter behavior; adds updateBucket revision guard conflict behavior.
src/simulator/fidelity.test.ts Adds integration-style simulator fidelity tests for bucket config validation, listBuckets filtering, and revision guard conflicts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/simulator/index.ts Outdated
Copilot AI review requested due to automatic review settings July 30, 2026 19:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

src/simulator/index.ts:2936

  • b2_list_buckets supports the special filter bucketTypes: ["all"] to mean “no type filtering”. Even if validateBucketTypes allows it, the current .includes(bucket.bucketType) filter will return an empty list for ["all"]. Consider normalizing ["all"] to an undefined filter before applying the predicate.
      .filter((bucket) => req.bucketId === undefined || bucket.bucketId === req.bucketId)
      .filter((bucket) => req.bucketName === undefined || bucket.bucketName === req.bucketName)
      .filter(
        (bucket) => req.bucketTypes === undefined || req.bucketTypes.includes(bucket.bucketType),
      )

src/simulator/validation.ts:449

  • validateBucketTypes is missing documented b2_list_buckets semantics: Backblaze treats an empty bucketTypes array as a bad request, and allows the special value ["all"] (but rejects using "all" alongside other types). The current implementation accepts empty arrays and rejects "all", so the simulator can diverge from real B2 behavior.
export function validateBucketTypes(bucketTypes: unknown): ValidationError | null {
  if (bucketTypes === undefined) return null
  if (!Array.isArray(bucketTypes)) {
    return { code: 'bad_request', message: 'bucketTypes must be an array' }
  }

src/types/bucket.ts:38

  • Making LifecycleRule.daysFromHidingToDeleting / daysFromUploadingToHiding optional changes their read-side type to include undefined, which is a potentially breaking public API change for SDK consumers. If this is intentional (to match real B2 responses/requests), it should be called out in the release notes; if not, consider normalizing rules to always include explicit null fields (or introducing a separate “input” type) to avoid widening the response type.
/** Rule that automatically hides or deletes files after a specified number of days. */
export interface LifecycleRule {
  /** Days after hiding before automatic deletion, or null to never auto-delete hidden files. */
  readonly daysFromHidingToDeleting?: number | null
  /** Days after upload before automatic hiding, or null to never auto-hide. */
  readonly daysFromUploadingToHiding?: number | null
  /** Days after starting before automatic cancellation of unfinished large files, or null to never auto-cancel. */
  readonly daysFromStartingToCancelingUnfinishedLargeFiles?: number | null

src/simulator/index.ts:1350

  • When requiresAccountLevelBucketAccess triggers, the error currently uses bucketScopeRequiredMessage() ("bucket scope is required"), but the actual constraint is that bucket-scoped keys are not allowed for this operation. This message is misleading for callers trying to understand why bucket creation is denied.

This issue also appears on line 2932 of the same file.

    if (grant.bucketIds !== null) {
      if (scope?.requiresAccountLevelBucketAccess === true) {
        return this.error(403, 'unauthorized', grant.bucketScopeRequiredMessage())
      }

Copilot AI review requested due to automatic review settings July 30, 2026 19:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

src/simulator/index.ts:475

  • file_lock_not_enabled is returned with a different message here than in requireFileLockEnabled() (which uses "Bucket does not have file lock enabled"). Keeping the error message consistent for the same error code makes simulator behavior easier to assert against and reduces confusion for SDK consumers.
    if (
      requestRecord(fields.defaultRetention)?.['mode'] !== BucketRetentionMode.None &&
      !options.objectLockEnabled
    ) {
      return {
        code: 'file_lock_not_enabled',
        message: 'Bucket must have Object Lock enabled to set defaultRetention',
      }

Copilot AI review requested due to automatic review settings July 30, 2026 19:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

src/simulator/index.ts:456

  • validateBucketConfigurationFields passes bucketInfo straight into validateBucketInfo without checking that it’s a plain object. If a caller sends bucketInfo: null (or any non-object), Object.entries() in validateBucketInfo will throw (or non-objects may be coerced), causing the simulator to crash instead of returning a structured 400 error.
  if (fields.bucketInfo !== undefined) {
    const infoError = validateBucketInfo(fields.bucketInfo as Record<string, string>)
    if (infoError) return infoError
  }

src/simulator/index.ts:1581

  • Optional chaining in destination bucket replication lookup doesn’t protect the indexed access. If asReplicationDestination is null/undefined, ...?.sourceToDestinationKeyMapping[sourceApplicationKeyId] will still attempt [sourceApplicationKeyId] on undefined and throw. Use optional chaining on the index access (or split into a temporary mapping variable).
      const destinationBucket = this.buckets.get(destinationBucketId)
      const destinationApplicationKeyId =
        destinationBucket?.info.replicationConfiguration.asReplicationDestination
          ?.sourceToDestinationKeyMapping[sourceApplicationKeyId]
      if (destinationApplicationKeyId === undefined) {

@goanpeca
goanpeca marked this pull request as ready for review July 30, 2026 19:49
@quality-keeper

Copy link
Copy Markdown

Quality Keeper — Advisory Review

Source: backblaze-labs/b2-sdk-typescript

Advisory only — this is not a required status check. Quality Keeper reviewed this repository snapshot and is reporting what it found. It changes no status and merges nothing; suggested scaffolds are starting points, not proof.

Quality Keeper assessed the repository, executed checks, and collected results, covering 14 quality expectations.

Result

  • 10 expectations are covered by acceptable evidence.
  • 4 still need attention — Quality Keeper cannot count them as covered yet.

Covered

Quality Keeper found acceptable evidence for one or more expectations in these Testing Types.

  • Build Verification — 2 expectations covered (Ran and passed).
    Confirms the software actually builds/packages into a runnable or publishable artifact.
    • What Quality Keeper ran:
      • pnpm run buildpassed
      • pnpm run typecheckpassed
      • Full command output/logs are in the run artifacts (see Proof below).
  • Compatibility Testing — 2 expectations covered (Ran and passed).
    Checks the software works across the consumers, platforms, or versions it claims to support.
    • What Quality Keeper ran:
      • pnpm run verify:releasepassed
      • Full command output/logs are in the run artifacts (see Proof below).
  • Configuration & Artifact Review — 1 expectation covered (Verified before execution).
    Checks that config files, manifests, and build artifacts are present, well-formed, and say what they should.
  • Metadata & Discoverability Review — 1 expectation covered (Verified before execution).
    Checks that a repository or package is findable and understandable — description, topics, README, keywords.
  • Specification Conformity Review — 2 expectations covered (Ran and passed).
    Checks that documented assets actually match the specification or documented behavior they claim.
    • What Quality Keeper ran:
      • pnpm run lint:docspassed
      • Full command output/logs are in the run artifacts (see Proof below).
  • Static Security Analysis — 1 expectation covered (Verified before execution).
    Scans code and configuration for security problems without running it — secrets, unsafe patterns, misconfigurations.
  • Unit Testing — 1 expectation covered (Ran and passed).
    Exercises individual functions or modules in isolation.
    • What Quality Keeper ran:
      • pnpm run testpassed — 1897 passed, 2 skipped
      • pnpm run test:allpassed — 1897 passed, 2 skipped
      • Full command output/logs are in the run artifacts (see Proof below).

Needs attention

Quality Keeper does not yet have acceptable evidence for these Testing Types.

Some have partial checks that need to run, be fixed, or be connected. Others need new proof or seeded tests. In both cases, Quality Keeper cannot count them as covered yet.

  • Contract Testing
    Checks that a public or network boundary (API, SDK surface, schema) matches its agreed shape.
  • Environmental Isolation Testing
    Checks that tests set up and tear down their own state and do not depend on leftover data, shared buckets, or each other.
  • Integration Testing
    Checks that two or more components work together — a module plus a real dependency, service, or datastore.
  • Static Security Analysis
    Scans code and configuration for security problems without running it — secrets, unsafe patterns, misconfigurations.

Quality Keeper changes no Evidence State. Pending, missing, executed-but-not-exercised, and review items are not passing, and seeded scaffolds are next actions, not evidence, until applied and re-evaluated.

Full breakdown (14 expectations)
Area Status Expectation Why it applies
Build Verification Ran and passed packaged_or_runnable_assets_need_build_verification The interpreted asset has package, script, workflow, UI, service, CLI, edge-runtime, or game-runtime signals, so it needs a reproducible build or equivalent construction check.
Build Verification Ran and passed published_packages_need_sdk_build_artifact_review The interpreted asset is a published package with source code, so the distribution manifest completeness, pack tarball integrity, and declared file list need verification as part of the release artifact.
Compatibility Testing Ran and passed consumer_or_platform_boundaries_need_compatibility_testing The interpreted asset is published or consumed across environments (published package, exported API, command-line tool, or edge target), so compatibility across platforms, versions, and consumers needs verification.
Compatibility Testing Ran and passed published_typescript_packages_need_type_declaration_check The interpreted asset is a published TypeScript package (tsconfig.json or has_types in package.json detected), so generated .d.ts declaration files and type resolution must be verified from a consumer project's perspective.
Configuration & Artifact Review Verified before execution every_asset_needs_configuration_review Every interpreted asset needs a static review of its declared files, manifests, configuration, and artifacts so Quality Keeper can explain its shape before evidence is harvested.
Contract Testing Selected but not executed public_or_network_boundaries_need_contract_testing The interpreted asset publishes a boundary that other software depends on (network API, CLI, or package exports), so the public interface contract needs verification.
Environmental Isolation Testing Selected but not executed external_resource_assets_need_environmental_isolation_testing The interpreted asset uses external resources, deployment configuration, infrastructure-as-code, or automated workflows, so repeated runs need isolated names, cleanup, and teardown verification.
Integration Testing Selected but not executed published_sdk_packages_need_integration_testing The interpreted asset is a published package or library that creates an integration boundary for consumers, so the package's integration paths, API contracts, and external service behaviors need integration-level verification.
Metadata & Discoverability Review Verified before execution every_asset_needs_discoverability_review Every interpreted asset needs enough metadata and documentation for humans and systems to identify what it is and how to work with it.
Specification Conformity Review Ran and passed documented_assets_need_specification_conformity_review The asset carries documentation, so documented commands, setup instructions, examples, and claims need to stay aligned with the implementation.
Specification Conformity Review Ran and passed published_packages_with_docs_need_example_validation The interpreted asset is a published package with detected documentation, so code examples in README, docs, or examples directories should be compiled or type-checked to confirm they match the actual public API.
Static Security Analysis Missing — no proof yet code_and_configuration_assets_need_static_security_analysis The interpreted asset contains code, configuration, dependencies, secrets coordinates, infrastructure-as-code, or service boundaries, so static security analysis is required before runtime evidence is trusted.
Static Security Analysis Verified before execution published_packages_need_provenance_proof The interpreted asset is a published package with source code, so published release artifacts should carry provenance metadata (npm provenance, SLSA attestation, or sigstore signature) to protect the supply chain.
Unit Testing Ran and passed code_assets_need_unit_testing The interpreted asset contains runnable or reusable source code, so the smallest testable units need isolated verification.

New to these terms? Start with the Quality Keeper Standards Wiki to learn what each testing type means, why it applies, and what counts as evidence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: simulator Area: simulator bug Something isn't working priority: medium Medium severity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

simulator: bucket config not validated; listBuckets ignores filters; updateBucket ignores ifRevisionMatch

2 participants