Ecency canonical to /@user/permlink, refresh app registry, validate data - #16
Conversation
apps.json: - ecency and esteem canonical is now https://ecency.com/@{username}/{permlink}. ecency.com serves a 308 from /{category}/@{author}/{permlink} to that form, so the registry was pointing every frontend at a redirect. - leofinance renamed to InLeo and repointed to inleo.io. - Added leothreads, liketu, scrobble.life and waivio, which publish today but had no entry, so their posts canonicalised to whichever frontend rendered them. - Removed clicktrackprofit, proofofbrain, eskateraleigh, stemsocial and inji. ctptalk.com now 301s off-site to an unrelated domain, proofofbrain.io serves a parked-domain redirect, eskateraleigh.com has no NS or SOA records, stem.openhive.network 404s on every path, and inji.com returns a persistent 522. Dropping the key makes apps[identifier] falsy so consumers fall back to their own scheme, which is the correct behaviour for an app with no live home. - Keys sorted. Lists: - bad-domains.json was a strict subset of spaminator-domains.json; pulled in the 49 domains it was missing. - Added the punycode form of the one homograph entry. new URL().hostname always yields punycode, so the Unicode-only entry could never match a parsed link. - Dropped two entries from spaminator-all.json that cannot be Hive accounts. - Sorted and de-duplicated every list. README: - The canonical recipe threw a TypeError on apps that have no url_scheme, and beempy and steempress are both in the registry and in active use. It also assumed json_metadata was an object and app was a string. Rewritten to handle all three, plus case-insensitive identifiers. - Bad-actor and bad-domain recipes use a Set, normalise input, parse the URL instead of regexing it, and walk parent domains so subdomains are caught. - Documented every file and that url_scheme is optional. Tooling: - scripts/validate.mjs checks list shape, sorting, duplicates, casing, good/bad overlap and apps.json url_scheme placeholders. Runs on pull requests and before publish. - Workflows moved off checkout@v1, setup-node@v1 and Node 12. - package.json gained description, keywords, homepage, bugs and a files allowlist, so .github and lockfiles no longer ship to npm.
…script 1. bad-domains.json listed the bare public suffix web.app, which the parent-domain walk turns into a match for every Firebase-hosted site. The entry arrived in this PR via the spaminator sync and the specific abusive host, security-alert-portal.web.app, was already listed, so the suffix is removed from both domain lists. Added a validator guard against the whole class, using a vendored snapshot of the Public Suffix List. It rejects web.app, github.io, co.uk and the other 8,797 multi-label rules. The snapshot is dev-only, not published, and scripts/update-public-suffix-list.mjs refreshes it. 2. The bad-domain recipe left a terminal DNS dot on the hostname, so https://steemit24.cf./ resolved like the listed domain but returned false. Normalised with .replace(/\.$/, "") before matching. 3. validate.mjs skipped the homepage check for entries without a url_scheme, which is exactly the beempy and steempress shape. Moved name and homepage above the optional-scheme branch. 4. package.json advertised a test script whose file the files allowlist does not publish. Dropped the scripts block; CI already calls the validator directly, and README documents it for contributors. All 25 validator guards re-verified by mutating the data. The bad-domain regression cases are run against the recipe extracted from README itself, so the documented code cannot drift from what was tested.
|
All four fixed in a88eb05..HEAD. Thanks, #1 was mine and it was a bad one. 1.
Since the entry came in from an upstream import, removing it alone would not stop the next one, so I checked all three lists against the Public Suffix List: 2. Trailing dot (Medium). Confirmed: 3. Homepage check (Medium). Confirmed. Moved 4. Test script (Low). Confirmed. Dropped the VerificationRegression cases are run against the
Validator guards are now 25, each re-verified by mutating the data and confirming it fails; all 25 fire and the clean tree passes. |
1. The updater flattened "*." and "!" prefixes, which is wrong in both directions. Dropping "*.ck" lost the public suffix foo.ck, and rewriting "!www.ck" as "www.ck" marked a registrable domain as a suffix. 283 wildcard and 8 exception rules were affected. The snapshot now stores all three rule types verbatim, and validate.mjs implements the PSL algorithm: exception rules win outright, otherwise the longest matching rule prevails, with the implicit "*" as fallback. Unicode rules are converted to punycode at fetch time, which is the form the data files store and the form new URL().hostname produces; 459 rules were unicode and could never have matched a punycode entry. Verified against wildcards (foo.ck, foo.kawasaki.jp), exceptions (www.ck, city.kawasaki.jp), registrable subdomains and a punycode IDN rule. 2. The canonical recipe assumed an object-form app carried a string name, so {app: {name: 42}} threw "raw?.split is not a function". json_metadata is arbitrary author-supplied JSON, so the type is now checked before any string method. Covered {app:{name:42}}, {app:{}}, {app:42}, {app:null}, {app:[]} and json_metadata:null; none throw. 3. validate.mjs accepted any truthy value as the registry, so an apps.json of [] passed while reporting "0 apps". It now requires a non-null, non-array object and rejects an empty registry. apps.json of null previously reached Object.keys(null) and failed with a stack trace instead of a message. Guards are now 31, each verified by mutation. Added four negative cases so the suffix check cannot regress into rejecting registrable domains.
|
All three fixed in 1. PSL rule types (Medium). Confirmed, and wrong in both directions as you describe. The snapshot now stores all three rule types verbatim and One more problem in the same vendoring step: 459 upstream rules are unicode (
I also added four negative mutation cases ( 2. Non-string app name (Medium). Confirmed: 3. apps.json shape (Medium). Confirmed: VerificationThe PSL cases run against the algorithm read out of Guards are now 31 must-fail plus 4 must-pass, all re-verified by mutating the data; every one behaves correctly and the clean tree passes. |
1. The homepage check coerced with String(), so ["https://example.com"] passed by array-to-string coercion. It now requires a string, then https, then a parseable URL. Verified against array, object, number, boolean, null and "https://" with no host; a valid string and an absent homepage still pass. 2. The updater stripped every upstream comment, including the Public Suffix List's MPL 2.0 notice, leaving the snapshot with no licence while the repository declares MIT. The notice is now carried into the generated file, read from upstream rather than hardcoded so a reworded notice still lands, and the updater refuses to write if it cannot find it. README records that the snapshot is MPL 2.0 and dev-only, outside the files allowlist, so the published package remains MIT. Guards are now 34 must-fail plus 6 must-pass, all verified by mutation, and the licence guard was verified by serving the updater a list with the notice removed.
|
Both fixed in 1. Non-string homepage (Medium). Confirmed: Verified against array, object, number, boolean, This was the last field checked by coercion. 2. MPL 2.0 notice (Low). Confirmed, and worth fixing properly rather than pasting the text in. The notice is now read from the upstream file and carried into the generated snapshot, so a reworded upstream notice still lands, and the updater refuses to write if it cannot find one. Verified by serving it a copy of the list with the notice stripped: The generated file now opens with the upstream Exhibit A text followed by attribution: README records the same, including that the snapshot sits outside the Guards are now 34 must-fail plus 6 must-pass, all verified by mutation, plus the licence guard above. |
Prepares
1.4.0.Ecency canonical
ecencyandesteemnow usehttps://ecency.com/@{username}/{permlink}.ecency.comalready serves a 308 from/{category}/@{author}/{permlink}to the short form, so the registry was telling every frontend to emit a canonical URL that permanently redirects. Links posted on-chain have already moved: in a sample of recent posts,/@author/permlinkoutnumbered the category form.Registry accuracy
leofinancerenamed to InLeo and repointed toinleo.io, which is where its post URLs resolve.Added four apps that publish today but had no entry, so their posts canonicalised to whichever frontend happened to render them:
leothreadshttps://inleo.io/@{username}/{permlink}liketuhttps://liketu.com/post/{username}/{permlink}scrobble.lifehttps://scrobble.life/p/{username}/{permlink}waiviohttps://www.waivio.com/@{username}/{permlink}scrobble.lifeandwaiviowere checked against live posts:/@user/permlink308s to/p/...on the former, and the apex 301s towwwon the latter.Removed entries
Five entries pointed at domains that are no longer the app:
clicktrackprofitctptalk.comandwww.ctptalk.comboth 301 to an unrelated domain that lands on a gambling siteproofofbraineskateraleigheskateraleigh.comhas no NS and no SOA recordstemsocialstem.openhive.network404s on every path; its CNAME target is an unpublished GitHub Pages site. Its#!/scheme was never usable as a canonical URL anywayinjiRemoving the key rather than blanking
url_schemeis deliberate:apps[identifier]becomes falsy and consumers fall back to their own scheme. That is correct behaviour for an app with no live home, and it works on old and new copies of the README recipe alike.clicktrackprofitis the urgent one, since canonical links pass ranking authority to whoever holds the domain now.README recipe fixes
The documented canonical recipe threw a
TypeErroron any entry without aurl_scheme, andbeempyandsteempressare both in the registry and in active use (beempywas 1.2% of the sample). It also assumedjson_metadatawas an object andappwas a string, neither of which holds across all API methods and apps.Old vs new, same inputs:
beempy(nourl_scheme)TypeErrorsteempress(nourl_scheme)TypeErrorappwritten as an objectTypeErrorjson_metadataas a string (condenser_api)Ecency/3.1.4(uppercase)The bad-actor and bad-domain recipes now use a
Set, normalise input, and parse the URL instead of regexing it. Two consequences:new URL().hostnamereturns punycode, so homograph entries finally match, and walking parent domains catcheslogin.phishing-site.tkagainst an entry forphishing-site.tk.Data
bad-domains.jsonwas a strict subset ofspaminator-domains.json. Pulled in the 49 domains it was missing, sincebad-domains.jsonis the list the README tells developers to use.spaminator-all.jsonthat cannot be Hive accounts (names must start with a letter).Tooling
scripts/validate.mjsruns on pull requests and before publish. It checks list shape, sorting, duplicates, casing, whitespace, good/bad overlap, homograph coverage, andapps.jsonplaceholders,https, and hash fragments. Every guard was verified by mutating the data and confirming it fails; all 21 fire.Workflows moved off
checkout@v1,setup-node@v1and Node 12.package.jsongained afilesallowlist, so.githuband lockfiles no longer ship to npm.Left for a separate decision
steemitstill canonicalises Hive posts tosteemit.com. Those are pre-fork posts that exist on both chains, so it is a community call rather than a data fix.