Introduce CAA record
continuous-integration/drone/push Build is passing Details

This commit is contained in:
nemunaire 2023-02-24 16:51:10 +01:00
parent cd29ec9eb0
commit 99689080de
9 changed files with 1008 additions and 2 deletions

269
services/caa.go Normal file
View File

@ -0,0 +1,269 @@
// Copyright or © or Copr. happyDNS (2023)
//
// contact@happydomain.org
//
// This software is a computer program whose purpose is to provide a modern
// interface to interact with DNS systems.
//
// This software is governed by the CeCILL license under French law and abiding
// by the rules of distribution of free software. You can use, modify and/or
// redistribute the software under the terms of the CeCILL license as
// circulated by CEA, CNRS and INRIA at the following URL
// "http://www.cecill.info".
//
// As a counterpart to the access to the source code and rights to copy, modify
// and redistribute granted by the license, users are provided only with a
// limited warranty and the software's author, the holder of the economic
// rights, and the successive licensors have only limited liability.
//
// In this respect, the user's attention is drawn to the risks associated with
// loading, using, modifying and/or developing or reproducing the software by
// the user in light of its specific status of free software, that may mean
// that it is complicated to manipulate, and that also therefore means that it
// is reserved for developers and experienced professionals having in-depth
// computer knowledge. Users are therefore encouraged to load and test the
// software's suitability as regards their requirements in conditions enabling
// the security of their systems and/or data to be ensured and, more generally,
// to use and operate it in the same conditions as regards security.
//
// The fact that you are presently reading this means that you have had
// knowledge of the CeCILL license and that you accept its terms.
package svcs
import (
"net/url"
"strings"
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/miekg/dns"
"git.happydns.org/happyDomain/model"
"git.happydns.org/happyDomain/services/common"
"git.happydns.org/happyDomain/utils"
)
type CAAParameter struct {
Tag string
Value string
}
type CAAIssueValue struct {
IssuerDomainName string
Parameters []CAAParameter
}
func parseIssueValue(value string) (ret CAAIssueValue) {
tmp := strings.Split(value, ";")
ret.IssuerDomainName = strings.TrimSpace(tmp[0])
for _, param := range tmp[1:] {
tmpparam := strings.SplitN(param, "=", 2)
ret.Parameters = append(ret.Parameters, CAAParameter{
Tag: strings.TrimSpace(tmpparam[0]),
Value: strings.TrimSpace(tmpparam[1]),
})
}
return
}
func (v *CAAIssueValue) String() string {
var b strings.Builder
b.WriteString(v.IssuerDomainName)
if len(v.Parameters) > 0 {
b.WriteString(";")
for _, param := range v.Parameters {
b.WriteString(param.Tag)
b.WriteString("=")
b.WriteString(param.Value)
}
}
return b.String()
}
type CAA struct {
DisallowIssue bool
Issue []CAAIssueValue
DisallowWildcardIssue bool
IssueWild []CAAIssueValue
Iodef []*common.URL
}
func (s *CAA) GetNbResources() int {
nb := 0
if s.DisallowIssue {
nb += 1
} else {
nb += len(s.Issue)
if s.DisallowWildcardIssue {
nb += 1
} else {
nb += len(s.IssueWild)
}
}
return nb + len(s.Iodef)
}
func (s *CAA) GenComment(origin string) string {
if s.DisallowIssue {
return "Certificate issuance disallowed"
} else {
var issuance []string
for _, iss := range s.Issue {
issuance = append(issuance, iss.IssuerDomainName)
}
ret := strings.Join(issuance, ", ")
if s.DisallowWildcardIssue {
if ret != "" {
ret += "; "
}
ret += "Wildcard issuance disallowed"
} else if len(s.IssueWild) > 0 {
if ret != "" {
ret += "; wildcard: "
}
var issuancew []string
for _, iss := range s.IssueWild {
issuancew = append(issuancew, iss.IssuerDomainName)
}
ret += strings.Join(issuancew, ", ")
}
return ret
}
}
func (s *CAA) GenRRs(domain string, ttl uint32, origin string) (rrs models.Records) {
if s.DisallowIssue {
rc := utils.NewRecordConfig(domain, "CAA", ttl, origin)
rc.CaaFlag = 0
rc.CaaTag = "issue"
rc.SetTarget(";")
rrs = append(rrs, rc)
} else {
for _, issue := range s.Issue {
rc := utils.NewRecordConfig(domain, "CAA", ttl, origin)
rc.CaaFlag = 0
rc.CaaTag = "issue"
rc.SetTarget(issue.String())
rrs = append(rrs, rc)
}
if s.DisallowWildcardIssue {
rc := utils.NewRecordConfig(domain, "CAA", ttl, origin)
rc.CaaFlag = 0
rc.CaaTag = "issuewild"
rc.SetTarget(";")
rrs = append(rrs, rc)
} else {
for _, issue := range s.IssueWild {
rc := utils.NewRecordConfig(domain, "CAA", ttl, origin)
rc.CaaFlag = 0
rc.CaaTag = "issuewild"
rc.SetTarget(issue.String())
rrs = append(rrs, rc)
}
}
}
if len(s.Iodef) > 0 {
for _, iodef := range s.Iodef {
rc := utils.NewRecordConfig(domain, "CAA", ttl, origin)
rc.CaaFlag = 0
rc.CaaTag = "iodef"
rc.SetTarget(iodef.String())
rrs = append(rrs, rc)
}
}
return
}
func caa_analyze(a *Analyzer) (err error) {
pool := map[string]*CAA{}
for _, record := range a.SearchRR(AnalyzerRecordFilter{Type: dns.TypeCAA}) {
domain := record.NameFQDN
if record.Type == "CAA" {
if _, ok := pool[domain]; !ok {
pool[domain] = &CAA{}
}
analyzed := pool[domain]
if record.CaaTag == "issue" {
value := record.GetTargetField()
if value == ";" {
analyzed.DisallowIssue = true
} else {
analyzed.Issue = append(analyzed.Issue, parseIssueValue(value))
}
}
if record.CaaTag == "issuewild" {
value := record.GetTargetField()
if value == ";" {
analyzed.DisallowWildcardIssue = true
} else {
analyzed.IssueWild = append(analyzed.IssueWild, parseIssueValue(value))
}
}
if record.CaaTag == "iodef" {
if u, err := url.Parse(record.GetTargetField()); err != nil {
continue
} else {
tmp := common.URL(*u)
analyzed.Iodef = append(analyzed.Iodef, &tmp)
}
}
err = a.UseRR(record, domain, pool[domain])
if err != nil {
return
}
}
}
return nil
}
func init() {
RegisterService(
func() happydns.Service {
return &CAA{}
},
caa_analyze,
ServiceInfos{
Name: "Certification Authority Authorization (CAA)",
Description: "Indicate to certificate authorities whether they are authorized to issue digital certificates for a particular domain name.",
Categories: []string{
"tls",
},
Restrictions: ServiceRestrictions{
Single: true,
NeedTypes: []uint16{
dns.TypeCAA,
},
},
},
1,
)
}

