wow-secret-lint 1.4.2 → 1.6.0
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/CHANGELOG.md +119 -0
- package/README.md +153 -14
- package/action/index.mjs +12 -4
- package/action.yml +6 -2
- package/bin/wow-secret-lint.mjs +67 -10
- package/data/api-snapshot.json +1 -1
- package/package.json +1 -1
- package/src/analyze.mjs +380 -24
- package/src/apidata.mjs +89 -13
- package/src/baseline.mjs +0 -0
- package/src/index.mjs +39 -9
- package/src/report.mjs +11 -0
- package/src/rules.mjs +138 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,124 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.6.0 - 2026-09-07
|
|
4
|
+
|
|
5
|
+
The four things 1.5.0 left on the table. No rule changes; the 12.0 and 12.1 baselines and
|
|
6
|
+
the 12-addon corpus are byte-identical to 1.5.0.
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **`--baseline=<path>` and `--write-baseline=<path>`.** Record the findings an addon has
|
|
11
|
+
today, then gate CI on what is new. Entries key on file, rule and message with a count,
|
|
12
|
+
never on line and column, so edits above a known finding do not resurface it. Entries that
|
|
13
|
+
no longer match are counted and the summary says to re-record. The GitHub Action takes the
|
|
14
|
+
file through a new `baseline` input.
|
|
15
|
+
- **Widget types cross files, in `.toc` load order.** An AuraContainer, AuraButton, animation
|
|
16
|
+
group or protected cooldown stored in a global, in `_G[...]`, or in the addon's private table
|
|
17
|
+
(`local _, ns = ...` or `local ns = select(2, ...)`) is known to every file loaded after the
|
|
18
|
+
one that created it, under whatever local name each file gives that table. A file that
|
|
19
|
+
redeclares the name as a local keeps its own meaning. Separate targets on one command line
|
|
20
|
+
do not share widgets.
|
|
21
|
+
- **WSL021 sees metatable hooks.** A handler installed with
|
|
22
|
+
`hooksecurefunc(getmetatable(<a Cooldown>).__index, name, fn)` runs for every cooldown in
|
|
23
|
+
the game, so its first parameter is a cooldown that may be protected and a protected method
|
|
24
|
+
called on it is reported. Hooking a protected frame directly counts too. The guard the rule
|
|
25
|
+
asks for, `if self:IsProtected() then return end`, is recognised and clears the frame for
|
|
26
|
+
the rest of its block; `IsForbidden()` is not a substitute. OmniCC is clean under this
|
|
27
|
+
because its handlers call a proxy `Cooldown` of their own, never `self`.
|
|
28
|
+
- **`--version` prints a second line** naming the snapshot patch and build the run uses. The
|
|
29
|
+
first line is still the bare version. JSON output carries `snapshot.patch` and
|
|
30
|
+
`snapshot.build` too.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- An early-exit guard (`if issecretvalue(x) then return end`) applied to the rest of its
|
|
35
|
+
block was never released when the block ended, so the path stayed cleared for the rest of
|
|
36
|
+
the file. No fixture and no corpus file changed output when this was fixed, but it was
|
|
37
|
+
wrong, and the new `IsProtected` guard would have inherited it.
|
|
38
|
+
|
|
39
|
+
### Verified
|
|
40
|
+
|
|
41
|
+
- 249 tests. The recorded 12.0 and 12.1 baselines match byte for byte. The 12-addon corpus
|
|
42
|
+
reports 379 errors and 121 warnings under both `--patch=12.1` and `--patch=12.1.5`, and
|
|
43
|
+
489/11 under `--strict`, all unchanged; 4,218 further files at current HEADs report nothing
|
|
44
|
+
from WSL019-WSL021.
|
|
45
|
+
- Along the way: a `local Frame = Frame` upvalue cache sent the hook-target resolver into
|
|
46
|
+
infinite recursion on KkthnxUI and SpartanUI, caught by the corpus run before release.
|
|
47
|
+
|
|
48
|
+
## 1.5.0 - 2026-09-07
|
|
49
|
+
|
|
50
|
+
Patch 12.1.5 (build 69594) went live on 2026-09-03. This catches the linter up to it: the
|
|
51
|
+
snapshot, three new rules, and a new default `--patch` surface. `--patch=12.0` and
|
|
52
|
+
`--patch=12.1` produce v1.2.0's and v1.4.2's output byte for byte, which is now enforced by
|
|
53
|
+
recorded baselines rather than asserted.
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
- **WSL019** (error): querying the progress or running state of an animation that carries
|
|
58
|
+
the new `QueryAnimationProgress` forbidden aspect, which *"prevents querying the progress
|
|
59
|
+
of an animation or whether it is running"*.
|
|
60
|
+
- **WSL020** (error): adding an animation to, or reparenting one onto, a group that carries
|
|
61
|
+
the new `AddAnimations` forbidden aspect, which *"prevents animations from being added or
|
|
62
|
+
reparented"*. The reparenting half is `SimpleAnim:SetParent`, where the aspect is checked
|
|
63
|
+
on the `parent` argument rather than on the receiver.
|
|
64
|
+
- **WSL021** (error): *"the `SetCooldown` and `Clear` cooldown APIs can no longer be called
|
|
65
|
+
from tainted code when the cooldown frame itself is protected."* Blizzard marked six
|
|
66
|
+
`FrameAPICooldown` methods `IsProtectedFunction = true` in 12.1.5 and none in 12.1, so the
|
|
67
|
+
method list is read from the snapshot rather than transcribed.
|
|
68
|
+
- **`--patch=12.1.5`, and it is the new default.** `PATCHES` is ordered now, so rules gate on
|
|
69
|
+
"at or after" a surface instead of a not-equals check against `12.0`.
|
|
70
|
+
- **`--patch=auto`** reads the addon's own `## Interface` number and checks the surface it
|
|
71
|
+
actually ships against: 120105 and up gets 12.1.5, 120100 gets 12.1, older gets 12.0. It
|
|
72
|
+
says so and falls back to the default when there is no `.toc` to read.
|
|
73
|
+
- **`--refresh-ref=<ref>`** rebuilds the snapshot from any branch or tag of the mirror, and
|
|
74
|
+
`--force` overrides the new guard that stops a refresh walking the snapshot backwards onto
|
|
75
|
+
an older client build. The mirror tags a build before it moves `live`, which is how 12.1.5
|
|
76
|
+
arrived here on the day it shipped; the weekly refresh workflow now reports that as a
|
|
77
|
+
no-op instead of a downgrade.
|
|
78
|
+
|
|
79
|
+
### Changed
|
|
80
|
+
|
|
81
|
+
- Refreshed `data/api-snapshot.json` from `Gethe/wow-ui-source@12.1.5`: 10,250 documented
|
|
82
|
+
functions and 760 structures, up from 10,099 and 752, with 20 `SecretReturns` (unchanged)
|
|
83
|
+
and 314 conditionally secret (up from 310). Every count is regenerated, not carried over.
|
|
84
|
+
The new globals are in it, so WSL006 and WSL007 stop analysing 12.1.5 code against a
|
|
85
|
+
snapshot that has never heard of `math.clamp`, `string.startswith`, `table.keys`,
|
|
86
|
+
`C_Intl`, `C_Weather`, `C_Timer.NewTimedSignalMap` or `CreateFrameWithOptions`. A file
|
|
87
|
+
passing a secret to `math.clamp`, `C_Intl.ToUpper` and `string.startswith` reports three
|
|
88
|
+
WSL006 errors against this snapshot and zero against 1.4.2's, which is the whole point of
|
|
89
|
+
the refresh. It also brought in a new conditional marker with no code change:
|
|
90
|
+
`SecretWhenLuaTableHasSecretKeys` on `table.count`, `table.getcountinfo` and
|
|
91
|
+
`table.isempty`.
|
|
92
|
+
- The snapshot now carries two more Blizzard markers, keyed by widget system because method
|
|
93
|
+
names collide across widget types: `ChecksForbiddenAspects` (which forbidden aspect a
|
|
94
|
+
method checks, and on which argument) and `IsProtectedFunction`. It also records the patch,
|
|
95
|
+
build, commit and ref it was generated from.
|
|
96
|
+
- A `local` bound to a folded string constant (`"PetActionButton" .. 4 .. "Cooldown"`) now
|
|
97
|
+
resolves the same way a plain string literal always did, and a numeric for-loop counter
|
|
98
|
+
inside one folds to a digit, so `_G["ActionButton" .. i .. "Cooldown"]` is recognised in
|
|
99
|
+
the loop every action-bar addon writes. A local or parameter that shares a Blizzard frame's
|
|
100
|
+
name is never taken for the frame.
|
|
101
|
+
- The GitHub Action's `patch` input defaults to `12.1.5` and accepts `auto`.
|
|
102
|
+
|
|
103
|
+
### Verified
|
|
104
|
+
|
|
105
|
+
- 230 tests pass. `--patch=12.0` and `--patch=12.1`, with and without
|
|
106
|
+
`--strict --conditional=warn`, reproduce v1.4.2's output over the whole fixture corpus
|
|
107
|
+
byte for byte, recorded before any of this work started and checked by
|
|
108
|
+
`test/patch1215.test.mjs`.
|
|
109
|
+
- On the 12-addon corpus the README measures, `--patch=12.1.5` reports the same 379 errors
|
|
110
|
+
and 121 warnings as `--patch=12.1`: the three new rules add nothing and take nothing away.
|
|
111
|
+
A second pass over each addon's current HEAD plus OmniCC, Dominos and Blizzard's own
|
|
112
|
+
interface code, 4,076 further files, also reports nothing from them. Four days in, nobody
|
|
113
|
+
has adopted the Pandemic animation APIs yet.
|
|
114
|
+
- One candidate finding was found and thrown out. An early draft treated a `Cooldown` created
|
|
115
|
+
under a secure-template button as protected, which flagged SpartanUI's quest button.
|
|
116
|
+
`ScriptRegion:IsProtected` says protection runs the other way: *"anchoring or parenting a
|
|
117
|
+
protected frame to another frame makes that frame implicitly protected as well"*, so the
|
|
118
|
+
child is not the one that changes. The inference was dropped and the rule now fires only
|
|
119
|
+
where the file names one of Blizzard's action-button cooldowns or builds a `Cooldown` from
|
|
120
|
+
a secure template.
|
|
121
|
+
|
|
3
122
|
## 1.4.2 - 2026-08-31
|
|
4
123
|
|
|
5
124
|
Data only. No rule, message or analysis behaviour changed.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Static analysis for World of Warcraft **retail** addons. It maps where your Lua touches Blizzard's secret-value API, and hard-fails your build on the things Blizzard documents as certain to break.
|
|
4
4
|
|
|
5
|
-
Patch 12.0 introduced [secret values](https://warcraft.wiki.gg/wiki/Secret_Values). A lot of the API now hands your addon a value you are allowed to store, pass around and print, but not to do arithmetic on, compare, index, call, or measure with `#`. Patch 12.1 (Curse of Ula'tek, live 2026-08-11) [widened that surface enormously](https://warcraft.wiki.gg/wiki/Patch_12.1.0/API_changes): every `UnitAura` API returns full secrets or nil while auras are secret, seventeen unit identity APIs joined them, and the new AuraButton/AuraContainer frames carry forbidden aspects. When tainted code does something disallowed, the wiki is blunt about the result:
|
|
5
|
+
Patch 12.0 introduced [secret values](https://warcraft.wiki.gg/wiki/Secret_Values). A lot of the API now hands your addon a value you are allowed to store, pass around and print, but not to do arithmetic on, compare, index, call, or measure with `#`. Patch 12.1 (Curse of Ula'tek, live 2026-08-11) [widened that surface enormously](https://warcraft.wiki.gg/wiki/Patch_12.1.0/API_changes): every `UnitAura` API returns full secrets or nil while auras are secret, seventeen unit identity APIs joined them, and the new AuraButton/AuraContainer frames carry forbidden aspects. Patch 12.1.5 (build 69594, live 2026-09-03) [added two more forbidden aspects and closed the cooldown APIs](https://warcraft.wiki.gg/wiki/Patch_12.1.5/API_changes). When tainted code does something disallowed, the wiki is blunt about the result:
|
|
6
6
|
|
|
7
7
|
> When an operation that is not allowed is performed, the result will be an **immediate** Lua error.
|
|
8
8
|
|
|
@@ -17,7 +17,7 @@ That one is [KkthnxUI#121](https://github.com/Kkthnx-Wow/KkthnxUI/issues/121), f
|
|
|
17
17
|
|
|
18
18
|
`wow-secret-lint` reads Blizzard's own generated API documentation, tracks which of your locals hold a secret, and tells you where you touch one.
|
|
19
19
|
|
|
20
|
-
**It reports, it does not accuse.** The rules that fail your build by default are the ones Blizzard states outright: `WSL008` (combat log registration errors),
|
|
20
|
+
**It reports, it does not accuse.** The rules that fail your build by default are the ones Blizzard states outright: `WSL008` (combat log registration errors), the 12.1 rules `WSL012`/`WSL013` (aura and identity APIs "will now either return full secrets or nil when called by addons"), `WSL014` (the symbol no longer exists) and `WSL017` (forbidden aspects), and the 12.1.5 rules `WSL019`/`WSL020` (the two new forbidden aspects) and `WSL021` (protected cooldown frames). The 12.0-era `SecretReturns` tier stays a warning, because whether those APIs really hand a secret to tainted code on every call is not something static analysis can confirm; run `--strict` to gate on it. [The reasoning is spelled out below](#the-open-question-on-severity) and it is worth two minutes before you wire this into CI.
|
|
21
21
|
|
|
22
22
|
## Install
|
|
23
23
|
|
|
@@ -93,6 +93,64 @@ Exit code 1. Under `--patch=12.0` the same file reports zero findings and exits
|
|
|
93
93
|
|
|
94
94
|
None of this comes from Blizzard's generated documentation, which carries no marker for aura secrecy. The 12.1 behaviour is published only in the patch notes, so these rules seed their taint from a list transcribed there, and `--patch` selects whether that list is active.
|
|
95
95
|
|
|
96
|
+
### The 12.1.5 surface
|
|
97
|
+
|
|
98
|
+
Patch 12.1.5 gave `CustomAuraButton` Pandemic-triggered animations, and paid for them with two new forbidden aspects: *"`QueryAnimationProgress`: prevents querying the progress of an animation or whether it is running"* and *"`AddAnimations`: prevents animations from being added or reparented."* The same notes close the cooldown APIs: *"the `SetCooldown` and `Clear` cooldown APIs can no longer be called from tainted code when the cooldown frame itself is protected."*
|
|
99
|
+
|
|
100
|
+
`Core/Pandemic.lua` builds a glow the documented way and then keeps poking it:
|
|
101
|
+
|
|
102
|
+
```lua
|
|
103
|
+
local group = glow:CreateAnimationGroup()
|
|
104
|
+
local fade = group:CreateAnimation("Alpha")
|
|
105
|
+
fade:SetDuration(0.5)
|
|
106
|
+
|
|
107
|
+
button:AddPandemicActiveAnimation(group)
|
|
108
|
+
|
|
109
|
+
if group:IsPlaying() then group:Stop() end
|
|
110
|
+
group:CreateAnimation("Scale")
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`Core/Cooldowns.lua` drives one of Blizzard's action buttons:
|
|
114
|
+
|
|
115
|
+
```lua
|
|
116
|
+
local cooldown = _G["ActionButton1Cooldown"]
|
|
117
|
+
cooldown:SetCooldown(GetTime(), 10)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
$ npx wow-secret-lint ./PandemicGlow
|
|
122
|
+
Core/Cooldowns.lua:2:1 error WSL021 cooldown:SetCooldown() cannot be called from tainted code in 12.1.5 because the cooldown frame itself is protected; drive your own Cooldown frame instead, or leave the protected one to Blizzard
|
|
123
|
+
Core/Pandemic.lua:10:4 error WSL019 querying the progress or running state of an animation group registered with a Pandemic trigger is disallowed by its QueryAnimationProgress forbidden aspect: group:IsPlaying()
|
|
124
|
+
Core/Pandemic.lua:11:1 error WSL020 adding an animation to an animation group registered with a Pandemic trigger is disallowed by its AddAnimations forbidden aspect: group:CreateAnimation()
|
|
125
|
+
3 errors, 0 warnings
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Exit code 1. The first five lines of `Core/Pandemic.lua` are Blizzard's own sample from the 12.1.5 notes and stay silent: building a group and filling it is fine, and the aspects arrive with the `AddPandemicActiveAnimation` call. `--patch=12.1` reports nothing at all in either file.
|
|
129
|
+
|
|
130
|
+
If your `.toc` is still on an older interface, `--patch=auto` reads the number and checks the surface that addon actually ships against:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
$ npx wow-secret-lint --patch=auto ./OldAddon # ## Interface: 120100
|
|
134
|
+
0 errors, 0 warnings
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Gating CI on new findings only
|
|
138
|
+
|
|
139
|
+
Nine of the twelve addons measured below fail CI on the default settings, some with over a hundred findings. Nobody adopts a linter that way. Record what is there today and gate on what is new:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npx wow-secret-lint ./MyAddon --write-baseline=wow-secret-lint.baseline.json
|
|
143
|
+
npx wow-secret-lint ./MyAddon --baseline=wow-secret-lint.baseline.json
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
$ npx wow-secret-lint --strict --baseline=baseline.json Core/UnitFrame.lua
|
|
148
|
+
2 findings suppressed by baseline baseline.json
|
|
149
|
+
0 errors, 0 warnings
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The baseline keys each finding on file, rule and message with a count, never on line and column, so editing code above a known finding does not resurface it. The message already names the value and the API it came from, which is what makes that stable. A third identical finding in a file that had two is reported; an entry that matches nothing any more is counted and the summary line says to re-record. Paths are relative to where you run, so record and lint from the same directory. Commit the file next to your `.toc`; the GitHub Action takes it through the `baseline` input.
|
|
153
|
+
|
|
96
154
|
### Exit codes
|
|
97
155
|
|
|
98
156
|
| Code | Meaning |
|
|
@@ -126,7 +184,8 @@ Inputs:
|
|
|
126
184
|
| Input | Default | Description |
|
|
127
185
|
| --- | --- | --- |
|
|
128
186
|
| `path` | `.` | addon folder, `.toc`, or `.lua`. Several allowed, space separated |
|
|
129
|
-
| `patch` | `12.1` | patch surface to check; `
|
|
187
|
+
| `patch` | `12.1.5` | patch surface to check; an older value pins that rule set, `auto` reads the addon `.toc` |
|
|
188
|
+
| `baseline` | `""` | path to a file written with `--write-baseline`; only findings not in it are reported |
|
|
130
189
|
| `args` | `""` | extra CLI arguments, e.g. `--conditional=warn --disable=WSL011` |
|
|
131
190
|
| `format` | `github` | `github`, `stylish`, or `json` |
|
|
132
191
|
|
|
@@ -134,7 +193,7 @@ Outputs: `errors`, `warnings`.
|
|
|
134
193
|
|
|
135
194
|
## Rules
|
|
136
195
|
|
|
137
|
-
Every rule maps to one sentence Blizzard publishes. `wow-secret-lint --rules` prints the table with the source for each. WSL008, WSL012, WSL013, WSL014, WSL017 and
|
|
196
|
+
Every rule maps to one sentence Blizzard publishes. `wow-secret-lint --rules` prints the table with the source for each. WSL008, WSL012, WSL013, WSL014, WSL017, WSL018, WSL019, WSL020 and WSL021 fail a build by default; the rest are warnings.
|
|
138
197
|
|
|
139
198
|
| Rule | Severity | What it catches |
|
|
140
199
|
| --- | --- | --- |
|
|
@@ -156,6 +215,9 @@ Every rule maps to one sentence Blizzard publishes. `wow-secret-lint --rules` pr
|
|
|
156
215
|
| WSL016 | warning | 12.1: `showCountdownFrame` passed to a private-aura API |
|
|
157
216
|
| WSL017 | error | 12.1: forbidden-aspect operation on an AuraButton or AuraContainer |
|
|
158
217
|
| WSL018 | error | 12.1: call of an aura API reached by index, slot or instance id |
|
|
218
|
+
| WSL019 | error | 12.1.5: querying the progress or running state of an animation that carries `QueryAnimationProgress` |
|
|
219
|
+
| WSL020 | error | 12.1.5: adding or reparenting an animation on a group that carries `AddAnimations` |
|
|
220
|
+
| WSL021 | error | 12.1.5: `SetCooldown`/`Clear` on a cooldown frame Blizzard protects |
|
|
159
221
|
|
|
160
222
|
WSL008 comes from the [12.0.0 API changes](https://warcraft.wiki.gg/wiki/Patch_12.0.0/API_changes): *"COMBAT_LOG_EVENT and COMBAT_LOG_EVENT_UNFILTERED will error when trying to register them."* Use `COMBAT_LOG_EVENT_INTERNAL_UNFILTERED` or the `C_CombatLog` namespace.
|
|
161
223
|
|
|
@@ -185,6 +247,37 @@ The rename is the minimal fix, but it is probably not the one you want. Reportin
|
|
|
185
247
|
|
|
186
248
|
**WSL017** knows a local is an AuraContainer or AuraButton when it comes from `CreateFrame("AuraContainer", ...)`/`CreateFrame("AuraButton", ...)` or arrives as the `initializeFrame` callback parameter of `AddAuraGroup`/`AddAuraSlot`/`AddItemEnchantment`, and it follows the value into a table field, so `self.buttons[i] = button` keeps the type. On those it flags `SetScript`/`HookScript`, event registration, input calls (`EnableMouse`, `RegisterForClicks`, `Click`, ...) and focus queries (`IsMouseOver`, ...), naming the forbidden aspect each one trips. Construction itself is never flagged.
|
|
187
249
|
|
|
250
|
+
### The 12.1.5 rules in detail
|
|
251
|
+
|
|
252
|
+
**WSL019** and **WSL020** are the two forbidden aspects 12.1.5 added. The notes name them outright: *"`QueryAnimationProgress`: prevents querying the progress of an animation or whether it is running"* and *"`AddAnimations`: prevents animations from being added or reparented"*. Unlike the 12.1 aura rules, this surface is in Blizzard's generated documentation, so the method lists are not transcribed by hand: every affected method carries `ChecksForbiddenAspects`, and the snapshot keeps them keyed by widget system. That is 8 methods on `SimpleAnimAPI` and 8 on `SimpleAnimGroupAPI` for `QueryAnimationProgress`, and `SimpleAnimGroupAPI:CreateAnimation` plus `SimpleAnimAPI:SetParent` for `AddAnimations`. The second of those is the "or reparented" half, and it is checked on the `parent` argument rather than on the receiver.
|
|
253
|
+
|
|
254
|
+
The aspects are applied when an animation group is registered with a Pandemic trigger, which is what the notes mean by *"two new forbidden aspects were added to support this"* and *"animations used by Pandemic triggers also gain the existing `ChangeAnimationTarget` forbidden aspect"*. Blizzard's own sample in those notes builds the group, adds its animations, and registers it last, so an addon that follows that order is silent. Everything after `AddPandemicEnterAnimation`/`AddPandemicActiveAnimation`/`AddPandemicLeaveAnimation` is not:
|
|
255
|
+
|
|
256
|
+
```lua
|
|
257
|
+
local group = glow:CreateAnimationGroup()
|
|
258
|
+
local fade = group:CreateAnimation("Alpha") -- fine, the group is still yours
|
|
259
|
+
button:AddPandemicActiveAnimation(group) -- from here the client owns it
|
|
260
|
+
|
|
261
|
+
if group:IsPlaying() then end -- WSL019
|
|
262
|
+
group:CreateAnimation("Scale") -- WSL020
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Tracking follows the group through aliases, into a table field, and onto the animations that were already inside it when it was registered, and drops it the moment the local is reassigned. Guards do not silence either rule, for the same reason they do not silence WSL018: the call itself is what errors.
|
|
266
|
+
|
|
267
|
+
**WSL021** covers *"the `SetCooldown` and `Clear` cooldown APIs can no longer be called from tainted code when the cooldown frame itself is protected"*. The six methods are the ones Blizzard newly marked `IsProtectedFunction = true` on `FrameAPICooldown` in 12.1.5 and marked on none of them in 12.1: `SetCooldown`, `SetCooldownDuration`, `SetCooldownFromDurationObject`, `SetCooldownFromExpirationTime`, `SetCooldownUNIX` and `Clear`. Reading a protected cooldown is untouched, and so is a cooldown frame your addon created.
|
|
268
|
+
|
|
269
|
+
A cooldown counts as protected in two cases. It is one of Blizzard's action-button cooldowns, named directly, reached through `_G[...]` including a name built by concatenation with a numeric loop counter (`"ActionButton" .. i .. "Cooldown"` resolves), or reached as the `cooldown`/`chargeCooldown`/`lossOfControlCooldown` field of the button. A local or parameter that happens to share one of those names is left alone. The button names come from `Blizzard_ActionBar/Shared/ActionBar.lua`, which names each one `"ActionButton"..i`, `"StanceButton"..i`, `"PetActionButton"..i`, `"PossessButton"..i` or `actionBarName.."Button"..i` for the seven MultiBars, and `ActionButtonTemplate.xml` names the cooldown child `$parentCooldown` with `parentKey="cooldown"`. Or your own `Cooldown` inherits a `Secure*Template`, which is what "explicitly specified as protected at the time of creation" means for an addon.
|
|
270
|
+
|
|
271
|
+
The other way to reach a protected cooldown is not to name it at all. OmniCC and its relatives do `hooksecurefunc(getmetatable(ActionButton1Cooldown).__index, "SetCooldown", handler)`, which installs the handler on the Cooldown widget type itself, so it runs for every cooldown in the game including the protected ones. Inside such a handler the first parameter is treated as a cooldown that may be protected, and a protected method called on it is reported. `hooksecurefunc(ActionButton1Cooldown, ...)` on the frame directly counts too, `local mt = getmetatable(x).__index` is followed, and a `Cooldown` the addon created works as the metatable source, since the metatable is shared. The correct guard is recognised: `if self:IsProtected() then return end` clears the frame for the rest of the block. `IsForbidden()` does not, because forbidden and protected are different properties. OmniCC itself is clean under this, as it should be: its handlers hand the call to a proxy `Cooldown` frame of their own rather than calling `self`.
|
|
272
|
+
|
|
273
|
+
A cooldown your addon parents to a secure button is **not** protected, and that took a false positive to get right. `ScriptRegion:IsProtected` says protection travels the other way: *"anchoring or parenting a protected frame to another frame makes that frame implicitly protected as well"*, recursively, so the child is not the one that changes.
|
|
274
|
+
|
|
275
|
+
```lua
|
|
276
|
+
local cd = _G["ActionButton1Cooldown"]
|
|
277
|
+
cd:SetCooldown(GetTime(), 10) -- WSL021
|
|
278
|
+
cd:IsPaused() -- silent, reading is not a protected function
|
|
279
|
+
```
|
|
280
|
+
|
|
188
281
|
### What it will never flag
|
|
189
282
|
|
|
190
283
|
The wiki has an explicit allowed list, and flagging any of it is a false positive that gets a linter uninstalled. Each of these has a passing negative test in `test/`:
|
|
@@ -216,7 +309,7 @@ npx wow-secret-lint ./MyAddon --secret-guard=IsLocked --access-guard=CanRead
|
|
|
216
309
|
|
|
217
310
|
Blizzard marks a second tier in the generated docs: functions that return a secret only while a specific restriction is active. `C_Spell.GetSpellCooldown` carries `SecretWhenCooldownsRestricted`; `C_LFGList.GetSearchResultInfo` carries `SecretInChatMessagingLockdown`; `UnitGUID`, `UnitName` and `UnitClass` carry `SecretWhenUnitIdentityRestricted`. In practice that means "secret in PvP, in restricted instances, and for non-player or pet units in combat". Your code works fine right up until it does not.
|
|
218
311
|
|
|
219
|
-
|
|
312
|
+
From `--patch=12.1` on, the identity APIs WSL013 covers are promoted out of this tier and report as errors regardless of the `--conditional` setting. The rest of the tier is **off by default**, because measured against 12 real addons it produces about one warning per Lua file, which is a tax rather than a signal. Turn it on for a deeper audit:
|
|
220
313
|
|
|
221
314
|
```bash
|
|
222
315
|
npx wow-secret-lint ./MyAddon --conditional=warn # report as warnings
|
|
@@ -249,12 +342,13 @@ Run against 12 real retail addons (BigWigs, LittleWigs, DBM, WeakAuras, Details,
|
|
|
249
342
|
|
|
250
343
|
| Mode | Errors | Warnings | Addons that would fail CI |
|
|
251
344
|
| --- | --- | --- | --- |
|
|
252
|
-
| `--patch=12.1` (default) | 379 | 121 | 9 of 12 |
|
|
345
|
+
| `--patch=12.1.5` (default) | 379 | 121 | 9 of 12 |
|
|
346
|
+
| `--patch=12.1` | 379 | 121 | 9 of 12 |
|
|
253
347
|
| `--patch=12.1 --strict` | 489 | 11 | 9 of 12 |
|
|
254
348
|
| `--patch=12.0` | 20 | 90 | 3 of 12 |
|
|
255
349
|
| `--patch=12.0 --strict` | 110 | 0 | 5 of 12 |
|
|
256
350
|
|
|
257
|
-
The two `12.0` rows are identical to what v1.2.0 reported, which is the point of the flag. Everything above them is the 12.1 surface landing, concentrated exactly where the [PTR forum thread](https://us.forums.blizzard.com/en/wow/t/minicc-and-similar-addons-might-be-partially-broken-in-121/2310937) predicted breakage:
|
|
351
|
+
The two `12.0` rows are identical to what v1.2.0 reported, and the `12.1` rows to what v1.4.2 reported, which is the point of the flag. Everything above them is the 12.1 surface landing, concentrated exactly where the [PTR forum thread](https://us.forums.blizzard.com/en/wow/t/minicc-and-similar-addons-might-be-partially-broken-in-121/2310937) predicted breakage:
|
|
258
352
|
|
|
259
353
|
| Rule | Count | Where |
|
|
260
354
|
| --- | --- | --- |
|
|
@@ -264,9 +358,12 @@ The two `12.0` rows are identical to what v1.2.0 reported, which is the point of
|
|
|
264
358
|
| WSL014 | 10 | mostly `GetInventorySlotInfo`, plus `GetInspectSpecialization`, `GetWeaponEnchantInfo` and SpartanUI's `UIParentLoadAddOn` |
|
|
265
359
|
| WSL016 | 3 | oUF `privateauras.lua:133` and both copies vendored into KkthnxUI and SpartanUI |
|
|
266
360
|
| WSL017 | 0 | no addon in the corpus uses AuraContainers yet; exercised by fixtures |
|
|
361
|
+
| WSL019, WSL020, WSL021 | 0 | see below |
|
|
267
362
|
|
|
268
363
|
Two numbers are worth reading together. **DBM and BigWigs report no WSL012 at all**, because both already wrap every aura lookup in an `issecretvalue` guard and the analysis credits that idiom. **BigWigs still has four WSL018 findings**, because guarding the result does not help when the call itself is what errors. That pair is the honest summary of where the ecosystem is: the careful addons did the guard work, and the guard work is not enough.
|
|
269
364
|
|
|
365
|
+
The three 12.1.5 rules report **nothing** on this corpus, and nothing on a second pass on 2026-09-07 over each addon's current HEAD plus OmniCC, Dominos and Blizzard's own interface code: 4,218 more files, zero findings. Cross-file widget typing and metatable-hook awareness, both added in 1.6.0, changed no number in either pass. Four days after the patch, no addon in reach has adopted the Pandemic animation APIs, and none of them drives Blizzard's action-button cooldowns in a way a static reader can see. OmniCC gets closest and is the honest limit of the rule: it hooks `SetCooldown` on `getmetatable(ActionButton1Cooldown).__index` and dispatches through a proxy with the method name as a string, so the frame is never named at the call site. WSL021 sees a protected cooldown only where the file names one.
|
|
366
|
+
|
|
270
367
|
A sample of the WSL012/WSL013 findings across every affected repo was read against its source by hand; each one performs an operation the 12.1 notes document as disallowed, on a value those notes document as secret, with no guard in scope. The pre-12.1 findings are unchanged from v1.2.0, where all 110 strict findings were read by hand.
|
|
271
368
|
|
|
272
369
|
Contamination from Classic-flavour code is 0, checked by resolving every retail `.toc` in each repo and matching it against the findings.
|
|
@@ -295,22 +392,49 @@ Settling it needs someone to run a flagged line in game and watch what happens,
|
|
|
295
392
|
```
|
|
296
393
|
--format=<stylish|json|github> output format (default: stylish)
|
|
297
394
|
--game=<retail|classic> classic has no secret values and exits 0 immediately
|
|
298
|
-
--patch=<12.0|12.1>
|
|
299
|
-
|
|
395
|
+
--patch=<12.0|12.1|12.1.5|auto> patch surface the built-in rules check (default: 12.1.5);
|
|
396
|
+
an older value pins that rule set, auto reads the addon .toc
|
|
300
397
|
--strict raise SecretReturns findings from warning to error
|
|
301
398
|
--conditional=<off|warn|error> conditionally secret APIs (default: off)
|
|
302
399
|
--secret-guard=<names> extra is-secret wrapper functions, comma separated
|
|
303
400
|
--access-guard=<names> extra can-access wrapper functions, comma separated
|
|
304
401
|
--disable=<ids> rule ids to silence, e.g. WSL010,WSL011
|
|
305
402
|
--max-warnings=<n> exit 1 when warnings exceed n
|
|
403
|
+
--baseline=<path> report only findings not recorded in this file
|
|
404
|
+
--write-baseline=<path> record this run's findings to the file and exit 0
|
|
306
405
|
--snapshot=<path> use a different API snapshot
|
|
307
406
|
--refresh rebuild the vendored API snapshot (the only networked command)
|
|
407
|
+
--refresh-ref=<ref> branch or tag of the mirror to rebuild from (default: live)
|
|
408
|
+
--force let --refresh write an older client build than the vendored one
|
|
308
409
|
--rules print the rule table with its sources
|
|
410
|
+
--version the version, then the snapshot patch and build it runs against
|
|
309
411
|
```
|
|
310
412
|
|
|
311
413
|
## Where the data comes from
|
|
312
414
|
|
|
313
|
-
`data/api-snapshot.json` is built from Blizzard's generated API documentation, mirrored at [Gethe/wow-ui-source](https://github.com/Gethe/wow-ui-source)
|
|
415
|
+
`data/api-snapshot.json` is built from Blizzard's generated API documentation, mirrored at [Gethe/wow-ui-source](https://github.com/Gethe/wow-ui-source). The current snapshot is patch **12.1.5, build 69594**, rebuilt from that mirror's `12.1.5` tag, and carries **10,250 documented functions and 760 structures**: 20 with `SecretReturns = true`, 314 conditionally secret, and per-field `NeverSecret` markers on 20 structures. It also carries the widget markers the 12.1.5 rules read: `ChecksForbiddenAspects` and `IsProtectedFunction`, keyed by widget system because method names collide across widget types.
|
|
416
|
+
|
|
417
|
+
This is the half of the release with no new rules in it and it still changes results. WSL006 and WSL007 consult the snapshot before they fire, so an addon that has already adopted a 12.1.5 API was being analysed against a documentation set that had never heard of the symbol:
|
|
418
|
+
|
|
419
|
+
```
|
|
420
|
+
$ npx wow-secret-lint --strict --snapshot=./api-snapshot-1.4.2.json Adopter.lua
|
|
421
|
+
0 errors, 0 warnings
|
|
422
|
+
|
|
423
|
+
$ npx wow-secret-lint --strict Adopter.lua
|
|
424
|
+
Adopter.lua:2:24 error WSL006 secret value passed to math.clamp(), which is documented SecretArguments = "AllowedWhenUntainted" and addon code is always tainted: 'hp' derives from UnitHealth() (SecretReturns=true)
|
|
425
|
+
Adopter.lua:3:29 error WSL006 secret value passed to C_Intl.ToUpper(), which is documented SecretArguments = "AllowedWhenUntainted" and addon code is always tainted: derives from UnitSpellTargetName() (SecretReturns=true)
|
|
426
|
+
Adopter.lua:4:22 error WSL006 secret value passed to string.startswith(), which is documented SecretArguments = "AllowedWhenUntainted" and addon code is always tainted: derives from UnitSpellTargetName() (SecretReturns=true)
|
|
427
|
+
3 errors, 0 warnings
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
The refresh also picked up a new conditional marker with no code change needed: `table.count`, `table.getcountinfo` and `table.isempty` carry `SecretWhenLuaTableHasSecretKeys`, so `--conditional=warn` reports them like any other `SecretWhen*` API.
|
|
431
|
+
|
|
432
|
+
Every count on this page comes out of the snapshot itself:
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
node -p "const s=require('./data/api-snapshot.json'); [s.patch, s.build, s.functionCount, s.structureCount, s.secretReturnCount, s.conditionalCount, s.annotatedStructureCount].join(' ')"
|
|
436
|
+
12.1.5 69594 10250 760 20 314 20
|
|
437
|
+
```
|
|
314
438
|
|
|
315
439
|
It is parsed with `luaparse`, not regexed, so nested tables and multi-line entries cannot skew it. Rebuild it any time:
|
|
316
440
|
|
|
@@ -320,7 +444,22 @@ npx wow-secret-lint --refresh
|
|
|
320
444
|
|
|
321
445
|
A scheduled workflow in this repo does that weekly and opens a pull request when the docs move.
|
|
322
446
|
|
|
323
|
-
The
|
|
447
|
+
The mirror tags a client build before it moves its `live` branch, which is how 12.1.5 was picked up here on the day it shipped:
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
npx wow-secret-lint --refresh --refresh-ref=12.1.5
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
The snapshot records the patch and build it came from, read off the mirror's own commit message, and `--refresh` will not walk it backwards onto an older build without `--force`. Point it at a stale ref and it says so and leaves the file alone:
|
|
454
|
+
|
|
455
|
+
```
|
|
456
|
+
$ npx wow-secret-lint --refresh --refresh-ref=12.1.0
|
|
457
|
+
rebuilding API snapshot from Gethe/wow-ui-source@12.1.0 ...
|
|
458
|
+
wow-secret-lint: @12.1.0 is still 12.1.0 (build 69587), older than the vendored 12.1.5
|
|
459
|
+
(build 69594); left the snapshot alone. Pass --force to write it anyway.
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
The 12.1 aura and identity secrecy is deliberately **not** part of the snapshot: Blizzard's generated documentation carries no marker for it, only the patch notes describe it, so the WSL012/WSL013 API lists live in `src/rules.mjs` with the source quoted, and `--patch` gates them. The 12.1.5 rules are the other way round. Blizzard does mark those in the generated docs, so WSL019 and WSL020 read their method lists out of the snapshot and WSL021 reads the six `IsProtectedFunction` cooldown methods out of it; only the question of *which objects* carry the aspects lives in `src/rules.mjs`. There is one snapshot, and `--snapshot`/`--refresh` behave the same under every patch.
|
|
324
463
|
|
|
325
464
|
## Limitations and non-goals
|
|
326
465
|
|
|
@@ -328,9 +467,9 @@ The 12.1 aura and identity secrecy is deliberately **not** part of the snapshot:
|
|
|
328
467
|
- **No runtime component and no in-game addon.** This is a build-time linter.
|
|
329
468
|
- **No Classic support.** Classic has no secret values, so `--game=classic` exits 0 immediately.
|
|
330
469
|
- **It cannot diagnose a taint-spread crash.** Some of the worst reports look like `attempt to perform arithmetic on local 'textHeight' (a secret number value, while execution tainted by 'YourAddon')` where every frame of the stack is a Blizzard file. Your addon tainted execution, and then Blizzard's code did the arithmetic. There is no forbidden operation in your Lua to point at, so this tool reports nothing. Running it over the real [aura-questor](https://github.com/lucascodev/aura-questor) source, which has exactly that open report, gives a clean run across 114 files. The regression fixture named after that issue reproduces the shape of the trace, not a finding in their code.
|
|
331
|
-
- **
|
|
470
|
+
- **Taint does not cross files.** It follows plain assignment, table field stores, the return value of a file-local function, and one level of intra-file call-argument passing. A secret that leaves through a global and comes back in another file is not tracked. Widget types do cross files: see the next point.
|
|
332
471
|
- **No LuaJIT or Lua 5.4 syntax.** Files are parsed as Lua 5.1. WoW accepts a semicolon after `break`, which stock 5.1 does not, so a file that fails on 5.1 gets one retry under the 5.2 grammar before it is reported as a parse error.
|
|
333
|
-
- **Method calls are not resolved to a widget type**, so WSL006 only applies to plain and namespaced calls (`UnitHealth(...)`, `C_CVar.SetCVar(...)`), never to `frame:SetText(...)`. WSL017
|
|
472
|
+
- **Method calls are not resolved to a widget type**, so WSL006 only applies to plain and namespaced calls (`UnitHealth(...)`, `C_CVar.SetCVar(...)`), never to `frame:SetText(...)`. WSL017 and WSL019-WSL021 are the exceptions, and their typing is deliberately narrow: a local counts as an AuraContainer/AuraButton only when it comes straight from `CreateFrame` with a literal type string or from an `initializeFrame` callback, an animation group carries the 12.1.5 aspects only from the `AddPandemic*Animation` call that registers it, and a cooldown is protected only where the file names Blizzard's frame, builds one from a secure template, or receives one through a metatable hook. Those types do follow the addon across files, in `.toc` load order: a widget stored in a global, in `_G[...]`, or in the addon's private table (`local _, ns = ...` or `local ns = select(2, ...)`) is known to every file loaded after the one that created it, whatever local name each file gives that table. A widget passed through a function argument, returned from a function, or stored in any other table is still only known inside its own file, and a value dispatched through a proxy that takes the method name as a string is not tracked at all.
|
|
334
473
|
- **XML is followed for `<Script>`/`<Include>` and checked only for removed templates.** WSL014 reads `inherits` attributes in every `.xml` the `.toc` pulls in, so a declarative aura header is caught. Nothing else in the markup is analysed: widget scripts written inline in XML are not parsed as Lua.
|
|
335
474
|
- **`string.format` output is not tracked as secret.** The wiki names it as the sanctioned way to render a secret, and following it would flood every `SetText` call site. The trade is a known blind spot on `#string.format(...)`.
|
|
336
475
|
- A `.toc` listing a file that is not on disk warns and keeps going. A file that will not parse is reported and the run exits 2.
|
|
@@ -341,7 +480,7 @@ The 12.1 aura and identity secrecy is deliberately **not** part of the snapshot:
|
|
|
341
480
|
npm test
|
|
342
481
|
```
|
|
343
482
|
|
|
344
|
-
|
|
483
|
+
249 tests. The suite covers every rule, the guard forms, the permitted-operations negative cases, the three reporters, the CLI surface, one violating and one clean fixture per 12.1 and 12.1.5 rule (`test/fixtures/rules-121/`, `test/fixtures/rules-1215/`), recorded baselines proving `--patch=12.0` reproduces the v1.2.0 output and `--patch=12.1` the v1.4.2 output byte for byte over the whole fixture corpus (`test/fixtures/patch/`), a six-file addon exercising widget typing across files in load order (`test/fixtures/cross-file/`), the `--baseline` round trip, and eight regression fixtures reconstructed from real shipped traces:
|
|
345
484
|
|
|
346
485
|
| Fixture | Issue |
|
|
347
486
|
| --- | --- |
|
package/action/index.mjs
CHANGED
|
@@ -8,7 +8,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
8
8
|
import process from 'node:process';
|
|
9
9
|
import { lintPaths, VERSION } from '../src/index.mjs';
|
|
10
10
|
import { format, FORMATS, counts } from '../src/report.mjs';
|
|
11
|
-
import { RULE_IDS, PATCHES, DEFAULT_PATCH } from '../src/rules.mjs';
|
|
11
|
+
import { RULE_IDS, PATCHES, DEFAULT_PATCH, patchList } from '../src/rules.mjs';
|
|
12
|
+
import { applyBaselineFile } from '../src/baseline.mjs';
|
|
12
13
|
|
|
13
14
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
14
15
|
|
|
@@ -46,6 +47,7 @@ function fail(message) {
|
|
|
46
47
|
const paths = splitArgs(input('path', '.'));
|
|
47
48
|
const extra = splitArgs(input('args', ''));
|
|
48
49
|
const fmt = input('format', 'github');
|
|
50
|
+
let baselinePath = input('baseline', '');
|
|
49
51
|
|
|
50
52
|
if (!FORMATS.includes(fmt)) fail(`unknown format "${fmt}" (expected one of: ${FORMATS.join(', ')})`);
|
|
51
53
|
|
|
@@ -94,6 +96,9 @@ for (let i = 0; i < extra.length; i++) {
|
|
|
94
96
|
case '--snapshot':
|
|
95
97
|
options.snapshotPath = val();
|
|
96
98
|
break;
|
|
99
|
+
case '--baseline':
|
|
100
|
+
baselinePath = val();
|
|
101
|
+
break;
|
|
97
102
|
default:
|
|
98
103
|
fail(`unknown value in "args": ${key}`);
|
|
99
104
|
}
|
|
@@ -105,8 +110,8 @@ if (!['off', 'warn', 'error'].includes(options.conditional)) {
|
|
|
105
110
|
if (!['retail', 'classic'].includes(options.game)) {
|
|
106
111
|
fail(`unknown --game "${options.game}" (expected retail or classic)`);
|
|
107
112
|
}
|
|
108
|
-
if (!PATCHES.includes(options.patch)) {
|
|
109
|
-
fail(`unknown patch "${options.patch}" (expected ${
|
|
113
|
+
if (options.patch !== 'auto' && !PATCHES.includes(options.patch)) {
|
|
114
|
+
fail(`unknown patch "${options.patch}" (expected ${patchList()}, or auto)`);
|
|
110
115
|
}
|
|
111
116
|
for (const id of options.disable) {
|
|
112
117
|
if (!RULE_IDS.includes(id)) fail(`unknown rule id "${id}" in --disable`);
|
|
@@ -129,6 +134,8 @@ try {
|
|
|
129
134
|
fail(err.message);
|
|
130
135
|
}
|
|
131
136
|
|
|
137
|
+
if (baselinePath) await applyBaselineFile(merged, baselinePath).catch((err) => fail(err.message));
|
|
138
|
+
|
|
132
139
|
process.stdout.write(format(merged, fmt) + '\n');
|
|
133
140
|
|
|
134
141
|
const { errors, warnings } = counts(merged);
|
|
@@ -142,10 +149,11 @@ const rows = merged.findings
|
|
|
142
149
|
|
|
143
150
|
await summary(
|
|
144
151
|
`## wow-secret-lint ${VERSION}\n\n` +
|
|
145
|
-
`${merged.filesScanned} Lua file(s) scanned against ${merged.snapshot.functionCount ?? 0} documented APIs ` +
|
|
152
|
+
`${merged.filesScanned} Lua file(s) scanned on the ${merged.patch} surface against ${merged.snapshot.functionCount ?? 0} documented APIs ` +
|
|
146
153
|
`(${merged.snapshot.secretReturnCount ?? 0} with \`SecretReturns=true\`).\n\n` +
|
|
147
154
|
`**${errors} error(s), ${warnings} warning(s)**` +
|
|
148
155
|
(merged.parseErrors.length ? `, ${merged.parseErrors.length} parse error(s)` : '') +
|
|
156
|
+
(merged.baseline ? `, ${merged.baseline.suppressed} suppressed by baseline` : '') +
|
|
149
157
|
'\n\n' +
|
|
150
158
|
(rows
|
|
151
159
|
? `| Location | Severity | Rule | Message |\n| --- | --- | --- | --- |\n${rows}\n` +
|
package/action.yml
CHANGED
|
@@ -11,9 +11,13 @@ inputs:
|
|
|
11
11
|
required: false
|
|
12
12
|
default: '.'
|
|
13
13
|
patch:
|
|
14
|
-
description: 'Patch surface to check: 12.1 (default)
|
|
14
|
+
description: 'Patch surface to check: 12.1.5 (default), 12.1, 12.0, or auto to read the addon .toc.'
|
|
15
15
|
required: false
|
|
16
|
-
default: '12.1'
|
|
16
|
+
default: '12.1.5'
|
|
17
|
+
baseline:
|
|
18
|
+
description: 'Path to a baseline recorded with --write-baseline; only findings not in it are reported.'
|
|
19
|
+
required: false
|
|
20
|
+
default: ''
|
|
17
21
|
args:
|
|
18
22
|
description: 'Extra CLI arguments, e.g. "--conditional=warn --disable=WSL011".'
|
|
19
23
|
required: false
|