@sorandomains/owner 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,11 +5,13 @@
5
5
  > [release status](https://docs.soran.domains/reference/release-status) for package and service availability.
6
6
 
7
7
 
8
- Version 0.12.0 targets Stellar SDK17 (`>=17 <18`). ASCII names
8
+ Version 0.12.1 targets Stellar SDK17 (`>=17 <18`). ASCII names
9
9
  and labels are validated before lowercase normalization; Unicode lookalikes are
10
10
  rejected. Writes continue to target the owning Registry/Registrar/Resolver. Universal
11
11
  Lookup is the read entry point in `@sorandomains/lookup` 0.10.0 or later.
12
12
 
13
+ Version 0.12.1 rejects simulated transactions whose source, operation, contract, method, arguments or authorization differ from the requested legacy account-signed write. Unexpected authorizers and nested calls are rejected before signing.
14
+
13
15
  Releases older than 0.11.0 (and any built against Lookup 0.6.0 to 0.9.0) default to the retired 5 and 6 September 2026 testnet stacks; upgrade rather than pinning a retired Registry. See [Older versions and retired stacks](../../README.md#older-versions-and-retired-stacks).
14
16
 
15
17
  Ownership, issuer, holder and treasury inputs remain G/C account or contract
@@ -23,7 +23,6 @@ export declare class FundingReader {
23
23
  ledger: number;
24
24
  }>;
25
25
  configuration(): Promise<{
26
- ledger: number;
27
26
  registry: string;
28
27
  nativeAsset: string;
29
28
  lookup: string;
@@ -31,6 +30,7 @@ export declare class FundingReader {
31
30
  governance: string;
32
31
  paused: boolean;
33
32
  revision: bigint;
33
+ ledger: number;
34
34
  }>;
35
35
  currentPosition(namespace: string): Promise<{
36
36
  id: string;
package/dist/index.d.ts CHANGED
@@ -352,8 +352,15 @@ export declare class SoranOwner {
352
352
  * auth demands an address the envelope signature cannot satisfy.
353
353
  */
354
354
  private assertSatisfiableAuth;
355
- /** Authorize exactly the selected policy with no additional child operations. */
356
- private assertChainPolicyIntent;
355
+ /**
356
+ * (SR-01 / SW-05) Default-deny for every legacy write: this source account, one invoke-contract operation on the
357
+ * intended contract, function and arguments, and exactly one source-account authorization whose root is that same
358
+ * call with NO sub-invocations. Every owner writer is a single call (issue, issue_batch, reclaim, renew,
359
+ * set_treasury, make_permanent, namespace transfers, set_resolver, set_chain_policy, set_subname_policy). The
360
+ * Registrar an owner talks to can be upgraded by whoever owned the namespace before them, so the callee is not a
361
+ * safe assumption: a recorded `token.transfer(owner -> attacker)` child call must never be signed.
362
+ */
363
+ private assertExactIntent;
357
364
  private attempt;
358
365
  private restore;
359
366
  private confirm;
package/dist/index.js CHANGED
@@ -930,25 +930,30 @@ export class SoranOwner {
930
930
  : `${fn}: this operation must be authorized by ${required}, but the signer is ${pub} — use that account's signer`, contractId, fn);
931
931
  }
932
932
  }
933
- /** Authorize exactly the selected policy with no additional child operations. */
934
- assertChainPolicyIntent(prepared, pub, contractId, fn, args) {
935
- if (!["set_chain_policy", "set_subname_policy"].includes(fn))
936
- return;
933
+ /**
934
+ * (SR-01 / SW-05) Default-deny for every legacy write: this source account, one invoke-contract operation on the
935
+ * intended contract, function and arguments, and exactly one source-account authorization whose root is that same
936
+ * call with NO sub-invocations. Every owner writer is a single call (issue, issue_batch, reclaim, renew,
937
+ * set_treasury, make_permanent, namespace transfers, set_resolver, set_chain_policy, set_subname_policy). The
938
+ * Registrar an owner talks to can be upgraded by whoever owned the namespace before them, so the callee is not a
939
+ * safe assumption: a recorded `token.transfer(owner -> attacker)` child call must never be signed.
940
+ */
941
+ assertExactIntent(prepared, pub, contractId, fn, args) {
937
942
  if (prepared.source !== pub || prepared.operations.length !== 1)
938
- throw new OwnerError("unexpected chain policy transaction", contractId, fn);
943
+ throw new OwnerError(`${fn}: unexpected transaction source or operation count`, contractId, fn);
939
944
  const op = prepared.operations[0];
940
945
  if (op.type !== "invokeHostFunction" || (op.source !== undefined && op.source !== pub) || op.func.type !== "hostFunctionTypeInvokeContract")
941
- throw new OwnerError("unexpected chain policy operation", contractId, fn);
946
+ throw new OwnerError(`${fn}: unexpected operation`, contractId, fn);
942
947
  const call = op.func.invokeContract;
943
948
  if (Address.fromScAddress(call.contractAddress).toString() !== contractId || call.functionName.toString() !== fn ||
944
949
  call.args.length !== args.length || call.args.some((value, i) => value.toXDR("base64") !== args[i].toXDR("base64")))
945
- throw new OwnerError("chain policy operation differs from selected intent", contractId, fn);
950
+ throw new OwnerError(`${fn}: operation differs from selected intent`, contractId, fn);
946
951
  if (!op.auth || op.auth.length !== 1 || op.auth[0].credentials.type !== "sorobanCredentialsSourceAccount")
947
- throw new OwnerError("chain policy requires exactly the source owner authorization", contractId, fn);
952
+ throw new OwnerError(`${fn}: requires exactly the source owner authorization`, contractId, fn);
948
953
  const root = op.auth[0].rootInvocation;
949
954
  if (root.function.type !== "sorobanAuthorizedFunctionTypeContractFn" || root.subInvocations.length ||
950
955
  root.function.contractFn.toXDR("base64") !== call.toXDR("base64"))
951
- throw new OwnerError("chain policy authorization differs from selected intent", contractId, fn);
956
+ throw new OwnerError(`${fn}: authorization differs from selected intent (unexpected nested authorization)`, contractId, fn);
952
957
  }
953
958
  async attempt(contractId, fn, args, errNames) {
954
959
  const pub = await this.signer.publicKey();
@@ -980,7 +985,7 @@ export class SoranOwner {
980
985
  const prepared = rpc.assembleTransaction(tx, sim).build();
981
986
  this.assertNetworkFee(prepared.fee, fn);
982
987
  this.assertSatisfiableAuth(prepared, pub, contractId, fn);
983
- this.assertChainPolicyIntent(prepared, pub, contractId, fn, args);
988
+ this.assertExactIntent(prepared, pub, contractId, fn, args);
984
989
  // The hash is fixed before signatures — compute it now so every failure
985
990
  // past this point can carry it (the "re-check before retrying" contract).
986
991
  const txHash = toHex(prepared.hash()); // (SDK17) hash() is Uint8Array
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sorandomains/owner",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "Run your Soran namespace on Stellar: configure claims, issue names and manage namespace policies.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -16,7 +16,7 @@
16
16
  "typecheck": "tsc --noEmit",
17
17
  "prepublishOnly": "tsc",
18
18
  "check:browser": "esbuild dist/index.js --bundle --platform=browser --external:@stellar/stellar-sdk --outfile=browser-check.js --legal-comments=none --log-level=error && node -e \"const fs=require('fs');const s=fs.readFileSync('browser-check.js','utf8');fs.unlinkSync('browser-check.js');if(/\\bBuffer\\b/.test(s)){console.error('FAIL: bare Buffer reference in browser bundle');process.exit(1)}console.log('browser bundle clean')\"",
19
- "test": "node --import tsx test/decode-failure.test.mts && node --import tsx --test test/integration.test.mts test/hardening.test.mts test/error-codes.test.mts test/native-*.test.mts && node --import tsx --test test/funding.test.mts test/funding-history.test.mts test/funding-bounds.test.mts && node --import tsx --test test/multichain.test.mts && node --import tsx --test test/subnames.test.mts",
19
+ "test": "node --import tsx test/decode-failure.test.mts && node --import tsx --test test/integration.test.mts test/hardening.test.mts test/error-codes.test.mts test/nested-authorization.test.mts test/native-*.test.mts && node --import tsx --test test/funding.test.mts test/funding-history.test.mts test/funding-bounds.test.mts && node --import tsx --test test/multichain.test.mts && node --import tsx --test test/subnames.test.mts",
20
20
  "test:hardening": "node --import tsx --test test/hardening.test.mts"
21
21
  },
22
22
  "license": "MIT",
@@ -42,10 +42,10 @@
42
42
  },
43
43
  "devDependencies": {
44
44
  "@stellar/stellar-sdk": "^17.0.1",
45
- "@types/node": "^22.20.1",
46
- "esbuild": "^0.25.0",
45
+ "@types/node": "^26.6.3",
46
+ "esbuild": "^0.28.2",
47
47
  "tsx": "^4.19.2",
48
- "typescript": "^5.7.3"
48
+ "typescript": "^7.0.2"
49
49
  },
50
50
  "exports": {
51
51
  ".": {