59
services/common/url.go Normal file
View File

@ -0,0 +1,59 @@
// Copyright or © or Copr. happyDNS (2023)
//
// contact@happydomain.org
//
// This software is a computer program whose purpose is to provide a modern
// interface to interact with DNS systems.
//
// This software is governed by the CeCILL license under French law and abiding
// by the rules of distribution of free software. You can use, modify and/or
// redistribute the software under the terms of the CeCILL license as
// circulated by CEA, CNRS and INRIA at the following URL
// "http://www.cecill.info".
//
// As a counterpart to the access to the source code and rights to copy, modify
// and redistribute granted by the license, users are provided only with a
// limited warranty and the software's author, the holder of the economic
// rights, and the successive licensors have only limited liability.
//
// In this respect, the user's attention is drawn to the risks associated with
// loading, using, modifying and/or developing or reproducing the software by
// the user in light of its specific status of free software, that may mean
// that it is complicated to manipulate, and that also therefore means that it
// is reserved for developers and experienced professionals having in-depth
// computer knowledge. Users are therefore encouraged to load and test the
// software's suitability as regards their requirements in conditions enabling
// the security of their systems and/or data to be ensured and, more generally,
// to use and operate it in the same conditions as regards security.
//
// The fact that you are presently reading this means that you have had
// knowledge of the CeCILL license and that you accept its terms.
package common
import (
"encoding/json"
"net/url"
)
type URL url.URL
func (u URL) String() string {
tmp := url.URL(u)
return (&tmp).String()
}
func (u URL) MarshalJSON() ([]byte, error) {
tmp := url.URL(u)
return json.Marshal((&tmp).String())
}
func (u *URL) UnmarshalJSON(b []byte) error {
var v string
if err := json.Unmarshal(b, &v); err != nil {
return err
}
tmp, err := url.Parse(v)
*u = URL(*tmp)
return err
}

