fix(npm): npmremote tarball rewriter still carries four shapes the OCI equivalent corrected

internal/format/oci/ociremote/transform.go corrects four shapes that npm's original in internal/format/npm/npmremote/transform.go still carries. The OCI side fixed them where it was written, so the two state machines now sit apart on purpose until npm follows. This issue is the home for the npm change.

Raised in review of !1281 (merged).

The one that matters: an ignored constructor error panics

NewTarballURLRewriter returns (nil, err) on an empty base URL, an empty package, or a base URL carrying a control byte (transform.go:146-157). Transform then guards the zero value, not a nil receiver (transform.go:187-194):

func (rw *TarballURLRewriter) Transform(src io.Reader) io.Reader {
	if rw.arTarballPrefix == "" {
		return errorReader{err: errUnconfiguredRewriter}
	}

Reading rw.arTarballPrefix off a nil pointer is itself the panic. The guard catches &TarballURLRewriter{} — the case its comment describes — but not nil, so a caller that ignores the constructor error crashes the request goroutine instead of getting errUnconfiguredRewriter back.

Not reachable today: nothing outside transform_test.go calls NewTarballURLRewriter, so no production call site exists to ignore the error. It becomes reachable when npm remote is wired (#287).

The other three

Named by the fix(oci) commit body on !1281 (merged); the OCI file is the reference for what each correction looks like. Only the nil guard above has been checked in detail — confirm these three against ociremote/transform.go before changing them.

  • Decode placement — npm decodes the key in finalizeKey (transform.go:478-499).
  • One-shot output sizing — npm's Read grows r.out in a loop (transform.go:294-325).
  • Escape-encoded comment example — the type comments at transform.go:31 and transform.go:172 use "tarball" as the escaped-key example.

Scope note

This is separate from #500, which tracks the nesting-depth cap scoping leak in both rewriters. That one is confidential; nothing here is a reachable vulnerability, so this issue is public.