View File

@ -10,7 +10,7 @@ export async function listServiceSpecs(): Promise<Record<string, ServiceInfos>>
}
export async function getServiceSpec(ssid: string): Promise<ServiceSpec> {
if (ssid == "string") {
if (ssid == "string" || ssid == "common.URL") {
return Promise.resolve(<ServiceSpec>{fields: null});
} else {
const res = await fetch(`/api/service_specs/` + ssid, {

View File

@ -2,6 +2,7 @@
import { createEventDispatcher } from 'svelte';
import BasicInput from '$lib/components/resources/basic.svelte';
import CAAForm from '$lib/components/resources/CAA.svelte';
import MapInput from '$lib/components/resources/map.svelte';
import ObjectInput from '$lib/components/resources/object.svelte';
import RawInput from '$lib/components/resources/raw.svelte';
@ -45,6 +46,16 @@
type={sanitizeType(type)}
bind:value={value}
/>
{:else if type == "svcs.CAA"}
<CAAForm
edit={edit || editToolbar}
{index}
{readonly}
{specs}
bind:value={value}
on:delete-this-service={(event) => dispatch("delete-this-service", event.detail)}
on:update-this-service={(event) => dispatch("update-this-service", event.detail)}
/>
{:else if typeof value === 'object' || Array.isArray(specs)}
<ObjectInput
{edit}

View File

@ -0,0 +1,68 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import {
Button,
Icon,
Input,
} from 'sveltestrap';
const dispatch = createEventDispatcher();
export let newone = false;
export let readonly = false;
export let value: any;
let kind: string = "web";
let url: string;
$: if (value) switch (value.split(":")[0]) {
case "mailto":
kind = "mail";
url = value.split(":")[1];
break;
default:
kind = "web";
url = value;
}
function updateValue(url) {
if (kind == "mail") {
value = "mailto:" + url;
} else {
value = url;
}
}
$: updateValue(url);
</script>
<div class="d-flex gap-2 mb-2">
<Input type="select" bind:value={kind}>
<option value="mail">Mail</option>
<option value="web">Webhook</option>
</Input>
<Input type={kind == "mail" ? "email" : "text"} bind:value={url} />
{#if !newone}
<Button
type="button"
color="danger"
outline
on:click={() => dispatch("delete-iodef")}
>
<Icon name="trash" />
</Button>
{:else}
<Button
color="success"
outline
type="button"
disabled={!value}
on:click={() => {dispatch("add-iodef", value); value = { }}}
>
<Icon name="plus" />
</Button>
{/if}
</div>

View File

@ -0,0 +1,106 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import {
Badge,
Button,
FormGroup,
Icon,
Input,
} from 'sveltestrap';
import { issuers, rev_issuers } from './CAA-issuers';
const dispatch = createEventDispatcher();
export let edit = false;
export let index: string;
export let newone = false;
export let readonly = false;
export let value: any;
$: if (!value) value = { };
</script>
<div class="d-flex gap-2 mb-2">
{#if (newone && value.IssuerDomainName == "") || rev_issuers[value.IssuerDomainName]}
<Input type="select" name="select" id="exampleSelect" readonly={readonly} bind:value={value.IssuerDomainName}>
{#each Object.keys(issuers) as issuer}
<option value={issuers[issuer][0]}>{issuer}</option>
{/each}
<option value={" "}>Autre</option>
</Input>
{:else}
<Input type="text" bind:value={value.IssuerDomainName} />
{/if}
{#if !newone}
<Button
type="button"
color="danger"
outline
on:click={() => dispatch("delete-issuer")}
>
<Icon name="trash" />
</Button>
{:else}
<Button
color="success"
outline
type="button"
disabled={!value}
on:click={() => {dispatch("add-issuer", value); value = { }}}
>
<Icon name="plus" />
</Button>
{/if}
</div>
{#if !newone}
<div class="d-flex align-items-center">
{#if value.Parameters}
{#each value.Parameters as parameter, k}
<Badge color="info" class="me-1">
{#if parameter.edit}
<form
class="d-flex align-items-center gap-1"
on:submit|preventDefault={() => parameter.edit = false}
>
<Input size="sm" placeholder="key" bind:value={parameter.Tag} />
=
<Input size="sm" placeholder="value" bind:value={parameter.Value} />
<Button
type="submit"
color="success"
size="sm"
>
<Icon
name="check"
/>
</Button>
</form>
{:else}
<span
on:dblclick={() => parameter.edit = true}
>
{parameter.Tag}={parameter.Value}
</span>
<span
role="button"
on:click={() => {value.Parameters.splice(k, 1); value = value;}}
>
<Icon
name="x-circle-fill"
/>
</span>
{/if}
</Badge>
{/each}
{/if}
<span
class="badge bg-primary"
role="button"
on:click={() => {if (value.Parameters == null) value.Parameters = []; value.Parameters.push({Tag:"", Value: "", edit: true}); value = value;}}
>
<Icon name="plus" /> Add parameter
</span>
</div>
{/if}

View File

@ -0,0 +1,354 @@
export const issuers = {
"Actalis S.p.A.": [
"actalis.it"
],
"Amazon Trust Services LLC": [
"amazon.com",
"amazontrust.com",
"awstrust.com",
"amazonaws.com",
"aws.amazon.com"
],
"ANF AC": [
"anf.es"
],
"Asseco Data Systems (formely Certum)": [
"certum.pl",
"certum.eu"
],
"Apple": [
"pki.apple.com"
],
"Atos": [
"atos.net"
],
"Beijing CA": [
"bjca.cn"
],
"Buypass AS": [
"buypass.com",
"buypass.no"
],
"CAcert": [
"cacert.org"
],
"AC Camerfirma S.A.": [
"camerfirma.com"
],
"CATCert": [
"aoc.cat"
],
"Fastly/Certainly": [
"certainly.com"
],
"Certinomis": [
"www.certinomis.com",
"www.certinomis.fr"
],
"Dhimyotis": [
"certigna.fr"
],
"Certizen": [
"ecert.gov.hk",
"hongkongpost.gov.hk"
],
"certSIGN": [
"certsign.ro"
],
"China Financial CA (CFCA)": [
"cfca.com.cn"
],
"China Internet Network Information Center (CNNIC)": [
"cnnic.cn"
],
"Chunghwa Telecom": [
"pki.hinet.net",
"tls.hinet.net",
"eca.hinet.net",
"epki.com.tw",
"publicca.hinet.net"
],
"ComSign Ltd": [
"comsign.co.il",
"comsign.co.uk",
"comsigneurope.com"
],
"Cybertrust Japan": [
"cybertrust.ne.jp",
"jcsinc.co.jp"
],
"Deutsche Telekom Security": [
"telesec.de"
],
"DFN-PKI": [
"pki.dfn.de",
"dfn.de"
],
"DigiCert": [
"digicert.com",
"symantec.com",
"geotrust.com",
"rapidssl.com",
"thawte.com",
"digitalcertvalidation.com",
"volusion.digitalcertvalidation.com",
"stratossl.digitalcertvalidation.com",
"intermediatecertificate.digitalcertvalidation.com",
"1and1.digitalcertvalidation.com"
],
"DigitalSign CD": [
"digitalsign.pt"
],
"Disig, a.s.": [
"disig.sk"
],
"DocuSign": [
"docusign.fr"
],
"D-Trust GmbH": [
"dtrust.de",
"d-trust.de",
"dtrust.net",
"d-trust.net"
],
"DigitalTrust": [
"digitaltrust.ae"
],
"EDICOM": [
"edicomgroup.com"
],
"eMudhra Technologies Limited": [
"emsign.com"
],
"Entrust": [
"entrust.net",
"affirmtrust.com"
],
"E-TUGRA Inc.": [
"e-tugra.com",
"e-tugra.com.tr",
"etugra.com",
"etugra.com.tr"
],
"AC Firmaprofesional CIF": [
"firmaprofesional.com"
],
"Guang Dong CA Co.": [
"gdca.com.cn"
],
"GlobalSign": [
"globalsign.com"
],
"Global Trust": [
"globaltrust.eu"
],
"GoDaddy Inc.": [
"godaddy.com",
"starfieldtech.com"
],
"Google Trust Services (GTS)": [
"pki.goog",
"google.com"
],
"ACCV (Spain gov)": [
"accv.es"
],
"FNMT (Spain gov)": [
"fnmt.es"
],
"Agence Nationale de Certification Electronique (Tunisia gov)": [
"tuntrust.tn"
],
"GRCA": [
"gca.nat.gov.tw"
],
"HARICA": [
"harica.gr"
],
"Hongkong Post": [
"ecert.gov.hk",
"hongkongpost.gov.hk"
],
"IdenTrust": [
"identrust.com",
"www.identrust.com"
],
"iTrusChina Co.": [
"itrus.com.cn",
"itrus.cn"
],
"IZENPE S.A.": [
"izenpe.com",
"izenpe.eus"
],
"Japan Registry Services": [
"jprs.co.jp"
],
"Kamu Sertifikasyon Merkezi": [
"kamusm.gov.tr"
],
"KPN Corporate Market BV": [
"kpn.com"
],
"Krajowa Izba Rozliczeniowa S.A. (KIR)": [
"elektronicznypodpis.pl"
],
"LAWtrust": [
"lawtrust.co.za"
],
"Let's Encrypt": [
"letsencrypt.org"
],
"Logius PKIoverheid": [
"logius.nl"
],
"Microsec Ltd.": [
"e-szigno.hu"
],
"Microsoft": [
"microsoft.com"
],
"Microsoft IT": [
"ssladmin.microsoft.com"
],
"MSC Trustgate": [
"msctrustgate.com"
],
"National Center for Digital Certification (NCDC)": [
"ncdc.gov.sa"
],
"NAVER Business Platform Corp.": [
"certificate.naver.com"
],
"NetLock Kft.": [
"netlock.hu",
"netlock.net",
"netlock.eu"
],
"Networking4all": [
"trustproviderbv.digitalcertvalidation.com"
],
"Network Solutions LLC": [
"networksolutions.com",
"web.com"
],
"OISTE Foundation": [
"wisekey.com",
"hightrusted.com",
"certifyid.com",
"oiste.org"
],
"Open Access Technology International": [
"oati.com"
],
"Prvni certifikacni autorita, a.s.": [
"ica.cz"
],
"PKIoverheid": [
"www.pkioverheid.nl"
],
"QuoVadis": [
"quovadisglobal.com",
"digicert.com",
"digicert.ne.jp",
"cybertrust.ne.jp",
"symantec.com",
"thawte.com",
"geotrust.com",
"rapidssl.com",
"digitalcertvalidation.com"
],
"SECOM Trust Systems": [
"secomtrust.net"
],
"Sectigo": [
"sectigo.com",
"comodo.com",
"comodoca.com",
"usertrust.com",
"trust-provider.com"
],
"Shanghai Electronic Certification Authority Co. Ltd": [
"sheca.com",
"imtrust.cn",
"wwwtrust.cn"
],
"SK ID Solutions AS": [
"skidsolutions.eu"
],
"SSL Corporation": [
"ssl.com"
],
"Skaitmeninio sertifikavimo centras (SSC)": [
"ssc.lt"
],
"SwissSign AG": [
"swisssign.com",
"swisssign.net",
"swissign.com",
"swisssign.ch",
"swisssign.li",
"swissign.li",
"swisssign.org",
"swisssign.biz",
"swisstsa.ch",
"swisstsa.li",
"digitalid.ch",
"digital-id.ch",
"zert.ch",
"rootsigning.com",
"root-signing.ch",
"ssl-certificate.ch",
"managed-pki.ch",
"managed-pki.de",
"swissstick.com",
"swisssigner.ch",
"pki-posta.ch",
"pki-poste.ch",
"pki-post.ch",
"trustdoc.ch",
"trustsign.ch",
"swisssigner.com",
"postsuisseid.ch",
"suisseid-service.ch",
"signdemo.com",
"sirb.com"
],
"SecureTrust Corporation": [
"trustwave.com",
"securetrust.com"
],
"TAIWAN-CA Inc. (TWCA)": [
"twca.com.tw"
],
"Telia Finland Oyj": [
"telia.com",
"telia.fi",
"telia.se"
],
"TrustCor Systems": [
"trustcor.ca"
],
"T-Systems Enterprise Services": [
"t-systems.com"
],
"Visa": [
"visa.com"
],
"Zertificon": [
"zertificon.com"
],
"360": [
"browser.360.cn"
]
};
export const rev_issuers: Record<string, string> = { };
for (const issuer in issuers) {
for (const dn of issuers[issuer]) {
rev_issuers[dn] = issuer;
}
}
export default issuers;

View File

@ -0,0 +1,139 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import {
Alert,
Badge,
Button,
FormGroup,
Icon,
Input,
} from 'sveltestrap';
import TableInput from '$lib/components/resources/table.svelte';
import ResourceRawInput from '$lib/components/resources/raw.svelte';
import CAAIssuer from '$lib/components/resources/CAA-issuer.svelte';
import CAAIodef from '$lib/components/resources/CAA-iodef.svelte';
import issuers from './CAA-issuers';
const dispatch = createEventDispatcher();
export let edit = false;
export let index: string;
export let readonly = false;
export let specs: any;
export let value: any;
</script>
<h4>Certificates issuance</h4>
<FormGroup>
<Input id="issuedisabled" type="checkbox" label="Disallow any certificate issuance" bind:checked={value.DisallowIssue} />
</FormGroup>
<h5>
Authorized Issuers
</h5>
{#if !value.DisallowIssue}
<ul>
{#if value.Issue}
{#each value.Issue as issue, k}
<li class="mb-3">
<CAAIssuer
{readonly}
bind:value={value.Issue[k]}
on:delete-issuer={() => {value.Issue.splice(k, 1); value = value;}}
/>
</li>
{/each}
{:else}
<Alert color="warning" fade={false}>
<strong>All issuer authorized.</strong> With those parameters, all issuer can create certificate for this domain and subdomain.
</Alert>
{/if}
{#if !readonly}
<li style:list-style="'+ '">
<CAAIssuer
newone
on:add-issuer={(e) => {if (!value.Issue) value.Issue = []; value.Issue.push(e.detail); value = value;}}
/>
</li>
{/if}
</ul>
{:else}
<Alert color="danger" fade={false}>
<strong>No issuer authorized.</strong> With those parameters, no issuer is allowed to create certificate for this subdomain.
</Alert>
{/if}
<h4>Wildcard certificates issuance</h4>
<FormGroup>
<Input id="wildcardissuedisabled" type="checkbox" label="Disallow wildcard certificate issuance" bind:checked={value.DisallowWildcardIssue} />
</FormGroup>
<h5>
Authorized Issuers
</h5>
{#if !value.DisallowWildcardIssue}
<ul>
{#if value.IssueWild}
{#each value.IssueWild as issue, k}
<li class="mb-3">
<CAAIssuer
{readonly}
bind:value={value.IssueWild[k]}
on:delete-issuer={() => {value.IssueWild.splice(k, 1); value = value;}}
/>
</li>
{/each}
{:else if value.DisallowIssue}
<Alert color="danger" fade={false}>
<strong>No issuer authorized.</strong> With those parameters, no issuer is authorized to create wildcard certificate for this domain and subdomain. But this can be override with the following settings:
</Alert>
{:else if value.Issue}
<Alert color="warning" fade={false}>
<strong>Same as regular certificate issuance.</strong> With those parameters, all issuer authorized for certificate issuance can also create wildcard certificate for this domain and subdomain.
</Alert>
{:else}
<Alert color="warning" fade={false}>
<strong>All issuer authorized.</strong> With those parameters, all issuer can create wildcard certificate for this domain and subdomain.
</Alert>
{/if}
{#if !readonly}
<li style:list-style="'+ '">
<CAAIssuer
newone
on:add-issuer={(e) => {if (!value.IssueWild) value.IssueWild = []; value.IssueWild.push(e.detail); value = value;}}
/>
</li>
{/if}
</ul>
{:else}
<Alert color="danger" fade={false}>
<strong>No wildcard issuer authorized.</strong> With those parameters, no issuer is allowed to create wildcard certificate for this subdomain.
</Alert>
{/if}
<h4>Incident Response</h4>
<p>
How would you want to be contacted in case of violation of the current security policy?
</p>
{#if value.Iodef}
{#each value.Iodef as iodef, k}
<CAAIodef
{readonly}
bind:value={value.Iodef[k]}
/>
{/each}
{/if}
{#if !readonly}
<CAAIodef
newone
/>
{/if}

View File

@ -8,6 +8,6 @@ export function fillUndefinedValues(value: any, spec: Field) {
if (spec.default !== undefined) value[spec.id] = spec.default;
else if (vartype == "[]uint8") value[spec.id] = "";
else if (vartype.startsWith("[]")) value[spec.id] = [];
else if (vartype != "string" && !vartype.startsWith("uint") && !vartype.startsWith("int") && vartype != "net.IP" && vartype != "time.Duration" && vartype != "common.Duration") value[spec.id] = { };
else if (vartype != "string" && !vartype.startsWith("uint") && !vartype.startsWith("int") && vartype != "net.IP" && vartype != "common.URL" && vartype != "time.Duration" && vartype != "common.Duration") value[spec.id] = { };
}
}